public override void Encode(IChannelHandlerContext context, object message, List <object> output)
            {
                if (this.Upgraded)
                {
                    output.Add(ReferenceCountUtil.Retain(message));
                    return;
                }

                if (message is IHttpRequest request && !this.clientCodec.done)
                {
                    this.clientCodec.queue.Enqueue(request.Method);
                }

                base.Encode(context, message, output);

                if (this.clientCodec.failOnMissingResponse && !this.clientCodec.done)
                {
                    // check if the request is chunked if so do not increment
                    if (message is ILastHttpContent)
                    {
                        // increment as its the last chunk
                        Interlocked.Increment(ref this.clientCodec.requestResponseCounter);
                    }
                }
            }
Пример #2
0
            protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
            {
                if (_finished)
                {
                    _received.Enqueue(ReferenceCountUtil.Retain(msg));
                    Flush();
                    return;
                }

                bool finished = HandleProxyProtocol(ctx, msg);

                if (finished)
                {
                    _finished = true;
                    Task <IChannel> f = ConnectToDestination(ctx.Channel.EventLoop, new BackendHandler(_server, ctx));
                    f.ContinueWith(future =>
                    {
                        if (!future.IsSuccess())
                        {
                            _server.RecordException(future.Exception);
                            ctx.CloseAsync();
                        }
                        else
                        {
                            _backend = future.Result;
                            Flush();
                        }
                    }, TaskContinuationOptions.ExecuteSynchronously);
                }
            }
Пример #3
0
        protected override void DoWrite(ChannelOutboundBuffer buffer)
        {
            switch (this.state)
            {
            case State.Open:
            case State.Bound:
                throw new NotYetConnectedException();

            case State.Closed:
                throw DoWriteClosedChannelException;

            case State.Connected:
                break;
            }

            LocalChannel peer = this.peer;

            this.writeInProgress = true;
            try
            {
                for (;;)
                {
                    object msg = buffer.Current;
                    if (msg == null)
                    {
                        break;
                    }

                    try
                    {
                        // It is possible the peer could have closed while we are writing, and in this case we should
                        // simulate real socket behavior and ensure the write operation is failed.
                        if (peer.state == State.Connected)
                        {
                            peer.inboundBuffer.TryEnqueue(ReferenceCountUtil.Retain(msg));
                            buffer.Remove();
                        }
                        else
                        {
                            buffer.Remove(DoWriteClosedChannelException);
                        }
                    }
                    catch (Exception cause)
                    {
                        buffer.Remove(cause);
                    }
                }
            }
            finally
            {
                // The following situation may cause trouble:
                // 1. Write (with promise X)
                // 2. promise X is completed when in.remove() is called, and a listener on this promise calls close()
                // 3. Then the close event will be executed for the peer before the write events, when the write events
                // actually happened before the close event.
                this.writeInProgress = false;
            }

            this.FinishPeerRead(peer);
        }
Пример #4
0
        protected override void Decode(IChannelHandlerContext context, IRedisMessage message, List <object> output)
        {
            if (message is ArrayHeaderRedisMessage)
            {
                message = this.DecodeRedisArrayHeader((ArrayHeaderRedisMessage)message);
                if (message is null)
                {
                    return;
                }
            }
            else
            {
                ReferenceCountUtil.Retain(message);
            }

            while ((uint)this.depths.Count > 0u)
            {
                AggregateState current = this.depths.Peek();
                current.Children.Add(message);

                // if current aggregation completed, go to parent aggregation.
                if (current.Children.Count == current.Length)
                {
                    message = new ArrayRedisMessage(current.Children);
                    this.depths.Pop();
                }
                else
                {
                    // not aggregated yet. try next time.
                    return;
                }
            }

            output.Add(message);
        }
Пример #5
0
            protected override void Encode(IChannelHandlerContext context, object message, List <object> output)
            {
                if (Upgraded)
                {
                    output.Add(ReferenceCountUtil.Retain(message));
                    return;
                }

                if (message is IHttpRequest request)
                {
                    _clientCodec._queue.AddToBack(request.Method);
                }

                base.Encode(context, message, output);

                if (_clientCodec._failOnMissingResponse && !_clientCodec._done)
                {
                    // check if the request is chunked if so do not increment
                    if (message is ILastHttpContent)
                    {
                        // increment as its the last chunk
                        _ = Interlocked.Increment(ref _clientCodec._requestResponseCounter);
                    }
                }
            }
