1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.codec.http;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.channel.ChannelHandlerContext;
20 import io.netty5.channel.CombinedChannelDuplexHandler;
21 import io.netty5.channel.internal.DelegatingChannelHandlerContext;
22
23 import java.util.ArrayDeque;
24 import java.util.Queue;
25
26 import static io.netty5.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_HEADER_SIZE;
27 import static io.netty5.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_INITIAL_LINE_LENGTH;
28
29
30
31
32
33
34
35 public final class HttpServerCodec extends CombinedChannelDuplexHandler<HttpRequestDecoder, HttpResponseEncoder>
36 implements HttpServerUpgradeHandler.SourceCodec {
37
38
39 private final Queue<HttpMethod> queue = new ArrayDeque<>();
40
41
42
43
44
45
46 public HttpServerCodec() {
47 this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE);
48 }
49
50
51
52
53 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize) {
54 init(new HttpServerRequestDecoder(maxInitialLineLength, maxHeaderSize),
55 new HttpServerResponseEncoder());
56 }
57
58
59
60
61 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders) {
62 init(new HttpServerRequestDecoder(maxInitialLineLength, maxHeaderSize, validateHeaders),
63 new HttpServerResponseEncoder());
64 }
65
66
67
68
69 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders,
70 int initialBufferSize) {
71 init(
72 new HttpServerRequestDecoder(maxInitialLineLength, maxHeaderSize,
73 validateHeaders, initialBufferSize),
74 new HttpServerResponseEncoder());
75 }
76
77
78
79
80 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders,
81 int initialBufferSize, boolean allowDuplicateContentLengths) {
82 init(new HttpServerRequestDecoder(maxInitialLineLength, maxHeaderSize, validateHeaders,
83 initialBufferSize, allowDuplicateContentLengths),
84 new HttpServerResponseEncoder());
85 }
86
87
88
89
90
91 @Override
92 public void upgradeFrom(ChannelHandlerContext ctx) {
93 ctx.pipeline().remove(this);
94 }
95
96 private final class HttpServerRequestDecoder extends HttpRequestDecoder {
97
98 private ChannelHandlerContext context;
99
100 HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize) {
101 super(maxInitialLineLength, maxHeaderSize);
102 }
103
104 HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize,
105 boolean validateHeaders) {
106 super(maxInitialLineLength, maxHeaderSize, validateHeaders);
107 }
108
109 HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize,
110 boolean validateHeaders, int initialBufferSize) {
111 super(maxInitialLineLength, maxHeaderSize, validateHeaders, initialBufferSize);
112 }
113
114 @Override
115 protected void decode(final ChannelHandlerContext ctx, Buffer buffer) throws Exception {
116 super.decode(context, buffer);
117 }
118
119 HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize,
120 boolean validateHeaders, int initialBufferSize, boolean allowDuplicateContentLengths) {
121 super(maxInitialLineLength, maxHeaderSize, validateHeaders, initialBufferSize,
122 allowDuplicateContentLengths);
123 }
124
125 @Override
126 protected void handlerAdded0(final ChannelHandlerContext ctx) {
127 context = new DelegatingChannelHandlerContext(ctx) {
128
129 @Override
130 public ChannelHandlerContext fireChannelRead(Object msg) {
131 if (msg instanceof HttpRequest) {
132 queue.add(((HttpRequest) msg).method());
133 }
134 super.fireChannelRead(msg);
135 return this;
136 }
137 };
138 }
139 }
140
141 private final class HttpServerResponseEncoder extends HttpResponseEncoder {
142
143 private HttpMethod method;
144
145 @Override
146 protected void sanitizeHeadersBeforeEncode(HttpResponse msg, boolean isAlwaysEmpty) {
147 if (!isAlwaysEmpty && HttpMethod.CONNECT.equals(method)
148 && msg.status().codeClass() == HttpStatusClass.SUCCESS) {
149
150
151 msg.headers().remove(HttpHeaderNames.TRANSFER_ENCODING);
152 return;
153 }
154
155 super.sanitizeHeadersBeforeEncode(msg, isAlwaysEmpty);
156 }
157
158 @Override
159 protected boolean isContentAlwaysEmpty(@SuppressWarnings("unused") HttpResponse msg) {
160 method = queue.poll();
161 return HttpMethod.HEAD.equals(method) || super.isContentAlwaysEmpty(msg);
162 }
163 }
164 }