static long ContentLength(object message) { switch (message) { case IHttpContent content: return(content.Content.ReadableBytes); case IByteBuffer buffer: return(buffer.ReadableBytes); case IFileRegion region: return(region.Count); default: ThrowHelper.ThrowInvalidOperationException_UnexpectedMsg(message); return(default); } }
static object EncodeAndRetain(object message) { switch (message) { case IByteBuffer buffer: return(buffer.Retain()); case IHttpContent content: return(content.Content.Retain()); case IFileRegion region: return(region.Retain()); default: ThrowHelper.ThrowInvalidOperationException_UnexpectedMsg(message); return(null); } }
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; } }