Пример #6
0
        protected override void DoWrite(ChannelOutboundBuffer buffer)
        {
            switch (Volatile.Read(ref v_state))
            {
            case State.Open:
            case State.Bound:
                ThrowHelper.ThrowNotYetConnectedException(); break;

            case State.Closed:
                ThrowHelper.ThrowDoWriteClosedChannelException(); break;

            case State.Connected:
                break;
            }

            var peer = Volatile.Read(ref v_peer);

            _ = Interlocked.Exchange(ref v_writeInProgress, SharedConstants.True);
            try
            {
                while (true)
                {
                    object msg = buffer.Current;
                    if (msg is null)
                    {
                        break;
                    }

                    try
                    {
                        // It is possible the peer could have closed while we are writing, and in this case we should
                        // simulate real socket behavior and ensure the write operation is failed.
                        if (Volatile.Read(ref peer.v_state) == State.Connected)
                        {
                            _ = peer._inboundBuffer.TryEnqueue(ReferenceCountUtil.Retain(msg));
                            _ = buffer.Remove();
                        }
                        else
                        {
                            _ = buffer.Remove(DoWriteClosedChannelException);
                        }
                    }
                    catch (Exception cause)
                    {
                        _ = buffer.Remove(cause);
                    }
                }
            }
            finally
            {
                // The following situation may cause trouble:
                // 1. Write (with promise X)
                // 2. promise X is completed when in.remove() is called, and a listener on this promise calls close()
                // 3. Then the close event will be executed for the peer before the write events, when the write events
                // actually happened before the close event.
                _ = Interlocked.Exchange(ref v_writeInProgress, SharedConstants.False);
            }

            FinishPeerRead(peer);
        }
            protected override void ChannelRead0(IChannelHandlerContext ctx, IHttpMessage msg)
            {
                // If this handler is hit then no upgrade has been attempted and the client is just talking HTTP.
                s_logger.LogInformation("Directly talking: " + msg.ProtocolVersion + " (no upgrade was attempted)");
                IChannelPipeline pipeline = ctx.Pipeline;

                pipeline.AddAfter(ctx.Name, null, new Http2Helloworld.Server.HelloWorldHttp1Handler("Direct. No Upgrade Attempted."));
                pipeline.Replace(this, null, new HttpObjectAggregator(this.maxHttpContentLength));
                ctx.FireChannelRead(ReferenceCountUtil.Retain(msg));
            }
Пример #8
0
        protected override void DoWrite(ChannelOutboundBuffer input)
        {
            switch (_state)
            {
            case State.Open:
            case State.Bound:
                throw NotYetConnectedException.Instance;

            case State.Closed:
                throw ClosedChannelException.Instance;

            case State.Connected:
                break;
            }

            var peer = _peer;

            _writeInProgress = true;
            try
            {
                while (true)
                {
                    var msg = input.Current;
                    if (msg == null)
                    {
                        break;
                    }
                    try
                    {
                        // It is possible the peer could have closed while we are writing, and in this case we should
                        // simulate real socket behavior and ensure the write operation is failed.
                        if (peer._state == State.Connected)
                        {
                            peer._inboundBuffer.Enqueue(ReferenceCountUtil.Retain(msg));
                            input.Remove();
                        }
                        else
                        {
                            input.Remove(ClosedChannelException.Instance);
                        }
                    }
                    catch (Exception ex)
                    {
                        input.Remove(ex);
                    }
                }
            }
            finally
            {
                _writeInProgress = false;
            }

            FinishPeerRead(peer);
        }
