Skip to content

Commit 22c20ba

Browse files
simonbaslesmaldini
authored andcommitted
fix #150 Expose Netty HttpServerCodec options in HttpServer
This commits exposes options from Netty's HttpServerCodec (that is maxInitialLineLength, maxHeaderSize and maxChunkSize) through our HttpServerOptions. This should allow the codec to decode requests on very long URIs.
1 parent 1173f35 commit 22c20ba

File tree

3 files changed

+260
-9
lines changed

3 files changed

+260
-9
lines changed

src/main/java/reactor/ipc/netty/http/server/HttpServer.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,10 @@ protected ContextHandler<Channel> doHandler(
254254

255255
@Override
256256
public void accept(ChannelPipeline p, ContextHandler<Channel> c) {
257-
p.addLast(NettyPipeline.HttpCodec, new HttpServerCodec());
257+
p.addLast(NettyPipeline.HttpCodec, new HttpServerCodec(
258+
options.httpCodecMaxInitialLineLength(),
259+
options.httpCodecMaxHeaderSize(),
260+
options.httpCodecMaxChunkSize()));
258261

259262
if(options.minCompressionResponseSize() >= 0) {
260263
p.addLast(NettyPipeline.CompressionHandler, new CompressionHandler(options.minCompressionResponseSize()));

src/main/java/reactor/ipc/netty/http/server/HttpServerOptions.java

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,16 @@ public static HttpServerOptions.Builder builder() {
3838
}
3939

4040
private final int minCompressionResponseSize;
41+
private final int maxInitialLineLength;
42+
private final int maxHeaderSize;
43+
private final int maxChunkSize;
4144

4245
private HttpServerOptions(HttpServerOptions.Builder builder) {
4346
super(builder);
4447
this.minCompressionResponseSize = builder.minCompressionResponseSize;
48+
this.maxInitialLineLength = builder.maxInitialLineLength;
49+
this.maxHeaderSize = builder.maxHeaderSize;
50+
this.maxChunkSize = builder.maxChunkSize;
4551
}
4652

4753
/**
@@ -54,6 +60,36 @@ public int minCompressionResponseSize() {
5460
return minCompressionResponseSize;
5561
}
5662

63+
/**
64+
* Returns the maximum length configured for the initial HTTP line.
65+
*
66+
* @return the initial HTTP line maximum length
67+
* @see io.netty.handler.codec.http.HttpServerCodec
68+
*/
69+
public int httpCodecMaxInitialLineLength() {
70+
return maxInitialLineLength;
71+
}
72+
73+
/**
74+
* Returns the configured HTTP header maximum size.
75+
*
76+
* @return the configured HTTP header maximum size
77+
* @see io.netty.handler.codec.http.HttpServerCodec
78+
*/
79+
public int httpCodecMaxHeaderSize() {
80+
return maxHeaderSize;
81+
}
82+
83+
/**
84+
* Returns the configured HTTP chunk maximum size.
85+
*
86+
* @return the configured HTTP chunk maximum size
87+
* @see io.netty.handler.codec.http.HttpServerCodec
88+
*/
89+
public int httpCodecMaxChunkSize() {
90+
return maxChunkSize;
91+
}
92+
5793
@Override
5894
public HttpServerOptions duplicate() {
5995
return builder().from(this).build();
@@ -76,7 +112,9 @@ public String asSimpleString() {
76112
@Override
77113
public String asDetailedString() {
78114
return super.asDetailedString() +
79-
", minCompressionResponseSize=" + minCompressionResponseSize;
115+
", minCompressionResponseSize=" + minCompressionResponseSize +
116+
", httpCodecSizes={initialLine=" + this.maxInitialLineLength +
117+
",header=" + this.maxHeaderSize + ",chunk="+ this.maxChunkSize + "}";
80118
}
81119

82120
@Override
@@ -86,6 +124,9 @@ public String toString() {
86124

87125
public static final class Builder extends ServerOptions.Builder<Builder> {
88126
private int minCompressionResponseSize = -1;
127+
private int maxInitialLineLength = 4096;
128+
private int maxHeaderSize = 8192;
129+
private int maxChunkSize = 8192;
89130

90131
private Builder(){
91132
super(new ServerBootstrap());
@@ -120,6 +161,32 @@ public final Builder compression(int minResponseSize) {
120161
return get();
121162
}
122163

164+
/**
165+
* Configure the {@link io.netty.handler.codec.http.HttpServerCodec HTTP codec}
166+
* maximum initial HTTP line length, header size and chunk size.
167+
* <p>
168+
* Negative or zero values are not valid, but will be interpreted as "don't change
169+
* the current configuration for that field": on first call the Netty defaults of
170+
* {@code (4096,8192,8192)} will be used.
171+
*
172+
* @param maxInitialLineLength the HTTP initial line maximum length. Use 0 to ignore/keep default.
173+
* @param maxHeaderSize the HTTP header maximum size. Use 0 to ignore/keep default.
174+
* @param maxChunkSize the HTTP chunk maximum size. Use 0 to ignore/keep default.
175+
* @return {@code this}
176+
*/
177+
public final Builder httpCodecOptions(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
178+
if (maxInitialLineLength > 0) {
179+
this.maxInitialLineLength = maxInitialLineLength;
180+
}
181+
if (maxHeaderSize > 0) {
182+
this.maxHeaderSize = maxHeaderSize;
183+
}
184+
if (maxChunkSize > 0) {
185+
this.maxChunkSize = maxChunkSize;
186+
}
187+
return get();
188+
}
189+
123190
/**
124191
* Fill the builder with attribute values from the provided options.
125192
*
@@ -129,6 +196,9 @@ public final Builder compression(int minResponseSize) {
129196
public final Builder from(HttpServerOptions options) {
130197
super.from(options);
131198
this.minCompressionResponseSize = options.minCompressionResponseSize;
199+
this.maxInitialLineLength = options.maxInitialLineLength;
200+
this.maxHeaderSize = options.maxHeaderSize;
201+
this.maxChunkSize = options.maxChunkSize;
132202
return get();
133203
}
134204

src/test/java/reactor/ipc/netty/http/server/HttpServerOptionsTest.java

Lines changed: 185 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,151 @@ public void minResponseForCompressionPositive() {
4848
assertThat(builder.build().minCompressionResponseSize()).isEqualTo(10);
4949
}
5050

51+
@Test
52+
public void httpCodecSizesModified() {
53+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
54+
builder.httpCodecOptions(123, 456, 789);
55+
56+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(123);
57+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(456);
58+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(789);
59+
}
60+
61+
@Test
62+
public void httpCodecSizesDefaults() {
63+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
64+
65+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(4096);
66+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(8192);
67+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(8192);
68+
}
69+
70+
@Test
71+
public void httpCodecSizesLineNegativeDefaults() {
72+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
73+
builder.httpCodecOptions(-1, 456, 789);
74+
75+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(4096);
76+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(456);
77+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(789);
78+
}
79+
80+
@Test
81+
public void httpCodecSizesLineZeroDefaults() {
82+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
83+
builder.httpCodecOptions(0, 456, 789);
84+
85+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(4096);
86+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(456);
87+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(789);
88+
}
89+
90+
@Test
91+
public void httpCodecSizesLineNegativeIgnored() {
92+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
93+
builder.httpCodecOptions(123, 456, 789)
94+
.httpCodecOptions(-1, 1, 2);
95+
96+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(123);
97+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(1);
98+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(2);
99+
}
100+
101+
@Test
102+
public void httpCodecSizesLineZeroIgnored() {
103+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
104+
builder.httpCodecOptions(123, 456, 789)
105+
.httpCodecOptions(0, 1, 2);
106+
107+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(123);
108+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(1);
109+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(2);
110+
}
111+
112+
@Test
113+
public void httpCodecSizesHeaderNegativeDefaults() {
114+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
115+
builder.httpCodecOptions(123, -1, 789);
116+
117+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(123);
118+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(8192);
119+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(789);
120+
}
121+
122+
@Test
123+
public void httpCodecSizesHeaderZeroDefaults() {
124+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
125+
builder.httpCodecOptions(123, 0, 789);
126+
127+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(123);
128+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(8192);
129+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(789);
130+
}
131+
132+
@Test
133+
public void httpCodecSizesHeaderNegativeIgnored() {
134+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
135+
builder.httpCodecOptions(123, 456, 789)
136+
.httpCodecOptions(1, -1, 2);
137+
138+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(1);
139+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(456);
140+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(2);
141+
}
142+
143+
@Test
144+
public void httpCodecSizesHeaderZeroIgnored() {
145+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
146+
builder.httpCodecOptions(123, 456, 789)
147+
.httpCodecOptions(1, 0, 2);
148+
149+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(1);
150+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(456);
151+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(2);
152+
}
153+
154+
@Test
155+
public void httpCodecSizesChunkNegativeDefaults() {
156+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
157+
builder.httpCodecOptions(123, 456, -1);
158+
159+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(123);
160+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(456);
161+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(8192);
162+
}
163+
164+
@Test
165+
public void httpCodecSizesChunkZeroDefaults() {
166+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
167+
builder.httpCodecOptions(123, 456, 0);
168+
169+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(123);
170+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(456);
171+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(8192);
172+
}
173+
174+
@Test
175+
public void httpCodecSizesChunkNegativeIgnored() {
176+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
177+
builder.httpCodecOptions(123, 456, 789)
178+
.httpCodecOptions(1, 2, -1);
179+
180+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(1);
181+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(2);
182+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(789);
183+
}
184+
185+
@Test
186+
public void httpCodecSizesChunkZeroIgnored() {
187+
HttpServerOptions.Builder builder = HttpServerOptions.builder();
188+
builder.httpCodecOptions(123, 456, 789)
189+
.httpCodecOptions(1, 2, 0);
190+
191+
assertThat(builder.build().httpCodecMaxInitialLineLength()).isEqualTo(1);
192+
assertThat(builder.build().httpCodecMaxHeaderSize()).isEqualTo(2);
193+
assertThat(builder.build().httpCodecMaxChunkSize()).isEqualTo(789);
194+
}
195+
51196
@Test
52197
public void asSimpleString() {
53198
HttpServerOptions.Builder builder = HttpServerOptions.builder();
@@ -69,30 +214,62 @@ public void asSimpleString() {
69214
}
70215

71216
@Test
72-
public void asDetailedString() {
217+
public void asDetailedStringAddressAndCompression() {
73218
HttpServerOptions.Builder builder = HttpServerOptions.builder();
74219

75220
assertThat(builder.build().asDetailedString())
76221
.matches("^address=(0\\.0\\.0\\.0/0\\.0\\.0\\.0:0|/0:0:0:0:0:0:0:1).*")
77-
.endsWith(", minCompressionResponseSize=-1");
222+
.contains(", minCompressionResponseSize=-1");
78223

79224
//address
80225
builder.host("foo").port(123);
81226
assertThat(builder.build().asDetailedString())
82227
.startsWith("address=foo:123")
83-
.endsWith(", minCompressionResponseSize=-1");
228+
.contains(", minCompressionResponseSize=-1");
84229

85230
//gzip
86231
builder.compression(true);
87232
assertThat(builder.build().asDetailedString())
88233
.startsWith("address=foo:123")
89-
.endsWith(", minCompressionResponseSize=0");
234+
.contains(", minCompressionResponseSize=0");
90235

91236
//gzip with threshold
92237
builder.compression(534);
93238
assertThat(builder.build().asDetailedString())
94239
.startsWith("address=foo:123")
95-
.endsWith(", minCompressionResponseSize=534");
240+
.endsWith(", minCompressionResponseSize=534, httpCodecSizes={initialLine=4096,header=8192,chunk=8192}");
241+
}
242+
243+
@Test
244+
public void asDetailedStringHttpCodecSizes() {
245+
//defaults
246+
assertThat(HttpServerOptions.builder()
247+
.build().asDetailedString())
248+
.endsWith(", httpCodecSizes={initialLine=4096,header=8192,chunk=8192}");
249+
250+
//changed line length
251+
assertThat(HttpServerOptions.builder()
252+
.httpCodecOptions(123, 0, -1)
253+
.build().asDetailedString())
254+
.endsWith(", httpCodecSizes={initialLine=123,header=8192,chunk=8192}");
255+
256+
//changed header size
257+
assertThat(HttpServerOptions.builder()
258+
.httpCodecOptions(0, 123, -1)
259+
.build().asDetailedString())
260+
.endsWith(", httpCodecSizes={initialLine=4096,header=123,chunk=8192}");
261+
262+
//changed chunk size
263+
assertThat(HttpServerOptions.builder()
264+
.httpCodecOptions(0, -1, 123)
265+
.build().asDetailedString())
266+
.endsWith(", httpCodecSizes={initialLine=4096,header=8192,chunk=123}");
267+
268+
//changed all sizes
269+
assertThat(HttpServerOptions.builder()
270+
.httpCodecOptions(123, 456, 789)
271+
.build().asDetailedString())
272+
.endsWith(", httpCodecSizes={initialLine=123,header=456,chunk=789}");
96273
}
97274

98275
@Test
@@ -101,10 +278,11 @@ public void toStringContainsAsDetailedString() {
101278
.compression(534)
102279
.host("google.com")
103280
.port(123);
104-
assertThat(builder.build().toString())
281+
HttpServerOptions options = builder.build();
282+
assertThat(options.toString())
105283
.startsWith("HttpServerOptions{address=google.com")
106284
.contains(":123")
107-
.endsWith(", minCompressionResponseSize=534}");
285+
.endsWith(", minCompressionResponseSize=534, httpCodecSizes={initialLine=4096,header=8192,chunk=8192}}");
108286
}
109287

110288
}

0 commit comments

Comments
 (0)