예제 #1
0
        protected override void Encode(IChannelHandlerContext context, object message, List <object> output)
        {
            IByteBuffer buf = null;

            if (message is IHttpMessage)
            {
                if (this.state != StInit)
                {
                    ThrowHelper.ThrowInvalidOperationException_UnexpectedMsg(message, this.state);
                }

                var m = (T)message;

                buf = context.Allocator.Buffer((int)this.headersEncodedSizeAccumulator);
                // Encode the message.
                this.EncodeInitialLine(buf, m);
                this.state = this.IsContentAlwaysEmpty(m) ? StContentAlwaysEmpty
                    : HttpUtil.IsTransferEncodingChunked(m) ? StContentChunk : StContentNonChunk;

                this.SanitizeHeadersBeforeEncode(m, this.state == StContentAlwaysEmpty);

                this.EncodeHeaders(m.Headers, buf);
                _ = buf.WriteShort(HttpConstants.CrlfShort);

                this.headersEncodedSizeAccumulator = HeadersWeightNew * PadSizeForAccumulation(buf.ReadableBytes)
                                                     + HeadersWeightHistorical * this.headersEncodedSizeAccumulator;
            }

            // Bypass the encoder in case of an empty buffer, so that the following idiom works:
            //
            //     ch.write(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
            //
            // See https://github.com/netty/netty/issues/2983 for more information.
            if (message is IByteBuffer potentialEmptyBuf)
            {
                if (!potentialEmptyBuf.IsReadable())
                {
                    output.Add(potentialEmptyBuf.Retain());
                    return;
                }
            }

            switch (message)
            {
            case IHttpContent _:
            case IByteBuffer _:
            case IFileRegion _:
                switch (this.state)
                {
                case StInit:
                    ThrowHelper.ThrowInvalidOperationException_UnexpectedMsg(message); break;

                case StContentNonChunk:
                    long contentLength = ContentLength(message);
                    if (contentLength > 0)
                    {
                        if (buf is object && buf.WritableBytes >= contentLength && message is IHttpContent)
                        {
                            // merge into other buffer for performance reasons
                            _ = buf.WriteBytes(((IHttpContent)message).Content);
                            output.Add(buf);
                        }
                        else
                        {
                            if (buf is object)
                            {
                                output.Add(buf);
                            }
                            output.Add(EncodeAndRetain(message));
                        }

                        if (message is ILastHttpContent)
                        {
                            this.state = StInit;
                        }
                        break;
                    }

                    goto case StContentAlwaysEmpty;         // fall-through!

                case StContentAlwaysEmpty:
                    // ReSharper disable once ConvertIfStatementToNullCoalescingExpression
                    if (buf is object)
                    {
                        // We allocated a buffer so add it now.
                        output.Add(buf);
                    }
                    else
                    {
                        // Need to produce some output otherwise an
                        // IllegalStateException will be thrown as we did not write anything
                        // Its ok to just write an EMPTY_BUFFER as if there are reference count issues these will be
                        // propagated as the caller of the encode(...) method will release the original
                        // buffer.
                        // Writing an empty buffer will not actually write anything on the wire, so if there is a user
                        // error with msg it will not be visible externally
                        output.Add(Unpooled.Empty);
                    }

                    break;

                case StContentChunk:
                    if (buf is object)
                    {
                        // We allocated a buffer so add it now.
                        output.Add(buf);
                    }
                    this.EncodeChunkedContent(context, message, ContentLength(message), output);

                    break;

                default:
                    ThrowHelper.ThrowEncoderException_UnexpectedState(this.state, message); break;
                }

                if (message is ILastHttpContent)
                {
                    this.state = StInit;
                }
                break;

            default:
                output.Add(buf);
                break;
            }
        }