protected void decode()

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);
        }
    }