Пример #9
0
            /**
             * Consume the part of a message.
             *
             * @param byteCount count of byte to be consumed
             * @return the message currently being consumed
             */
            public object ConsumePart(int byteCount)
            {
                ChannelOutboundBuffer buf = this.Unsafe.OutboundBuffer;
                object msg = buf?.Current;

                if (msg != null)
                {
                    ReferenceCountUtil.Retain(msg);
                    buf.RemoveBytes(byteCount);
                    return(msg);
                }
                return(null);
            }
        static object SafeDuplicate(object message)
        {
            if (message is IByteBuffer buffer)
            {
                return(buffer.Duplicate().Retain());
            }

            if (message is IByteBufferHolder byteBufferHolder)
            {
                return(byteBufferHolder.Duplicate().Retain());
            }

            return(ReferenceCountUtil.Retain(message));
        }
Пример #11
0
        static object SafeDuplicate(object message)
        {
            switch (message)
            {
            case IByteBuffer buffer:
                return(buffer.RetainedDuplicate());

            case IByteBufferHolder byteBufferHolder:
                return(byteBufferHolder.RetainedDuplicate());

            default:
                return(ReferenceCountUtil.Retain(message));
            }
        }
Пример #12
0
        protected override void DoWrite(ChannelOutboundBuffer input)
        {
            while (true)
            {
                object msg = input.Current;
                if (msg is null)
                {
                    break;
                }

                ReferenceCountUtil.Retain(msg);
                HandleOutboundMessage(msg);
                input.Remove();
            }
        }
Пример #13
0
        protected override void DoWrite(ChannelOutboundBuffer input)
        {
            for (;;)
            {
                object msg = input.Current;
                if (msg == null)
                {
                    break;
                }

                ReferenceCountUtil.Retain(msg);
                this.OutboundMessages.Enqueue(msg);
                input.Remove();
            }
        }
Пример #14
0
 static object SafeDuplicate(object message)
 {
     if (message is IByteBuffer)
     {
         return(((IByteBuffer)message).Duplicate().Retain());
     }
     else if (message is IByteBufferHolder)
     {
         return(((IByteBufferHolder)message).Duplicate().Retain());
     }
     else
     {
         return(ReferenceCountUtil.Retain(message));
     }
 }
Пример #15
0
            public object Consume()
            {
                ChannelOutboundBuffer buf = Unsafe.OutboundBuffer;

                if (buf != null)
                {
                    Object msg = buf.Current;
                    if (msg != null)
                    {
                        ReferenceCountUtil.Retain(msg);
                        buf.Remove();
                        return(msg);
                    }
                }
                return(null);
            }
Пример #16
0
        protected override void Decode(IChannelHandlerContext ctx, IHttpRequest msg, List <object> output)
        {
            ICharSequence acceptedEncoding = msg.Headers.Get(HttpHeaderNames.AcceptEncoding, HttpContentDecoder.Identity);

            HttpMethod meth = msg.Method;

            if (ReferenceEquals(meth, HttpMethod.Head))
            {
                acceptedEncoding = ZeroLengthHead;
            }
            else if (ReferenceEquals(meth, HttpMethod.Connect))
            {
                acceptedEncoding = ZeroLengthConnect;
            }

            this.acceptEncodingQueue.Enqueue(acceptedEncoding);
            output.Add(ReferenceCountUtil.Retain(msg));
        }
        static object SafeDuplicate(object message)
        {
            var buffer = message as IByteBuffer;

            if (buffer != null)
            {
                return(buffer.Duplicate().Retain());
            }

            var byteBufferHolder = message as IByteBufferHolder;

            if (byteBufferHolder != null)
            {
                return(byteBufferHolder.Duplicate().Retain());
            }

            return(ReferenceCountUtil.Retain(message));
        }
        protected internal override void Decode(IChannelHandlerContext context, IHttpObject message, List <object> output)
        {
            // Determine if we're already handling an upgrade request or just starting a new one.
            this.handlingUpgrade |= IsUpgradeRequest(message);
            if (!this.handlingUpgrade)
            {
                // Not handling an upgrade request, just pass it to the next handler.
                ReferenceCountUtil.Retain(message);
                output.Add(message);
                return;
            }

            if (message is IFullHttpRequest fullRequest)
            {
                ReferenceCountUtil.Retain(fullRequest);
                output.Add(fullRequest);
            }
            else
            {
                // Call the base class to handle the aggregation of the full request.
                base.Decode(context, message, output);
                if (output.Count == 0)
                {
                    // The full request hasn't been created yet, still awaiting more data.
                    return;
                }

                // Finished aggregating the full request, get it from the output list.
                Debug.Assert(output.Count == 1);
                this.handlingUpgrade = false;
                fullRequest          = (IFullHttpRequest)output[0];
            }

            if (this.Upgrade(context, fullRequest))
            {
                // The upgrade was successful, remove the message from the output list
                // so that it's not propagated to the next handler. This request will
                // be propagated as a user event instead.
                output.Clear();
            }

            // The upgrade did not succeed, just allow the full request to propagate to the
            // next handler.
        }
