Bug 65365 - HTTP Tunneling (Forward Proxy) Support
Summary: HTTP Tunneling (Forward Proxy) Support
Alias: None
Product: Tomcat 9
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 9.0.26
Hardware: PC All
: P2 normal (vote)
Target Milestone: -----
Assignee: Tomcat Developers Mailing List
Depends on:
Reported: 2021-06-07 15:59 UTC by Fuwei Chin
Modified: 2021-06-08 07:20 UTC (History)
0 users

HTTP Tunneling messages (54.81 KB, image/png)
2021-06-07 15:59 UTC, Fuwei Chin

Note You need to log in before you can comment on or make changes to this bug.
Description Fuwei Chin 2021-06-07 15:59:43 UTC
Created attachment 37893 [details]
HTTP Tunneling messages

Currently Tomcat 9.0 rejects CONNECT request

> HTTP Status 400 รข Bad Request
> Message: Invalid URI
> Description: The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

Node.js "http" module supports CONNECT method natively.

Here are my thought to enable HTTP Tunneling capability.

Basic ideas:

1). Update HTTP/1.1 parser, to support request line like

CONNECT example.com:443 HTTP/1.1
OTHERMETHOD http://example.com/ HTTP/1.1

2). Update `HttpServletRequest#getRequestURI()` implementation, as commented

> Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request

That means `getRequestURI()` may return raw URI value like "http://example.com/".


3). A forward proxy SHOULD have the capability to abort connection of a request. rather than responds 200, 400 or 500, since HTTP client don't know who generates the HTTP response. respond with no response, if upstream server cannot be reached.

For such reason, We need a mechanism to close HTTP connection before status code sent to client. e.g. update `HttpServletResponse#setStatus()`, if status was set to some value like 0 or -1. then at `HttpServletRequest#flushBuffer()` or same phase, close underlying socket connection.

See similar question https://stackoverflow.com/questions/3107631/how-to-close-a-http-connection-from-the-httpservlet

Security Consideration:

Apply above changes only when `allowTunneling="true"` configured in `server.xml`

<Connector port="1080" protocol="HTTP/1.1" allowTunneling="true" />

Logging Consideration:

With above changes, Web Developers can write their own forward proxy based on Tomcat and Servlet API.

See specification https://datatracker.ietf.org/doc/html/rfc7231#section-4.3.6
Comment 1 Mark Thomas 2021-06-07 16:08:38 UTC
There is no need to update the HTTP parser. Custom HTTP methods are already supported. CONNECT can be supported the same way. The users list is the place to ask for help if you do not know how to implement this.

The HttpServletRequest#getRequestURI() cannot be changed. The behaviour of that method is defined by the specification. You can obtain the information required from the Host header.

My reading of RFC 7231 does not support the need to close the connection without a status code.
Comment 2 Fuwei Chin 2021-06-08 03:41:03 UTC
As RFC 2116 describes:

> Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
> Request-URI    = "*" | absoluteURI | abs_path | authority

See https://datatracker.ietf.org/doc/html/rfc2616#section-5.1 and https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1

"Custom HTTP methods are already supported", Yes. while I mean the Request-URI.

A forward proxy requires the capability to accept CONNECT request and handle absoluteURI request.

A CONNECT request uses authority as Request-URI, while authority as Request-URI is not currently supported by Tomcat, Tomcat rejects CONNECT request with "400 Bad Request" before entering `MyGenericServlet#serice()`. And a request-to-proxy request uses absoluteURI as Request-URI, which is not fully supported by Tomcat, since the original absoluteURI cannot be obtained through Servlet API `HttpServletRequest#getRequestURI()`.

I ask for HTTP Tunneling (Forward Proxy) Support, maybe there are better approaches.
Comment 3 Mark Thomas 2021-06-08 07:20:43 UTC
Of course. The URI normalization checks fail. And even if they didn't you can't map the CONNECT request to a servlet.

This isn't possible to implement for a Servlet based web application.

With a CONNECT request, there is no URI so there is no ability to map a request to a virtual host, web application and servlet.

It should be possible to implement this in Tomcat with a custom protocol implementation.

I can't think of any good reason to write a forward proxy based on Tomcat. Writing spec compliant, secure HTTP proxies is hard and there are already implementations available such as httpd.