Am 2020-05-26 um 20:20 schrieb Oleg Kalnichevski:
On Tue, 2020-05-26 at 17:58 +0000, Bernd Eckenfels wrote:
Michael, this looks a bit like the packets in between have been TLS
handshakes which have not been carried out because the engine was not
kicked off. Maybe a starHandshake() would help? Or can you share the
full traces? Did you try http as well?


Michael,

I can only second what Bernd has said. Try to simplify the setup and
see if you can get things to work with plain HTTP first.

Also feel free to tweak my code as you see fit.

I followed Bernd's advise and disabled TLS. Although I already had a clue what is going wrong. Attached is a patch on top of Oleg's branch which makes it work unencrypted and encrypted. I will explain in detail why the changes are necessary.

-    public static final Timeout DEFAULT_WAIT_FOR_EARLY_RESPONSE = 
Timeout.ofMilliseconds(5);
+    public static final Timeout DEFAULT_WAIT_FOR_EARLY_RESPONSE = 
Timeout.ofMilliseconds(50);

unless client and server are physically next to each other 5 ms are virtually impossible. I have tried with our server at work. I am connected with my laptop via VPN to the corporate network, I don't know where the peering point of our VPN provider is, but the server is physically 10 km away from me and with TLS 1.3 it takes 28 ms to produce the 401.

Maybe both timeouts (expect and early) should be configurable via RequestConfig?

+            conn.flush();

This is why it did not work before is that headers were stuck in the local buffer and were flushed only when the body was sent. So the server never saw the headers before the body. The wait was pointless.

Please also note that even the buffer for headers might be large due to some authnz tokens. E.g., JWT or SPNEGO, cookies, etc.

+                        response = conn.receiveResponseHeader();

One needs to read the headers because the client shall be able to see the status code and if fast enough the response body. In my case the error page from Tomcat.

RFC 7230, section 6.5 says:
   A client sending a message body SHOULD monitor the network connection
   for an error response while it is transmitting the request.  If the
   client sees a response that indicates the server does not wish to
   receive the message body and is closing the connection, the client
   SHOULD immediately cease transmitting the body and close its side of
   the connection.

I have issued another request and HttpClient reuses the connection because Tomcat forgot to send "Connection: close". At some point the second request will fail and no additional 401 is responded (of course. I consider the missing "Connection: close" to be a bug in Tomcat [1]. So HttpClent behaves correctly. I assume that then a close would be send, response#close() will do the right thing and subsequent requests will use new connections.

WDYT?

Michael

[1] https://www.mail-archive.com/[email protected]/msg135255.html
diff --git 
a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
 
b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
index 8f777243a..402182740 100644
--- 
a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
+++ 
b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
@@ -74,7 +74,7 @@
 public class HttpRequestExecutor {
 
     public static final Timeout DEFAULT_WAIT_FOR_CONTINUE = 
Timeout.ofSeconds(3);
-    public static final Timeout DEFAULT_WAIT_FOR_EARLY_RESPONSE = 
Timeout.ofMilliseconds(5);
+    public static final Timeout DEFAULT_WAIT_FOR_EARLY_RESPONSE = 
Timeout.ofMilliseconds(50);
     public static final long LARGE_MESSAGE_LEN = 64 * 1024;
 
     private final Timeout waitForContinue;
@@ -156,13 +156,17 @@ public ClassicHttpResponse execute(
             if (streamListener != null) {
                 streamListener.onRequestHead(conn, request);
             }
+            conn.flush();
+
             boolean expectContinue = false;
             final HttpEntity entity = request.getEntity();
+            ClassicHttpResponse response = null;
             if (entity != null) {
                 final Header expect = 
request.getFirstHeader(HttpHeaders.EXPECT);
                 expectContinue = expect != null && 
HeaderElements.CONTINUE.equalsIgnoreCase(expect.getValue());
                 if (!expectContinue) {
                     if (entity.getContentLength() > LARGE_MESSAGE_LEN && 
conn.isDataAvailable(waitForEarlyResponse)) {
+                        response = conn.receiveResponseHeader();
                         conn.terminateRequest(request);
                     } else {
                         conn.sendRequestEntity(request);
@@ -170,7 +174,7 @@ public ClassicHttpResponse execute(
                 }
             }
             conn.flush();
-            ClassicHttpResponse response = null;
+
             while (response == null) {
                 if (expectContinue) {
                     if (conn.isDataAvailable(this.waitForContinue)) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to