Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 360665 - Proxying HTTPS request to HTTP port causes exception loop
Summary: Proxying HTTPS request to HTTP port causes exception loop
Status: RESOLVED FIXED
Alias: None
Product: Jetty
Classification: RT
Component: client (show other bugs)
Version: unspecified   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 7.5.x   Edit
Assignee: Thomas Becker CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-10-12 09:44 EDT by Chris Dumoulin CLA
Modified: 2011-11-04 10:52 EDT (History)
3 users (show)

See Also:


Attachments
Test program to reproduce the problem. (670 bytes, text/x-java)
2011-10-12 09:45 EDT, Chris Dumoulin CLA
no flags Details
Unit Test (2.65 KB, application/octet-stream)
2011-10-13 05:28 EDT, Thomas Becker CLA
no flags Details
TCP data recorded using wireshark (5.50 KB, application/octet-stream)
2011-10-14 10:27 EDT, Chris Dumoulin CLA
no flags Details
Test to reproduce the issue. (3.25 KB, application/octet-stream)
2011-10-31 08:59 EDT, Thomas Becker CLA
no flags Details
proposed patch (1.57 KB, patch)
2011-10-31 15:13 EDT, Thomas Becker CLA
no flags Details | Diff
proposed patch (7.57 KB, patch)
2011-11-02 09:06 EDT, Thomas Becker CLA
no flags Details | Diff
proposed patch (15.50 KB, application/x-tar)
2011-11-03 06:30 EDT, Thomas Becker CLA
no flags Details
proposed patch (23.00 KB, application/x-tar)
2011-11-03 10:01 EDT, Thomas Becker CLA
no flags Details
proposed patch (10.20 KB, patch)
2011-11-03 12:45 EDT, Thomas Becker CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Dumoulin CLA 2011-10-12 09:44:10 EDT
Build Identifier: 7.2.0.v20101020

If you set a port 80 proxy for the HttpClient and then try to send an HTTPS request, you'll get into a loop where exceptions are continually generated. I'll attach a test program. The output looks like this:

...
2011-10-12 09:34:09.574:WARN::EXCEPTION ConnectExchange@14633156=CONNECT//localhost:80google.com:443#9
HttpException(400,null,null)
	at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:362)
	at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:212)
	at org.eclipse.jetty.client.HttpConnection.handle(HttpConnection.java:262)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:510)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint.access$000(SelectChannelEndPoint.java:34)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:40)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:450)
	at java.lang.Thread.run(Thread.java:662)
2011-10-12 09:34:09.577:WARN::EXCEPTION ConnectExchange@5569009=CONNECT//localhost:80google.com:443#9
HttpException(400,null,null)
	at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:362)
	at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:212)
	at org.eclipse.jetty.client.HttpConnection.handle(HttpConnection.java:262)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:510)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint.access$000(SelectChannelEndPoint.java:34)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:40)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:450)
	at java.lang.Thread.run(Thread.java:662)
...

Reproducible: Always

Steps to Reproduce:
1. Have a web server running on localhost, port 80
2. Run the attached BadProxyTest program
3. Observe the exception loop
Comment 1 Chris Dumoulin CLA 2011-10-12 09:45:08 EDT
Created attachment 205037 [details]
Test program to reproduce the problem.
Comment 2 Thomas Becker CLA 2011-10-13 05:28:48 EDT
Created attachment 205109 [details]
Unit Test

Hi Chris,

I've written a pure jetty unit test which is doing what leads to your exception loop.

