Skip to content
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

bug fix #61471 (request error handling) #2180

Merged
merged 5 commits into from
Dec 29, 2016

Conversation

axot
Copy link

@axot axot commented Oct 30, 2016

php_handler does not process request error well.
Should we fix this?

https://bugs.php.net/bug.php?id=61471

@smalyshev smalyshev added the Bug label Oct 31, 2016
@marcosptf
Copy link
Contributor

@axot
i think the bugfix needs a test.
what do you think about?

@axot
Copy link
Author

axot commented Nov 10, 2016

@marcosptf There may have an impact on performance, so I also think this needs a test. Is there any exists mod_php related test case to use as reference?

@axot
Copy link
Author

axot commented Nov 10, 2016

while (ap_get_brigade(r->input_filters, brigade, AP_MODE_READBYTES, APR_BLOCK_READ, len) == APR_SUCCESS) {
    ...
}

ap_get_brigade will return error include timeout, maybe a better way is handle error cases inside php_apache_sapi_read_post function?

@KalleZ
Copy link
Member

KalleZ commented Nov 10, 2016

@marcosptf I looked around a bit, and our general coverage for SAPIs is low, so I'd be alright with no test as the fix itself is relatively simple

@marcosptf
Copy link
Contributor

@axot
like the PHP Master @KalleZ said, this bugfix is fine without .phpt
Thanks!

@axot
Copy link
Author

axot commented Nov 11, 2016

@marcosptf @KalleZ Before merging to master I want do some local tests for performance and large post data, I'll report the test result when it is done. By the way, how to make this commit to merge into PHP-5.6 branch, do I need create a new commit for this purpose?

@marcosptf
Copy link
Contributor

marcosptf commented Nov 11, 2016

@axot you can commit in this same branch!

@axot
Copy link
Author

axot commented Nov 11, 2016

@marcosptf Thank you!

@axot
Copy link
Author

axot commented Dec 3, 2016

I tested this patch with PHP 5.6.27 and 7.0.11 in CentOS 7.

Before patch:

$ nc shao-test-xxx.ap-northeast-1.elb.amazonaws.com 80
POST /ajax/errors/test HTTP/1.1
HOST: shao-test-xxx.ap-northeast-1.elb.amazonaws.com
User-Agent: telnet
Accept: */*
Content-Length: 7
Content-Type: application/x-www-form-urlencoded

b=te
HTTP/1.1 200 OK
Date: Sat, 03 Dec 2016 12:55:15 GMT
Server: Apache
Charset: x-user-defined
Content-Length: 64
Connection: close
Content-Type: application/x-msgpack

With this patch:

$ nc shao-test-xxx.ap-northeast-1.elb.amazonaws.com 80
POST /ajax/errors/test HTTP/1.1
HOST: shao-test-xxx.ap-northeast-1.elb.amazonaws.com
User-Agent: telnet
Accept: */*
Content-Length: 7
Content-Type: application/x-www-form-urlencoded

b=te
HTTP/1.1 408 Request Timeout
Date: Sat, 03 Dec 2016 12:58:45 GMT
Server: Apache
Content-Length: 324
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>408 Request Timeout</title>
</head><body>
<h1>Request Timeout</h1>
<p>Server timeout waiting for the HTTP request from the client.</p>
<hr>
<address>Apache Server at shao-test-xxx.ap-northeast-1.elb.amazonaws.com Port 80</address>
</body></html>

@axot
Copy link
Author

axot commented Dec 3, 2016

@KalleZ @laruence I updated the code, and tested it lightly, but I have not tested it in the production environment and there is no test case, so is there a problem such as memory leak? If possible, will you be able to review it in case?

@axot axot changed the base branch from master to PHP-5.6 December 5, 2016 14:55
@laruence
Copy link
Member

laruence commented Dec 6, 2016

hmm, I am not sure about this fix, but maybe we could fix this in the place where we calling read_post, because we already have SG(request_info).content_length.

And I think the fix could be more clean than the current one...

what do you think ? thanks

@axot
Copy link
Author

axot commented Dec 6, 2016

Thank you @laruence , did you mean to check SG(request_info).content_length in sapi_read_post_block? But I don't know how to return an error code to apache side, which apache will call php_handler function directly and want know what error(timeout or bad request?) occurred from it.

@axot
Copy link
Author

axot commented Dec 6, 2016

Here is a related discussion in apache community.
https://bz.apache.org/bugzilla/show_bug.cgi?id=60313

By Yann Ylavic 2016-10-28 22:57:17 UTC

If the connection has issues (timeout, closed, reset...) before the announced body is fully received (per Content-Length), httpd tells so to the module asking for it (mod_php or whatever).
If that module (as a request handler) wants httpd to respond with the relevant HTTP error, it simply returns the corresponding ap_map_http_request_error().
But if the caller ignores the error, there is nothing httpd can do.
No builtin httpd module should ignore errors, but mod_php is not maintained by the Apache HTTP server community, and I don't know enough about its internals to tell whether the error is ignored by it or the final application.

@laruence
Copy link
Member

laruence commented Dec 8, 2016

I understand the problem, your current fix is not quite right, like at least you should not use a global flag, because this sapi could also be used in apache-worker mpm(ZTS).

anyway, the flow is php_handler() calls read_post, maybe we could compare readed length and SG(request_info).content_length, then return 400 to apache, but I am not sure about the side-affect.

need some time and more work to verify it. thanks

@axot
Copy link
Author

axot commented Dec 8, 2016

Thanks for the tips, I did not consider the MPM case.

@axot
Copy link
Author

