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
54 private final Queue<HttpMethod> queue = new ArrayDeque<HttpMethod>();
55
56
57
58
59 private boolean mustCloseAfterResponse;
60
61
62
63
64
65
66 public HttpServerCodec() {
67 this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE, DEFAULT_MAX_CHUNK_SIZE);
68 }
69
70
71
72
73 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
74 this(new HttpDecoderConfig()
75 .setMaxInitialLineLength(maxInitialLineLength)
76 .setMaxHeaderSize(maxHeaderSize)
77 .setMaxChunkSize(maxChunkSize));
78 }
79
80
81
82
83
84
85
86 @Deprecated
87 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders) {
88 this(new HttpDecoderConfig()
89 .setMaxInitialLineLength(maxInitialLineLength)
90 .setMaxHeaderSize(maxHeaderSize)
91 .setMaxChunkSize(maxChunkSize)
92 .setValidateHeaders(validateHeaders));
93 }
94
95
96
97
98
99
100
101 @Deprecated
102 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
103 int initialBufferSize) {
104 this(new HttpDecoderConfig()
105 .setMaxInitialLineLength(maxInitialLineLength)
106 .setMaxHeaderSize(maxHeaderSize)
107 .setMaxChunkSize(maxChunkSize)
108 .setValidateHeaders(validateHeaders)
109 .setInitialBufferSize(initialBufferSize));
110 }
111
112
113
114
115
116
117
118 @Deprecated
119 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
120 int initialBufferSize, boolean allowDuplicateContentLengths) {
121 this(new HttpDecoderConfig()
122 .setMaxInitialLineLength(maxInitialLineLength)
123 .setMaxHeaderSize(maxHeaderSize)
124 .setMaxChunkSize(maxChunkSize)
125 .setValidateHeaders(validateHeaders)
126 .setInitialBufferSize(initialBufferSize)
127 .setAllowDuplicateContentLengths(allowDuplicateContentLengths));
128 }
129
130
131
132
133
134
135
136 @Deprecated
137 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
138 int initialBufferSize, boolean allowDuplicateContentLengths, boolean allowPartialChunks) {
139 this(new HttpDecoderConfig()
140 .setMaxInitialLineLength(maxInitialLineLength)
141 .setMaxHeaderSize(maxHeaderSize)
142 .setMaxChunkSize(maxChunkSize)
143 .setValidateHeaders(validateHeaders)
144 .setInitialBufferSize(initialBufferSize)
145 .setAllowDuplicateContentLengths(allowDuplicateContentLengths)
146 .setAllowPartialChunks(allowPartialChunks));
147 }
148
149
150
151
152 public HttpServerCodec(HttpDecoderConfig config) {
153 init(new HttpServerRequestDecoder(config), new HttpServerResponseEncoder());
154 }
155
156
157
158
159
160 @Override
161 public void upgradeFrom(ChannelHandlerContext ctx) {
162 ctx.pipeline().remove(this);
163 }
164
165 private final class HttpServerRequestDecoder extends HttpRequestDecoder {
166 HttpServerRequestDecoder(HttpDecoderConfig config) {
167 super(config);
168 }
169
170 @Override
171 protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
172 int oldSize = out.size();
173 super.decode(ctx, buffer, out);
174 int size = out.size();
175 for (int i = oldSize; i < size; i++) {
176 Object obj = out.get(i);
177 if (obj instanceof HttpRequest) {
178 queue.add(((HttpRequest) obj).method());
179 }
180 }
181 }
182
183 @Override
184 protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) {
185 super.handleTransferEncodingChunkedWithContentLength(message);
186 mustCloseAfterResponse = true;
187 }
188 }
189
190 private final class HttpServerResponseEncoder extends HttpResponseEncoder {
191
192 private HttpMethod method;
193
194 @Override
195 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
196 if (mustCloseAfterResponse && msg instanceof LastHttpContent) {
197 mustCloseAfterResponse = false;
198 promise = promise.unvoid().addListener(ChannelFutureListener.CLOSE);
199 }
200 super.write(ctx, msg, promise);
201 }
202
203 @Override
204 protected void sanitizeHeadersBeforeEncode(HttpResponse msg, boolean isAlwaysEmpty) {
205 if (!isAlwaysEmpty && HttpMethod.CONNECT.equals(method)
206 && msg.status().codeClass() == HttpStatusClass.SUCCESS) {
207
208
209 msg.headers().remove(HttpHeaderNames.TRANSFER_ENCODING);
210 return;
211 }
212
213 super.sanitizeHeadersBeforeEncode(msg, isAlwaysEmpty);
214 }
215
216 @Override
217 protected boolean isContentAlwaysEmpty(@SuppressWarnings("unused") HttpResponse msg) {
218 if (msg.status().codeClass() == HttpStatusClass.INFORMATIONAL) {
219
220
221
222 return super.isContentAlwaysEmpty(msg);
223 }
224 method = queue.poll();
225 return HttpMethod.HEAD.equals(method) || super.isContentAlwaysEmpty(msg);
226 }
227 }
228 }