protected internal override void Encode(IChannelHandlerContext context, object message, List <object> output) { IByteBuffer buf = null; if (message is IHttpMessage) { if (this.state != StInit) { throw new InvalidOperationException($"unexpected message type: {StringUtil.SimpleClassName(message)}"); } 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; } } if (message is IHttpContent || message is IByteBuffer || message is IFileRegion) { switch (this.state) { case StInit: throw new InvalidOperationException($"unexpected message type: {StringUtil.SimpleClassName(message)}"); case StContentNonChunk: long contentLength = ContentLength(message); if (contentLength > 0) { if (buf != null && buf.WritableBytes >= contentLength && message is IHttpContent) { // merge into other buffer for performance reasons buf.WriteBytes(((IHttpContent)message).Content); output.Add(buf); } else { if (buf != null) { 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 != null) { // 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 != null) { // We allocated a buffer so add it now. output.Add(buf); } this.EncodeChunkedContent(context, message, ContentLength(message), output); break; default: throw new EncoderException($"unexpected state {this.state}: {StringUtil.SimpleClassName(message)}"); } if (message is ILastHttpContent) { this.state = StInit; } } else if (buf != null) { output.Add(buf); } }
State?ReadHeaders(IByteBuffer buffer) { IHttpMessage httpMessage = this.message; HttpHeaders headers = httpMessage.Headers; AppendableCharSequence line = this.headerParser.Parse(buffer); if (line == null) { return(null); } // ReSharper disable once ConvertIfDoToWhile if (line.Count > 0) { do { byte firstChar = line.Bytes[0]; if (this.name != null && (firstChar == ' ' || firstChar == '\t')) { ICharSequence trimmedLine = CharUtil.Trim(line); this.value = new AsciiString($"{this.value} {trimmedLine}"); } else { if (this.name != null) { headers.Add(this.name, this.value); } this.SplitHeader(line); } line = this.headerParser.Parse(buffer); if (line == null) { return(null); } } while (line.Count > 0); } // Add the last header. if (this.name != null) { headers.Add(this.name, this.value); } // reset name and value fields this.name = null; this.value = null; State nextState; if (this.IsContentAlwaysEmpty(httpMessage)) { HttpUtil.SetTransferEncodingChunked(httpMessage, false); nextState = State.SkipControlChars; } else if (HttpUtil.IsTransferEncodingChunked(httpMessage)) { nextState = State.ReadChunkSize; } else if (this.ContentLength() >= 0) { nextState = State.ReadFixedLengthContent; } else { nextState = State.ReadVariableLengthContent; } return(nextState); }