@Override public int writeFinal(ByteBuffer src) throws IOException { //todo: we could optimise this to just set a content length if no data has been written if(!src.hasRemaining()) { terminateWrites(); return 0; } if (lastChunkBuffer == null) { createLastChunk(true); } return doWrite(src); }
@Override public void truncateWrites() throws IOException { try { if (lastChunkBuffer != null) { lastChunkBuffer.close(); } if (allAreClear(state, FLAG_FINISHED)) { invokeFinishListener(); } } finally { super.truncateWrites(); } }
if (chunkleft == 0 && !chunkingSepBuffer.hasRemaining()) { chunkingBuffer.clear(); putIntAsHexString(chunkingBuffer, src.remaining()); chunkingBuffer.put(CRLF); chunkingBuffer.flip();
private static StreamSinkConduit handleExplicitTransferEncoding(HttpServerExchange exchange, StreamSinkConduit channel, ConduitListener<StreamSinkConduit> finishListener, HeaderMap responseHeaders, String transferEncodingHeader, boolean headRequest) { HttpString transferEncoding = new HttpString(transferEncodingHeader); if (transferEncoding.equals(Headers.CHUNKED)) { if (headRequest) { return channel; } Boolean preChunked = exchange.getAttachment(HttpAttachments.PRE_CHUNKED_RESPONSE); if(preChunked != null && preChunked) { return new PreChunkedStreamSinkConduit(channel, finishListener, exchange); } else { return new ChunkedStreamSinkConduit(channel, exchange.getConnection().getByteBufferPool(), true, !exchange.isPersistent(), responseHeaders, finishListener, exchange); } } else { if (headRequest) { return channel; } log.trace("Cancelling persistence because response is identity with no content length"); // make it not persistent - very unfortunate for the next request handler really... exchange.setPersistent(false); responseHeaders.put(Headers.CONNECTION, Headers.CLOSE.toString()); return new FinishableStreamSinkConduit(channel, terminateResponseListener(exchange)); } }
@Override public long write(final ByteBuffer[] srcs, final int offset, final int length) throws IOException { for (int i = offset; i < length; ++i) { if (srcs[i].hasRemaining()) { return write(srcs[i]); } } return 0; }
@Override public int write(final ByteBuffer src) throws IOException { return doWrite(src); }
@Override public void terminateWrites() throws IOException { if(anyAreSet(state, FLAG_WRITES_SHUTDOWN)) { return; } if (this.chunkleft != 0) { UndertowLogger.REQUEST_IO_LOGGER.debugf("Channel closed mid-chunk"); next.truncateWrites(); } if (!anyAreSet(state, FLAG_FIRST_DATA_WRITTEN)) { //if no data was actually sent we just remove the transfer encoding header, and set content length 0 //TODO: is this the best way to do it? //todo: should we make this behaviour configurable? responseHeaders.put(Headers.CONTENT_LENGTH, "0"); //according to the spec we don't actually need this, but better to be safe responseHeaders.remove(Headers.TRANSFER_ENCODING); state |= FLAG_NEXT_SHUTDOWN | FLAG_WRITES_SHUTDOWN; if(anyAreSet(state, CONF_FLAG_PASS_CLOSE)) { next.terminateWrites(); } } else { createLastChunk(false); state |= FLAG_WRITES_SHUTDOWN; } }
private static StreamSinkConduit handleResponseConduit(HttpServerExchange exchange, boolean headRequest, StreamSinkConduit channel, HeaderMap responseHeaders, ConduitListener<StreamSinkConduit> finishListener, String transferEncodingHeader) { if (transferEncodingHeader == null) { if (exchange.isHttp11()) { if (exchange.isPersistent()) { responseHeaders.put(Headers.TRANSFER_ENCODING, Headers.CHUNKED.toString()); if (headRequest) { return channel; } return new ChunkedStreamSinkConduit(channel, exchange.getConnection().getByteBufferPool(), true, !exchange.isPersistent(), responseHeaders, finishListener, exchange); } else { if (headRequest) { return channel; } return new FinishableStreamSinkConduit(channel, finishListener); } } else { exchange.setPersistent(false); responseHeaders.put(Headers.CONNECTION, Headers.CLOSE.toString()); if (headRequest) { return channel; } return new FinishableStreamSinkConduit(channel, finishListener); } } else { //moved outside because this is rarely used //and makes the method small enough to be inlined return handleExplicitTransferEncoding(exchange, channel, finishListener, responseHeaders, transferEncodingHeader, headRequest); } }
@Override public long write(final ByteBuffer[] srcs, final int offset, final int length) throws IOException { for (int i = offset; i < length; ++i) { if (srcs[i].hasRemaining()) { return write(srcs[i]); } } return 0; }
@Override public int write(final ByteBuffer src) throws IOException { return doWrite(src); }
@Override public void terminateWrites() throws IOException { if(anyAreSet(state, FLAG_WRITES_SHUTDOWN)) { return; } if (this.chunkleft != 0) { UndertowLogger.REQUEST_IO_LOGGER.debugf("Channel closed mid-chunk"); next.truncateWrites(); } if (!anyAreSet(state, FLAG_FIRST_DATA_WRITTEN)) { //if no data was actually sent we just remove the transfer encoding header, and set content length 0 //TODO: is this the best way to do it? //todo: should we make this behaviour configurable? responseHeaders.put(Headers.CONTENT_LENGTH, "0"); //according to the spec we don't actually need this, but better to be safe responseHeaders.remove(Headers.TRANSFER_ENCODING); state |= FLAG_NEXT_SHUTDOWN | FLAG_WRITES_SHUTDOWN; if(anyAreSet(state, CONF_FLAG_PASS_CLOSE)) { next.terminateWrites(); } } else { createLastChunk(false); state |= FLAG_WRITES_SHUTDOWN; } }
@Override public int writeFinal(ByteBuffer src) throws IOException { //todo: we could optimise this to just set a content length if no data has been written if(!src.hasRemaining()) { terminateWrites(); return 0; } if (lastChunkBuffer == null) { createLastChunk(true); } return doWrite(src); }
return; conduit = new ChunkedStreamSinkConduit(conduit, httpClientExchange.getConnection().getBufferPool(), false, false, httpClientExchange.getRequest().getRequestHeaders(), requestFinishListener, httpClientExchange); } else { conduit = new ClientFixedLengthStreamSinkConduit(conduit, 0, false, false, currentRequest);
boolean val = next.flush(); if (val && allAreClear(state, FLAG_FINISHED)) { invokeFinishListener(); boolean val = next.flush(); if (val && allAreClear(state, FLAG_FINISHED)) { invokeFinishListener();
@Override public long write(final ByteBuffer[] srcs, final int offset, final int length) throws IOException { for (int i = offset; i < length; ++i) { if (srcs[i].hasRemaining()) { return write(srcs[i]); } } return 0; }
@Override public int write(final ByteBuffer src) throws IOException { return doWrite(src); }
@Override public void terminateWrites() throws IOException { if(anyAreSet(state, FLAG_WRITES_SHUTDOWN)) { return; } if (this.chunkleft != 0) { UndertowLogger.REQUEST_IO_LOGGER.debugf("Channel closed mid-chunk"); next.truncateWrites(); } if (!anyAreSet(state, FLAG_FIRST_DATA_WRITTEN)) { //if no data was actually sent we just remove the transfer encoding header, and set content length 0 //TODO: is this the best way to do it? //todo: should we make this behaviour configurable? responseHeaders.put(Headers.CONTENT_LENGTH, "0"); //according to the spec we don't actually need this, but better to be safe responseHeaders.remove(Headers.TRANSFER_ENCODING); state |= FLAG_NEXT_SHUTDOWN | FLAG_WRITES_SHUTDOWN; if(anyAreSet(state, CONF_FLAG_PASS_CLOSE)) { next.terminateWrites(); } } else { createLastChunk(false); state |= FLAG_WRITES_SHUTDOWN; } }
if (chunkleft == 0 && !chunkingSepBuffer.hasRemaining()) { chunkingBuffer.clear(); putIntAsHexString(chunkingBuffer, src.remaining()); chunkingBuffer.put(CRLF); chunkingBuffer.flip();
@Override public int writeFinal(ByteBuffer src) throws IOException { //todo: we could optimise this to just set a content length if no data has been written if(!src.hasRemaining()) { terminateWrites(); return 0; } if (lastChunkBuffer == null) { createLastChunk(true); } return doWrite(src); }
private static StreamSinkConduit handleExplicitTransferEncoding(HttpServerExchange exchange, StreamSinkConduit channel, ConduitListener<StreamSinkConduit> finishListener, HeaderMap responseHeaders, String transferEncodingHeader, boolean headRequest) { HttpString transferEncoding = new HttpString(transferEncodingHeader); if (transferEncoding.equals(Headers.CHUNKED)) { if (headRequest) { return channel; } Boolean preChunked = exchange.getAttachment(HttpAttachments.PRE_CHUNKED_RESPONSE); if(preChunked != null && preChunked) { return new PreChunkedStreamSinkConduit(channel, finishListener, exchange); } else { return new ChunkedStreamSinkConduit(channel, exchange.getConnection().getByteBufferPool(), true, !exchange.isPersistent(), responseHeaders, finishListener, exchange); } } else { if (headRequest) { return channel; } log.trace("Cancelling persistence because response is identity with no content length"); // make it not persistent - very unfortunate for the next request handler really... exchange.setPersistent(false); responseHeaders.put(Headers.CONNECTION, Headers.CLOSE.toString()); return new FinishableStreamSinkConduit(channel, terminateResponseListener(exchange)); } }