CelestiaTheDryad opened a new pull request, #516:
URL: https://github.com/apache/httpcomponents-core/pull/516

   We encountered issues where our client would send a request to the HTTP 
server and not receive a response, errored or otherwise. The request would 
remain in limbo until either side declared a socket timeout. The server 
application would receive the request and process it and submit the response to 
the Apache HTTP server. Wireshark showed the the HTTP server would ACK the 
request packet but never send data of its own.
   
   I eventually traced this  to the server applying pipelining logic at 
https://github.com/apache/httpcomponents-core/blob/d0707cdca67dc4084e372c04a675cda132e20649/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java#L368-L378
   
   This was interesting, as the client was not pipelining requests. I 
determined this was because the server was skipping 
https://github.com/apache/httpcomponents-core/blob/d0707cdca67dc4084e372c04a675cda132e20649/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java#L439-L447
   
   This caused the next request on that connection to never be considered 
"active", so it would never send data to the client.
   
   I reproduced this case by simulating a race condition between the 
application thread calling `ResponseTrigger.submitResponse()` to submit an 
response asynchronously and the IO worker thread handling the "Output Ready" 
event. If you enforce the timing between 1 and 3, the repro is guaranteed.
   
   prereq:
   requires HTTP response to have no body
   
   1. App thread submits response and pauses before executing 
ServerHttp1StreamHandler#196
   2. IO thread begins executing outputReady stack.
   3. IO thread does not execute IF at ServerHttp1StreamDuplexer#439 because 
outgoing.isResponseFinal() returns false
   4. Application thread resumes and sets the completed flag. This is its last 
action.
   5. IO thread resumes
   
   This PR contains that change I've deployed in our environment and appears to 
solve the issue.
   
   ---
   
   Aside: I'm 95% sure the presence of this code, and thus my adaptation of it, 
is also a bug:
   
https://github.com/apache/httpcomponents-core/blob/5aad6bb138372c2ed5ffd8d9317cff49dd17c2dd/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java#L199
   
   As I have to use a custom BasicResponseProducer with a synchronized 
`produce()` method to avoid fake Connection Closed exceptions when multiple 
threads execute: 
https://github.com/apache/httpcomponents-core/blob/5aad6bb138372c2ed5ffd8d9317cff49dd17c2dd/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/BasicAsyncEntityProducer.java#L130-L132
   
   The two threads in question are again the application thread submitting the 
response async and the IO worker thread. Multiple threads can enter the if 
because the `bytebuf.hasRemaining()` is not atomic WRT reading from the buffer 
inside `write()`.
    


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@hc.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@hc.apache.org
For additional commands, e-mail: dev-h...@hc.apache.org

Reply via email to