Skip to content

Improved responses to different requests on static resources in built-in web server #8215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 64 additions & 40 deletions sapi/cli/php_cli_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ typedef struct php_cli_server_http_response_status_code_pair {
static php_cli_server_http_response_status_code_pair template_map[] = {
{ 400, "<h1>%s</h1><p>Your browser sent a request that this server could not understand.</p>" },
{ 404, "<h1>%s</h1><p>The requested resource <code class=\"url\">%s</code> was not found on this server.</p>" },
{ 405, "<h1>%s</h1><p>Requested method not allowed.</p>" },
{ 500, "<h1>%s</h1><p>The server is temporarily unavailable.</p>" },
{ 501, "<h1>%s</h1><p>Request method not supported.</p>" }
};
Expand Down Expand Up @@ -1980,49 +1981,51 @@ static zend_result php_cli_server_send_error_page(php_cli_server *server, php_cl
php_cli_server_content_sender_ctor(&client->content_sender);
client->content_sender_initialized = true;

escaped_request_uri = php_escape_html_entities_ex((const unsigned char *) ZSTR_VAL(client->request.request_uri), ZSTR_LEN(client->request.request_uri), 0, ENT_QUOTES, NULL, /* double_encode */ 0, /* quiet */ 0);
if (client->request.request_method != PHP_HTTP_HEAD) {
escaped_request_uri = php_escape_html_entities_ex((const unsigned char *) ZSTR_VAL(client->request.request_uri), ZSTR_LEN(client->request.request_uri), 0, ENT_QUOTES, NULL, /* double_encode */ 0, /* quiet */ 0);

{
static const char prologue_template[] = "<!doctype html><html><head><title>%d %s</title>";
php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(prologue_template) + 3 + strlen(status_string) + 1);
if (!chunk) {
goto fail;
{
static const char prologue_template[] = "<!doctype html><html><head><title>%d %s</title>";
php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(prologue_template) + 3 + strlen(status_string) + 1);
if (!chunk) {
goto fail;
}
snprintf(chunk->data.heap.p, chunk->data.heap.len, prologue_template, status, status_string);
chunk->data.heap.len = strlen(chunk->data.heap.p);
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
snprintf(chunk->data.heap.p, chunk->data.heap.len, prologue_template, status, status_string);
chunk->data.heap.len = strlen(chunk->data.heap.p);
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
{
php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(php_cli_server_css, sizeof(php_cli_server_css) - 1);
if (!chunk) {
goto fail;
{
php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(php_cli_server_css, sizeof(php_cli_server_css) - 1);
if (!chunk) {
goto fail;
}
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
{
static const char template[] = "</head><body>";
php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(template, sizeof(template) - 1);
if (!chunk) {
goto fail;
{
static const char template[] = "</head><body>";
php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(template, sizeof(template) - 1);
if (!chunk) {
goto fail;
}
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
{
php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(content_template) + ZSTR_LEN(escaped_request_uri) + 3 + strlen(status_string) + 1);
if (!chunk) {
goto fail;
{
php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(content_template) + ZSTR_LEN(escaped_request_uri) + 3 + strlen(status_string) + 1);
if (!chunk) {
goto fail;
}
snprintf(chunk->data.heap.p, chunk->data.heap.len, content_template, status_string, ZSTR_VAL(escaped_request_uri));
chunk->data.heap.len = strlen(chunk->data.heap.p);
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
snprintf(chunk->data.heap.p, chunk->data.heap.len, content_template, status_string, ZSTR_VAL(escaped_request_uri));
chunk->data.heap.len = strlen(chunk->data.heap.p);
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
{
static const char epilogue_template[] = "</body></html>";
php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(epilogue_template, sizeof(epilogue_template) - 1);
if (!chunk) {
goto fail;
{
static const char epilogue_template[] = "</body></html>";
php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(epilogue_template, sizeof(epilogue_template) - 1);
if (!chunk) {
goto fail;
}
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}

{
Expand All @@ -2038,6 +2041,15 @@ static zend_result php_cli_server_send_error_page(php_cli_server *server, php_cl
smart_str_appends_ex(&buffer, "Content-Length: ", 1);
smart_str_append_unsigned_ex(&buffer, php_cli_server_buffer_size(&client->content_sender.buffer), 1);
smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
if (status == 405) {
smart_str_appends_ex(&buffer, "Allow: ", 1);
smart_str_appends_ex(&buffer, php_http_method_str(PHP_HTTP_GET), 1);
smart_str_appends_ex(&buffer, ", ", 1);
smart_str_appends_ex(&buffer, php_http_method_str(PHP_HTTP_HEAD), 1);
smart_str_appends_ex(&buffer, ", ", 1);
smart_str_appends_ex(&buffer, php_http_method_str(PHP_HTTP_POST), 1);
smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
}
smart_str_appendl_ex(&buffer, "\r\n", 2, 1);

chunk = php_cli_server_chunk_heap_new(buffer.s, ZSTR_VAL(buffer.s), ZSTR_LEN(buffer.s));
Expand All @@ -2053,14 +2065,18 @@ static zend_result php_cli_server_send_error_page(php_cli_server *server, php_cl
if (errstr) {
pefree(errstr, 1);
}
zend_string_free(escaped_request_uri);
if (escaped_request_uri) {
zend_string_free(escaped_request_uri);
}
return SUCCESS;

fail:
if (errstr) {
pefree(errstr, 1);
}
zend_string_free(escaped_request_uri);
if (escaped_request_uri) {
zend_string_free(escaped_request_uri);
}
return FAILURE;
} /* }}} */

Expand Down Expand Up @@ -2088,6 +2104,12 @@ static zend_result php_cli_server_begin_send_static(php_cli_server *server, php_
int fd;
int status = 200;

if (client->request.request_method == PHP_HTTP_DELETE
|| client->request.request_method == PHP_HTTP_PUT
|| client->request.request_method == PHP_HTTP_PATCH) {
return php_cli_server_send_error_page(server, client, 405);
}

if (client->request.path_translated && strlen(client->request.path_translated) != client->request.path_translated_len) {
/* can't handle paths that contain nul bytes */
return php_cli_server_send_error_page(server, client, 400);
Expand Down Expand Up @@ -2115,7 +2137,9 @@ static zend_result php_cli_server_begin_send_static(php_cli_server *server, php_

php_cli_server_content_sender_ctor(&client->content_sender);
client->content_sender_initialized = true;
client->file_fd = fd;
if (client->request.request_method != PHP_HTTP_HEAD) {
client->file_fd = fd;
}

{
php_cli_server_chunk *chunk;
Expand Down
87 changes: 85 additions & 2 deletions sapi/cli/tests/php_cli_server_013.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,60 @@ HEAD /main/foo/bar HTTP/1.1
Host: {$host}


HEADER
)) {
while (!feof($fp)) {
$output .= fgets($fp);
}
}

echo preg_replace("/<style>(.*?)<\/style>/s", "<style>AAA</style>", $output), "\n";
fclose($fp);

$output = '';
$fp = php_cli_server_connect();

if(fwrite($fp, <<<HEADER
DELETE / HTTP/1.1
Host: {$host}


HEADER
)) {
while (!feof($fp)) {
$output .= fgets($fp);
}
}

echo preg_replace("/<style>(.*?)<\/style>/s", "<style>AAA</style>", $output), "\n";
fclose($fp);

$output = '';
$fp = php_cli_server_connect();

if(fwrite($fp, <<<HEADER
PUT / HTTP/1.1
Host: {$host}


HEADER
)) {
while (!feof($fp)) {
$output .= fgets($fp);
}
}

echo preg_replace("/<style>(.*?)<\/style>/s", "<style>AAA</style>", $output), "\n";
fclose($fp);

$output = '';
$fp = php_cli_server_connect();

if(fwrite($fp, <<<HEADER
PATCH / HTTP/1.1
Host: {$host}


HEADER
)) {
while (!feof($fp)) {
Expand Down Expand Up @@ -94,5 +148,34 @@ Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d

<!doctype html><html><head><title>404 Not Found</title><style>AAA</style>
</head><body><h1>Not Found</h1><p>The requested resource <code class="url">/main/foo/bar</code> was not found on this server.</p></body></html>

HTTP/1.1 405 Method Not Allowed
Host: %s
Date: %s
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d
Allow: GET, HEAD, POST

<!doctype html><html><head><title>405 Method Not Allowed</title><style>AAA</style>
</head><body><h1>Method Not Allowed</h1><p>Requested method not allowed.</p></body></html>
HTTP/1.1 405 Method Not Allowed
Host: %s
Date: %s
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d
Allow: GET, HEAD, POST

<!doctype html><html><head><title>405 Method Not Allowed</title><style>AAA</style>
</head><body><h1>Method Not Allowed</h1><p>Requested method not allowed.</p></body></html>
HTTP/1.1 405 Method Not Allowed
Host: %s
Date: %s
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d
Allow: GET, HEAD, POST

<!doctype html><html><head><title>405 Method Not Allowed</title><style>AAA</style>
</head><body><h1>Method Not Allowed</h1><p>Requested method not allowed.</p></body></html>