Skip to content

Commit 81c99d8

Browse files
alexeybakhtingnu-andrew
authored andcommitted
8328286: Enhance HTTP client
Reviewed-by: andrew, mbalao Backport-of: cf8dc79f392c8ec3414d8b36803f026852c4e386
1 parent 260fe06 commit 81c99d8

File tree

4 files changed

+99
-4
lines changed

4 files changed

+99
-4
lines changed

jdk/src/share/classes/java/net/doc-files/net-properties.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,14 @@ <H2>Misc HTTP properties</H2>
220220
property is defined, then its value will be used a the domain
221221
name.</P>
222222
</OL>
223+
<LI><P><B>{@systemProperty jdk.http.maxHeaderSize}</B> (default: 393216 or 384kB)<BR>
224+
This is the maximum header field section size that a client is prepared to accept.
225+
This is computed as the sum of the size of the uncompressed header name, plus
226+
the size of the uncompressed header value, plus an overhead of 32 bytes for
227+
each field section line. If a peer sends a field section that exceeds this
228+
size a {@link java.net.ProtocolException ProtocolException} will be raised.
229+
This applies to all versions of the HTTP protocol. A value of zero or a negative
230+
value means no limit. If left unspecified, the default value is 393216 bytes.
223231
</UL>
224232
<P>All these properties are checked only once at startup.</P>
225233
<a name="AddressCache"></a>

jdk/src/share/classes/sun/net/www/MessageHeader.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
package sun.net.www;
3131

3232
import java.io.*;
33+
import java.lang.reflect.Array;
34+
import java.net.ProtocolException;
3335
import java.util.Collections;
3436
import java.util.*;
3537

