php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73936 Certain special header() calls wrongly prevent connection from closing
Submitted: 2017-01-14 18:38 UTC Modified: 2021-11-08 09:50 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:1 (100.0%)
From: markamery at btinternet dot com Assigned:
Status: Open Package: Apache2 related
PHP Version: 7.4.3 OS: Ubuntu
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If this is not your bug, you can add a comment by following this link.
If this is your bug, but you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: markamery at btinternet dot com
New email:
PHP Version: OS:

 

 [2017-01-14 18:38 UTC] markamery at btinternet dot com
Description:
------------
Running under mod_php, calling

    header('HTTP/1.1 200 OK');

seems to prevent the connection from being closed once the PHP script finishes. This can be demonstrated by, for instance, hitting the script with ab (the Apache benchmark tool), which (unlike most HTTP clients, including browsers) only considers a request to have finished once the connection is closed, rather than when Content-Length bytes have been received. http://stackoverflow.com/q/34367115/1709587 describes this case in more detail.

Bizarrely,

    header('http/1.1 200 OK');

does not have the same effect, despite producing a character-for-character identical HTTP response (as can be observed with `curl -i --raw`).

Test script:
---------------
<?php

header('HTTP/1.1 200 OK');


Expected result:
----------------
Unless `Connection: keep-alive` is specified in the request, the connection should close once the PHP script finishes. In particular, the behaviour should be identical to other calls with the same meaning, like

    <?php
    
        http_response_code(200);

or

    <?php

        header('http/1.1 200 OK')

Actual result:
--------------
The connection does not close, and the alternate scripts suggested above have different behaviours to the test script.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-11-04 14:03 UTC] cmb@php.net
-Assigned To: +Assigned To: cmb
 [2021-11-04 14:03 UTC] cmb@php.net
> Unless `Connection: keep-alive` is specified in the request, the
> connection should close once the PHP script finishes.

For HTTP/1.1 requests, keep-alive is the default.  Only HTTP/1.0
defaults to close.[1]

> header('http/1.1 200 OK')

This is not valid according to RFC 7230, since the HTTP-name needs
to be upper case[2].

> http_response_code(200);

I tentatively agree that this should behave identically to
header('HTTP/1.1 200 OK').  I'll' have a closer look.

[1] <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection>
[2] <https://datatracker.ietf.org/doc/html/rfc7230#appendix-B>
 [2021-11-05 14:22 UTC] markamery at btinternet dot com
> > header('http/1.1 200 OK')

> This is not valid according to RFC 7230, since the HTTP-name needs
> to be upper case

It's valid according to PHP's own docs at https://www.php.net/manual/en/function.header.php, which state that

> There are two special-case header calls. The first is a header that starts with the string "HTTP/" (case is not significant) ...

For whatever reason, the design decision has been made by PHP to let you pass such strings in lowercase and then convert them to uppercase for you to make them spec-compliant. The really mysterious thing is that invoking that case-conversion behaviour somehow prevents this bug from exhibiting.
 [2021-11-05 19:17 UTC] cmb@php.net
-Status: Assigned +Status: Feedback
 [2021-11-05 19:17 UTC] cmb@php.net
Anyway, I cannot reproduce any difference between the three
scripts with PHP-7.4 on Windows using Apache 2.4.39.0.  The
response always has

    Connection: Keep-Alive
    Keep-Alive: timeout=5, max=100

and the connection is closed after ~ 5 seconds.

If I add

    Connection: close

to the request, the connection is immediately closed after the
response has been received.

So either the issue has been fixed in the meantime, or is specific
to some Apache versions or the operating system.

Can you still reproduce this with any of the actively supported
PHP versions[1]?  If so, what's your Apache version?

[1] <https://www.php.net/supported-versions.php>
 [2021-11-06 15:19 UTC] markamery at btinternet dot com
I can reproduce on Ubuntu. I just tried installing PHP and Apache with

    sudo apt install php

