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); } } }
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); } }
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); }
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); }
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); } } }
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)); }
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); }
/** * 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)); }
static object SafeDuplicate(object message) { switch (message) { case IByteBuffer buffer: return(buffer.RetainedDuplicate()); case IByteBufferHolder byteBufferHolder: return(byteBufferHolder.RetainedDuplicate()); default: return(ReferenceCountUtil.Retain(message)); } }
protected override void DoWrite(ChannelOutboundBuffer input) { while (true) { object msg = input.Current; if (msg is null) { break; } ReferenceCountUtil.Retain(msg); HandleOutboundMessage(msg); input.Remove(); } }
protected override void DoWrite(ChannelOutboundBuffer input) { for (;;) { object msg = input.Current; if (msg == null) { break; } ReferenceCountUtil.Retain(msg); this.OutboundMessages.Enqueue(msg); input.Remove(); } }
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)); } }
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); }
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. }
/// <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); }
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); }
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)); }
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)); } }
/// <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)); }
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); } } }
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; } } }
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; } }