Repository: netty/netty
File: codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java (Java)

Code before:
            throwUnexpectedMessageTypeEx(msg, state);
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    private void encodeFullHttpMessage(ChannelHandlerContext ctx, Object o, List<Object> out)
            throws Exception {
        assert o instanceof FullHttpMessage;
        final FullHttpMessage msg = (FullHttpMessage) o;
        try {
            if (state != ST_INIT) {
                throwUnexpectedMessageTypeEx(o, state);
            }

            final H m = (H) o;

            final ByteBuf buf = ctx.alloc().buffer((int) headersEncodedSizeAccumulator);

            encodeInitialLine(buf, m);

            final int state = isContentAlwaysEmpty(m) ? ST_CONTENT_ALWAYS_EMPTY :
                    HttpUtil.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;

            sanitizeHeadersBeforeEncode(m, state == ST_CONTENT_ALWAYS_EMPTY);

            encodeHeaders(m.headers(), buf);
            ByteBufUtil.writeShortBE(buf, CRLF_SHORT);

            headersEncodedSizeAccumulator = HEADERS_WEIGHT_NEW * padSizeForAccumulation(buf.readableBytes()) +
                    HEADERS_WEIGHT_HISTORICAL * headersEncodedSizeAccumulator;

            encodeByteBufHttpContent(state, ctx, buf, msg.content(), msg.trailingHeaders(), out);
        } finally {
            msg.release();
        }
    }

    private static boolean encodeContentNonChunk(List<Object> out, ByteBuf buf, ByteBuf content) {
        final int contentLength = content.readableBytes();
        if (contentLength > 0) {
            if (buf.writableBytes() >= contentLength) {
                // merge into other buffer for performance reasons
                buf.writeBytes(content);
                out.add(buf);
            } else {
                out.add(buf);
                out.add(content.retain());
            }
            return true;
        }

Review comment (bug):
A corner case here might be that we have a heap buffer of a very large size so we decide to copy but are wrong on the header estimation resulting in too small of a buffer to fit the headers and the content. Is it worth hedging our bet on that as well?

Also, is it worth lifting the `128` into a constant for consistency?

Code after:
            return;
        }
        try {
            throwUnexpectedMessageTypeEx(msg, state);
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    private void encodeFullHttpMessage(ChannelHandlerContext ctx, Object o, List<Object> out)
            throws Exception {
        assert o instanceof FullHttpMessage;
        final FullHttpMessage msg = (FullHttpMessage) o;
        try {
            if (state != ST_INIT) {
                throwUnexpectedMessageTypeEx(o, state);
            }

            final H m = (H) o;

            final int state = isContentAlwaysEmpty(m) ? ST_CONTENT_ALWAYS_EMPTY :
                    HttpUtil.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;

            ByteBuf content = msg.content();

            final boolean accountForContentSize = content.readableBytes() > 0 &&
                                        state == ST_CONTENT_NON_CHUNK &&
                                        // try embed the content if less or equals than
                                        // the biggest of ~12.5% of the header estimated size and COPY_DATA_THRESHOLD:
                                        // it limits a wrong estimation to waste too much memory
                                        content.readableBytes() <=
                                        Math.max(COPY_CONTENT_THRESHOLD, ((int) headersEncodedSizeAccumulator) / 8);

            final int headersAndContentSize = (int) headersEncodedSizeAccumulator +
                                                  (accountForContentSize? content.readableBytes() : 0);
            final ByteBuf buf = ctx.alloc().buffer(headersAndContentSize);

            encodeInitialLine(buf, m);

            sanitizeHeadersBeforeEncode(m, state == ST_CONTENT_ALWAYS_EMPTY);

            encodeHeaders(m.headers(), buf);
            ByteBufUtil.writeShortBE(buf, CRLF_SHORT);

            // don't consider the copyContent case here: the statistics is just related the headers
            headersEncodedSizeAccumulator = HEADERS_WEIGHT_NEW * padSizeForAccumulation(buf.readableBytes()) +
                    HEADERS_WEIGHT_HISTORICAL * headersEncodedSizeAccumulator;

            encodeByteBufHttpContent(state, ctx, buf, content, msg.trailingHeaders(), out);
        } finally {
            msg.release();