Hi Simon,
I have done some more investigation, and
I believe the issue here is that the `express` server
doesn't like the connection upgrade that our HTTP Client
asks for.
As a result instead of ignoring the upgrade and simply
replying with HTTP/1.1 the server seems to close the
connection just after receiving the requests headers,
without sending anything back to the client.
This is what is causing the EOF exception, because the
connection gets closed right away when trying to read
the response headers.
This can be easily demonstrated by running the small fake client
below at the end of this email.
You will see that if you comment out the line:
"Connection: Upgrade, HTTP2-Settings",
from the request headers, then the server sends back the response.
Otherwise, it just closes the connection, and nothing is received
by the client.
One work around is to explicitly configure your request to be sent
over HTTP/1.1 (otherwise, by default, the client tries the upgrade
to HTTP/2):
HttpRequest getRequest = HttpRequest.newBuilder()
.uri(uri)
.version(HttpClient.Version.HTTP_1_1)
.GET().build();
In that case the client will use HTTP/1.1 and won't send
the upgrade headers and everything appears to work fine.
Thank you for reporting this issue to us though, as it lead
us to log a couple of RFEs to help better diagnosis.
Hope this helps,
-- daniel
-------------------------------------------------
static final String[] request = {
"GET /simple.html HTTP/1.1",
"Connection: Upgrade, HTTP2-Settings",
"Content-Length: 0",
"Host: localhost:8080",
"HTTP2-Settings: AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA",
"Upgrade: h2c",
"User-Agent: Java-http-client/11-internal",
"",
};
public static void main(String[] args) throws IOException {
try (Socket s = new Socket("localhost", 8080)) {
OutputStream so = s.getOutputStream();
for (var line : request) {
System.out.println("> " + line);
so.write(line.getBytes(StandardCharsets.US_ASCII));
so.write('\r');
so.write('\n');
}
so.flush();
InputStream si = s.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(si, StandardCharsets.UTF_8));
reader.lines()
.map(line -> "< " + line)
.forEach(System.out::println);
}
}
------------------------------------------------------------------
On 16/05/2018 17:24, Simon Roberts wrote:
OK, here goes.
1) install node 8.11 from here: https://nodejs.org/
this should consist (though I could be wrong for mac or windows!) of
expanding the archive, and adding the contained 'bin' directory to your
path.
You should be able to execute the commands:
node --version
(note two minus-signs. Should report v8.11.??)
and
npm -version
(note single minus sign! Might report 5.6?? but might be older, not a
big deal)
2) clone this: https://github.com/SimonHGR/RESTserver.git
3) in the resulting directory (RESTserver) enter:
npm install
(it'll download a bunch of javascript libraries)
4) still in the same directory enter:
npm start
(it should report Express listening on port 8080)
You should now be able to do
curl -v http://localhost:8080/simple.html
and see:
--------------------------------------------------------------------
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /simple.html HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 95
< ETag: W/"5f-zPY21XEeilaiEZvppUEI+1JlLE8"
< Date: Wed, 16 May 2018 16:15:35 GMT
< Connection: keep-alive
<
<html>
<head>
</head>
<body>
<h1>A Heading</h1>
<p>A paragraph</p>
</body>
</html>
* Connection #0 to host localhost left intact
--------------------------------------------------------------------
Notes, as a result of the discussion, I deliberately added code, at line
56 of server.js, to try to ensure that it's sending \r\n. This code
isn't "normal" in node, but I believe it's irrelevant anyway, as I've
already shown that httpClient is failing before it ever processes the
body (perhaps the headers aren't being sent with \r\n!?)
Also, I did try, last night, to turn off the HTTP2.0 mode in httpClient
using:
HttpClient client =
HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
but that did not change anything.
I'll also note that when trying my "trivial serer", it reproduced the
problem when it was sending the wrong line-endings.
Anyway, hopefully you can look at the protocol exchange and see what
node is doing that's wrong.
If you need any updates to the node server, just holler, I'll be around
and able to put a few minutes into this here and there most of today (at
least up through 3pm Mountain time).
Cheers
On Wed, May 16, 2018 at 9:49 AM Simon Roberts
<si...@dancingcloudservices.com <mailto:si...@dancingcloudservices.com>>
wrote:
Thanks for investigating this, Chris. I will put together a trivial
node server and instructions on how to set it up (it's delightfully
simple) Should be much less than an hour, if I can give it my
un-distracted attention...
On Wed, May 16, 2018 at 8:58 AM Chris Hegarty
<chris.hega...@oracle.com <mailto:chris.hega...@oracle.com>> wrote:
Bernd,
> On 16 May 2018, at 00:43, Bernd Eckenfels
<e...@zusammenkunft.net <mailto:e...@zusammenkunft.net>> wrote:
>
> ...
> For the httpclient code, the following improvements are IMHO
possible:
>
> • As you mentioned the EOF should contain the callsite
and not be transorted from a worker thread context. This can
either be done by rethrwing the EOF Exception or by actually
constructing them in the read(). The same (or arguably even
worse) Problem is a „connection refused“ type of exception which
also has no clear calcite.
Agreed. The synchronous send should recreate exceptions
before throwing.
I filed https://bugs.openjdk.java.net/browse/JDK-8203298
> • Instead of throwing an EOF exception when the read
Buffer exceeds the Body lenth I would return a short read till
the last available Byte and only throw at the next read. This
way the handler can get all of the partial Body. This is however
not a good optimization for BodyHander.asString().
Agreed.
I think that the client implementation can provide a more
helpful exception detail message, than what it does today.
I filed https://bugs.openjdk.java.net/browse/JDK-8203302
While maybe not helpful in the string case, it is important
that all response body is delivered to the handler before
EOF. The following has been file to ensure that that is the
case.
I filed https://bugs.openjdk.java.net/browse/JDK-8203303
> It is still unlear why the node.js expresss Server has
Problems. For that I think its a good idea to trace it either
with Wireshark/tcpdump or Maybe one of the web app Debugging
reverse proxies.
It would be helpful to us to understand exactly what the
problem is here. I can try out node.js myself, or if anyone
has some instructions to help set that up it would be
helpful.
-Chris.
--
Simon Roberts
(303) 249 3613
--
Simon Roberts
(303) 249 3613