Пример #19
0
        /// <summary>
        /// Performs the opening handshake
        /// When call this method you <c>MUST NOT</c> retain the <see cref="IHttpRequest"/> which is passed in.
        /// </summary>
        /// <param name="channel">Channel</param>
        /// <param name="req">HTTP Request</param>
        /// <param name="responseHeaders">Extra headers to add to the handshake response or <code>null</code> if no extra headers should be added</param>
        /// <returns></returns>
        public Task HandshakeAsync(IChannel channel, IHttpRequest req, HttpHeaders responseHeaders)
        {
            if (req is IFullHttpRequest request)
            {
                return(HandshakeAsync(channel, request, responseHeaders));
            }
            if (Logger.DebugEnabled)
            {
                Logger.WebSocketVersionServerHandshake(channel, _version);
            }
            IChannelPipeline       p   = channel.Pipeline;
            IChannelHandlerContext ctx = p.Context <HttpRequestDecoder>();

            if (ctx is null)
            {
                // this means the user use an HttpServerCodec
                ctx = p.Context <HttpServerCodec>();
                if (ctx is null)
                {
                    return(ThrowHelper.ThrowInvalidOperationException_NoHttpDecoderAndServerCodec());
                }
            }

            // Add aggregator and ensure we feed the HttpRequest so it is aggregated. A limit o 8192 should be more then
            // enough for the websockets handshake payload.
            //
            // TODO: Make handshake work without HttpObjectAggregator at all.
            string aggregatorName = "httpAggregator";

            _ = p.AddAfter(ctx.Name, aggregatorName, new HttpObjectAggregator(8192));
            var completion = channel.NewPromise();

            _ = p.AddAfter(aggregatorName, "handshaker", new Handshaker(this, channel, responseHeaders, completion));
            try
            {
                _ = ctx.FireChannelRead(ReferenceCountUtil.Retain(req));
            }
            catch (Exception cause)
            {
                _ = completion.TrySetException(cause);
            }
            return(completion.Task);
        }
Пример #20
0
        public Task HandshakeAsync(IChannel channel, IHttpRequest req, HttpHeaders responseHeaders)
        {
            if (req is IFullHttpRequest request)
            {
                return(this.HandshakeAsync(channel, request, responseHeaders));
            }
            if (Logger.DebugEnabled)
            {
                Logger.Debug("{} WebSocket version {} server handshake", channel, this.version);
            }
            IChannelPipeline       p   = channel.Pipeline;
            IChannelHandlerContext ctx = p.Context <HttpRequestDecoder>();

            if (ctx == null)
            {
                // this means the user use a HttpServerCodec
                ctx = p.Context <HttpServerCodec>();
                if (ctx == null)
                {
                    return(TaskEx.FromException(new InvalidOperationException("No HttpDecoder and no HttpServerCodec in the pipeline")));
                }
            }

            // Add aggregator and ensure we feed the HttpRequest so it is aggregated. A limit o 8192 should be more then
            // enough for the websockets handshake payload.
            //
            // TODO: Make handshake work without HttpObjectAggregator at all.
            string aggregatorName = "httpAggregator";

            p.AddAfter(ctx.Name, aggregatorName, new HttpObjectAggregator(8192));
            var completion = new TaskCompletionSource();

            p.AddAfter(aggregatorName, "handshaker", new Handshaker(this, channel, responseHeaders, completion));
            try
            {
                ctx.FireChannelRead(ReferenceCountUtil.Retain(req));
            }
            catch (Exception cause)
            {
                completion.TrySetException(cause);
            }
            return(completion.Task);
        }
