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