void cancel(final HttpServerExchange exchange) { final ProxyConnection connectionAttachment = exchange.getAttachment(CONNECTION); if (connectionAttachment != null) { ClientConnection clientConnection = connectionAttachment.getConnection(); UndertowLogger.PROXY_REQUEST_LOGGER.timingOutRequest( clientConnection.getPeerAddress() + "" + exchange.getRequestURI()); IoUtils.safeClose(clientConnection); } else { UndertowLogger.PROXY_REQUEST_LOGGER.timingOutRequest(exchange.getRequestURI()); } if (exchange.isResponseStarted()) { IoUtils.safeClose(exchange.getConnection()); } else { exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); exchange.endExchange(); } }
@Override public void run() { ProxyConnection connection = provider.getConnection(); if (connection == null) { logger.error("No connection found to send CRLF keep alive" + " with " + provider); return; } ListeningPoint lp = provider.getListeningPoint(connection.getTransport()); if (!(lp instanceof ListeningPointExt)) { logger.error("ListeningPoint is not ListeningPointExt" + "(or is null)"); return; } InetSocketAddress address = connection.getAddress(); try { ((ListeningPointExt) lp) .sendHeartbeat(address.getAddress().getHostAddress(), address.getPort()); } catch (IOException e) { logger.error("Error while sending a heartbeat", e); } }
@Override public void run() { final ClientRequest request = new ClientRequest(); StringBuilder requestURI = new StringBuilder(); if (!clientConnection.getTargetPath().isEmpty() && !clientConnection.getTargetPath().equals("/")) { requestURI.append(clientConnection.getTargetPath()); } if (exchange.isHostIncludedInRequestURI()) { int uriPart = exchange.getRequestURI().indexOf("//"); if (uriPart == -1) { requestURI.append(exchange.getRequestURI()); } else { uriPart = exchange.getRequestURI().indexOf("/", uriPart); requestURI.append(exchange.getRequestURI().substring(uriPart)); } } else { requestURI.append(exchange.getRequestURI()); } String qs = exchange.getQueryString(); if (qs != null && !qs.isEmpty()) { requestURI.append('?'); requestURI.append(qs); } request.setPath(requestURI.toString()).setMethod(exchange.getRequestMethod()); final HeaderMap inboundRequestHeaders = exchange.getRequestHeaders(); final HeaderMap outboundRequestHeaders = request.getRequestHeaders(); copyHeaders(outboundRequestHeaders, inboundRequestHeaders); if (!exchange.isPersistent()) { // just because the client side is non-persistent // we don't want to close the connection to the backend outboundRequestHeaders.put(Headers.CONNECTION, "keep-alive"); } if ("h2c".equals(exchange.getRequestHeaders().getFirst(Headers.UPGRADE))) { // we don't allow h2c upgrade requests to be passed through to the backend exchange.getRequestHeaders().remove(Headers.UPGRADE); outboundRequestHeaders.put(Headers.CONNECTION, "keep-alive"); } for (Map.Entry<HttpString, ExchangeAttribute> entry : requestHeaders.entrySet()) { String headerValue = entry.getValue().readAttribute(exchange); if (headerValue == null || headerValue.isEmpty()) { outboundRequestHeaders.remove(entry.getKey()); } else { outboundRequestHeaders.put(entry.getKey(), headerValue.replace('\n', ' ')); } } final SocketAddress address = exchange.getConnection().getPeerAddress(); final String remoteHost = (address != null && address instanceof InetSocketAddress) ? ((InetSocketAddress) address).getHostString() : "localhost"; request.putAttachment(ProxiedRequestAttachments.REMOTE_HOST, remoteHost); if (reuseXForwarded && request.getRequestHeaders().contains(Headers.X_FORWARDED_FOR)) { // We have an existing header so we shall simply append the host to the existing list final String current = request.getRequestHeaders().getFirst(Headers.X_FORWARDED_FOR); if (current == null || current.isEmpty()) { // It was empty so just add it request.getRequestHeaders().put(Headers.X_FORWARDED_FOR, remoteHost); } else { // Add the new entry and reset the existing header request.getRequestHeaders().put(Headers.X_FORWARDED_FOR, current + "," + remoteHost); } } else { // No existing header or not allowed to reuse the header so set it here request.getRequestHeaders().put(Headers.X_FORWARDED_FOR, remoteHost); } // if we don't support push set a header saying so // this is non standard, and a problem with the HTTP2 spec, but they did not want to listen if (!exchange.getConnection().isPushSupported() && clientConnection.getConnection().isPushSupported()) { request.getRequestHeaders().put(Headers.X_DISABLE_PUSH, "true"); } // Set the protocol header and attachment if (reuseXForwarded && exchange.getRequestHeaders().contains(Headers.X_FORWARDED_PROTO)) { final String proto = exchange.getRequestHeaders().getFirst(Headers.X_FORWARDED_PROTO); request.putAttachment(ProxiedRequestAttachments.IS_SSL, proto.equals("https")); } else { final String proto = exchange.getRequestScheme().equals("https") ? "https" : "http"; request.getRequestHeaders().put(Headers.X_FORWARDED_PROTO, proto); request.putAttachment(ProxiedRequestAttachments.IS_SSL, proto.equals("https")); } // Set the server name if (reuseXForwarded && exchange.getRequestHeaders().contains(Headers.X_FORWARDED_SERVER)) { final String hostName = exchange.getRequestHeaders().getFirst(Headers.X_FORWARDED_SERVER); request.putAttachment(ProxiedRequestAttachments.SERVER_NAME, hostName); } else { final String hostName = exchange.getHostName(); request.getRequestHeaders().put(Headers.X_FORWARDED_SERVER, hostName); request.putAttachment(ProxiedRequestAttachments.SERVER_NAME, hostName); } if (!exchange.getRequestHeaders().contains(Headers.X_FORWARDED_HOST)) { final String hostName = exchange.getHostName(); if (hostName != null) { request.getRequestHeaders().put(Headers.X_FORWARDED_HOST, hostName); } } // Set the port if (reuseXForwarded && exchange.getRequestHeaders().contains(Headers.X_FORWARDED_PORT)) { try { int port = Integer.parseInt(exchange.getRequestHeaders().getFirst(Headers.X_FORWARDED_PORT)); request.putAttachment(ProxiedRequestAttachments.SERVER_PORT, port); } catch (NumberFormatException e) { int port = exchange.getConnection().getLocalAddress(InetSocketAddress.class).getPort(); request.getRequestHeaders().put(Headers.X_FORWARDED_PORT, port); request.putAttachment(ProxiedRequestAttachments.SERVER_PORT, port); } } else { int port = exchange.getConnection().getLocalAddress(InetSocketAddress.class).getPort(); request.getRequestHeaders().put(Headers.X_FORWARDED_PORT, port); request.putAttachment(ProxiedRequestAttachments.SERVER_PORT, port); } SSLSessionInfo sslSessionInfo = exchange.getConnection().getSslSessionInfo(); if (sslSessionInfo != null) { X509Certificate[] peerCertificates; try { peerCertificates = sslSessionInfo.getPeerCertificateChain(); if (peerCertificates.length > 0) { request.putAttachment( ProxiedRequestAttachments.SSL_CERT, Certificates.toPem(peerCertificates[0])); } } catch (SSLPeerUnverifiedException | CertificateEncodingException | RenegotiationRequiredException e) { // ignore } request.putAttachment( ProxiedRequestAttachments.SSL_CYPHER, sslSessionInfo.getCipherSuite()); request.putAttachment( ProxiedRequestAttachments.SSL_SESSION_ID, sslSessionInfo.getSessionId()); } if (rewriteHostHeader) { InetSocketAddress targetAddress = clientConnection.getConnection().getPeerAddress(InetSocketAddress.class); request .getRequestHeaders() .put(Headers.HOST, targetAddress.getHostString() + ":" + targetAddress.getPort()); request .getRequestHeaders() .put(Headers.X_FORWARDED_HOST, exchange.getRequestHeaders().getFirst(Headers.HOST)); } if (log.isDebugEnabled()) { log.debugf( "Sending request %s to target %s for exchange %s", request, remoteHost, exchange); } clientConnection .getConnection() .sendRequest( request, new ClientCallback<ClientExchange>() { @Override public void completed(final ClientExchange result) { if (log.isDebugEnabled()) { log.debugf( "Sent request %s to target %s for exchange %s", request, remoteHost, exchange); } result.putAttachment(EXCHANGE, exchange); boolean requiresContinueResponse = HttpContinue.requiresContinueResponse(exchange); if (requiresContinueResponse) { result.setContinueHandler( new ContinueNotification() { @Override public void handleContinue(final ClientExchange clientExchange) { if (log.isDebugEnabled()) { log.debugf( "Relieved continue response to request %s to target %s for exchange %s", request, remoteHost, exchange); } HttpContinue.sendContinueResponse( exchange, new IoCallback() { @Override public void onComplete( final HttpServerExchange exchange, final Sender sender) { // don't care } @Override public void onException( final HttpServerExchange exchange, final Sender sender, final IOException exception) { IoUtils.safeClose(clientConnection.getConnection()); exchange.endExchange(); UndertowLogger.REQUEST_IO_LOGGER.ioException(exception); } }); } }); } // handle server push if (exchange.getConnection().isPushSupported() && result.getConnection().isPushSupported()) { result.setPushHandler( new PushCallback() { @Override public boolean handlePush( ClientExchange originalRequest, final ClientExchange pushedRequest) { if (log.isDebugEnabled()) { log.debugf( "Sending push request %s received from %s to target %s for exchange %s", pushedRequest.getRequest(), request, remoteHost, exchange); } final ClientRequest request = pushedRequest.getRequest(); exchange .getConnection() .pushResource( request.getPath(), request.getMethod(), request.getRequestHeaders(), new HttpHandler() { @Override public void handleRequest(final HttpServerExchange exchange) throws Exception { String path = request.getPath(); int i = path.indexOf("?"); if (i > 0) { path = path.substring(0, i); } exchange.dispatch( SameThreadExecutor.INSTANCE, new ProxyAction( new ProxyConnection( pushedRequest.getConnection(), path), exchange, requestHeaders, rewriteHostHeader, reuseXForwarded)); } }); return true; } }); } result.setResponseListener(new ResponseCallback(exchange)); final IoExceptionHandler handler = new IoExceptionHandler(exchange, clientConnection.getConnection()); if (requiresContinueResponse) { try { if (!result.getRequestChannel().flush()) { result .getRequestChannel() .getWriteSetter() .set( ChannelListeners.flushingChannelListener( new ChannelListener<StreamSinkChannel>() { @Override public void handleEvent(StreamSinkChannel channel) { Transfer.initiateTransfer( exchange.getRequestChannel(), result.getRequestChannel(), ChannelListeners.closingChannelListener(), new HTTPTrailerChannelListener(exchange, result), handler, handler, exchange.getConnection().getByteBufferPool()); } }, handler)); result.getRequestChannel().resumeWrites(); return; } } catch (IOException e) { handler.handleException(result.getRequestChannel(), e); } } Transfer.initiateTransfer( exchange.getRequestChannel(), result.getRequestChannel(), ChannelListeners.closingChannelListener(), new HTTPTrailerChannelListener(exchange, result), handler, handler, exchange.getConnection().getByteBufferPool()); } @Override public void failed(IOException e) { UndertowLogger.PROXY_REQUEST_LOGGER.proxyRequestFailed( exchange.getRequestURI(), e); if (!exchange.isResponseStarted()) { exchange.setStatusCode(StatusCodes.SERVICE_UNAVAILABLE); exchange.endExchange(); } else { IoUtils.safeClose(exchange.getConnection()); } } }); }
// TODO: factor out repetitive debugging code private synchronized void close(boolean known_invalid) throws SQLException { // System.err.println("Closing " + this); if (physicalConnection != null) { try { StringBuffer debugOnlyLog = null; if (Debug.DEBUG && known_invalid) { debugOnlyLog = new StringBuffer(); debugOnlyLog.append("[ exceptions: "); } Exception exc = cleanupUncachedActiveStatements(); if (Debug.DEBUG && exc != null) { if (known_invalid) debugOnlyLog.append(exc.toString() + ' '); else logger.log( MLevel.WARNING, "An exception occurred while cleaning up uncached active Statements.", exc); // exc.printStackTrace(); } try { // we've got to use silentClose() rather than close() here, // 'cuz if there's still an exposedProxy (say a user forgot to // close his Connection) before we close, and we use regular (loud) // close, we will try to check this dead or dying PooledConnection // back into the pool. We only want to do this when close is called // on user proxies, and the underlying PooledConnection might still // be good. The PooledConnection itself should only be closed by the // pool. if (exposedProxy != null) exposedProxy.silentClose(known_invalid); } catch (Exception e) { if (Debug.DEBUG) { if (known_invalid) debugOnlyLog.append(e.toString() + ' '); else logger.log(MLevel.WARNING, "An exception occurred.", exc); // e.printStackTrace(); } exc = e; } try { this.closeAll(); } catch (Exception e) { if (Debug.DEBUG) { if (known_invalid) debugOnlyLog.append(e.toString() + ' '); else logger.log(MLevel.WARNING, "An exception occurred.", exc); // e.printStackTrace(); } exc = e; } try { physicalConnection.close(); } catch (Exception e) { if (Debug.DEBUG) { if (known_invalid) debugOnlyLog.append(e.toString() + ' '); else logger.log(MLevel.WARNING, "An exception occurred.", exc); e.printStackTrace(); } exc = e; } if (exc != null) { if (known_invalid) { debugOnlyLog.append(" ]"); if (Debug.DEBUG) { // System.err.print("[DEBUG]" + this + ": while closing a PooledConnection known // to be invalid, "); // System.err.println(" some exceptions occurred. This is probably not a // problem:"); // System.err.println( debugOnlyLog.toString() ); logger.fine( this + ": while closing a PooledConnection known to be invalid, " + " some exceptions occurred. This is probably not a problem: " + debugOnlyLog.toString()); } } else throw new SQLException( "At least one error occurred while attempting " + "to close() the PooledConnection: " + exc); } if (Debug.TRACE == Debug.TRACE_MAX) logger.fine("C3P0PooledConnection closed. [" + this + ']'); // System.err.println("C3P0PooledConnection closed. [" + this + ']'); } finally { physicalConnection = null; } } }