Пример #21
0
        static object SafeDuplicate(FABMessage message)
        {
            //AbstractByteBuffer buffer = new AbstractByteBuffer(message.DataLength);
            var buffer = message.Data as IByteBuffer;

            if (buffer != null)
            {
                return(buffer.Duplicate().Retain());
            }

            var byteBufferHolder = message.Data as IByteBufferHolder;

            if (byteBufferHolder != null)
            {
                return(byteBufferHolder.Duplicate().Retain());
            }

            return(ReferenceCountUtil.Retain(message));
        }
Пример #22
0
        void EncodeChunkedContent(IChannelHandlerContext context, object message, long contentLength, ICollection <object> output)
        {
            if (contentLength > 0)
            {
                var         lengthHex = new AsciiString(Convert.ToString(contentLength, 16), Encoding.ASCII);
                IByteBuffer buf       = context.Allocator.Buffer(lengthHex.Count + 2);
                buf.WriteCharSequence(lengthHex, Encoding.ASCII);
                buf.WriteShort(HttpConstants.CrlfShort);
                output.Add(buf);
                output.Add(EncodeAndRetain(message));
                output.Add(HttpConstants.CrlfBuf.Duplicate());
            }

            if (message is ILastHttpContent content)
            {
                HttpHeaders headers = content.TrailingHeaders;
                if (headers.IsEmpty)
                {
                    output.Add(HttpConstants.ZeroCrlfCrlfBuf.Duplicate());
                }
                else
                {
                    IByteBuffer buf = context.Allocator.Buffer((int)this.trailersEncodedSizeAccumulator);
                    buf.WriteMedium(HttpConstants.ZeroCrlfMedium);
                    this.EncodeHeaders(headers, buf);
                    buf.WriteShort(HttpConstants.CrlfShort);
                    this.trailersEncodedSizeAccumulator = TrailersWeightNew * PadSizeForAccumulation(buf.ReadableBytes)
                                                          + TrailersWeightHistorical * this.trailersEncodedSizeAccumulator;
                    output.Add(buf);
                }
            }
            else if (contentLength == 0)
            {
                // Need to produce some output otherwise an
                // IllegalstateException will be thrown
                output.Add(ReferenceCountUtil.Retain(message));
            }
        }
Пример #23
0
        /// <inheritdoc />
        protected override void Decode(IChannelHandlerContext ctx, IHttpRequest msg, List <object> output)
        {
            var           acceptEncodingHeaders = msg.Headers.GetAll(HttpHeaderNames.AcceptEncoding);
            ICharSequence acceptEncoding        = acceptEncodingHeaders.Count switch
            {
                0 => HttpContentDecoder.Identity,
                1 => acceptEncodingHeaders[0],
                _ => StringUtil.Join(",", acceptEncodingHeaders),// Multiple message-header fields https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
            };
            HttpMethod meth = msg.Method;

            if (HttpMethod.Head.Equals(meth))
            {
                acceptEncoding = ZeroLengthHead;
            }
            else if (HttpMethod.Connect.Equals(meth))
            {
                acceptEncoding = ZeroLengthConnect;
            }

            _acceptEncodingQueue.AddToBack(acceptEncoding);
            output.Add(ReferenceCountUtil.Retain(msg));
        }
