mirror of
https://github.com/php/php-src.git
synced 2024-11-23 01:44:06 +08:00
Add request_parse_body() function
RFC: https://wiki.php.net/rfc/rfc1867-non-post This function allows populating the $_POST and $_FILES globals for non-post requests. This avoids manual parsing of RFC1867 requests. Fixes #55815 Closes GH-11472
This commit is contained in:
parent
2f894389b6
commit
cd66fcc68b
@ -143,6 +143,11 @@ PHP 8.4 UPGRADE NOTES
|
||||
2. New Features
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Added request_parse_body() function that allows parsing RFC1867 (multipart)
|
||||
requests in non-POST HTTP requests.
|
||||
RFC: https://wiki.php.net/rfc/rfc1867-non-post
|
||||
|
||||
- Date:
|
||||
. Added static methods
|
||||
DateTime[Immutable]::createFromTimestamp(int|float $timestamp): static.
|
||||
|
@ -599,6 +599,7 @@ static const func_info_t func_infos[] = {
|
||||
F1("fsockopen", MAY_BE_RESOURCE|MAY_BE_FALSE),
|
||||
FN("pfsockopen", MAY_BE_RESOURCE|MAY_BE_FALSE),
|
||||
F1("http_build_query", MAY_BE_STRING),
|
||||
F1("request_parse_body", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("image_type_to_mime_type", MAY_BE_STRING),
|
||||
F1("image_type_to_extension", MAY_BE_STRING|MAY_BE_FALSE),
|
||||
F1("getimagesize", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE),
|
||||
|
25
Zend/tests/request_parse_body/invalid_boundary.phpt
Normal file
25
Zend/tests/request_parse_body/invalid_boundary.phpt
Normal file
@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
request_parse_body() with multipart and invalid boundary
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary="foobar
|
||||
empty
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body();
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: Invalid boundary in multipart/form-data POST data
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
56
Zend/tests/request_parse_body/multipart.phpt
Normal file
56
Zend/tests/request_parse_body/multipart.phpt
Normal file
@ -0,0 +1,56 @@
|
||||
--TEST--
|
||||
request_parse_body() with multipart
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="post_field_name"
|
||||
|
||||
post field data
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="file_name"; filename="original_file_name.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
file data
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
[$_POST, $_FILES] = request_parse_body();
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
$file_path = __DIR__ . '/put_multipart_uploaded_file.txt';
|
||||
move_uploaded_file($_FILES['file_name']['tmp_name'], $file_path);
|
||||
var_dump(file_get_contents($file_path));
|
||||
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
$file_path = __DIR__ . '/put_multipart_uploaded_file.txt';
|
||||
@unlink($file_path);
|
||||
?>
|
||||
--EXPECTF--
|
||||
array(1) {
|
||||
["post_field_name"]=>
|
||||
string(15) "post field data"
|
||||
}
|
||||
array(1) {
|
||||
["file_name"]=>
|
||||
array(6) {
|
||||
["name"]=>
|
||||
string(22) "original_file_name.txt"
|
||||
["full_path"]=>
|
||||
string(22) "original_file_name.txt"
|
||||
["type"]=>
|
||||
string(10) "text/plain"
|
||||
["tmp_name"]=>
|
||||
string(%d) "%s"
|
||||
["error"]=>
|
||||
int(0)
|
||||
["size"]=>
|
||||
int(9)
|
||||
}
|
||||
}
|
||||
string(9) "file data"
|
32
Zend/tests/request_parse_body/multipart_garbled.phpt
Normal file
32
Zend/tests/request_parse_body/multipart_garbled.phpt
Normal file
@ -0,0 +1,32 @@
|
||||
--TEST--
|
||||
request_parse_body() with multipart and garbled field
|
||||
--INI--
|
||||
max_file_uploads=1
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data;
|
||||
Content-Type: text/plain
|
||||
|
||||
post field data
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body();
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: File Upload Mime headers garbled
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
37
Zend/tests/request_parse_body/multipart_max_files.phpt
Normal file
37
Zend/tests/request_parse_body/multipart_max_files.phpt
Normal file
@ -0,0 +1,37 @@
|
||||
--TEST--
|
||||
request_parse_body() with multipart and exceeding max files
|
||||
--INI--
|
||||
max_file_uploads=1
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="file1"; filename="file1.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
file data
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="file2"; filename="file2.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
file data
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body();
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: Maximum number of allowable file uploads has been exceeded
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
35
Zend/tests/request_parse_body/multipart_max_input_vars.phpt
Normal file
35
Zend/tests/request_parse_body/multipart_max_input_vars.phpt
Normal file
@ -0,0 +1,35 @@
|
||||
--TEST--
|
||||
request_parse_body() with multipart and exceeding max input vars
|
||||
--INI--
|
||||
max_input_vars=1
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="field1"
|
||||
|
||||
post field data
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="field2"
|
||||
|
||||
post field data
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body();
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: Input variables exceeded 1. To increase the limit change max_input_vars in php.ini.
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
36
Zend/tests/request_parse_body/multipart_max_parts.phpt
Normal file
36
Zend/tests/request_parse_body/multipart_max_parts.phpt
Normal file
@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
request_parse_body() with multipart and exceeding max parts
|
||||
--INI--
|
||||
max_multipart_body_parts=1
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="post_field_name"
|
||||
|
||||
post field data
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="file_name"; filename="original_file_name.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
file data
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body();
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: Multipart body parts limit exceeded 1. To increase the limit change max_multipart_body_parts in php.ini.
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
request_parse_body() with multipart and missing boundary
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data
|
||||
empty
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$stream = fopen('php://memory','r+');
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body();
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: Missing boundary in multipart/form-data POST data
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
request_parse_body() invalid key
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
request_parse_body(options: ['foo' => 1]);
|
||||
} catch (Error $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
request_parse_body(options: [42 => 1]);
|
||||
} catch (Error $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
ValueError: Invalid key "foo" in $options argument
|
||||
ValueError: Invalid integer key in $options argument
|
@ -0,0 +1,17 @@
|
||||
--TEST--
|
||||
request_parse_body() invalid quantity
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
request_parse_body(options: [
|
||||
'upload_max_filesize' => '1GB',
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: Invalid quantity "1GB": unknown multiplier "B", interpreting as "1" for backwards compatibility in %s on line %d
|
||||
RequestParseBodyException: Request does not provide a content type
|
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
request_parse_body() invalid value type
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
request_parse_body(options: [
|
||||
'max_input_vars' => [],
|
||||
]);
|
||||
} catch (Error $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
ValueError: Invalid array value in $options argument
|
@ -0,0 +1,39 @@
|
||||
--TEST--
|
||||
request_parse_body() max_file_uploads option
|
||||
--INI--
|
||||
max_file_uploads=10
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="file1"; filename="file1.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
file data
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="file2"; filename="file2.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
file data
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body([
|
||||
'max_file_uploads' => 1,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: Maximum number of allowable file uploads has been exceeded
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
--TEST--
|
||||
request_parse_body() max_input_vars option
|
||||
--INI--
|
||||
max_input_vars=10
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="field1"
|
||||
|
||||
post field data
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="field2"
|
||||
|
||||
post field data
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body([
|
||||
'max_input_vars' => 1,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: Input variables exceeded 1. To increase the limit change max_input_vars in php.ini.
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
--TEST--
|
||||
request_parse_body() max_multipart_body_parts option
|
||||
--INI--
|
||||
max_multipart_body_parts=10
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="post_field_name"
|
||||
|
||||
post field data
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="file_name"; filename="original_file_name.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
file data
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body([
|
||||
'max_multipart_body_parts' => 1,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: Multipart body parts limit exceeded 1. To increase the limit change max_multipart_body_parts in php.ini.
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
--TEST--
|
||||
request_parse_body() post_max_size option
|
||||
--INI--
|
||||
post_max_size=1M
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="field1"
|
||||
|
||||
post field data
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: form-data; name="field2"
|
||||
|
||||
post file data
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body([
|
||||
'post_max_size' => '302',
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
RequestParseBodyException: POST Content-Length of 303 bytes exceeds the limit of 302 bytes
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
--TEST--
|
||||
request_parse_body() upload_max_filesize option
|
||||
--INI--
|
||||
upload_max_filesize=1M
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: multipart/form-data; boundary=---------------------------84000087610663814162942123332
|
||||
-----------------------------84000087610663814162942123332
|
||||
Content-Disposition: name="file1"; filename="file1.txt"
|
||||
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
-----------------------------84000087610663814162942123332--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body([
|
||||
'upload_max_filesize' => '128',
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
array(0) {
|
||||
}
|
||||
array(1) {
|
||||
["file1"]=>
|
||||
array(6) {
|
||||
["name"]=>
|
||||
string(9) "file1.txt"
|
||||
["full_path"]=>
|
||||
string(9) "file1.txt"
|
||||
["type"]=>
|
||||
string(0) ""
|
||||
["tmp_name"]=>
|
||||
string(0) ""
|
||||
["error"]=>
|
||||
int(1)
|
||||
["size"]=>
|
||||
int(0)
|
||||
}
|
||||
}
|
27
Zend/tests/request_parse_body/unsupported_content_type.phpt
Normal file
27
Zend/tests/request_parse_body/unsupported_content_type.phpt
Normal file
@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
request_parse_body() with multipart and unsupported Content-Type
|
||||
--INI--
|
||||
max_input_vars=1
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: application/json
|
||||
{"hello": "world"}
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
[$_POST, $_FILES] = request_parse_body();
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e), ': ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
InvalidArgumentException: Content-Type "application/json" is not supported
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
}
|
34
Zend/tests/request_parse_body/urlencoded.phpt
Normal file
34
Zend/tests/request_parse_body/urlencoded.phpt
Normal file
@ -0,0 +1,34 @@
|
||||
--TEST--
|
||||
request_parse_body() with urlencoded
|
||||
--ENV--
|
||||
REQUEST_METHOD=PUT
|
||||
--POST_RAW--
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
foo=foo&bar[]=1&bar[]=2
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
[$_POST, $_FILES] = request_parse_body();
|
||||
|
||||
var_dump($_POST, $_FILES);
|
||||
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
$file_path = __DIR__ . '/put_multipart_uploaded_file.txt';
|
||||
@unlink($file_path);
|
||||
?>
|
||||
--EXPECT--
|
||||
array(2) {
|
||||
["foo"]=>
|
||||
string(3) "foo"
|
||||
["bar"]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(1) "1"
|
||||
[1]=>
|
||||
string(1) "2"
|
||||
}
|
||||
}
|
||||
array(0) {
|
||||
}
|
@ -42,6 +42,7 @@ ZEND_API zend_class_entry *zend_ce_value_error;
|
||||
ZEND_API zend_class_entry *zend_ce_arithmetic_error;
|
||||
ZEND_API zend_class_entry *zend_ce_division_by_zero_error;
|
||||
ZEND_API zend_class_entry *zend_ce_unhandled_match_error;
|
||||
ZEND_API zend_class_entry *zend_ce_request_parse_body_exception;
|
||||
|
||||
/* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does not* execute finally blocks. */
|
||||
static zend_class_entry zend_ce_unwind_exit;
|
||||
@ -788,6 +789,9 @@ void zend_register_default_exception(void) /* {{{ */
|
||||
zend_ce_unhandled_match_error = register_class_UnhandledMatchError(zend_ce_error);
|
||||
zend_init_exception_class_entry(zend_ce_unhandled_match_error);
|
||||
|
||||
zend_ce_request_parse_body_exception = register_class_RequestParseBodyException(zend_ce_exception);
|
||||
zend_init_exception_class_entry(zend_ce_request_parse_body_exception);
|
||||
|
||||
INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL);
|
||||
|
||||
INIT_CLASS_ENTRY(zend_ce_graceful_exit, "GracefulExit", NULL);
|
||||
|
@ -36,6 +36,7 @@ extern ZEND_API zend_class_entry *zend_ce_value_error;
|
||||
extern ZEND_API zend_class_entry *zend_ce_arithmetic_error;
|
||||
extern ZEND_API zend_class_entry *zend_ce_division_by_zero_error;
|
||||
extern ZEND_API zend_class_entry *zend_ce_unhandled_match_error;
|
||||
extern ZEND_API zend_class_entry *zend_ce_request_parse_body_exception;
|
||||
|
||||
ZEND_API void zend_exception_set_previous(zend_object *exception, zend_object *add_previous);
|
||||
ZEND_API void zend_exception_save(void);
|
||||
|
@ -170,3 +170,7 @@ class DivisionByZeroError extends ArithmeticError
|
||||
class UnhandledMatchError extends Error
|
||||
{
|
||||
}
|
||||
|
||||
class RequestParseBodyException extends Exception
|
||||
{
|
||||
}
|
||||
|
17
Zend/zend_exceptions_arginfo.h
generated
17
Zend/zend_exceptions_arginfo.h
generated
@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 4cf2c620393f468968a219b5bd12a2b5f6b03ecc */
|
||||
* Stub hash: ba1562ca8fe2fe48c40bc52d10545aa989afd86c */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Throwable_getMessage, 0, 0, IS_STRING, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
@ -187,6 +187,11 @@ static const zend_function_entry class_UnhandledMatchError_methods[] = {
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
|
||||
static const zend_function_entry class_RequestParseBodyException_methods[] = {
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
static zend_class_entry *register_class_Throwable(zend_class_entry *class_entry_Stringable)
|
||||
{
|
||||
zend_class_entry ce, *class_entry;
|
||||
@ -401,3 +406,13 @@ static zend_class_entry *register_class_UnhandledMatchError(zend_class_entry *cl
|
||||
|
||||
return class_entry;
|
||||
}
|
||||
|
||||
static zend_class_entry *register_class_RequestParseBodyException(zend_class_entry *class_entry_Exception)
|
||||
{
|
||||
zend_class_entry ce, *class_entry;
|
||||
|
||||
INIT_CLASS_ENTRY(ce, "RequestParseBodyException", class_RequestParseBodyException_methods);
|
||||
class_entry = zend_register_internal_class_ex(&ce, class_entry_Exception);
|
||||
|
||||
return class_entry;
|
||||
}
|
||||
|
@ -221,8 +221,9 @@ const mbfl_encoding *_php_mb_encoding_handler_ex(const php_mb_encoding_handler_i
|
||||
var = php_strtok_r(NULL, info->separator, &strtok_buf);
|
||||
}
|
||||
|
||||
if (ZEND_SIZE_T_GT_ZEND_LONG(n, (PG(max_input_vars) * 2))) {
|
||||
php_error_docref(NULL, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", PG(max_input_vars));
|
||||
zend_long max_input_vars = REQUEST_PARSE_BODY_OPTION_GET(max_input_vars, PG(max_input_vars));
|
||||
if (ZEND_SIZE_T_GT_ZEND_LONG(n, max_input_vars * 2)) {
|
||||
php_error_docref(NULL, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", max_input_vars);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -3027,6 +3027,13 @@ function pfsockopen(string $hostname, int $port = -1, &$error_code = null, &$err
|
||||
/** @refcount 1 */
|
||||
function http_build_query(array|object $data, string $numeric_prefix = "", ?string $arg_separator = null, int $encoding_type = PHP_QUERY_RFC1738): string {}
|
||||
|
||||
/**
|
||||
* @param array|null $options
|
||||
* @return array<int, array>
|
||||
* @refcount 1
|
||||
*/
|
||||
function request_parse_body(?array $options = null): array {}
|
||||
|
||||
/* image.c */
|
||||
|
||||
/**
|
||||
|
8
ext/standard/basic_functions_arginfo.h
generated
8
ext/standard/basic_functions_arginfo.h
generated
@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 7584d1aec417e84718a7a60e244cb00df2dc039f */
|
||||
* Stub hash: 0bd0ac5d23881670cac81cda3e274cbee1e9a8dc */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0)
|
||||
@ -1506,6 +1506,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_http_build_query, 0, 1, IS_STRIN
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding_type, IS_LONG, 0, "PHP_QUERY_RFC1738")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_request_parse_body, 0, 0, IS_ARRAY, 0)
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_image_type_to_mime_type, 0, 1, IS_STRING, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, image_type, IS_LONG, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
@ -2715,6 +2719,7 @@ ZEND_FUNCTION(vfprintf);
|
||||
ZEND_FUNCTION(fsockopen);
|
||||
ZEND_FUNCTION(pfsockopen);
|
||||
ZEND_FUNCTION(http_build_query);
|
||||
ZEND_FUNCTION(request_parse_body);
|
||||
ZEND_FUNCTION(image_type_to_mime_type);
|
||||
ZEND_FUNCTION(image_type_to_extension);
|
||||
ZEND_FUNCTION(getimagesize);
|
||||
@ -3354,6 +3359,7 @@ static const zend_function_entry ext_functions[] = {
|
||||
ZEND_FE(fsockopen, arginfo_fsockopen)
|
||||
ZEND_FE(pfsockopen, arginfo_pfsockopen)
|
||||
ZEND_FE(http_build_query, arginfo_http_build_query)
|
||||
ZEND_FE(request_parse_body, arginfo_request_parse_body)
|
||||
ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(image_type_to_mime_type, arginfo_image_type_to_mime_type)
|
||||
ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(image_type_to_extension, arginfo_image_type_to_extension)
|
||||
ZEND_FE(getimagesize, arginfo_getimagesize)
|
||||
|
@ -17,6 +17,9 @@
|
||||
#include "php_http.h"
|
||||
#include "php_ini.h"
|
||||
#include "url.h"
|
||||
#include "SAPI.h"
|
||||
#include "zend_exceptions.h"
|
||||
#include "ext/spl/spl_exceptions.h"
|
||||
|
||||
static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
|
||||
int encoding_type, zend_ulong index_int,
|
||||
@ -235,3 +238,125 @@ PHP_FUNCTION(http_build_query)
|
||||
RETURN_STR(smart_str_extract(&formstr));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_result cache_request_parse_body_option(HashTable *options, zval *option, int cache_offset)
|
||||
{
|
||||
if (option) {
|
||||
zend_long result;
|
||||
if (Z_TYPE_P(option) == IS_STRING) {
|
||||
zend_string *errstr;
|
||||
result = zend_ini_parse_quantity(Z_STR_P(option), &errstr);
|
||||
if (errstr) {
|
||||
zend_error(E_WARNING, "%s", ZSTR_VAL(errstr));
|
||||
zend_string_release(errstr);
|
||||
}
|
||||
} else if (Z_TYPE_P(option) == IS_LONG) {
|
||||
result = Z_LVAL_P(option);
|
||||
} else {
|
||||
zend_value_error("Invalid %s value in $options argument", zend_zval_value_name(option));
|
||||
return FAILURE;
|
||||
}
|
||||
SG(request_parse_body_context).options_cache[cache_offset].set = true;
|
||||
SG(request_parse_body_context).options_cache[cache_offset].value = result;
|
||||
} else {
|
||||
SG(request_parse_body_context).options_cache[cache_offset].set = false;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static zend_result cache_request_parse_body_options(HashTable *options)
|
||||
{
|
||||
zend_string *key;
|
||||
zval *value;
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(options, key, value) {
|
||||
if (!key) {
|
||||
zend_value_error("Invalid integer key in $options argument");
|
||||
return FAILURE;
|
||||
}
|
||||
if (ZSTR_LEN(key) == 0) {
|
||||
zend_value_error("Invalid empty string key in $options argument");
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
#define CHECK_OPTION(name) \
|
||||
if (zend_string_equals_literal_ci(key, #name)) { \
|
||||
if (cache_request_parse_body_option(options, value, REQUEST_PARSE_BODY_OPTION_ ## name) == FAILURE) { \
|
||||
return FAILURE; \
|
||||
} \
|
||||
continue; \
|
||||
}
|
||||
|
||||
switch (ZSTR_VAL(key)[0]) {
|
||||
case 'm':
|
||||
case 'M':
|
||||
CHECK_OPTION(max_file_uploads);
|
||||
CHECK_OPTION(max_input_vars);
|
||||
CHECK_OPTION(max_multipart_body_parts);
|
||||
break;
|
||||
case 'p':
|
||||
case 'P':
|
||||
CHECK_OPTION(post_max_size);
|
||||
break;
|
||||
case 'u':
|
||||
case 'U':
|
||||
CHECK_OPTION(upload_max_filesize);
|
||||
break;
|
||||
}
|
||||
|
||||
zend_value_error("Invalid key \"%s\" in $options argument", ZSTR_VAL(key));
|
||||
return FAILURE;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
#undef CACHE_OPTION
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
PHP_FUNCTION(request_parse_body)
|
||||
{
|
||||
HashTable *options = NULL;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(0, 1)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_ARRAY_HT_OR_NULL(options)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
SG(request_parse_body_context).throw_exceptions = true;
|
||||
if (options) {
|
||||
if (cache_request_parse_body_options(options) == FAILURE) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!SG(request_info).content_type) {
|
||||
zend_throw_error(zend_ce_request_parse_body_exception, "Request does not provide a content type");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
sapi_read_post_data();
|
||||
if (!SG(request_info).post_entry) {
|
||||
zend_throw_error(spl_ce_InvalidArgumentException, "Content-Type \"%s\" is not supported", SG(request_info).content_type);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
zval post, files, old_post, old_files;
|
||||
zval *global_post = &PG(http_globals)[TRACK_VARS_POST];
|
||||
zval *global_files = &PG(http_globals)[TRACK_VARS_FILES];
|
||||
|
||||
ZVAL_COPY_VALUE(&old_post, global_post);
|
||||
ZVAL_COPY_VALUE(&old_files, global_files);
|
||||
array_init(global_post);
|
||||
array_init(global_files);
|
||||
sapi_handle_post(global_post);
|
||||
ZVAL_COPY_VALUE(&post, global_post);
|
||||
ZVAL_COPY_VALUE(&files, global_files);
|
||||
ZVAL_COPY_VALUE(global_post, &old_post);
|
||||
ZVAL_COPY_VALUE(global_files, &old_files);
|
||||
|
||||
RETVAL_ARR(zend_new_pair(&post, &files));
|
||||
|
||||
exit:
|
||||
SG(request_parse_body_context).throw_exceptions = false;
|
||||
memset(&SG(request_parse_body_context).options_cache, 0, sizeof(SG(request_parse_body_context).options_cache));
|
||||
}
|
||||
|
16
main/SAPI.c
16
main/SAPI.c
@ -169,7 +169,7 @@ SAPI_API void sapi_handle_post(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
static void sapi_read_post_data(void)
|
||||
SAPI_API void sapi_read_post_data(void)
|
||||
{
|
||||
sapi_post_entry *post_entry;
|
||||
uint32_t content_type_length = (uint32_t)strlen(SG(request_info).content_type);
|
||||
@ -255,9 +255,11 @@ SAPI_API size_t sapi_read_post_block(char *buffer, size_t buflen)
|
||||
|
||||
SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data)
|
||||
{
|
||||
if ((SG(post_max_size) > 0) && (SG(request_info).content_length > SG(post_max_size))) {
|
||||
zend_long post_max_size = REQUEST_PARSE_BODY_OPTION_GET(post_max_size, SG(post_max_size));
|
||||
|
||||
if (post_max_size > 0 && SG(request_info).content_length > post_max_size) {
|
||||
php_error_docref(NULL, E_WARNING, "POST Content-Length of " ZEND_LONG_FMT " bytes exceeds the limit of " ZEND_LONG_FMT " bytes",
|
||||
SG(request_info).content_length, SG(post_max_size));
|
||||
SG(request_info).content_length, post_max_size);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -281,8 +283,8 @@ SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data)
|
||||
}
|
||||
}
|
||||
|
||||
if ((SG(post_max_size) > 0) && (SG(read_post_bytes) > SG(post_max_size))) {
|
||||
php_error_docref(NULL, E_WARNING, "Actual POST length does not match Content-Length, and exceeds " ZEND_LONG_FMT " bytes", SG(post_max_size));
|
||||
if (post_max_size > 0 && SG(read_post_bytes) > post_max_size) {
|
||||
php_error_docref(NULL, E_WARNING, "Actual POST length does not match Content-Length, and exceeds " ZEND_LONG_FMT " bytes", post_max_size);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -455,6 +457,8 @@ SAPI_API void sapi_activate(void)
|
||||
SG(request_info).headers_only = 0;
|
||||
}
|
||||
SG(rfc1867_uploaded_files) = NULL;
|
||||
SG(request_parse_body_context).throw_exceptions = false;
|
||||
memset(&SG(request_parse_body_context).options_cache, 0, sizeof(SG(request_parse_body_context).options_cache));
|
||||
|
||||
/* Handle request method */
|
||||
if (SG(server_context)) {
|
||||
@ -1018,7 +1022,7 @@ SAPI_API zend_stat_t *sapi_get_stat(void)
|
||||
SAPI_API char *sapi_getenv(const char *name, size_t name_len)
|
||||
{
|
||||
char *value, *tmp;
|
||||
|
||||
|
||||
if (!sapi_module.getenv) {
|
||||
return NULL;
|
||||
}
|
||||
|
22
main/SAPI.h
22
main/SAPI.h
@ -108,6 +108,26 @@ typedef struct {
|
||||
int proto_num;
|
||||
} sapi_request_info;
|
||||
|
||||
typedef struct {
|
||||
bool throw_exceptions;
|
||||
struct {
|
||||
bool set;
|
||||
zend_long value;
|
||||
} options_cache[5];
|
||||
} sapi_request_parse_body_context;
|
||||
|
||||
typedef enum {
|
||||
REQUEST_PARSE_BODY_OPTION_max_file_uploads = 0,
|
||||
REQUEST_PARSE_BODY_OPTION_max_input_vars,
|
||||
REQUEST_PARSE_BODY_OPTION_max_multipart_body_parts,
|
||||
REQUEST_PARSE_BODY_OPTION_post_max_size,
|
||||
REQUEST_PARSE_BODY_OPTION_upload_max_filesize,
|
||||
} request_parse_body_option;
|
||||
|
||||
#define REQUEST_PARSE_BODY_OPTION_GET(name, fallback) \
|
||||
(SG(request_parse_body_context).options_cache[REQUEST_PARSE_BODY_OPTION_ ## name].set \
|
||||
? SG(request_parse_body_context).options_cache[REQUEST_PARSE_BODY_OPTION_ ## name].value \
|
||||
: (fallback))
|
||||
|
||||
typedef struct _sapi_globals_struct {
|
||||
void *server_context;
|
||||
@ -127,6 +147,7 @@ typedef struct _sapi_globals_struct {
|
||||
HashTable known_post_content_types;
|
||||
zval callback_func;
|
||||
zend_fcall_info_cache fci_cache;
|
||||
sapi_request_parse_body_context request_parse_body_context;
|
||||
} sapi_globals_struct;
|
||||
|
||||
|
||||
@ -186,6 +207,7 @@ SAPI_API int sapi_add_header_ex(const char *header_line, size_t header_line_len,
|
||||
SAPI_API int sapi_send_headers(void);
|
||||
SAPI_API void sapi_free_header(sapi_header_struct *sapi_header);
|
||||
SAPI_API void sapi_handle_post(void *arg);
|
||||
SAPI_API void sapi_read_post_data(void);
|
||||
SAPI_API size_t sapi_read_post_block(char *buffer, size_t buflen);
|
||||
SAPI_API int sapi_register_post_entries(const sapi_post_entry *post_entry);
|
||||
SAPI_API int sapi_register_post_entry(const sapi_post_entry *post_entry);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "php_content_types.h"
|
||||
#include "SAPI.h"
|
||||
#include "zend_globals.h"
|
||||
#include "zend_exceptions.h"
|
||||
|
||||
/* for systems that need to override reading of environment variables */
|
||||
void _php_import_environment_variables(zval *array_ptr);
|
||||
@ -378,7 +379,7 @@ static bool add_post_var(zval *arr, post_var_data_t *var, bool eof)
|
||||
|
||||
static inline int add_post_vars(zval *arr, post_var_data_t *vars, bool eof)
|
||||
{
|
||||
uint64_t max_vars = PG(max_input_vars);
|
||||
uint64_t max_vars = REQUEST_PARSE_BODY_OPTION_GET(max_input_vars, PG(max_input_vars));
|
||||
|
||||
vars->ptr = ZSTR_VAL(vars->str.s);
|
||||
vars->end = ZSTR_VAL(vars->str.s) + ZSTR_LEN(vars->str.s);
|
||||
@ -538,8 +539,9 @@ SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data)
|
||||
}
|
||||
}
|
||||
|
||||
if (++count > PG(max_input_vars)) {
|
||||
php_error_docref(NULL, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", PG(max_input_vars));
|
||||
zend_long max_input_vars = REQUEST_PARSE_BODY_OPTION_GET(max_input_vars, PG(max_input_vars));
|
||||
if (++count > max_input_vars) {
|
||||
php_error_docref(NULL, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", max_input_vars);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "php_variables.h"
|
||||
#include "rfc1867.h"
|
||||
#include "zend_smart_string.h"
|
||||
#include "zend_exceptions.h"
|
||||
|
||||
#ifndef DEBUG_FILE_UPLOAD
|
||||
# define DEBUG_FILE_UPLOAD 0
|
||||
@ -646,7 +647,7 @@ static char *multipart_buffer_read_body(multipart_buffer *self, size_t *len)
|
||||
*
|
||||
*/
|
||||
|
||||
SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)
|
||||
{
|
||||
char *boundary, *s = NULL, *boundary_end = NULL, *start_arr = NULL, *array_index = NULL;
|
||||
char *lbuf = NULL, *abuf = NULL;
|
||||
@ -658,18 +659,30 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
HashTable *uploaded_files = NULL;
|
||||
multipart_buffer *mbuff;
|
||||
zval *array_ptr = (zval *) arg;
|
||||
bool throw_exceptions = SG(request_parse_body_context).throw_exceptions;
|
||||
int fd = -1;
|
||||
zend_llist header;
|
||||
void *event_extra_data = NULL;
|
||||
unsigned int llen = 0;
|
||||
int upload_cnt = INI_INT("max_file_uploads");
|
||||
int body_parts_cnt = INI_INT("max_multipart_body_parts");
|
||||
zend_long upload_cnt = REQUEST_PARSE_BODY_OPTION_GET(max_file_uploads, INI_INT("max_file_uploads"));
|
||||
zend_long body_parts_cnt = REQUEST_PARSE_BODY_OPTION_GET(max_multipart_body_parts, INI_INT("max_multipart_body_parts"));
|
||||
zend_long post_max_size = REQUEST_PARSE_BODY_OPTION_GET(post_max_size, SG(post_max_size));
|
||||
zend_long max_input_vars = REQUEST_PARSE_BODY_OPTION_GET(max_input_vars, PG(max_input_vars));
|
||||
zend_long upload_max_filesize = REQUEST_PARSE_BODY_OPTION_GET(upload_max_filesize, PG(upload_max_filesize));
|
||||
const zend_encoding *internal_encoding = zend_multibyte_get_internal_encoding();
|
||||
php_rfc1867_getword_t getword;
|
||||
php_rfc1867_getword_conf_t getword_conf;
|
||||
php_rfc1867_basename_t _basename;
|
||||
zend_long count = 0;
|
||||
|
||||
#define EMIT_WARNING_OR_ERROR(...) do { \
|
||||
if (throw_exceptions) { \
|
||||
zend_throw_exception_ex(zend_ce_request_parse_body_exception, 0, __VA_ARGS__); \
|
||||
} else { \
|
||||
php_error_docref(NULL, E_WARNING, __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
if (php_rfc1867_encoding_translation() && internal_encoding) {
|
||||
getword = php_rfc1867_getword;
|
||||
getword_conf = php_rfc1867_getword_conf;
|
||||
@ -680,13 +693,13 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
_basename = php_ap_basename;
|
||||
}
|
||||
|
||||
if (SG(post_max_size) > 0 && SG(request_info).content_length > SG(post_max_size)) {
|
||||
sapi_module.sapi_error(E_WARNING, "POST Content-Length of " ZEND_LONG_FMT " bytes exceeds the limit of " ZEND_LONG_FMT " bytes", SG(request_info).content_length, SG(post_max_size));
|
||||
if (post_max_size > 0 && SG(request_info).content_length > post_max_size) {
|
||||
EMIT_WARNING_OR_ERROR("POST Content-Length of " ZEND_LONG_FMT " bytes exceeds the limit of " ZEND_LONG_FMT " bytes", SG(request_info).content_length, post_max_size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body_parts_cnt < 0) {
|
||||
body_parts_cnt = PG(max_input_vars) + upload_cnt;
|
||||
body_parts_cnt = max_input_vars + upload_cnt;
|
||||
}
|
||||
int body_parts_limit = body_parts_cnt;
|
||||
|
||||
@ -705,7 +718,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
}
|
||||
|
||||
if (!boundary || !(boundary = strchr(boundary, '='))) {
|
||||
sapi_module.sapi_error(E_WARNING, "Missing boundary in multipart/form-data POST data");
|
||||
EMIT_WARNING_OR_ERROR("Missing boundary in multipart/form-data POST data");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -716,7 +729,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
boundary++;
|
||||
boundary_end = strchr(boundary, '"');
|
||||
if (!boundary_end) {
|
||||
sapi_module.sapi_error(E_WARNING, "Invalid boundary in multipart/form-data POST data");
|
||||
EMIT_WARNING_OR_ERROR("Invalid boundary in multipart/form-data POST data");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -729,10 +742,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
}
|
||||
|
||||
/* Initialize the buffer */
|
||||
if (!(mbuff = multipart_buffer_new(boundary, boundary_len))) {
|
||||
sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer");
|
||||
return;
|
||||
}
|
||||
mbuff = multipart_buffer_new(boundary, boundary_len);
|
||||
|
||||
/* Initialize $_FILES[] */
|
||||
zend_hash_init(&PG(rfc1867_protected_variables), 8, NULL, NULL, 0);
|
||||
@ -775,7 +785,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
int end = 0;
|
||||
|
||||
if (--body_parts_cnt < 0) {
|
||||
php_error_docref(NULL, E_WARNING, "Multipart body parts limit exceeded %d. To increase the limit change max_multipart_body_parts in php.ini.", body_parts_limit);
|
||||
EMIT_WARNING_OR_ERROR("Multipart body parts limit exceeded %d. To increase the limit change max_multipart_body_parts in php.ini.", body_parts_limit);
|
||||
goto fileupload_done;
|
||||
}
|
||||
|
||||
@ -849,7 +859,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
}
|
||||
}
|
||||
|
||||
if (++count <= PG(max_input_vars) && sapi_module.input_filter(PARSE_POST, param, &value, value_len, &new_val_len)) {
|
||||
if (++count <= max_input_vars && sapi_module.input_filter(PARSE_POST, param, &value, value_len, &new_val_len)) {
|
||||
if (php_rfc1867_callback != NULL) {
|
||||
multipart_event_formdata event_formdata;
|
||||
size_t newlength = new_val_len;
|
||||
@ -868,8 +878,8 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
}
|
||||
safe_php_register_variable(param, value, new_val_len, array_ptr, 0);
|
||||
} else {
|
||||
if (count == PG(max_input_vars) + 1) {
|
||||
php_error_docref(NULL, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", PG(max_input_vars));
|
||||
if (count == max_input_vars + 1) {
|
||||
EMIT_WARNING_OR_ERROR("Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", max_input_vars);
|
||||
}
|
||||
|
||||
if (php_rfc1867_callback != NULL) {
|
||||
@ -900,13 +910,13 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
skip_upload = 1;
|
||||
if (upload_cnt == 0) {
|
||||
--upload_cnt;
|
||||
sapi_module.sapi_error(E_WARNING, "Maximum number of allowable file uploads has been exceeded");
|
||||
EMIT_WARNING_OR_ERROR("Maximum number of allowable file uploads has been exceeded");
|
||||
}
|
||||
}
|
||||
|
||||
/* Return with an error if the posted data is garbled */
|
||||
if (!param && !filename) {
|
||||
sapi_module.sapi_error(E_WARNING, "File Upload Mime headers garbled");
|
||||
EMIT_WARNING_OR_ERROR("File Upload Mime headers garbled");
|
||||
goto fileupload_done;
|
||||
}
|
||||
|
||||
@ -988,7 +998,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
fd = php_open_temporary_fd_ex(PG(upload_tmp_dir), "php", &temp_filename, PHP_TMP_FILE_OPEN_BASEDIR_CHECK_ON_FALLBACK);
|
||||
upload_cnt--;
|
||||
if (fd == -1) {
|
||||
sapi_module.sapi_error(E_WARNING, "File upload error - unable to create a temporary file");
|
||||
EMIT_WARNING_OR_ERROR("File upload error - unable to create a temporary file");
|
||||
cancel_upload = PHP_UPLOAD_ERROR_E;
|
||||
}
|
||||
}
|
||||
@ -1010,9 +1020,9 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
}
|
||||
}
|
||||
|
||||
if (PG(upload_max_filesize) > 0 && (zend_long)(total_bytes+blen) > PG(upload_max_filesize)) {
|
||||
if (upload_max_filesize > 0 && (zend_long)(total_bytes+blen) > upload_max_filesize) {
|
||||
#if DEBUG_FILE_UPLOAD
|
||||
sapi_module.sapi_error(E_NOTICE, "upload_max_filesize of " ZEND_LONG_FMT " bytes exceeded - file [%s=%s] not saved", PG(upload_max_filesize), param, filename);
|
||||
sapi_module.sapi_error(E_NOTICE, "upload_max_filesize of " ZEND_LONG_FMT " bytes exceeded - file [%s=%s] not saved", upload_max_filesize, param, filename);
|
||||
#endif
|
||||
cancel_upload = PHP_UPLOAD_ERROR_A;
|
||||
} else if (max_file_size && ((zend_long)(total_bytes+blen) > max_file_size)) {
|
||||
@ -1060,7 +1070,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
}
|
||||
#if DEBUG_FILE_UPLOAD
|
||||
if (filename[0] != '\0' && total_bytes == 0 && !cancel_upload) {
|
||||
sapi_module.sapi_error(E_WARNING, "Uploaded file size 0 - file [%s=%s] not saved", param, filename);
|
||||
EMIT_WARNING_OR_ERROR("Uploaded file size 0 - file [%s=%s] not saved", param, filename);
|
||||
cancel_upload = 5;
|
||||
}
|
||||
#endif
|
||||
@ -1134,7 +1144,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
|
||||
register_http_post_files_variable(lbuf, s, &PG(http_globals)[TRACK_VARS_FILES], 0);
|
||||
s = NULL;
|
||||
|
||||
/* Add full path of supplied file for folder uploads via
|
||||
/* Add full path of supplied file for folder uploads via
|
||||
* <input type="file" name="files" multiple webkitdirectory>
|
||||
*/
|
||||
/* Add $foo[full_path] */
|
||||
@ -1263,6 +1273,8 @@ fileupload_done:
|
||||
if (mbuff->boundary) efree(mbuff->boundary);
|
||||
if (mbuff->buffer) efree(mbuff->buffer);
|
||||
if (mbuff) efree(mbuff);
|
||||
|
||||
#undef EMIT_WARNING_OR_ERROR
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -2324,7 +2324,9 @@ TEST $file
|
||||
}
|
||||
|
||||
$env['CONTENT_LENGTH'] = strlen($request);
|
||||
$env['REQUEST_METHOD'] = 'POST';
|
||||
if (empty($env['REQUEST_METHOD'])) {
|
||||
$env['REQUEST_METHOD'] = 'POST';
|
||||
}
|
||||
|
||||
if (empty($request)) {
|
||||
$junit->markTestAs('BORK', $shortname, $tested, null, 'empty $request');
|
||||
|
@ -2222,6 +2222,8 @@ static void php_cli_server_request_shutdown(php_cli_server *server, php_cli_serv
|
||||
destroy_request_info(&SG(request_info));
|
||||
SG(server_context) = NULL;
|
||||
SG(rfc1867_uploaded_files) = NULL;
|
||||
SG(request_parse_body_context).throw_exceptions = false;
|
||||
memset(&SG(request_parse_body_context).options_cache, 0, sizeof(SG(request_parse_body_context).options_cache));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -2315,6 +2317,8 @@ static zend_result php_cli_server_dispatch(php_cli_server *server, php_cli_serve
|
||||
sapi_module.send_headers = send_header_func;
|
||||
SG(sapi_headers).send_default_content_type = 1;
|
||||
SG(rfc1867_uploaded_files) = NULL;
|
||||
SG(request_parse_body_context).throw_exceptions = false;
|
||||
memset(&SG(request_parse_body_context).options_cache, 0, sizeof(SG(request_parse_body_context).options_cache));
|
||||
}
|
||||
if (FAILURE == php_cli_server_begin_send_static(server, client)) {
|
||||
php_cli_server_close_connection(server, client);
|
||||
|
@ -45,7 +45,7 @@ $tester->close();
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Warning: Maximum number of allowable file uploads has been exceeded in Unknown on line 0
|
||||
Warning: PHP Request Startup: Maximum number of allowable file uploads has been exceeded in Unknown on line 0
|
||||
int(5)
|
||||
--CLEAN--
|
||||
<?php
|
||||
|
97
sapi/fpm/tests/request_parse_body_multipart.phpt
Normal file
97
sapi/fpm/tests/request_parse_body_multipart.phpt
Normal file
@ -0,0 +1,97 @@
|
||||
--TEST--
|
||||
PUT multipart
|
||||
--EXTENSIONS--
|
||||
zend_test
|
||||
--SKIPIF--
|
||||
<?php include "skipif.inc"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require_once "tester.inc";
|
||||
|
||||
$cfg = <<<EOT
|
||||
[global]
|
||||
error_log = {{FILE:LOG}}
|
||||
[unconfined]
|
||||
listen = {{ADDR}}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 1
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
EOT;
|
||||
|
||||
$code = <<<'EOT'
|
||||
<?php
|
||||
$_POST = ['post_global'];
|
||||
$_FILES = ['files_global'];
|
||||
[$post, $files] = request_parse_body();
|
||||
$file_path = __DIR__ . '/put_multipart_uploaded_file.txt';
|
||||
move_uploaded_file($files[0]['tmp_name'], $file_path);
|
||||
$file_content = file_get_contents($file_path);
|
||||
unlink($file_path);
|
||||
echo json_encode([
|
||||
'post' => $post,
|
||||
'files' => $files,
|
||||
'file_content' => $file_content,
|
||||
'post_global' => $_POST,
|
||||
'files_global' => $_FILES,
|
||||
], JSON_PRETTY_PRINT);
|
||||
EOT;
|
||||
|
||||
$tester = new FPM\Tester($cfg, $code);
|
||||
$tester->start();
|
||||
$tester->expectLogStartNotices();
|
||||
echo $tester
|
||||
->request(method: 'PUT', stdin: [
|
||||
'parts' => [
|
||||
[
|
||||
"disposition" => "form-data",
|
||||
"param" => "name",
|
||||
"name" => "get_parameter",
|
||||
"value" => "foo",
|
||||
],
|
||||
[
|
||||
"disposition" => "form-data",
|
||||
"param" => "filename",
|
||||
"name" => "uploaded_file",
|
||||
"value" => "bar",
|
||||
],
|
||||
],
|
||||
])
|
||||
->getBody();
|
||||
$tester->terminate();
|
||||
$tester->expectLogTerminatingNotices();
|
||||
$tester->close();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
{
|
||||
"post": {
|
||||
"get_parameter": "foo"
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"name": "uploaded_file",
|
||||
"full_path": "uploaded_file",
|
||||
"type": "",
|
||||
"tmp_name": "%s",
|
||||
"error": 0,
|
||||
"size": 3
|
||||
}
|
||||
],
|
||||
"file_content": "bar",
|
||||
"post_global": [
|
||||
"post_global"
|
||||
],
|
||||
"files_global": [
|
||||
"files_global"
|
||||
]
|
||||
}
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once "tester.inc";
|
||||
FPM\Tester::clean();
|
||||
$file_path = __DIR__ . '/put_multipart_uploaded_file.txt';
|
||||
@unlink($file_path);
|
||||
?>
|
75
sapi/fpm/tests/request_parse_body_urlencoded.phpt
Normal file
75
sapi/fpm/tests/request_parse_body_urlencoded.phpt
Normal file
@ -0,0 +1,75 @@
|
||||
--TEST--
|
||||
PUT x-www-form-urlencoded
|
||||
--EXTENSIONS--
|
||||
zend_test
|
||||
--SKIPIF--
|
||||
<?php include "skipif.inc"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require_once "tester.inc";
|
||||
|
||||
$cfg = <<<EOT
|
||||
[global]
|
||||
error_log = {{FILE:LOG}}
|
||||
[unconfined]
|
||||
listen = {{ADDR}}
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 1
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
EOT;
|
||||
|
||||
$code = <<<'EOT'
|
||||
<?php
|
||||
$_POST = ['post_global'];
|
||||
$_FILES = ['files_global'];
|
||||
[$post, $files] = request_parse_body();
|
||||
echo json_encode([
|
||||
'post' => $post,
|
||||
'files' => $files,
|
||||
'post_global' => $_POST,
|
||||
'files_global' => $_FILES,
|
||||
], JSON_PRETTY_PRINT);
|
||||
EOT;
|
||||
|
||||
$tester = new FPM\Tester($cfg, $code);
|
||||
$tester->start();
|
||||
$tester->expectLogStartNotices();
|
||||
echo $tester
|
||||
->request(
|
||||
method: 'PUT',
|
||||
headers: ['CONTENT_TYPE' => 'application/x-www-form-urlencoded'],
|
||||
stdin: 'foo=foo&bar[]=1&bar[]=2'
|
||||
)
|
||||
->getBody();
|
||||
$tester->terminate();
|
||||
$tester->expectLogTerminatingNotices();
|
||||
$tester->close();
|
||||
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once "tester.inc";
|
||||
FPM\Tester::clean();
|
||||
$file_path = __DIR__ . '/put_multipart_uploaded_file.txt';
|
||||
@unlink($file_path);
|
||||
?>
|
||||
--EXPECT--
|
||||
{
|
||||
"post": {
|
||||
"foo": "foo",
|
||||
"bar": [
|
||||
"1",
|
||||
"2"
|
||||
]
|
||||
},
|
||||
"files": [],
|
||||
"post_global": [
|
||||
"post_global"
|
||||
],
|
||||
"files_global": [
|
||||
"files_global"
|
||||
]
|
||||
}
|
@ -697,7 +697,8 @@ class Tester
|
||||
string $uri = null,
|
||||
string $scriptFilename = null,
|
||||
string $scriptName = null,
|
||||
?string $stdin = null
|
||||
?string $stdin = null,
|
||||
?string $method = null,
|
||||
): array {
|
||||
if (is_null($scriptFilename)) {
|
||||
$scriptFilename = $this->makeSourceFile();
|
||||
@ -712,7 +713,7 @@ class Tester
|
||||
$params = array_merge(
|
||||
[
|
||||
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
|
||||
'REQUEST_METHOD' => is_null($stdin) ? 'GET' : 'POST',
|
||||
'REQUEST_METHOD' => $method ?? (is_null($stdin) ? 'GET' : 'POST'),
|
||||
'SCRIPT_FILENAME' => $scriptFilename === '' ? null : $scriptFilename,
|
||||
'SCRIPT_NAME' => $scriptName,
|
||||
'QUERY_STRING' => $query,
|
||||
@ -836,6 +837,7 @@ class Tester
|
||||
bool $expectError = false,
|
||||
int $readLimit = -1,
|
||||
int $writeDelay = 0,
|
||||
?string $method = null,
|
||||
): Response {
|
||||
if ($this->hasError()) {
|
||||
return $this->createResponse(expectInvalid: true);
|
||||
@ -845,7 +847,7 @@ class Tester
|
||||
$stdin = $this->parseStdin($stdin, $headers);
|
||||
}
|
||||
|
||||
$params = $this->getRequestParams($query, $headers, $uri, $scriptFilename, $scriptName, $stdin);
|
||||
$params = $this->getRequestParams($query, $headers, $uri, $scriptFilename, $scriptName, $stdin, $method);
|
||||
$this->trace('Request params', $params);
|
||||
|
||||
try {
|
||||
|
@ -16,7 +16,7 @@ var_dump($_FILES);
|
||||
var_dump($_POST);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: File Upload Mime headers garbled in %s
|
||||
Warning: PHP Request Startup: File Upload Mime headers garbled in %s
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
|
@ -15,7 +15,7 @@ var_dump($_FILES);
|
||||
var_dump($_POST);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: Invalid boundary in multipart/form-data POST data in %s
|
||||
Warning: PHP Request Startup: Invalid boundary in multipart/form-data POST data in %s
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
|
@ -15,7 +15,7 @@ var_dump($_FILES);
|
||||
var_dump($_POST);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: Missing boundary in multipart/form-data POST data in %s
|
||||
Warning: PHP Request Startup: Missing boundary in multipart/form-data POST data in %s
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
|
@ -15,7 +15,7 @@ var_dump($_FILES);
|
||||
var_dump($_POST);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: POST Content-Length of %d bytes exceeds the limit of 1 bytes in %s
|
||||
Warning: PHP Request Startup: POST Content-Length of 168 bytes exceeds the limit of 1 bytes in %s
|
||||
array(0) {
|
||||
}
|
||||
array(0) {
|
||||
|
Loading…
Reference in New Issue
Block a user