use of io.netty.handler.codec.http.LastHttpContent in project netty by netty.
the class Http2ServerDowngraderTest method testDowngradeTrailers.
@Test
public void testDowngradeTrailers() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
Http2Headers headers = new DefaultHttp2Headers();
headers.set("key", "value");
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true)));
LastHttpContent trailers = ch.readInbound();
try {
assertThat(trailers.content().readableBytes(), is(0));
assertThat(trailers.trailingHeaders().get("key").toString(), is("value"));
assertFalse(trailers instanceof FullHttpRequest);
} finally {
trailers.release();
}
assertThat(ch.readInbound(), is(nullValue()));
assertFalse(ch.finish());
}
use of io.netty.handler.codec.http.LastHttpContent in project riposte by Nike-Inc.
the class ResponseSender method synchronizeAndSetupResponseInfoAndFirstChunk.
protected void synchronizeAndSetupResponseInfoAndFirstChunk(ResponseInfo<?> responseInfo, HttpResponse actualResponseObject, RequestInfo requestInfo, ChannelHandlerContext ctx) {
// Set the content type header.
// NOTE: This is ok even if the response doesn't have a body - in the case of chunked messages we don't
// know whether body content will be coming later or not so we have to be proactive here in case
// there *is* body content later.
// ALSO NOTE: The responseInfo may have already had a Content-Type header specified (e.g. reverse proxied
// response), but the way we build the header this will be ok. If responseInfo wanted to override it
// we allow that, and if not then the Content-Type in the headers will be honored, and if both of
// those are unspecified then the default mime type and charset are used.
responseInfo.getHeaders().set(CONTENT_TYPE, buildContentTypeHeader(responseInfo));
// Set the HTTP status code on the ResponseInfo object from the actualResponseObject if necessary.
if (responseInfo.getHttpStatusCode() == null)
responseInfo.setHttpStatusCode(actualResponseObject.getStatus().code());
// Make sure a trace ID is in the headers.
if (!responseInfo.getHeaders().contains(TraceHeaders.TRACE_ID)) {
// All responses must contain a trace ID. Try to get it from the request
// since it wasn't already in the response.
String traceId = extractDistributedTraceId(requestInfo, ctx);
if (traceId == null) {
// Couldn't find a valid trace ID anywhere, so just create a dummy one, and log what happened so if
// someone searches for that ID they'll find something explaining what happened.
traceId = TraceAndSpanIdGenerator.generateId();
String warningMsg = "Generating a dummy Trace ID for response header because a real Trace ID did not exist. This " + "probably happened because the request was not processed by the channel pipeline. dummy_trace_id=" + traceId;
runnableWithTracingAndMdc(() -> logger.warn(warningMsg), ctx).run();
}
responseInfo.getHeaders().set(TraceHeaders.TRACE_ID, traceId);
}
// Handle any keep-alive stuff
if (responseInfo.isForceConnectionCloseAfterResponseSent()) {
// We'll be closing the connection after this response is sent, so send the appropriate Connection header.
responseInfo.getHeaders().set(CONNECTION, HttpHeaders.Values.CLOSE);
} else if (requestInfo.isKeepAliveRequested()) {
// what the content length will be.
if (actualResponseObject instanceof LastHttpContent) {
responseInfo.getHeaders().set(CONTENT_LENGTH, ((LastHttpContent) actualResponseObject).content().readableBytes());
} else {
// If we have one of those responses, we mark it with content-length 0
if (isContentAlwaysEmpty(responseInfo)) {
responseInfo.getHeaders().remove(TRANSFER_ENCODING);
responseInfo.getHeaders().set(CONTENT_LENGTH, 0);
} else {
// Not a must-be-empty-payload status code. For these there might be a payload and we can't know the
// content length since it's being sent to us in chunks, so we have to set the
// Transfer-Encoding header to chunked in order for the response sending to be successful
// (otherwise the receiving client would just hang waiting for the connection to be closed).
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6
// and http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 for the technical explanation.
// See http://en.wikipedia.org/wiki/Chunked_transfer_encoding for a more straightforward explanation
responseInfo.getHeaders().remove(CONTENT_LENGTH);
responseInfo.getHeaders().set(TRANSFER_ENCODING, CHUNKED);
}
}
// Add keep alive header as per
// http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection
responseInfo.getHeaders().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
}
// Synchronize the ResponseInfo headers with the actualResponseObject
// (copy from responseInfo into actualResponseObject)
actualResponseObject.headers().add(responseInfo.getHeaders());
// Add cookies (if any)
if (responseInfo.getCookies() != null) {
for (Cookie cookie : responseInfo.getCookies()) {
actualResponseObject.headers().add(HttpHeaders.Names.SET_COOKIE, ServerCookieEncoder.LAX.encode(cookie.name(), cookie.value()));
}
}
}
use of io.netty.handler.codec.http.LastHttpContent in project jersey by jersey.
the class JerseyServerHandler method channelRead.
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
if (msg instanceof HttpRequest) {
final HttpRequest req = (HttpRequest) msg;
if (HttpUtil.is100ContinueExpected(req)) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE));
}
// clearing the content - possible leftover from previous request processing.
isList.clear();
final ContainerRequest requestContext = createContainerRequest(ctx, req);
requestContext.setWriter(new NettyResponseWriter(ctx, req, container));
// must be like this, since there is a blocking read from Jersey
container.getExecutorService().execute(new Runnable() {
@Override
public void run() {
container.getApplicationHandler().handle(requestContext);
}
});
}
if (msg instanceof HttpContent) {
HttpContent httpContent = (HttpContent) msg;
ByteBuf content = httpContent.content();
if (content.isReadable()) {
isList.add(new ByteBufInputStream(content));
}
if (msg instanceof LastHttpContent) {
isList.add(NettyInputStream.END_OF_INPUT);
}
}
}
use of io.netty.handler.codec.http.LastHttpContent in project netty by netty.
the class Http2ServerDowngrader method decode.
@Override
protected void decode(ChannelHandlerContext ctx, Http2StreamFrame frame, List<Object> out) throws Exception {
if (frame instanceof Http2HeadersFrame) {
// not really the id
int id = 0;
Http2HeadersFrame headersFrame = (Http2HeadersFrame) frame;
Http2Headers headers = headersFrame.headers();
if (headersFrame.isEndStream()) {
if (headers.method() == null) {
LastHttpContent last = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);
HttpConversionUtil.addHttp2ToHttpHeaders(id, headers, last.trailingHeaders(), HttpVersion.HTTP_1_1, true, true);
out.add(last);
} else {
FullHttpRequest full = HttpConversionUtil.toFullHttpRequest(id, headers, ctx.alloc(), validateHeaders);
out.add(full);
}
} else {
HttpRequest req = HttpConversionUtil.toHttpRequest(id, headersFrame.headers(), validateHeaders);
if (!HttpUtil.isContentLengthSet(req)) {
req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
}
out.add(req);
}
} else if (frame instanceof Http2DataFrame) {
Http2DataFrame dataFrame = (Http2DataFrame) frame;
if (dataFrame.isEndStream()) {
out.add(new DefaultLastHttpContent(dataFrame.content(), validateHeaders));
} else {
out.add(new DefaultHttpContent(dataFrame.content()));
}
}
ReferenceCountUtil.retain(frame);
}
use of io.netty.handler.codec.http.LastHttpContent in project rest.li by linkedin.
the class RAPResponseDecoder method channelRead0.
@Override
protected void channelRead0(final ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (msg instanceof HttpResponse) {
HttpResponse m = (HttpResponse) msg;
_shouldCloseConnection = !HttpUtil.isKeepAlive(m);
if (HttpUtil.is100ContinueExpected(m)) {
ctx.writeAndFlush(CONTINUE).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
ctx.fireExceptionCaught(future.cause());
}
}
});
}
if (!m.decoderResult().isSuccess()) {
ctx.fireExceptionCaught(m.decoderResult().cause());
return;
}
// remove chunked encoding.
if (HttpUtil.isTransferEncodingChunked(m)) {
HttpUtil.setTransferEncodingChunked(m, false);
}
Timeout<None> timeout = ctx.channel().attr(TIMEOUT_ATTR_KEY).getAndRemove();
if (timeout == null) {
LOG.debug("dropped a response after channel inactive or exception had happened.");
return;
}
final TimeoutBufferedWriter writer = new TimeoutBufferedWriter(ctx, _maxContentLength, BUFFER_HIGH_WATER_MARK, BUFFER_LOW_WATER_MARK, timeout);
EntityStream entityStream = EntityStreams.newEntityStream(writer);
_chunkedMessageWriter = writer;
StreamResponseBuilder builder = new StreamResponseBuilder();
builder.setStatus(m.status().code());
for (Map.Entry<String, String> e : m.headers()) {
String key = e.getKey();
String value = e.getValue();
if (key.equalsIgnoreCase(HttpConstants.RESPONSE_COOKIE_HEADER_NAME)) {
builder.addCookie(value);
} else {
builder.unsafeAddHeaderValue(key, value);
}
}
ctx.fireChannelRead(builder.build(entityStream));
} else if (msg instanceof HttpContent) {
HttpContent chunk = (HttpContent) msg;
TimeoutBufferedWriter currentWriter = _chunkedMessageWriter;
// Sanity check
if (currentWriter == null) {
throw new IllegalStateException("received " + HttpContent.class.getSimpleName() + " without " + HttpResponse.class.getSimpleName());
}
if (!chunk.decoderResult().isSuccess()) {
this.exceptionCaught(ctx, chunk.decoderResult().cause());
}
currentWriter.processHttpChunk(chunk);
if (chunk instanceof LastHttpContent) {
_chunkedMessageWriter = null;
}
} else {
// something must be wrong, but let's proceed so that
// handler after us has a chance to process it.
ctx.fireChannelRead(msg);
}
}
Aggregations