Пример #24
0
        protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
        {
            if (msg is Packet message)
            {
                switch (message.PacketType)
                {
                case PacketType.PINGREQ:
                    HandlePingReq(ctx.Channel);
                    break;

                case PacketType.PINGRESP:
                    HandlePingResp();
                    break;

                default:
                    ctx.FireChannelRead(ReferenceCountUtil.Retain(msg));
                    break;
                }
            }
            else
            {
                ctx.FireChannelRead(msg);
            }
        }
 public virtual IReferenceCounted Retain()
 {
     ReferenceCountUtil.Retain(this.Content);
     return(this);
 }
 public virtual IReferenceCounted Retain(int increment)
 {
     ReferenceCountUtil.Retain(this.Content, increment);
     return(this);
 }
        public override void Decode(IChannelHandlerContext context, IHttpObject message, List <object> output)
        {
            if (message is IHttpResponse response && response.Status.Code == 100)
            {
                if (!(response is ILastHttpContent))
                {
                    this.continueResponse = true;
                }
                // 100-continue response must be passed through.
                output.Add(ReferenceCountUtil.Retain(message));
                return;
            }

            if (this.continueResponse)
            {
                if (message is ILastHttpContent)
                {
                    this.continueResponse = false;
                }
                // 100-continue response must be passed through.
                output.Add(ReferenceCountUtil.Retain(message));
                return;
            }

            if (message is IHttpMessage httpMessage)
            {
                this.Cleanup();
                HttpHeaders headers = httpMessage.Headers;

                // Determine the content encoding.
                if (headers.TryGet(HttpHeaderNames.ContentEncoding, out ICharSequence contentEncoding))
                {
                    contentEncoding = AsciiString.Trim(contentEncoding);
                }
                else
                {
                    contentEncoding = Identity;
                }
                this.decoder = this.NewContentDecoder(contentEncoding);

                if (this.decoder == null)
                {
                    if (httpMessage is IHttpContent httpContent)
                    {
                        httpContent.Retain();
                    }
                    output.Add(httpMessage);
                    return;
                }

                // Remove content-length header:
                // the correct value can be set only after all chunks are processed/decoded.
                // If buffering is not an issue, add HttpObjectAggregator down the chain, it will set the header.
                // Otherwise, rely on LastHttpContent message.
                if (headers.Contains(HttpHeaderNames.ContentLength))
                {
                    headers.Remove(HttpHeaderNames.ContentLength);
                    headers.Set(HttpHeaderNames.TransferEncoding, HttpHeaderValues.Chunked);
                }
                // Either it is already chunked or EOF terminated.
                // See https://github.com/netty/netty/issues/5892

                // set new content encoding,
                ICharSequence targetContentEncoding = this.GetTargetContentEncoding(contentEncoding);
                if (HttpHeaderValues.Identity.ContentEquals(targetContentEncoding))
                {
                    // Do NOT set the 'Content-Encoding' header if the target encoding is 'identity'
                    // as per: http://tools.ietf.org/html/rfc2616#section-14.11
                    headers.Remove(HttpHeaderNames.ContentEncoding);
                }
                else
                {
                    headers.Set(HttpHeaderNames.ContentEncoding, targetContentEncoding);
                }

                if (httpMessage is IHttpContent)
                {
                    // If message is a full request or response object (headers + data), don't copy data part into out.
                    // Output headers only; data part will be decoded below.
                    // Note: "copy" object must not be an instance of LastHttpContent class,
                    // as this would (erroneously) indicate the end of the HttpMessage to other handlers.
                    IHttpMessage copy;
                    if (httpMessage is IHttpRequest req)
                    {
                        // HttpRequest or FullHttpRequest
                        copy = new DefaultHttpRequest(req.ProtocolVersion, req.Method, req.Uri);
                    }
                    else if (httpMessage is IHttpResponse res)
                    {
                        // HttpResponse or FullHttpResponse
                        copy = new DefaultHttpResponse(res.ProtocolVersion, res.Status);
                    }
                    else
                    {
                        throw new CodecException($"Object of class {StringUtil.SimpleClassName(httpMessage.GetType())} is not a HttpRequest or HttpResponse");
                    }
                    copy.Headers.Set(httpMessage.Headers);
                    copy.Result = httpMessage.Result;
                    output.Add(copy);
                }
                else
                {
                    output.Add(httpMessage);
                }
            }

            if (message is IHttpContent c)
            {
                if (this.decoder == null)
                {
                    output.Add(c.Retain());
                }
                else
                {
                    this.DecodeContent(c, output);
                }
            }
        }