@@ -46,11 +48,32 @@ class MessageHeader {
4648
private String values[];
4749
private int nkeys;
4850

51+
// max number of bytes for headers, <=0 means unlimited;
52+
// this corresponds to the length of the names, plus the length
53+
// of the values, plus an overhead of 32 bytes per name: value
54+
// pair.
55+
// Note: we use the same definition as HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE
56+
// see RFC 9113, section 6.5.2.
57+
// https://www.rfc-editor.org/rfc/rfc9113.html#SETTINGS_MAX_HEADER_LIST_SIZE
58+
private final int maxHeaderSize;
59+
60+
// Aggregate size of the field lines (name + value + 32) x N
61+
// that have been parsed and accepted so far.
62+
// This is defined as a long to force promotion to long
63+
// and avoid overflows; see checkNewSize;
64+
private long size;
65+
4966
public MessageHeader () {
67+
this(0);
68+
}
69+
70+
public MessageHeader (int maxHeaderSize) {
71+
this.maxHeaderSize = maxHeaderSize;
5072
grow();
5173
}
5274

5375
public MessageHeader (InputStream is) throws java.io.IOException {
76+
maxHeaderSize = 0;
5477
parseHeader(is);
5578
}
5679

@@ -466,10 +489,28 @@ public static String canonicalID(String id) {
466489
public void parseHeader(InputStream is) throws java.io.IOException {
467490
synchronized (this) {
468491
nkeys = 0;
492+
size = 0;
469493
}
470494
mergeHeader(is);
471495
}
472496

497+
private void checkMaxHeaderSize(int sz) throws ProtocolException {
498+
if (maxHeaderSize > 0) checkNewSize(size, sz, 0);
499+
}
500+
501+
private long checkNewSize(long size, int name, int value) throws ProtocolException {
502+
// See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
503+
long newSize = size + name + value + 32;
504+
if (maxHeaderSize > 0 && newSize > maxHeaderSize) {
505+
Arrays.fill(keys, 0, nkeys, null);
506+
Arrays.fill(values,0, nkeys, null);
507+
nkeys = 0;
508+
throw new ProtocolException(String.format("Header size too big: %s > %s",
509+
newSize, maxHeaderSize));
510+
}
511+
return newSize;
512+
}
513+
473514
/** Parse and merge a MIME header from an input stream. */
474515
@SuppressWarnings("fallthrough")
475516
public void mergeHeader(InputStream is) throws java.io.IOException {
@@ -483,7 +524,15 @@ public void mergeHeader(InputStream is) throws java.io.IOException {
483524
int c;
484525
boolean inKey = firstc > ' ';
485526
s[len++] = (char) firstc;
527+
checkMaxHeaderSize(len);
486528
parseloop:{
529+
// We start parsing for a new name value pair here.
530+
// The max header size includes an overhead of 32 bytes per
531+
// name value pair.
532+
// See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
533+
long maxRemaining = maxHeaderSize > 0
534+
? maxHeaderSize - size - 32
535+
: Long.MAX_VALUE;
487536
while ((c = is.read()) >= 0) {
488537
switch (c) {
489538
case ':':
@@ -517,6 +566,9 @@ public void mergeHeader(InputStream is) throws java.io.IOException {
517566
s = ns;
518567
}
519568
s[len++] = (char) c;
569+
if (maxHeaderSize > 0 && len > maxRemaining) {
570+
checkMaxHeaderSize(len);
571+
}
520572
}
521573
firstc = -1;
522574
}
@@ -538,6 +590,9 @@ public void mergeHeader(InputStream is) throws java.io.IOException {
538590
v = new String();
539591
else
540592
v = String.copyValueOf(s, keyend, len - keyend);
593+
int klen = k == null ? 0 : k.length();
594+
595+
size = checkNewSize(size, klen, v.length());
541596
add(k, v);
542597
}
543598
}

jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
163163
*/
164164
private static int bufSize4ES = 0;
165165

166+
private static final int maxHeaderSize;
167+
166168
/*
167169
* Restrict setting of request headers through the public api
168170
* consistent with JavaScript XMLHttpRequest2 with a few
@@ -284,6 +286,19 @@ private static Set<String> schemesListToSet(String list) {
284286
} else {
285287
restrictedHeaderSet = null;
286288
}
289+
290+
int defMaxHeaderSize = 384 * 1024;
291+
String maxHeaderSizeStr = getNetProperty("jdk.http.maxHeaderSize");
292+
int maxHeaderSizeVal = defMaxHeaderSize;
293+
if (maxHeaderSizeStr != null) {
294+
try {
295+
maxHeaderSizeVal = Integer.parseInt(maxHeaderSizeStr);
296+
} catch (NumberFormatException n) {
297+
maxHeaderSizeVal = defMaxHeaderSize;
298+
}
299+
}
300+
if (maxHeaderSizeVal < 0) maxHeaderSizeVal = 0;
301+
maxHeaderSize = maxHeaderSizeVal;
287302
}
288303

289304
static final String httpVersion = "HTTP/1.1";
@@ -707,7 +722,7 @@ private void writeRequests() throws IOException {
707722
}
708723
ps = (PrintStream) http.getOutputStream();
709724
connected=true;
710-
responses = new MessageHeader();
725+
responses = new MessageHeader(maxHeaderSize);
711726
setRequests=false;
712727
writeRequests();
713728
}
@@ -862,7 +877,7 @@ protected HttpURLConnection(URL u, Proxy p, Handler handler)
862877
throws IOException {
863878
super(checkURL(u));
864879
requests = new MessageHeader();
865-
responses = new MessageHeader();
880+
responses = new MessageHeader(maxHeaderSize);
866881
userHeaders = new MessageHeader();
867882
this.handler = handler;
868883
instProxy = p;
@@ -2675,7 +2690,7 @@ private boolean followRedirect0(String loc, int stat, URL locUrl)
26752690
}
26762691

26772692
// clear out old response headers!!!!
2678-
responses = new MessageHeader();
2693+
responses = new MessageHeader(maxHeaderSize);
26792694
if (stat == HTTP_USE_PROXY) {
26802695
/* This means we must re-request the resource through the
26812696
* proxy denoted in the "Location:" field of the response.
@@ -2864,7 +2879,7 @@ private void reset() throws IOException {
28642879
} catch (IOException e) { }
28652880
}
28662881
responseCode = -1;
2867-
responses = new MessageHeader();
2882+
responses = new MessageHeader(maxHeaderSize);
28682883
connected = false;
28692884
}
28702885

jdk/src/share/lib/net.properties

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,20 @@ jdk.http.auth.tunneling.disabledSchemes=Basic
119119
#jdk.http.ntlm.transparentAuth=trustedHosts
120120
#
121121
jdk.http.ntlm.transparentAuth=disabled
122+
123+
#
124+
# Maximum HTTP field section size that a client is prepared to accept
125+
#
126+
# jdk.http.maxHeaderSize=393216
127+
#
128+
# This is the maximum header field section size that a client is prepared to accept.
129+
# This is computed as the sum of the size of the uncompressed header name, plus
130+
# the size of the uncompressed header value, plus an overhead of 32 bytes for
131+
# each field section line. If a peer sends a field section that exceeds this
132+
# size a {@link java.net.ProtocolException ProtocolException} will be raised.
133+
# This applies to all versions of the HTTP protocol. A value of zero or a negative
134+
# value means no limit. If left unspecified, the default value is 393216 bytes
135+
# or 384kB.
136+
#
137+
# Note: This property is currently used by the JDK Reference implementation. It
138+
# is not guaranteed to be examined and used by other implementations.

0 commit comments

Comments
 (0)