Bug 2038516 - OpenJDK does not comply with RFC7231#4.3.6
Summary: OpenJDK does not comply with RFC7231#4.3.6
Keywords:
Status: CLOSED WONTFIX
Alias: None
Product: Red Hat Enterprise Linux 7
Classification: Red Hat
Component: java-1.8.0-openjdk
Version: 7.9
Hardware: x86_64
OS: Linux
unspecified
unspecified
Target Milestone: rc
: ---
Assignee: Andrew John Hughes
QA Contact: OpenJDK QA
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2022-01-08 04:20 UTC by andrew
Modified: 2023-07-08 07:27 UTC (History)
1 user (show)

Fixed In Version:
Doc Type: ---
Doc Text:
Clone Of:
Environment:
Last Closed: 2023-07-08 07:27:58 UTC
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Github haproxy haproxy issues 1159 0 None closed CONNECT method: too strict checking of Host header 2022-01-08 04:20:55 UTC
Red Hat Issue Tracker RHELPLAN-107201 0 None None None 2022-01-08 04:25:11 UTC

Description andrew 2022-01-08 04:20:55 UTC
Description of problem:

RFC 7321#4.3.6 states that:

A client sending a CONNECT request MUST send the authority form of
request-target (Section 5.3 of [RFC7230]); i.e., the request-target
consists of only the host name and port number of the tunnel
destination, separated by a colon.  For example,

     CONNECT server.example.com:80 HTTP/1.1
     Host: server.example.com:80

The HttpURLConnection API in OpenJDK 8 implements CONNECT support as:

    /**
     * send a CONNECT request for establishing a tunnel to proxy server
     */
    private void sendCONNECTRequest() throws IOException {
        int port = url.getPort();

        requests.set(0, HTTP_CONNECT + " " + connectRequestURI(url)
                         + " " + httpVersion, null);
        requests.setIfNotSet("User-Agent", userAgent);

        String host = url.getHost();
        if (port != -1 && port != url.getDefaultPort()) {
            host += ":" + String.valueOf(port);
        }
        requests.setIfNotSet("Host", host);

The logic for deciding when to append ":" and the port number will not append them when it is the default port for the given protocol (80 for http, 443 for https), and thus will generate requests in these formats:

CONNECT example.com:443 HTTP/1.1
User-Agent: Java/1.8.0_232
Host: example.com

CONNECT example.com:80 HTTP/1.1
User-Agent: Java/1.8.0_232
Host: example.com

Note that neither of these request includes the port number in the Host header per the RFC example, causing the the request-target and host header to not match.

Starting in version 2.2, HAproxy implemented strict RFC compliance checking that will reject these requests with BADREQ:

https://github.com/haproxy/haproxy/commit/531b83e039bbe369e4fe6e775e9bfa310d780da1

The only current work-around for this non-compliant behavior by Java is to use the "accept-invalid-http-request" option, which is sub-optimal.

Version-Release number of selected component (if applicable):

Tested under 1.8.0.232.b09-0.el7_7, but this issue exists in all current OpenJDK releases

How reproducible: 100%

Steps to Reproduce:
1. Setup a HAproxy 2.4 http frontend that will accept CONNECT requests 
2. Use HttpURLConnection to generate a CONNECT request
3. Observe the rejection of the CONNECT request by HAproxy

Actual results:

A <BADREQ> is logged by HAproxy due to the mismatch between the authority and the Host header in the request generated by Java:

127.0.0.1:35039 127.0.0.1:80 [28/Dec/2021:04:19:12 +0000] tcp-80 tcp-80/<NOSRV> -1/-1/-1/-1/0 400 0 - - PR-- 1/1/0/0/0 0/0 "<BADREQ>"

and the CONNECT request is rejected by HAproxy with a 400 error:

HTTP/1.1 400 Bad request
Content-length: 90
Cache-Control: no-cache
Connection: close
Content-Type: text/html

<html><body><h1>400 Bad request</h1>
Your browser sent an invalid request.
</body></html>

Expected results:

A properly formatted RFC compliant CONNECT request being generated by Java that can be serviced by HAproxy without requiring enabling the "accept-invalid-http-request" option.

Additional info:

RFC compliant behavior should be able to be achieved by removing the port logic and simplifying the sendCONNECTRequest() method:

    /**
     * send a CONNECT request for establishing a tunnel to proxy server
     */
    private void sendCONNECTRequest() throws IOException {

        requests.set(0, HTTP_CONNECT + " " + connectRequestURI(url)
                         + " " + httpVersion, null);
        requests.setIfNotSet("User-Agent", userAgent);
        requests.setIfNotSet("Host", connectRequestURI(url));

Unfortunately there is not a good public conduit to report Java bugs directly to the OpenJDK project for resolution, so hopefully Red Hat can help facilitate this, as this issue has not been fixed in any newer JDK version either, making this a generic JDK issue across multiple supported JDK long-term releases and RHEL versions.

JDK 11: 

https://github.com/openjdk/jdk11/blob/37115c8ea4aff13a8148ee2b8832b20888a5d880/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java#L2197

JDK 17:

https://github.com/openjdk/jdk17u/blob/master/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java#L2301

Comment 4 RHEL Program Management 2023-07-08 07:27:58 UTC
After evaluating this issue, there are no plans to address it further or fix it in an upcoming release.  Therefore, it is being closed.  If plans change such that this issue will be fixed in an upcoming release, then the bug can be reopened.


Note You need to log in before you can comment on or make changes to this bug.