Пример #28
0
        protected override void Encode(IChannelHandlerContext ctx, IHttpObject msg, List <object> output)
        {
            bool isFull = msg is IHttpResponse && msg is ILastHttpContent;

            switch (this.state)
            {
            case State.AwaitHeaders:
            {
                EnsureHeaders(msg);
                Debug.Assert(this.encoder == null);

                var           res  = (IHttpResponse)msg;
                int           code = res.Status.Code;
                ICharSequence acceptEncoding;
                if (code == ContinueCode)
                {
                    // We need to not poll the encoding when response with CONTINUE as another response will follow
                    // for the issued request. See https://github.com/netty/netty/issues/4079
                    acceptEncoding = null;
                }
                else
                {
                    // Get the list of encodings accepted by the peer.
                    acceptEncoding = this.acceptEncodingQueue.Count > 0 ? this.acceptEncodingQueue.Dequeue() : null;
                    if (acceptEncoding == null)
                    {
                        throw new InvalidOperationException("cannot send more responses than requests");
                    }
                }

                //
                // per rfc2616 4.3 Message Body
                // All 1xx (informational), 204 (no content), and 304 (not modified) responses MUST NOT include a
                // message-body. All other responses do include a message-body, although it MAY be of zero length.
                //
                // 9.4 HEAD
                // The HEAD method is identical to GET except that the server MUST NOT return a message-body
                // in the response.
                //
                // Also we should pass through HTTP/1.0 as transfer-encoding: chunked is not supported.
                //
                // See https://github.com/netty/netty/issues/5382
                //
                if (IsPassthru(res.ProtocolVersion, code, acceptEncoding))
                {
                    if (isFull)
                    {
                        output.Add(ReferenceCountUtil.Retain(res));
                    }
                    else
                    {
                        output.Add(res);
                        // Pass through all following contents.
                        this.state = State.PassThrough;
                    }
                    break;
                }

                if (isFull)
                {
                    // Pass through the full response with empty content and continue waiting for the the next resp.
                    if (!((IByteBufferHolder)res).Content.IsReadable())
                    {
                        output.Add(ReferenceCountUtil.Retain(res));
                        break;
                    }
                }

                // Prepare to encode the content.
                Result result = this.BeginEncode(res, acceptEncoding);

                // If unable to encode, pass through.
                if (result == null)
                {
                    if (isFull)
                    {
                        output.Add(ReferenceCountUtil.Retain(res));
                    }
                    else
                    {
                        output.Add(res);
                        // Pass through all following contents.
                        this.state = State.PassThrough;
                    }
                    break;
                }

                this.encoder = result.ContentEncoder;

                // Encode the content and remove or replace the existing headers
                // so that the message looks like a decoded message.
                res.Headers.Set(HttpHeaderNames.ContentEncoding, result.TargetContentEncoding);

                // Output the rewritten response.
                if (isFull)
                {
                    // Convert full message into unfull one.
                    var newRes = new DefaultHttpResponse(res.ProtocolVersion, res.Status);
                    newRes.Headers.Set(res.Headers);
                    output.Add(newRes);

                    EnsureContent(res);
                    this.EncodeFullResponse(newRes, (IHttpContent)res, output);
                    break;
                }
                else
                {
                    // Make the response chunked to simplify content transformation.
                    res.Headers.Remove(HttpHeaderNames.ContentLength);
                    res.Headers.Set(HttpHeaderNames.TransferEncoding, HttpHeaderValues.Chunked);

                    output.Add(res);
                    this.state = State.AwaitContent;

                    if (!(msg is IHttpContent))
                    {
                        // only break out the switch statement if we have not content to process
                        // See https://github.com/netty/netty/issues/2006
                        break;
                    }
                    // Fall through to encode the content
                    goto case State.AwaitContent;
                }
            }

            case State.AwaitContent:
            {
                EnsureContent(msg);
                if (this.EncodeContent((IHttpContent)msg, output))
                {
                    this.state = State.AwaitHeaders;
                }
                break;
            }

            case State.PassThrough:
            {
                EnsureContent(msg);
                output.Add(ReferenceCountUtil.Retain(msg));
                // Passed through all following contents of the current response.
                if (msg is ILastHttpContent)
                {
                    this.state = State.AwaitHeaders;
                }
                break;
            }
            }
        }