- create a jetty server on a random port acting as the proxy
- start a client using that proxy
- send an https request (e.g. I tried https://google.com, https://web.de, etc.). 

Test is always green and the response is as expected. Could you please confirm that you get this problem also when the proxy is running on a high port (e.g. 8080) and with a more recent jetty version?

Attached is the unit test. Feel free to modify it to reproduce your issue.

Cheers,
Thomas
Comment 3 Thomas Becker CLA 2011-10-13 05:29:39 EDT
Oh and what kind of proxy are you running on port 80?
Comment 4 Thomas Becker CLA 2011-10-13 05:42:34 EDT
For the record: 

I've had a look at the code from jetty 7.2.0 and the exception is thrown if a non printing char occurs in the parsed string:

                    else if (ch < HttpTokens.SPACE && ch>=0)
                    {
                        throw new HttpException(HttpStatus.BAD_REQUEST_400);
                    }

I can't see an obvious reason how this can happen in your case. So it'll be good to get the information I requested above.

Cheers,
Thomas
Comment 5 Chris Dumoulin CLA 2011-10-13 08:45:28 EDT
(In reply to comment #3)
> Oh and what kind of proxy are you running on port 80?

Nginx was running on port 80. Using wireshark I can see that the HttpClient sends an HTTP CONNECT request, which I've read nginx doesn't support. Nginx sends back a malformed response; it contains some HTML but no HTTP headers.

I'm not concerned that an exception is thrown; the problem is the fact that it loops continuously once this happens.

I'll try running the unit test that was attached.
Comment 6 Chris Dumoulin CLA 2011-10-13 09:06:25 EDT
I haven't been able to run the unit test, but I doubt it would show the problem. I'm pretty sure you need a web server that doesn't handle the HTTP CONNECT request.

I initially saw the problem using a site mirroring tool called netspiegel (https://github.com/ariya/X2/tree/d6fac7ebe456956854bd17e9405d4e873fd1b4f7/network/netspiegel). This program can act as a web server, and simply drops the connection if it gets an HTTP request whose method isn't GET.

Using wireshark I'm able to see the HTTP CONNECT being sent, and then the connection is broken. The exception I see when using netspiegel is:

2011-10-13 08:55:26.751:WARN::EXCEPTION ConnectExchange@6131844=CONNECT//ec2-67-202-16-167.compute-1.amazonaws.com:80google.com:443#9
org.eclipse.jetty.io.EofException
	at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:319)
	at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:212)
	at org.eclipse.jetty.client.HttpConnection.handle(HttpConnection.java:262)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:510)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint.access$000(SelectChannelEndPoint.java:34)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:40)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:450)
	at java.lang.Thread.run(Thread.java:662)
Comment 7 Thomas Becker CLA 2011-10-14 10:00:23 EDT
Are you 100% sure that the client is not rerequesting and that jetty loops with a single request being made by the client?

Can you attach a snoop file for a single request which I can examine in wireshark?
Comment 8 Chris Dumoulin CLA 2011-10-14 10:25:54 EDT
(In reply to comment #7)
> Are you 100% sure that the client is not rerequesting and that jetty loops with
> a single request being made by the client?
> 
> Can you attach a snoop file for a single request which I can examine in
> wireshark?

There are repeated HTTP CONNECT requests made, but only one call to HttpClient.send(). I'm using BadProxyTest.java which only does one send() and has no looping.

I'm attaching a traffic dump taken from wireshark.
Comment 9 Chris Dumoulin CLA 2011-10-14 10:27:40 EDT
Created attachment 205200 [details]
TCP data recorded using wireshark
Comment 10 Thomas Becker CLA 2011-10-31 08:59:05 EDT
Created attachment 206205 [details]
Test to reproduce the issue.

Given the new information I've been able to reproduce the issue with a Unit test using plain jetty. 

The client ends up in the loop when the proxy responsible for handling the Connect request will simply close the connection without sending any response (as shown in your wireshark).

I will now find and fix the root cause. :)
Comment 11 Thomas Becker CLA 2011-10-31 15:13:39 EDT
Created attachment 206236 [details]
proposed patch

Proposed patch to remove the HttpExchange from HttpClients queue in HttpDestination.ConnectExchange.onException().
Comment 12 Thomas Becker CLA 2011-11-02 09:06:13 EDT
Created attachment 206323 [details]
proposed patch

Better patch attached + Tests.
Comment 13 Thomas Becker CLA 2011-11-03 06:30:54 EDT
Created attachment 206394 [details]
proposed patch

New patch files. Now with support for onExpire and little changes in onResponseComplete as discussed.
Comment 14 Thomas Becker CLA 2011-11-03 10:01:46 EDT
Created attachment 206406 [details]
proposed patch

Additional commit for a new testcase (504 returned by proxy) and enhanced unit tests by reusing the proxy.
Comment 15 Thomas Becker CLA 2011-11-03 12:45:33 EDT
Created attachment 206417 [details]
proposed patch

Merged the 5 commits to a single one as requested.
Comment 16 Simone Bordet CLA 2011-11-04 10:51:39 EDT
Patch applied with some additional tweaks.
Comment 17 Simone Bordet CLA 2011-11-04 10:52:26 EDT
Fixed.