Hi Hank, On Fri, Dec 19, 2025 at 08:55:43AM +0100, Hank Bowen wrote: > Good morning, > > I have a couple of questions regarding http-reuse and idle streams. In > fact, I have sent a similar e-mail to this mailing list about relatively > recently but I think that it might have been too long and by this > discouraging to be read. > > I will ask my questions in points as I think it will help readability: > > 1. https://docs.haproxy.org/2.6/configuration.html#http-reuse states in the > description of http-reuse that specific modes differ in how the first > request of a session is handled. > > What is session in this context?
Ah interesting. We're progressively trying to reduce the number of occurrences of this "session" word which probably is one of those with the most different definitions in the same area of computing. Originally in haproxy a session was the entity which is instantiated by an incoming TCP connection. It contains the client's source IP address and some info. In the past, when keep-alive was not supported, there was no distinction between a session and a stream, so all the HTTP parsing was done on the "session" and the state was stored there, thus there was a single request/response per session. That's why you can see "sessions" in the backend stats by the way. It's the number of times a backend was selected by a given session. Later, in 1.4, we stated to support keep-alive, the session was in fact basically reset to process another request, so it was possible to have more than one request per session. It didn't change the backend stats based on the definition above. Then in 1.5 with the arrival of SSL support, we figured that the session was only completed when SSL was finished, so the notion became a bit more complex to describe: the session continues to be instantiated very early (to carry some basic info such as the source address) but it's only usable when all handshakes (including SSL) are done. Thus the "tcp-request session" rules apply immediately after these handshakes and have access to what was negotiated for SSL (e.g. certificates and SNI are known). And now the definition has not changed, so the session remains directly tied to a client-initiated incoming connection and is accessible from all the chain from the (front facing connection to back facing one). It's very likely that some wording remains in the doc mixing the two concepts of session and request, though we've been careful to try to clarify everything. Of course, clarifying when you know the difference always leaves some room for ambiguity. > 2. We read there also that in case of safe mode that "The first > request of a session is always sent over its own connection (...) This > ensures that in case the server closes the connection when the request is > being sent, the browser can decide to silently retry it" Yes! > How does the fact that the first request of a session is always sent over > its own connections ensure that the browser can decide to silently retry it? It's by virtue of reusing a connection: HTTP says that a client may reuse a connection after the end of a request/response exchange to send a new independent request and expect a response. However, by doing so the client is willing to take the risk that the connection closes during or after the emission of the request, because a close on inactivity delay was initiated by the server or any other intermediary component before the client started to send, and that client must be prepared to resend, assuming the request was not processed. Thus a client sending a second request on a connection is expected to always be able to retry if it gets no response (and there is code in haproxy to detect the server close and forward just a close to the client with no response to trigger its retry mechanism). > Why wouldn't it be possible in case when that first request were dispatched > over an already existing connection? Because nothing guarantees that the client would retry. Some do, but it's their choice. You can even check that with curl by the way. Look: $ curl -v 0:8888/one * Trying 0.0.0.0:8888... * connect to 0.0.0.0 port 8888 failed: Connection refused * Failed to connect to 0.0.0.0 port 8888 after 0 ms: Couldn't connect to server * Closing connection 0 curl: (7) Failed to connect to 0.0.0.0 port 8888 after 0 ms: Couldn't connect to server => first request failed on connection error, no retry. Let's try again with a server that listens to that port and closes after one second: $ sleep 1 | ncat -lp8888 $ curl -v 0:8888/one * Trying 0.0.0.0:8888... * Connected to 0.0.0.0 (127.0.0.1) port 8888 (#0) > GET /one HTTP/1.1 > Host: 0.0.0.0:8888 > User-Agent: curl/7.88.1-DEV > Accept: */* > * Empty reply from server * Closing connection 0 curl: (52) Empty reply from server => The request was properly sent, no response, it's a definitive error. Now you bind a listener that responds in HTTP/1.1 and quickly closes the connection after that respones, and you send two requests in a row: $ (printf "HTTP/1.1 200 OK\r\nContent-length: 0\r\n\r\n";sleep 1) | ncat -lp8888 $ curl -v 0:8888/one 0:8888/two => ncat gets the two requests one after the other: GET /one HTTP/1.1 Host: 0.0.0.0:8888 User-Agent: curl/7.88.1-DEV Accept: */* GET /two HTTP/1.1 Host: 0.0.0.0:8888 User-Agent: curl/7.88.1-DEV Accept: */* => curl sees: * Trying 0.0.0.0:8888... * Connected to 0.0.0.0 (127.0.0.1) port 8888 (#0) > GET /one HTTP/1.1 > Host: 0.0.0.0:8888 > User-Agent: curl/7.88.1-DEV > Accept: */* > < HTTP/1.1 200 OK < Content-length: 0 < * Connection #0 to host 0.0.0.0 left intact * Found bundle for host: 0x55d7c8a6b720 [serially] * Can not multiplex, even if we wanted to * Re-using existing connection #0 with host 0.0.0.0 > GET /two HTTP/1.1 > Host: 0.0.0.0:8888 > User-Agent: curl/7.88.1-DEV > Accept: */* > =>* Connection died, retrying a fresh connect (retry count: 1) * Closing connection 0 * Issue another request to this URL: 'http://0.0.0.0:8888/two' * Hostname 0.0.0.0 was found in DNS cache * Trying 0.0.0.0:8888... * connect to 0.0.0.0 port 8888 failed: Connection refused * Failed to connect to 0.0.0.0 port 8888 after 0 ms: Couldn't connect to server * Closing connection 1 curl: (7) Failed to connect to 0.0.0.0 port 8888 after 0 ms: Couldn't connect to server Have you seen "Connection died, retrying a fresh connect" above ? > 3. After what time and by what criteria is a connection recognized idle in > tcp and http mode? It's not time, it's purely a protocol thing: once both a request and response have been fully exchanged, by definition the connection is idle again and reusable for a new request/response exchange. What haproxy does in safe mode is that it considers that a client is likely unable to retry the first failed request, so it will always deliver the first request of a client's connection to a fresh connection on the server side. But if it's a client's second connection, it will be sent over any existing server connection. A lot of users deploy with "http-reuse always" when they know that clients are able to retry (e.g. typically applications, sometimes browsers), or that the server-side connections are super reliable and the low failure rate will be mostly covered by some clients, to reach a final rate which is within the usual end-user experience. E.g. if you fail one request on 100 million, in the life of a user it's almost non-existing and can easily be ignored by may sites. And for what matters we're using that on haproxy.org as well for static files (i.e. everything except gitweb) and for the instance that forwards to my home pages since it's not important and failures are more likely caused by my internet access rather than dropping connections. Hoping this helps, Willy

