https://bz.apache.org/bugzilla/show_bug.cgi?id=66058
Bug ID: 66058
Summary: Apache Benchmark (ab) does not always read the end of
a chunked transfer when using keep-alive
Product: Apache httpd-2
Version: 2.5-HEAD
Hardware: PC
OS: Linux
Status: NEW
Severity: major
Priority: P2
Component: All
Assignee: bugs@httpd.apache.org
Reporter: nik...@seyfarth.de
Target Milestone: ---
Created attachment 38282
--> https://bz.apache.org/bugzilla/attachment.cgi?id=38282&action=edit
Example HTTP server (PHP 8.1 + ReactPHP) to reproduce the issue
When using ApacheBench with the keep-alive (-k) and the repeat (-n times) flag
and the server sends a chunked response then ApacheBench fails (most of the
time*).
I have reproduced it with other software (OpenSwoole), so it is not caused by
my script or the library I'm using to (re)produce this bug.
Expected behavior
I would expect ApacheBench to benchmark servers sending chunked responses in a
reused connection the same way as servers not sending chunked responses like it
happens when not using 'Connection: keep-alive'.
Reproduction Notes
I have attached a small ReactPHP script that can be run standalone on the CLI
after the required dependencies have been installed. This script will start a
web-server that responds with a response that uses 'Transfer-Encoding: chunked'
and supports 'Connection: keep-alive'.
One can easily check that the script is working by opening a connection with
telnet and sending repeatedly (don't forget the two newlines at the end):
$ telnet 127.0.0.1 8080
GET / HTTP/1.1
Host: 127.0.0.1:8080
Accept: */*
Connection: keep-alive
curl also produces reasonable output:
$ curl -v --raw http://127.0.0.1:8080 http://127.0.0.1:8080
http://127.0.0.1:8080
(log omitted)
Reproduction Steps
While any server supporting keep-alive and sending a chunked response is
running (please note that '-v 4' is not required for the error to happen;
greater values for -n increase the chance of it happening*):
$ ab -v 4 -n 3 -k http://127.0.0.1:8080/
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
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)...INFO: GET header ==
---
GET / HTTP/1.0
Connection: Keep-Alive
Host: 127.0.0.1:8080
User-Agent: ApacheBench/2.3
Accept: */*
---
LOG: header received:
HTTP/1.0 200 OK
Server: ReactPHP/1
Date: Tue, 10 May 2022 13:16:39 GMT
Connection: keep-alive
LOG: Response code = 200
LOG: header received:
chunk
apr_pollset_poll: The timeout specified has expired (70007)
Total of 1 requests completed
ab hangs for about 30 seconds after writing 'chunk' (third last line).
Variant: When sending an interrupt during the time ApacheBench hangs the
summary is printed:
(previous log omitted)
LOG: Response code = 200
LOG: header received:
chunk
^C
Server Software:ReactPHP/1
Server Hostname:127.0.0.1
Server Port:8080
Document Path: /
Document Length:0 bytes
Concurrency Level: 1
Time taken for tests: 26.229 seconds
Complete requests: 1
Failed requests:0
Keep-Alive requests:1
Total transferred: 105 bytes
HTML transferred: 0 bytes
Requests per second:0.04 [#/sec] (mean)
Time per request: 26229.224 [ms] (mean)
Time per request: 26229.224 [ms] (mean, across all concurrent requests)
Transfer rate: 0.00 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect:00 0.0 0 0
Processing: 00 0.0 0 0
Waiting:00 0.0 0 0
Total: 00 0.0 0 0
I've compiled ApacheBench from source using the current trunk (rev 1900571) and
reproduced the bug successfully (but the output differs):
$ /from/source/ab -n 3 -k http://127.0.0.1:8080/
This is ApacheBench, Version 2.3 <$Revision: 1900571 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd,
http://web.archive.org/web/2304112933/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)
Server Software:ReactPHP/1
Server Hostname:127.0.0.1
Server Port:8080
Document Path: /
Document Length:0 bytes
Number of workers: 1
Concurrency Level: 1
Concurrency achieved: 1
Rampup delay: 0 [ms]
Time taken for tests: 0.000 seconds
Complete requests: 0
Failed requests:0
Keep-Alive requests:0
Total transferred: 105 bytes
HTML transferred: 5 bytes
Additional Notes
*most of the time: While finding and exploring this bug and it's reason I had
cases where using -n 2 sometimes caused this