[php-src] Issue #11274: POST/PATCH request via file_get_contents + stream_context_create switches to GET after a HTTP 308 redirect
| From: | ThiefMaster | Date: | Fri, 19 May 2023 09:25:10 +0000 |
| Subject: | [php-src] Issue #11274: POST/PATCH request via file_get_contents + stream_context_create switches to GET after a HTTP 308 redirect | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-244487@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/11274
Author: ThiefMaster
### Description
The following code:
```php
<?php
echo file_get_contents('http://127.0.0.1:8080/test', false,
stream_context_create(['http' => ['method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query(['hello' => 'world'])]]));
echo file_get_contents('http://127.0.0.1:8080/test', false,
stream_context_create(['http' => ['method' => 'PATCH',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query(['hello' => 'world'])]]));
echo file_get_contents('http://127.0.0.1:8080/test/', false,
stream_context_create(['http' => ['method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query(['hello' => 'world'])]]));
echo file_get_contents('http://127.0.0.1:8080/test/', false,
stream_context_create(['http' => ['method' => 'PATCH',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query(['hello' => 'world'])]]));
```
Resulted in this output:
```json
{"method":"GET"}
{"method":"GET"}
{"hello":"world","method":"POST"}
{"hello":"world","method":"PATCH"}
```
But I expected this output instead:
```json
{"hello":"world","method":"POST"}
{"hello":"world","method":"PATCH"}
{"hello":"world","method":"POST"}
{"hello":"world","method":"PATCH"}
```
The server on 127.0.0.1:8080 is this small Flask app (I'm not really a PHP person anymore so I
couldn't be bothered to use PHP for this after not having written any PHP code in the last 15
or so years ;)):
```python
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/test/', methods=['GET', 'POST', 'PATCH'])
def test():
return jsonify(method=request.method, **request.form)
app.run(port=8080)
```
When sending a request to the URL without the trailing slash, it uses a HTTP 308 redirect to
redirect to the URL with that slash.
Clients are required to keep the method and payload when encountering such a redirect:
See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308):
> The request method and the body will not be altered, whereas
> [301](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301) may incorrectly sometimes be
> changed to a [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) method.
And even [RFC7538](https://www.rfc-editor.org/rfc/rfc7538.txt) specifying this status code:
> Note: This status code is similar to 301 (Moved Permanently) ([RFC7231], Section 6.4.2), except
> that it does **not allow changing the request method from POST to GET**.
### PHP Version
8.2.6
### Operating System
_No response_