Question:
Is there a way to support CONNECT HTTP Requests with the Oracle HTTPServer class?
Note: If you do not want to deal with this, Jetty seems to not have this problem with their HTTPServer class (even when not using their ProxyHTTP implementation ConnectHandler). Grizzly however seemed to have the same issue as the default Java implementation, not surprising since they were both made by Oracle.
Explanation and Difficulty:
Note: I include this to provided a detailed explanation of what I believe to be the issue.
I've recently been trying to make a HTTP proxy for secure (HTTPS) connections on Firefox. The way this is accomplished is the CONNECT method (IETF RFC 2817). An example of the CONNECT request from Firefox is the following (which occurred when requesting the Google homepage).
CONNECT www.google.com:443 HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:58.0) Gecko/20100101 Firefox/58.0
Proxy-Connection: keep-alive
Connection: keep-alive
Host: www.google.com:443
this differs from a normal GET request.
GET /index HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:58.0) Gecko/20100101 Firefox/58.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
The notable difference is the so called 'request target' (see this Mozilla article for details ). Specifically the normal GET request is a 'absolute path' whereas the CONNECT request target is the 'authority form'.
Using the demo server in this StackOverflow answer (simple Java HTTPServer implementation) we can see the way to register a HTTPHandler is server.createContext("/test", new MyHandler()), which according to the Oracle documentation MUST begin with a '/'. However because the 'authority form' doesn't contain a '/' at the start the handle is never triggered
Note: This is still somewhat speculation as Oracle's code is closed source, but it was confirmed by sending the same TCP request to the server with the same CONNECT request above, but one time with a leading '/' (so '/www.google.com' instead of 'www.google.com'). With the leading '/' the handler was triggered.
With all this said my question is this,
Is there a way around this, while still using the included libraries in the Oracle version of Java?
Note: Other than writing an new HTTP Server from scratch, or using another library. In my testing Jetty did trigger the HTTPHandler. Grizzly did not (and, in my opinion, has some code quality issues).
You can view the source code at below link
https://github.com/akashche/java-1.8.0-openjdk-1.8.0.151-2.b12.fc28.ppc64le
So below are the few files in play here
When the http server tries to get the path for a CONNECT
request
ctx = contexts.findContext (protocol, uri.getPath());
It has uri
as www.google.com:443
and uri.getPath()
returns null
as shown in below screenshot
The ContextList class has below methods of context
public synchronized HttpContextImpl createContext (String path, HttpHandler handler) {
if (handler == null || path == null) {
throw new NullPointerException ("null handler, or path parameter");
}
HttpContextImpl context = new HttpContextImpl (protocol, path, handler, this);
contexts.add (context);
logger.config ("context created: " + path);
return context;
}
public synchronized HttpContextImpl createContext (String path) {
if (path == null) {
throw new NullPointerException ("null path parameter");
}
HttpContextImpl context = new HttpContextImpl (protocol, path, null, this);
contexts.add (context);
logger.config ("context created: " + path);
return context;
}
Both have check for path null
and doesn't allow you to set a context with null path. This means we can never get the context to work in this case. For a CONNECT
request to work we need to be able to register a handler with path as null
or some kind of default catch all handler. This is not supported in the current code as I have checked. And all these class are not marked public, so you can't inherit from these and get it working.
But since you have the source code, if you really want to make it work, you will have to copy the two files and make some modifications and get it working.
So short answer is that it is NOT possible to do this with the original com.sun.net.httpserver.HttpServer
from Oracle. I have checked JDK 1.8 and not 1.9, but I doubt there would be much change to these classes in 1.9