Hyper: HTTP/1.0 and keep-alive bug

Created on 30 Jul 2018  路  2Comments  路  Source: hyperium/hyper

I was testing my hyper-based server with ab (Apache Bench) and it turns out that if you enable the -k (_keepalive_) option, ab just hangs.

Turns out that ab uses HTTP/1.0 requests. It sends a Connection: keep-alive header, and hyper does keep the connection alive, but it does not add a Connection: keep-alive header to the reply. ab assumes that the server does not support keep-alive, and waits for the server to close the connection, and as a result ab hangs.

Hyper should probably add a Connection: keep-alive header to the response if it is found in the request, the request protocol is HTTP/1.0 and there's no Connection header in the Response object present yet.

A-http1 E-easy S-bug

Most helpful comment

Yikes, you're right! Seems there's two parts to this bug:

  • When the version is HTTP/1.0, and the response didn't send Connection: keep-alive, it should have closed the connection.
  • When coercing an HTTP/1.1 response to HTTP/1.0 because that's what the client speaks, a Connection: keep-alive header should be added.

Probably the best place to do this is in Conn::enforce_version. If enforcing 1.0, then check for the response version. If also 1.0, then check for Connection: keep-alive, and it not present, call self.state.disable_keep_alive(). If the response is 1.1, then translating to 1.1 should add Connection: keep-alive if self.state.wants_keep_alive() is true.

All 2 comments

Yikes, you're right! Seems there's two parts to this bug:

  • When the version is HTTP/1.0, and the response didn't send Connection: keep-alive, it should have closed the connection.
  • When coercing an HTTP/1.1 response to HTTP/1.0 because that's what the client speaks, a Connection: keep-alive header should be added.

Probably the best place to do this is in Conn::enforce_version. If enforcing 1.0, then check for the response version. If also 1.0, then check for Connection: keep-alive, and it not present, call self.state.disable_keep_alive(). If the response is 1.1, then translating to 1.1 should add Connection: keep-alive if self.state.wants_keep_alive() is true.

PR #1634 should fix this.

Here is a comparison of the Apache Bench output before and after.

before change

$ ab -k http://127.0.0.1:3000/
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)...apr_pollset_poll: The timeout specified has expired (70007)

after change

$ ab -k http://127.0.0.1:3000/
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient).....done


Server Software:        
Server Hostname:        127.0.0.1
Server Port:            3000

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      1
Time taken for tests:   0.001 seconds
Complete requests:      1
Failed requests:        0
Keep-Alive requests:    1
Total transferred:      112 bytes
HTML transferred:       12 bytes
Requests per second:    939.85 [#/sec] (mean)
Time per request:       1.064 [ms] (mean)
Time per request:       1.064 [ms] (mean, across all concurrent requests)
Transfer rate:          102.80 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     1    1   0.0      1       1
Waiting:        1    1   0.0      1       1
Total:          1    1   0.0      1       1
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jgillich picture jgillich  路  25Comments

timonv picture timonv  路  24Comments

DemiMarie picture DemiMarie  路  25Comments

janus picture janus  路  21Comments

DiamondLovesYou picture DiamondLovesYou  路  19Comments