Пример #29
0
        protected override void Decode(IChannelHandlerContext context, IHttpObject message, List <object> output)
        {
            try
            {
                if (message is IHttpResponse response && response.Status.Code == StatusCodes.Status100Continue)
                {
                    if (!(response is ILastHttpContent))
                    {
                        _continueResponse = true;
                    }
                    // 100-continue response must be passed through.
                    output.Add(ReferenceCountUtil.Retain(message));
                    return;
                }

                if (_continueResponse)
                {
                    if (message is ILastHttpContent)
                    {
                        _continueResponse = false;
                    }
                    // 100-continue response must be passed through.
                    output.Add(ReferenceCountUtil.Retain(message));
                    return;
                }

                var httpContent = message as IHttpContent;
                if (message is IHttpMessage httpMessage)
                {
                    Cleanup();
                    HttpHeaders headers = httpMessage.Headers;

                    // Determine the content encoding.
                    if (headers.TryGet(HttpHeaderNames.ContentEncoding, out ICharSequence contentEncoding))
                    {
                        contentEncoding = AsciiString.Trim(contentEncoding);
                    }
                    else
                    {
                        if (headers.TryGet(HttpHeaderNames.TransferEncoding, out var transferEncoding))
                        {
                            int idx = transferEncoding.IndexOf(HttpConstants.CommaChar);
                            if (SharedConstants.TooBigOrNegative >= (uint)idx) // != -1
                            {
                                contentEncoding = AsciiString.Trim(transferEncoding.SubSequence(0, idx));
                            }
                            else
                            {
                                contentEncoding = AsciiString.Trim(transferEncoding);
                            }
                        }
                        else
                        {
                            contentEncoding = Identity;
                        }
                        //contentEncoding = Identity;
                    }
                    _decoder = NewContentDecoder(contentEncoding);

                    if (_decoder is null)
                    {
                        if (httpContent is object)
                        {
                            _ = httpContent.Retain();
                        }
                        output.Add(httpMessage);
                        return;
                    }

                    // Remove content-length header:
                    // the correct value can be set only after all chunks are processed/decoded.
                    // If buffering is not an issue, add HttpObjectAggregator down the chain, it will set the header.
                    // Otherwise, rely on LastHttpContent message.
                    if (headers.Contains(HttpHeaderNames.ContentLength))
                    {
                        _ = headers.Remove(HttpHeaderNames.ContentLength);
                        _ = headers.Set(HttpHeaderNames.TransferEncoding, HttpHeaderValues.Chunked);
                    }
                    // Either it is already chunked or EOF terminated.
                    // See https://github.com/netty/netty/issues/5892

                    // set new content encoding,
                    ICharSequence targetContentEncoding = GetTargetContentEncoding(contentEncoding);
                    if (HttpHeaderValues.Identity.ContentEquals(targetContentEncoding))
                    {
                        // Do NOT set the 'Content-Encoding' header if the target encoding is 'identity'
                        // as per: http://tools.ietf.org/html/rfc2616#section-14.11
                        _ = headers.Remove(HttpHeaderNames.ContentEncoding);
                    }
                    else
                    {
                        _ = headers.Set(HttpHeaderNames.ContentEncoding, targetContentEncoding);
                    }

                    if (httpContent is object)
                    {
                        // If message is a full request or response object (headers + data), don't copy data part into out.
                        // Output headers only; data part will be decoded below.
                        // Note: "copy" object must not be an instance of LastHttpContent class,
                        // as this would (erroneously) indicate the end of the HttpMessage to other handlers.
                        IHttpMessage copy = null;
                        switch (httpMessage)
                        {
                        case IHttpRequest req:
                            // HttpRequest or FullHttpRequest
                            copy = new DefaultHttpRequest(req.ProtocolVersion, req.Method, req.Uri);
                            break;

                        case IHttpResponse res:
                            // HttpResponse or FullHttpResponse
                            copy = new DefaultHttpResponse(res.ProtocolVersion, res.Status);
                            break;

                        default:
                            ThrowHelper.ThrowCodecException_InvalidHttpMsg(httpMessage);
                            break;
                        }
                        _           = copy.Headers.Set(httpMessage.Headers);
                        copy.Result = httpMessage.Result;
                        output.Add(copy);
                    }
                    else
                    {
                        output.Add(httpMessage);
                    }
                }

                if (httpContent is object)
                {
                    if (_decoder is null)
                    {
                        output.Add(httpContent.Retain());
                    }
                    else
                    {
                        DecodeContent(httpContent, output);
                    }
                }
            }
            finally
            {
                _needRead = 0u >= (uint)output.Count;
            }
        }