Skip to content

sapi/cli: support Expect 100-continue in PHP dev server - #23245

Closed
Sjord wants to merge 8 commits into
php:masterfrom
Sjord:cli-server-expect-100-continue
Closed

sapi/cli: support Expect 100-continue in PHP dev server#23245
Sjord wants to merge 8 commits into
php:masterfrom
Sjord:cli-server-expect-100-continue

Conversation

@Sjord

@Sjord Sjord commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

When posting large payloads, curl checks whether the server is ready for the body. It sends an Expect: 100-continue header and expects HTTP/1.1 100 Continue as the response before sending the body. The PHP development server did not support this, causing a timeout in curl. This made such requests take one second longer.

@Sjord

Sjord commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@php/release-managers-86 @mbeccati Is this something you want in PHP 8.6?

@mbeccati

mbeccati commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

@Sjord Seems like a useful fix to me. I will discuss with the team.

EDIT: we agree it's perfectly fine for 8.6

@Sjord

Sjord commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@iluuu1994 @Girgias Could you review this please?

@LamentXU123
LamentXU123 requested a review from devnexen August 20, 2026 13:20
@devnexen

Copy link
Copy Markdown
Member

ok will try my best to review, I have some questions ...

Comment thread sapi/cli/php_cli_server.c Outdated
Comment thread sapi/cli/tests/php_cli_server_expect_100_continue_curl.phpt
@devnexen

Copy link
Copy Markdown
Member

Would it be possible to add this test ?

--TEST--
Failure to send "100 Continue" is reported with ignore_user_abort=1
--SKIPIF--
<?php
include "skipif.inc";
if (!extension_loaded("sockets")) die("skip sockets extension required");
if (PHP_OS_FAMILY === "Windows") die("skip SO_LINGER reset behaviour differs on Windows");
?>
--FILE--
<?php
$log = tempnam(sys_get_temp_dir(), 'cli_server_log');
$log_fd = fopen($log, 'ab');
$server = proc_open(
    [getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY, '-n', '-d', 'ignore_user_abort=1', '-S', '127.0.0.1:0'],
    [0 => STDIN, 1 => $log_fd, 2 => $log_fd],
    $pipes,
    __DIR__
);

$port = null;
for ($i = 0; $i < 100 && $port === null; $i++) {
    usleep(50000);
    if (preg_match('@://127\.0\.0\.1:(\d+)\) started@', file_get_contents($log), $m)) {
        $port = $m[1];
    }
}

$fp = fsockopen('127.0.0.1', $port);
socket_set_option(socket_import_stream($fp), SOL_SOCKET, SO_LINGER, ['l_onoff' => 1, 'l_linger' => 0]);
fwrite($fp, "POST / HTTP/1.1\r\nExpect: 100-continue\r\nContent-Length: 4\r\n\r\n");
fclose($fp);

$output = '';
for ($i = 0; $i < 100 && !str_contains($output, 'Invalid request'); $i++) {
    usleep(50000);
    $output = file_get_contents($log);
}

var_dump(str_contains($output, 'Invalid request'), str_contains($output, 'Unexpected EOF'));

proc_terminate($server);
unlink($log);
?>
--EXPECT--
bool(true)
bool(false)

Comment thread sapi/cli/php_cli_server.c
@Sjord

Sjord commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks. I added the test and that led to quite a rabbit hole. But php_cli_server_client_send_through now returns the number of bytes sent and I check it in the calling code. I tried to make your test a bit nicer.

@devnexen

devnexen commented Aug 24, 2026

Copy link
Copy Markdown
Member

I think it makes sense this get merged first and you rebase from it.

Sjord added 7 commits August 24, 2026 17:19
When posting large payloads, curl checks whether the server is ready for
the body. It sends an `Expect: 100-continue` header and expects
`HTTP/1.1 100 Continue` as the response before sending the body. The PHP
development server did not support this, causing a timeout in curl. This
made such requests take one second longer.

- https://everything.curl.dev/http/post/expect100.html
- php#23242
This is not a curl test, but a test of the behavior of the PHP
development server. It should thus not be in the curl directory but in
the sapi/cli directory.
If php_cli_server_client_send_through fails while sending `HTTP/1.1 100
Continue` we don't want the server to exit.
- fix the return value of php_cli_server_client_send_through so that it
  returns the number of bytes sent.
- check that return value, and report error if we didn't send all bytes.
- add @devnexen's test.
Use php_cli_server.inc. Return the output file to the caller so that we
can check the error message in the output.
This does not functionally change anything, but it's nicer to have the
`nbytes_consumed` close together.
@Sjord
Sjord force-pushed the cli-server-expect-100-continue branch from 6115983 to 0d4332a Compare August 24, 2026 17:21
Comment thread sapi/cli/php_cli_server.c
HTTP/1.0 doesn't support this. Check for HTTP/1.1 before setting
client->expect_continue. This also makes it possible to hardcode the
response string, which makes this a bit simpler.

Add test that checks whether the server send something (100 Continue)
using stream_select. We expect that the server did not send anything,
since the test uses HTTP/1.0.
@devnexen devnexen closed this in b8c43c3 Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants