TFPHTTPClient: keep-alive connection after a response timeout
=============================================================

httptimeoutreuse.pas is a single self-contained program. It needs nothing
but an FPC that can compile fcl-web; there is no OpenSSL and no network
beyond the loopback interface.

    fpc -MObjFPC -Sh httptimeoutreuse.pas
    ./httptimeoutreuse --verbose

Exit code = failed plus inconclusive checks. 2 also means "setup failed";
the last line of output says which happened.

Options:  --delay=<ms>    how long the stub server stalls (default 1500)
          --timeout=<ms>  the client's IOTimeout (default 300)
          --verbose       print the bodies and the exception classes


What it measures
----------------

Six scenarios, fourteen checks. A stub HTTP server on 127.0.0.1, built from
raw sockets on purpose, so that the only fcl-web code under test is the
client. Every reply carries the path of the request it belongs to, so a
mismatch is not a matter of interpretation.

  stale   The server answers the first request later than the client's
          IOTimeout. Does the client drop the socket, and does the next
          request get its own response?

  resend  Same delay, but with KeepConnectionReconnectLimit at its default
          of 1, and a POST. How many times does the server parse it?

  drop    No delay at all: the server answers the first request and then
          closes the connection when the second one arrives. This is the
          case the retry was built for, and it should be recovered without
          the caller noticing.

  reset   The same, but abortive: SO_LINGER is set to zero before the
          close, so the client's recv fails instead of seeing an orderly
          end of stream. The two take different routes through the client -
          a graceful close is handled without an exception, an abortive one
          goes through the socket-error handler - which is why anything
          that changes the retry has to be measured against both.

  postclose  The server closes the connection gracefully when a POST
             arrives. That retry runs through SkipReconnect rather than the
             exception handler, and it does not ask what method it is
             repeating.

  resume     The server sends the headers and half the body, then resets.
             The half that already arrived is in the caller's stream, and
             the retry appends the whole answer to it.

Against FPC trunk 695611fdb3 (fphttpclient.pp identical to main at the time
of writing), six of the fourteen checks fail on both aarch64-win64 and
aarch64-linux. The drop and reset scenarios pass, and they are there to show
what a change to the retry must not break.


Two things worth knowing if you change the harness
--------------------------------------------------

Only the first request is stalled. If the server stalls every request, a
client that recovered correctly simply times out again, and the deciding
check stops telling the two behaviours apart.

The accepted sockets carry a receive timeout so that a server thread parked
in recv still notices a stop request. A recv that merely expired must not
be mistaken for a broken connection - treat every recv < 0 as "try again"
and the server sits on a dead connection instead of accepting the client's
next one, which silently defuses the whole test. On Windows there is a
second trap in the same area: accepted sockets inherit the listener's
non-blocking mode, so they are put back into blocking mode explicitly.
