in src/main/java/com/twitter/http2/HttpStreamDecoder.java [49:186]
protected void decode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
if (msg instanceof HttpHeadersFrame) {
HttpHeadersFrame httpHeadersFrame = (HttpHeadersFrame) msg;
int streamId = httpHeadersFrame.getStreamId();
StreamedHttpMessage message = messageMap.get(streamId);
if (message == null) {
if (httpHeadersFrame.headers().contains(":status")) {
// If a client receives a reply with a truncated header block,
// reply with a RST_STREAM frame with error code INTERNAL_ERROR.
if (httpHeadersFrame.isTruncated()) {
HttpRstStreamFrame httpRstStreamFrame =
new DefaultHttpRstStreamFrame(streamId, HttpErrorCode.INTERNAL_ERROR);
out.add(httpRstStreamFrame);
return;
}
try {
StreamedHttpResponse response = createHttpResponse(httpHeadersFrame);
if (httpHeadersFrame.isLast()) {
HttpHeaders.setContentLength(response, 0);
response.getContent().close();
} else {
// Response body will follow in a series of Data Frames
if (!HttpHeaders.isContentLengthSet(response)) {
HttpHeaders.setTransferEncodingChunked(response);
}
messageMap.put(streamId, response);
}
out.add(response);
} catch (Exception e) {
// If a client receives a SYN_REPLY without valid getStatus and version headers
// the client must reply with a RST_STREAM frame indicating a PROTOCOL_ERROR
HttpRstStreamFrame httpRstStreamFrame =
new DefaultHttpRstStreamFrame(streamId, HttpErrorCode.PROTOCOL_ERROR);
ctx.writeAndFlush(httpRstStreamFrame);
out.add(httpRstStreamFrame);
}
} else {
// If a client sends a request with a truncated header block, the server must
// reply with a HTTP 431 REQUEST HEADER FIELDS TOO LARGE reply.
if (httpHeadersFrame.isTruncated()) {
httpHeadersFrame = new DefaultHttpHeadersFrame(streamId);
httpHeadersFrame.setLast(true);
httpHeadersFrame.headers().set(
":status", HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.code());
ctx.writeAndFlush(httpHeadersFrame);
return;
}
try {
message = createHttpRequest(httpHeadersFrame);
if (httpHeadersFrame.isLast()) {
message.setDecoderResult(DecoderResult.SUCCESS);
message.getContent().close();
} else {
// Request body will follow in a series of Data Frames
messageMap.put(streamId, message);
}
out.add(message);
} catch (Exception e) {
// If a client sends a SYN_STREAM without all of the method, url (host and path),
// scheme, and version headers the server must reply with a HTTP 400 BAD REQUEST reply.
// Also sends HTTP 400 BAD REQUEST reply if header name/value pairs are invalid
httpHeadersFrame = new DefaultHttpHeadersFrame(streamId);
httpHeadersFrame.setLast(true);
httpHeadersFrame.headers().set(":status", HttpResponseStatus.BAD_REQUEST.code());
ctx.writeAndFlush(httpHeadersFrame);
}
}
} else {
LastHttpContent trailer = trailerMap.remove(streamId);
if (trailer == null) {
trailer = new DefaultLastHttpContent();
}
// Ignore trailers in a truncated HEADERS frame.
if (!httpHeadersFrame.isTruncated()) {
for (Map.Entry<String, String> e: httpHeadersFrame.headers()) {
trailer.trailingHeaders().add(e.getKey(), e.getValue());
}
}
if (httpHeadersFrame.isLast()) {
messageMap.remove(streamId);
message.addContent(trailer);
} else {
trailerMap.put(streamId, trailer);
}
}
} else if (msg instanceof HttpDataFrame) {
HttpDataFrame httpDataFrame = (HttpDataFrame) msg;
int streamId = httpDataFrame.getStreamId();
StreamedHttpMessage message = messageMap.get(streamId);
// If message is not in map discard Data Frame.
if (message == null) {
return;
}
ByteBuf content = httpDataFrame.content();
if (content.isReadable()) {
content.retain();
message.addContent(new DefaultHttpContent(content));
}
if (httpDataFrame.isLast()) {
messageMap.remove(streamId);
message.addContent(LastHttpContent.EMPTY_LAST_CONTENT);
message.setDecoderResult(DecoderResult.SUCCESS);
}
} else if (msg instanceof HttpRstStreamFrame) {
HttpRstStreamFrame httpRstStreamFrame = (HttpRstStreamFrame) msg;
int streamId = httpRstStreamFrame.getStreamId();
StreamedHttpMessage message = messageMap.remove(streamId);
if (message != null) {
message.getContent().close();
message.setDecoderResult(DecoderResult.failure(CANCELLATION_EXCEPTION));
}
} else {
// HttpGoAwayFrame
out.add(msg);
}
}