JDK-8185898 : setRequestProperty(key, null) results in HTTP header without colon in request
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 8,9,11
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2017-07-28
  • Updated: 2020-06-01
  • Resolved: 2019-08-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 13 JDK 14 JDK 8 Other
11.0.6-oracleFixed 13.0.2Fixed 14 b10Fixed 8u241Fixed openjdk8u242Fixed
Description
FULL PRODUCT VERSION :
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
If setRequestProperty method is called on HttpURLConnection object with non-empty "key" and "value" set to null, the corresponding HTTP header is inserted into the request, but without ":" (colon). The header name passed via "key" is directly followed by CR LF. This is violation of HTTP protocol, since colons in headers are mandatory.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the included source code and run it passing any HTTP URL as a parameter and inspect network traffic with a sniffer. For example:

java -cp . Test1 http://www.ya.ru

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
In no conditions an HTTP header without colon should ever be put into a request. Possible platform behavior for passed null as value of "value" argument may include:
- Throwing NullPointerException (the preferred approach)
- Adding a valid HTTP header with name passed in "key" argument and empty value. The colon MUST be present.
- Not adding a header.
ACTUAL -
The following request is being generated and sent to the server for described example. Note the missing ":" in "MyHeader" header:

GET / HTTP/1.1
MyHeader
User-Agent: Java/1.8.0_131
Host: www.ya.ru
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive



REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

class Test1 {
  public static void main(String[] asArguments) throws MalformedURLException, IOException {
    URL rURL = new URL(asArguments[0]);
    URLConnection rConnection = rURL.openConnection();
    rConnection.setRequestProperty("MyHeader", null);
    BufferedReader in = new BufferedReader(new InputStreamReader(rConnection.getInputStream()));
    String inputLine;
    while ((inputLine = in.readLine()) != null) System.out.println(inputLine);
    in.close();
  }
}
---------- END SOURCE ----------


Comments
8u review approval: https://mail.openjdk.java.net/pipermail/jdk8u-dev/2019-November/010678.html
04-12-2019

Fix request for 8u. Patch applies without problems. I had to slightly adapt test for JDK8 API. https://mail.openjdk.java.net/pipermail/jdk8u-dev/2019-November/010669.html
26-11-2019

Fix request (11u) Request backport to match Oracle 11.0.6. Patch applies cleanly. No test issues observed.
11-11-2019

URL: https://hg.openjdk.java.net/jdk/jdk/rev/0211b062843d User: michaelm Date: 2019-08-12 10:26:08 +0000
12-08-2019

From submitter: Please examine the real network traffic and not logger output. For example, here is a screenshot from MS Network Monitor. On the right pane you can see the incorrect header. On the left you can see that Network Monitor has erroneously combined headers ���MyHeader��� and ���User-Agent��� into one because ���MyHeader��� has no colon. (screenshot attached) I did a little bit of research. HTTP request headers are stored in class sun.net.www.MessageHeader, and they are sent to output stream using ���print��� method of this class. Its implementation can be found in OpenJDK and looks like this: public synchronized void print(PrintStream p) { for (int i = 0; i < nkeys; i++) if (keys[i] != null) { p.print(keys[i] + (values[i] != null ? ": "+values[i]: "") + "\r\n"); } p.print("\r\n"); p.flush(); } It does exactly that ��� colon is being output only if corresponding member of ���values��� array is not null. As far as I understand, this functionality is really used in some places, for example, HTTP request line is also stored in this class. But for normal HTTP headers generating a header without a colon is a vital standard violation, and peer systems behave in unpredictable ways when they see such malformed request.
10-08-2017

To submitter: I executed the following test case on JDK 8u131, 8u144 and 9-ea+180, but I see the output with ���MyHeader: null��� in all the cases. Is there any specific configuration/case where no colon is present. Please let me know. Output: Aug 04, 2017 1:45:26 PM sun.net.www.protocol.http.HttpURLConnection plainConnect0 FINEST: ProxySelector Request for http://www.ya.ru/ Aug 04, 2017 1:45:27 PM sun.net.www.protocol.http.HttpURLConnection plainConnect0 FINEST: Proxy used: DIRECT Aug 04, 2017 1:45:27 PM sun.net.www.protocol.http.HttpURLConnection writeRequests FINE: sun.net.www.MessageHeader@4e50df2e6 pairs: {GET / HTTP/1.1: null}{MyHeader: null}{User-Agent: Java/1.8.0_131}{Host: www.ya.ru}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive} Aug 04, 2017 1:45:27 PM sun.net.www.protocol.http.HttpURLConnection getInputStream0 FINE: sun.net.www.MessageHeader@1d81eb9311 pairs: {null: HTTP/1.1 302 Found}{Server: nginx}{Date: Fri, 04 Aug 2017 08:15:27 GMT}{Content-Length: 0}{Connection: keep-alive}{Cache-Control: no-cache,no-store,max-age=0,must-revalidate}{Location: http://ya.ru/}{Expires: Fri, 04 Aug 2017 08:15:28 GMT}{Last-Modified: Fri, 04 Aug 2017 08:15:28 GMT}{P3P: policyref="/w3c/p3p.xml", CP="NON DSP ADM DEV PSD IVDo OUR IND STP PHY PRE NAV UNI"}{Set-Cookie: yandexuid=2761501881501834528; Expires=Mon, 02-Aug-2027 08:15:27 GMT; Domain=.ya.ru; Path=/} Aug 04, 2017 1:45:27 PM sun.net.www.protocol.http.HttpURLConnection followRedirect0 FINE: Redirected from http://www.ya.ru to http://ya.ru/ Aug 04, 2017 1:45:27 PM sun.net.www.protocol.http.HttpURLConnection plainConnect0 FINEST: ProxySelector Request for http://ya.ru/ Aug 04, 2017 1:45:27 PM sun.net.www.protocol.http.HttpURLConnection plainConnect0 FINEST: Proxy used: DIRECT Aug 04, 2017 1:45:27 PM sun.net.www.protocol.http.HttpURLConnection writeRequests FINE: sun.net.www.MessageHeader@4e50df2e6 pairs: {GET / HTTP/1.1: null}{MyHeader: null}{User-Agent: Java/1.8.0_131}{Host: ya.ru}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive} Aug 04, 2017 1:45:28 PM sun.net.www.protocol.http.HttpURLConnection getInputStream0 FINE: sun.net.www.MessageHeader@7291c18f11 pairs: {null: HTTP/1.1 302 Found}{Server: nginx}{Date: Fri, 04 Aug 2017 08:15:28 GMT}{Content-Length: 0}{Connection: keep-alive}{Cache-Control: no-cache,no-store,max-age=0,must-revalidate}{Location: https://ya.ru/}{Expires: Fri, 04 Aug 2017 08:15:28 GMT}{Last-Modified: Fri, 04 Aug 2017 08:15:28 GMT}{P3P: policyref="/w3c/p3p.xml", CP="NON DSP ADM DEV PSD IVDo OUR IND STP PHY PRE NAV UNI"}{Set-Cookie: yandexuid=345603481501834528; Expires=Mon, 02-Aug-2027 08:15:28 GMT; Domain=.ya.ru; Path=/}
07-08-2017