which gives me these versions:

    $ php --version
    PHP 7.4.3 (cli) (built: Oct 25 2021 18:20:54) ( NTS )
    Copyright (c) The PHP Group
    Zend Engine v3.4.0, Copyright (c) Zend Technologies
        with Zend OPcache v7.4.3, Copyright (c), by Zend Technologies

    $ apache2 -v
    Server version: Apache/2.4.41 (Ubuntu)
    Server built:   2021-10-14T16:24:43

If I create a test file containing

    <?php
    
    http_response_code(200);

then ab takes 14ms per request:

    $ ab -n 1000 -c 200 localhost/marktest.php
    This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/

    Benchmarking localhost (be patient)
    Completed 100 requests
    Completed 200 requests
    Completed 300 requests
    Completed 400 requests
    Completed 500 requests
    Completed 600 requests
    Completed 700 requests
    Completed 800 requests
    Completed 900 requests
    Completed 1000 requests
    Finished 1000 requests


    Server Software:        Apache/2.4.41
    Server Hostname:        localhost
    Server Port:            80

    Document Path:          /marktest.php
    Document Length:        0 bytes

    Concurrency Level:      200
    Time taken for tests:   0.070 seconds
    Complete requests:      1000
    Failed requests:        0
    Total transferred:      166000 bytes
    HTML transferred:       0 bytes
    Requests per second:    14259.44 [#/sec] (mean)
    Time per request:       14.026 [ms] (mean)
    Time per request:       0.070 [ms] (mean, across all concurrent requests)
    Transfer rate:          2311.59 [Kbytes/sec] received

    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    5   1.0      5       7
    Processing:     3    8   2.1      9      12
    Waiting:        0    6   1.7      7       8
    Total:          4   13   1.9     13      16

    Percentage of the requests served within a certain time (ms)
      50%     13
      66%     14
      75%     14
      80%     14
      90%     15
      95%     15
      98%     16
      99%     16
     100%     16 (longest request)

but if I change it to call

    header('HTTP/1.1 200 OK');

instead, then it take 8 seconds per request:

    $ ab -n 1000 -c 200 localhost/marktest.php
    This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/

    Benchmarking localhost (be patient)
    Completed 100 requests
    Completed 200 requests
    Completed 300 requests
    Completed 400 requests
    Completed 500 requests
    Completed 600 requests
    Completed 700 requests
    Completed 800 requests
    Completed 900 requests
    Completed 1000 requests
    Finished 1000 requests


    Server Software:        Apache/2.4.41
    Server Hostname:        localhost
    Server Port:            80

    Document Path:          /marktest.php
    Document Length:        0 bytes

    Concurrency Level:      200
    Time taken for tests:   41.478 seconds
    Complete requests:      1000
    Failed requests:        0
    Total transferred:      147000 bytes
    HTML transferred:       0 bytes
    Requests per second:    24.11 [#/sec] (mean)
    Time per request:       8295.664 [ms] (mean)
    Time per request:       41.478 [ms] (mean, across all concurrent requests)
    Transfer rate:          3.46 [Kbytes/sec] received

    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    1   1.1      0       4
    Processing:  5005 7684 2262.9   6939   15408
    Waiting:        1 2680 2262.7   1935   10404
    Total:       5006 7685 2263.6   6940   15410

    Percentage of the requests served within a certain time (ms)
      50%   6940
      66%   7055
      75%   7062
      80%   8056
      90%  11452
      95%  13384
      98%  14394
      99%  15018
     100%  15410 (longest request)
 [2021-11-08 09:50 UTC] cmb@php.net
-Status: Feedback +Status: Open -PHP Version: Irrelevant +PHP Version: 7.4.3 -Assigned To: cmb +Assigned To:
 [2021-11-08 09:50 UTC] cmb@php.net
Well, PHP 7.4.3 isn't exactly a current release, but okay.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 25 20:01:45 2024 UTC