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