private static void addHttpHeaders(ClientRequest request, HttpHeaders headers) { HeaderMap headerMap = request.getRequestHeaders(); for (String name : headers.keySet()) { for (String value : headers.get(name)) { headerMap.add(HttpString.tryFromString(name), value); } } }
protected ResponseEntity<String> executeRequest( URI url, HttpString method, HttpHeaders headers, String body) { CountDownLatch latch = new CountDownLatch(1); List<ClientResponse> responses = new CopyOnWriteArrayList<ClientResponse>(); try { ClientConnection connection = this.undertowBufferSupport .httpClientConnect(this.httpClient, url, this.worker, this.optionMap) .get(); try { ClientRequest request = new ClientRequest().setMethod(method).setPath(url.getPath()); request.getRequestHeaders().add(HttpString.tryFromString(HttpHeaders.HOST), url.getHost()); if (body != null && !body.isEmpty()) { HttpString headerName = HttpString.tryFromString(HttpHeaders.CONTENT_LENGTH); request.getRequestHeaders().add(headerName, body.length()); } addHttpHeaders(request, headers); connection.sendRequest(request, createRequestCallback(body, responses, latch)); latch.await(); ClientResponse response = responses.iterator().next(); HttpStatus status = HttpStatus.valueOf(response.getResponseCode()); HttpHeaders responseHeaders = toHttpHeaders(response.getResponseHeaders()); String responseBody = response.getAttachment(RESPONSE_BODY); return (responseBody != null ? new ResponseEntity<String>(responseBody, responseHeaders, status) : new ResponseEntity<String>(responseHeaders, status)); } finally { IoUtils.safeClose(connection); } } catch (IOException ex) { throw new SockJsTransportFailureException("Failed to execute request to " + url, ex); } catch (InterruptedException ex) { throw new SockJsTransportFailureException( "Interrupted while processing request to " + url, ex); } }
/** * Handles writing out the header data. It can also take a byte buffer of user data, to enable * both user data and headers to be written out in a single operation, which has a noticeable * performance impact. * * <p>It is up to the caller to note the current position of this buffer before and after they * call this method, and use this to figure out how many bytes (if any) have been written. * * @param state * @param userData * @return * @throws java.io.IOException */ private int processWrite(int state, final ByteBuffer userData) throws IOException { if (state == STATE_START) { pooledBuffer = pool.allocate(); } ClientRequest request = this.request; ByteBuffer buffer = pooledBuffer.getResource(); Iterator<HttpString> nameIterator = this.nameIterator; Iterator<String> valueIterator = this.valueIterator; int charIndex = this.charIndex; int length; String string = this.string; HttpString headerName = this.headerName; int res; // BUFFER IS FLIPPED COMING IN if (state != STATE_START && buffer.hasRemaining()) { log.trace("Flushing remaining buffer"); do { res = next.write(buffer); if (res == 0) { return state; } } while (buffer.hasRemaining()); } buffer.clear(); // BUFFER IS NOW EMPTY FOR FILLING for (; ; ) { switch (state) { case STATE_BODY: { // shouldn't be possible, but might as well do the right thing anyway return state; } case STATE_START: { log.trace("Starting request"); // we assume that our buffer has enough space for the initial request line plus one more // CR+LF assert buffer.remaining() >= 50; request.getMethod().appendTo(buffer); buffer.put((byte) ' '); string = request.getPath(); length = string.length(); for (charIndex = 0; charIndex < length; charIndex++) { buffer.put((byte) string.charAt(charIndex)); } buffer.put((byte) ' '); request.getProtocol().appendTo(buffer); buffer.put((byte) '\r').put((byte) '\n'); HeaderMap headers = request.getRequestHeaders(); nameIterator = headers.getHeaderNames().iterator(); if (!nameIterator.hasNext()) { log.trace("No request headers"); buffer.put((byte) '\r').put((byte) '\n'); buffer.flip(); while (buffer.hasRemaining()) { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_BUF_FLUSH; } } pooledBuffer.free(); pooledBuffer = null; log.trace("Body"); return STATE_BODY; } headerName = nameIterator.next(); charIndex = 0; // fall thru } case STATE_HDR_NAME: { log.tracef("Processing header '%s'", headerName); length = headerName.length(); while (charIndex < length) { if (buffer.hasRemaining()) { buffer.put(headerName.byteAt(charIndex++)); } else { log.trace("Buffer flush"); buffer.flip(); do { res = next.write(buffer); if (res == 0) { this.string = string; this.headerName = headerName; this.charIndex = charIndex; this.valueIterator = valueIterator; this.nameIterator = nameIterator; log.trace("Continuation"); return STATE_HDR_NAME; } } while (buffer.hasRemaining()); buffer.clear(); } } // fall thru } case STATE_HDR_D: { if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); this.string = string; this.headerName = headerName; this.charIndex = charIndex; this.valueIterator = valueIterator; this.nameIterator = nameIterator; return STATE_HDR_D; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) ':'); // fall thru } case STATE_HDR_DS: { if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); this.string = string; this.headerName = headerName; this.charIndex = charIndex; this.valueIterator = valueIterator; this.nameIterator = nameIterator; return STATE_HDR_DS; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) ' '); if (valueIterator == null) { valueIterator = request.getRequestHeaders().get(headerName).iterator(); } assert valueIterator.hasNext(); string = valueIterator.next(); charIndex = 0; // fall thru } case STATE_HDR_VAL: { log.tracef("Processing header value '%s'", string); length = string.length(); while (charIndex < length) { if (buffer.hasRemaining()) { buffer.put((byte) string.charAt(charIndex++)); } else { buffer.flip(); do { res = next.write(buffer); if (res == 0) { this.string = string; this.headerName = headerName; this.charIndex = charIndex; this.valueIterator = valueIterator; this.nameIterator = nameIterator; log.trace("Continuation"); return STATE_HDR_VAL; } } while (buffer.hasRemaining()); buffer.clear(); } } charIndex = 0; if (!valueIterator.hasNext()) { if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_HDR_EOL_CR; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) 13); // CR if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_HDR_EOL_LF; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) 10); // LF if (nameIterator.hasNext()) { headerName = nameIterator.next(); valueIterator = null; state = STATE_HDR_NAME; break; } else { if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_HDR_FINAL_CR; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) 13); // CR if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_HDR_FINAL_LF; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) 10); // LF this.nameIterator = null; this.valueIterator = null; this.string = null; buffer.flip(); // for performance reasons we use a gather write if there is user data if (userData == null) { do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_BUF_FLUSH; } } while (buffer.hasRemaining()); } else { ByteBuffer[] b = {buffer, userData}; do { long r = next.write(b, 0, b.length); if (r == 0 && buffer.hasRemaining()) { log.trace("Continuation"); return STATE_BUF_FLUSH; } } while (buffer.hasRemaining()); } pooledBuffer.free(); pooledBuffer = null; log.trace("Body"); return STATE_BODY; } // not reached } // fall thru } // Clean-up states case STATE_HDR_EOL_CR: { if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_HDR_EOL_CR; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) 13); // CR } case STATE_HDR_EOL_LF: { if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_HDR_EOL_LF; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) 10); // LF if (valueIterator.hasNext()) { state = STATE_HDR_NAME; break; } else if (nameIterator.hasNext()) { headerName = nameIterator.next(); valueIterator = null; state = STATE_HDR_NAME; break; } // fall thru } case STATE_HDR_FINAL_CR: { if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_HDR_FINAL_CR; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) 13); // CR // fall thru } case STATE_HDR_FINAL_LF: { if (!buffer.hasRemaining()) { buffer.flip(); do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_HDR_FINAL_LF; } } while (buffer.hasRemaining()); buffer.clear(); } buffer.put((byte) 10); // LF this.nameIterator = null; this.valueIterator = null; this.string = null; buffer.flip(); // for performance reasons we use a gather write if there is user data if (userData == null) { do { res = next.write(buffer); if (res == 0) { log.trace("Continuation"); return STATE_BUF_FLUSH; } } while (buffer.hasRemaining()); } else { ByteBuffer[] b = {buffer, userData}; do { long r = next.write(b, 0, b.length); if (r == 0 && buffer.hasRemaining()) { log.trace("Continuation"); return STATE_BUF_FLUSH; } } while (buffer.hasRemaining()); } // fall thru } case STATE_BUF_FLUSH: { // buffer was successfully flushed above pooledBuffer.free(); pooledBuffer = null; return STATE_BODY; } default: { throw new IllegalStateException(); } } } }
@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()); } } }); }
@Override public void sendRequest(ClientRequest request, ClientCallback<ClientExchange> clientCallback) { request.getRequestHeaders().put(PATH, request.getPath()); request.getRequestHeaders().put(SCHEME, "https"); request.getRequestHeaders().put(VERSION, request.getProtocol().toString()); request.getRequestHeaders().put(METHOD, request.getMethod().toString()); request.getRequestHeaders().put(HOST, request.getRequestHeaders().getFirst(Headers.HOST)); request.getRequestHeaders().remove(Headers.HOST); SpdySynStreamStreamSinkChannel sinkChannel; try { sinkChannel = spdyChannel.createStream(request.getRequestHeaders()); } catch (IOException e) { clientCallback.failed(e); return; } SpdyClientExchange exchange = new SpdyClientExchange(this, sinkChannel, request); currentExchanges.put(sinkChannel.getStreamId(), exchange); boolean hasContent = true; String fixedLengthString = request.getRequestHeaders().getFirst(CONTENT_LENGTH); String transferEncodingString = request.getRequestHeaders().getLast(TRANSFER_ENCODING); if (fixedLengthString != null) { try { long length = Long.parseLong(fixedLengthString); hasContent = length != 0; } catch (NumberFormatException e) { handleError(new IOException(e)); return; } } else if (transferEncodingString == null) { hasContent = false; } if (clientCallback != null) { clientCallback.completed(exchange); } if (!hasContent) { // if there is no content we flush the response channel. // otherwise it is up to the user try { sinkChannel.shutdownWrites(); if (!sinkChannel.flush()) { sinkChannel .getWriteSetter() .set( ChannelListeners.flushingChannelListener( null, new ChannelExceptionHandler<StreamSinkChannel>() { @Override public void handleException( StreamSinkChannel channel, IOException exception) { handleError(exception); } })); sinkChannel.resumeWrites(); } } catch (IOException e) { handleError(e); } } else if (!sinkChannel.isWriteResumed()) { try { // TODO: this needs some more thought if (!sinkChannel.flush()) { sinkChannel .getWriteSetter() .set( new ChannelListener<StreamSinkChannel>() { @Override public void handleEvent(StreamSinkChannel channel) { try { if (channel.flush()) { channel.suspendWrites(); } } catch (IOException e) { handleError(e); } } }); sinkChannel.resumeWrites(); } } catch (IOException e) { handleError(e); } } }
private void initiateRequest(HttpClientExchange httpClientExchange) { currentRequest = httpClientExchange; pendingResponse = new HttpResponseBuilder(); ClientRequest request = httpClientExchange.getRequest(); String connectionString = request.getRequestHeaders().getFirst(CONNECTION); if (connectionString != null) { HttpString connectionHttpString = new HttpString(connectionString); if (connectionHttpString.equals(CLOSE)) { state |= CLOSE_REQ; } else if (connectionHttpString.equals(UPGRADE)) { state |= UPGRADE_REQUESTED; } } else if (request.getProtocol() != Protocols.HTTP_1_1) { state |= CLOSE_REQ; } if (request.getRequestHeaders().contains(UPGRADE)) { state |= UPGRADE_REQUESTED; } // setup the client request conduits final ConduitStreamSourceChannel sourceChannel = connection.getSourceChannel(); sourceChannel.setReadListener(clientReadListener); sourceChannel.resumeReads(); ConduitStreamSinkChannel sinkChannel = connection.getSinkChannel(); StreamSinkConduit conduit = originalSinkConduit; conduit = new HttpRequestConduit(conduit, bufferPool, request); String fixedLengthString = request.getRequestHeaders().getFirst(CONTENT_LENGTH); String transferEncodingString = request.getRequestHeaders().getLast(TRANSFER_ENCODING); boolean hasContent = true; if (fixedLengthString != null) { try { long length = Long.parseLong(fixedLengthString); conduit = new ClientFixedLengthStreamSinkConduit(conduit, length, false, false, currentRequest); hasContent = length != 0; } catch (NumberFormatException e) { handleError(new IOException(e)); return; } } else if (transferEncodingString != null) { if (!transferEncodingString .toLowerCase(Locale.ENGLISH) .contains(Headers.CHUNKED.toString())) { handleError( UndertowClientMessages.MESSAGES.unknownTransferEncoding(transferEncodingString)); return; } conduit = new ChunkedStreamSinkConduit( conduit, httpClientExchange.getConnection().getBufferPool(), false, false, httpClientExchange.getRequest().getRequestHeaders(), requestFinishListener, httpClientExchange); } else { conduit = new ClientFixedLengthStreamSinkConduit(conduit, 0, false, false, currentRequest); hasContent = false; } sinkChannel.setConduit(conduit); httpClientExchange.invokeReadReadyCallback(httpClientExchange); if (!hasContent) { // if there is no content we flush the response channel. // otherwise it is up to the user try { sinkChannel.shutdownWrites(); if (!sinkChannel.flush()) { sinkChannel.setWriteListener( ChannelListeners.flushingChannelListener( null, new ChannelExceptionHandler<ConduitStreamSinkChannel>() { @Override public void handleException( ConduitStreamSinkChannel channel, IOException exception) { handleError(exception); } })); } } catch (IOException e) { handleError(e); } } else if (!sinkChannel.isWriteResumed()) { try { // TODO: this needs some more thought if (!sinkChannel.flush()) { sinkChannel.setWriteListener( new ChannelListener<ConduitStreamSinkChannel>() { @Override public void handleEvent(ConduitStreamSinkChannel channel) { try { if (channel.flush()) { channel.suspendWrites(); } } catch (IOException e) { handleError(e); } } }); sinkChannel.resumeWrites(); } } catch (IOException e) { handleError(e); } } }