3

There are two headers relate to Http keep alive,

Connection: Keep-Alive
Keep-Alive: timeout=5, max=1000
  1. Which side sends HTTP1.1 "Keep-Alive: param"?
  2. When client and server use HTTP1.1, will client send TCP keep alive probe?
  3. In order to use HTTP1.1 keep alive, must client set Connection: Keep-Alive or Keep-Alive: param?

I have above qustions is because:

Recently I observed a problem using http client. After 2 hours since last time client sends request, once client sends request it would never hear response from server. In order to find reason, I did:

  1. having tcpdump runs on both client and server sides.
  2. client sends one request (and receives response)
  3. wait for 2 hours
  4. client sends another request (but does not receive any response) <-- this is the problem.

Between the two requests, pcaps on both sides show that no one sends FIN. And server does not receive the second request. I think something is messed up with HTTP1.1 keep alive transmission.

Tiina
  • 4,285
  • 7
  • 44
  • 73
  • Any firewalls between the client and server? – Greg Cowell Mar 04 '23 at 04:07
  • @GregCowell There is no firewall between them. Client only not receiving response if it sends request after 2 hours since last time it sent one, I'll update PO. – Tiina Mar 04 '23 at 05:51

2 Answers2

0

For Questions 1 and 3, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive

  • Both sides can send Connection: Keep-alive and Keep-alive: ....
  • Both headers are required to have an effect
  • The connection still may close at any time

For question 2, see: HTTP Keep Alive and TCP keep alive

  • Tcp keep alive is not related

If you have e.g. NAT between the client and server, 2 hours can be too long for an idle connection to stay functional, but then again if neither side sees a fin, then someone is not handling things according to spec.

Sami Sallinen
  • 3,203
  • 12
  • 16
0

After digging some time, this is some conclusions: HTTP1.1 headers for both Connection keepalive and Keepalive params does not necessarily mean both ends would do something for it. Still, both ends does need to send/receive FIN, client should be able to continue send packets until any side sends/receives FIN.

So I finally solve this problem by sending FIN explicitly. When client does not have data transmission for 60 seconds, it closes connection by sending FIN. While this should be implemented on server side. For example, server does not hear for 60 seconds, so it closes connection, but I did not find such configuration for Webflux netty server.

A working code is like this:

HttpClient
    .create()
    .tcpConfiguration(client -> client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
            .option(ChannelOption.SO_KEEPALIVE, true)
            .option(EpollChannelOption.TCP_KEEPIDLE, 300)
            .option(EpollChannelOption.TCP_KEEPINTVL, 60)
            .option(EpollChannelOption.TCP_KEEPCNT, 8)
    )

Not working code:

webClient.post()
    .uri(uri)
    .accept(MediaType.APPLICATION_JSON)
    .header("Keep-Alive", "timeout=60, max=1000") 
    .header(HttpHeaders.CONNECTION, "Keep-Alive")

adding transmission control in TCP works, while HTTP headers not, although for http API users, they should not consider layers below it.

Tiina
  • 4,285
  • 7
  • 44
  • 73