axot commented Dec 12, 2016

I delete the global variable and use SG(request_info).content_length to detect the error. It is more reasonable to me now, what do you think about this fix @laruence ? Thank you.

@axot axot changed the base branch from PHP-5.6 to PHP-7.0 December 17, 2016 04:38
@laruence
Copy link
Member

seems much better now, if there is no concerns show up in next few days, I will merge this, thanks

@@ -656,6 +665,13 @@ zend_first_try {
brigade = ctx->brigade;
}

if (SG(request_info).content_length > SG(read_post_bytes)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "attemp to read POST data error: %d", SG(sapi_headers).http_response_code);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in error message: attemp -> attempt? Though maybe something like "Error while attempting to read POST data: %d" might be better.

@laruence
Copy link
Member

laruence commented Dec 28, 2016

hmm,

/home/huixinchen/opensource/php-7.0/sapi/apache2handler/sapi_apache2.c: In functionphp_apache_sapi_read_post’:
/home/huixinchen/opensource/php-7.0/sapi/apache2handler/sapi_apache2.c:212:4: warning: implicit declaration of functionap_map_http_request_error’ [-Wimplicit-function-declaration]
    SG(sapi_headers).http_response_code = ap_map_http_request_error(ret, HTTP_REQUEST_TIME_OUT);
    ^

ap_map_http_request_error is not defined.

according to https://fossies.org/diffs/httpd/2.2.29_vs_2.2.31/include/http_protocol.h-diff.html , seems this function is added recently ? in that case, we should take care compatibility for old versions.

@axot
Copy link
Author

axot commented Dec 28, 2016

Thank you for mention about the compatibility issue. @laruence I pushed a new commit for fixing it. ap_map_http_request_error was added in httpd 2.2.31 and 2.4.16 above. So I tested this commit all passed in httpd 2.2.15, 2.2.31, 2.4.12, 2.4.16. For these older httpd version, I copied ap_map_http_request_error function for it, I think it is better than just return a HTTP_BAD_REQUEST error. How do you think about this commit? Thank you.

@laruence
Copy link
Member

laruence commented Dec 29, 2016

hmm, maybe a better way to do this is:

#if apache's version is newer than 2.2.31 or 2.4.16
#define php_ap_map_http_request_error  ap_map_http_request_error
#else
static int php_ap_map_http_request_error()  {
}
#endif

then later you could use php_ap_map_http_request_error without any extra checks

@axot
Copy link
Author

axot commented Dec 29, 2016

Thank you for figure out the code problem, does this look better now? @laruence

@php-pulls php-pulls merged commit 587497e into php:PHP-7.0 Dec 29, 2016
@alexpott
Copy link

alexpott commented Jan 4, 2017

This change has caused some interesting behaviour differences in Drupal 8 testing against PHP 7. We're now getting 200s and "Error while attempting to read POST data" in our Apache logs files when we post invalid JSON to a server. See https://www.drupal.org/node/2840974#comment-11852288 for more. Not sure why this change is causing that.

@alexpott
Copy link

alexpott commented Jan 5, 2017

So it is easy to write a test for the behaviour change.

curl -H "Content-Type: application/json" -X PATCH --data "al" http://localhost/test.php

Where test.php is:

<?php

var_dump(file_get_contents("php://input"));

PHP versions before this patch:
string(2) "al"

After this patch:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>200 OK</title>
</head><body>
<h1>OK</h1>
<p>The server encountered an internal error or
misconfiguration and was unable to complete
your request.</p>
<p>Please contact the server administrator at
 [no address given] to inform them of the time this error occurred,
 and the actions you performed just before this error.</p>
<p>More information about this error may be available
in the server error log.</p>
</body></html>

@alexpott
Copy link

alexpott commented Jan 5, 2017

If the method is POST everything still works as expected but PATCH is broken.

@alexpott
Copy link

alexpott commented Jan 5, 2017

It does not even have to be invalid JSON - that's what the test the discovered this was testing but even:

curl -H "Content-Type: application/json" -X PATCH --data '{"username":"xyz","password":"xyz"}' http://localhost/test.php

is broken.

@wimleers
Copy link

wimleers commented Jan 5, 2017

@alexpott I think we perhaps need to create a new ticket for this regression, and then just point to this PR. It's quite possible the PHP maintainers are not seeing new comments here, just like we don't see new comments on "closed" issues on drupal.org.

@krakjoe
Copy link
Member

krakjoe commented Jan 5, 2017

I'm seeing them ... please do open a ticket, put a link here, and in any subsequent PR's that are made to resolve it.

@nikic
Copy link
Member

nikic commented Jan 5, 2017

It looks like this was merged into 7.0, so this commit will need to be reverted from the release branches as well (a8931df).

/cc @weltling

@alexpott
Copy link

alexpott commented Jan 5, 2017

@nikic @krakjoe If you're going to revert this do I need to file a bug on https://bugs.php.net/report.php? I'm more than happy to if that helps - but don't want to create more noise than necessary.

@krakjoe
Copy link
Member

krakjoe commented Jan 5, 2017

I'll be doing whatever @weltling does, one or both of us will provide update here.

@weltling
Copy link
Contributor

weltling commented Jan 5, 2017

I've reverted this in dev for now and reopened https://bugs.php.net/61471. @axot please lets use another PR for the improved patch.

Thanks.

@axot
Copy link
Author

axot commented Jan 5, 2017

@weltling Thank you for reverting the commit.

@axot axot deleted the bugfix_timeout_61471 branch January 5, 2017 15:34
@axot axot mentioned this pull request Jan 20, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.