1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelFutureListener;
20 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.channel.ChannelPromise;
22 import io.netty.channel.CombinedChannelDuplexHandler;
23
24 import java.util.ArrayDeque;
25 import java.util.List;
26 import java.util.Queue;
27
28 import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_CHUNK_SIZE;
29 import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_HEADER_SIZE;
30 import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_INITIAL_LINE_LENGTH;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public final class HttpServerCodec extends CombinedChannelDuplexHandler<HttpRequestDecoder, HttpResponseEncoder>
51 implements HttpServerUpgradeHandler.SourceCodec {
52
53 private static final byte METHOD_FLAG_HEAD = 1;
54 private static final byte METHOD_FLAG_CONNECT = 2;
55 private static final byte METHOD_FLAG_OTHER = 3;
56
57
58
59 private static final int METHOD_FLAG_BITS = 2;
60 private static final int INLINE_QUEUE_CAPACITY = Long.SIZE / METHOD_FLAG_BITS;
61
62
63
64
65
66
67
68
69
70
71 private long methodQueue;
72 private int methodQueueSize;
73 private Queue<Byte> methodOverflowQueue;
74
75
76
77
78 private boolean mustCloseAfterResponse;
79
80
81
82
83
84
85 public HttpServerCodec() {
86 this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE, DEFAULT_MAX_CHUNK_SIZE);
87 }
88
89
90
91
92 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
93 this(new HttpDecoderConfig()
94 .setMaxInitialLineLength(maxInitialLineLength)
95 .setMaxHeaderSize(maxHeaderSize)
96 .setMaxChunkSize(maxChunkSize));
97 }
98
99
100
101
102
103
104
105 @Deprecated
106 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders) {
107 this(new HttpDecoderConfig()
108 .setMaxInitialLineLength(maxInitialLineLength)
109 .setMaxHeaderSize(maxHeaderSize)
110 .setMaxChunkSize(maxChunkSize)
111 .setValidateHeaders(validateHeaders));
112 }
113
114
115
116
117
118
119
120 @Deprecated
121 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
122 int initialBufferSize) {
123 this(new HttpDecoderConfig()
124 .setMaxInitialLineLength(maxInitialLineLength)
125 .setMaxHeaderSize(maxHeaderSize)
126 .setMaxChunkSize(maxChunkSize)
127 .setValidateHeaders(validateHeaders)
128 .setInitialBufferSize(initialBufferSize));
129 }
130
131
132
133
134
135
136
137 @Deprecated
138 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
139 int initialBufferSize, boolean allowDuplicateContentLengths) {
140 this(new HttpDecoderConfig()
141 .setMaxInitialLineLength(maxInitialLineLength)
142 .setMaxHeaderSize(maxHeaderSize)
143 .setMaxChunkSize(maxChunkSize)
144 .setValidateHeaders(validateHeaders)
145 .setInitialBufferSize(initialBufferSize)
146 .setAllowDuplicateContentLengths(allowDuplicateContentLengths));
147 }
148
149
150
151
152
153
154
155 @Deprecated
156 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
157 int initialBufferSize, boolean allowDuplicateContentLengths, boolean allowPartialChunks) {
158 this(new HttpDecoderConfig()
159 .setMaxInitialLineLength(maxInitialLineLength)
160 .setMaxHeaderSize(maxHeaderSize)
161 .setMaxChunkSize(maxChunkSize)
162 .setValidateHeaders(validateHeaders)
163 .setInitialBufferSize(initialBufferSize)
164 .setAllowDuplicateContentLengths(allowDuplicateContentLengths)
165 .setAllowPartialChunks(allowPartialChunks));
166 }
167
168
169
170
171 public HttpServerCodec(HttpDecoderConfig config) {
172 init(new HttpServerRequestDecoder(config), new HttpServerResponseEncoder());
173 }
174
175
176
177
178
179 @Override
180 public void upgradeFrom(ChannelHandlerContext ctx) {
181 ctx.pipeline().remove(this);
182 }
183
184 private void enqueueMethod(HttpMethod method) {
185 final byte flag;
186 if (HttpMethod.HEAD.equals(method)) {
187 flag = METHOD_FLAG_HEAD;
188 } else if (HttpMethod.CONNECT.equals(method)) {
189 flag = METHOD_FLAG_CONNECT;
190 } else {
191 flag = METHOD_FLAG_OTHER;
192 }
193
194
195 Queue<Byte> overflowQueue = methodOverflowQueue;
196 if (overflowQueue != null) {
197 overflowQueue.add(flag);
198 return;
199 }
200
201 if (methodQueueSize < INLINE_QUEUE_CAPACITY) {
202 methodQueue |= (long) flag << (methodQueueSize << 1);
203 methodQueueSize++;
204 } else {
205 overflowQueue = new ArrayDeque<>(4);
206 overflowQueue.add(flag);
207 methodOverflowQueue = overflowQueue;
208 }
209 }
210
211 private byte pollMethod() {
212 if (methodQueueSize != 0) {
213
214 byte flag = (byte) (methodQueue & 0x3L);
215 methodQueue >>>= METHOD_FLAG_BITS;
216 methodQueueSize--;
217 return flag;
218 }
219
220 Queue<Byte> overflowQueue = methodOverflowQueue;
221 if (overflowQueue != null) {
222 Byte flag = overflowQueue.poll();
223 if (overflowQueue.isEmpty()) {
224 methodOverflowQueue = null;
225 }
226 return flag != null ? flag : METHOD_FLAG_OTHER;
227 }
228
229 return METHOD_FLAG_OTHER;
230 }
231
232 private final class HttpServerRequestDecoder extends HttpRequestDecoder {
233 HttpServerRequestDecoder(HttpDecoderConfig config) {
234 super(config);
235 }
236
237 @Override
238 protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
239 int oldSize = out.size();
240 super.decode(ctx, buffer, out);
241 int size = out.size();
242 for (int i = oldSize; i < size; i++) {
243 Object obj = out.get(i);
244 if (obj instanceof HttpRequest) {
245 enqueueMethod(((HttpRequest) obj).method());
246 }
247 }
248 }
249
250 @Override
251 protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) {
252 super.handleTransferEncodingChunkedWithContentLength(message);
253 mustCloseAfterResponse = true;
254 }
255 }
256
257 private final class HttpServerResponseEncoder extends HttpResponseEncoder {
258
259 private byte methodFlag;
260
261 @Override
262 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
263 if (mustCloseAfterResponse && msg instanceof LastHttpContent) {
264 mustCloseAfterResponse = false;
265 promise = promise.unvoid().addListener(ChannelFutureListener.CLOSE);
266 }
267 super.write(ctx, msg, promise);
268 }
269
270 @Override
271 protected void sanitizeHeadersBeforeEncode(HttpResponse msg, boolean isAlwaysEmpty) {
272 if (!isAlwaysEmpty && methodFlag == METHOD_FLAG_CONNECT
273 && msg.status().codeClass() == HttpStatusClass.SUCCESS) {
274
275
276 msg.headers().remove(HttpHeaderNames.TRANSFER_ENCODING);
277 return;
278 }
279
280 super.sanitizeHeadersBeforeEncode(msg, isAlwaysEmpty);
281 }
282
283 @Override
284 protected boolean isContentAlwaysEmpty(HttpResponse msg) {
285 methodFlag = pollMethod();
286 return methodFlag == METHOD_FLAG_HEAD || super.isContentAlwaysEmpty(msg);
287 }
288 }
289 }