79
79
80
80
#define HTTP_WRAPPER_HEADER_INIT 1
81
81
#define HTTP_WRAPPER_REDIRECTED 2
82
+ #define HTTP_WRAPPER_KEEP_METHOD 4
82
83
83
84
static inline void strip_header (char * header_bag , char * lc_header_bag ,
84
85
const char * lc_header_name )
@@ -140,6 +141,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
140
141
char * user_headers = NULL ;
141
142
int header_init = ((flags & HTTP_WRAPPER_HEADER_INIT ) != 0 );
142
143
int redirected = ((flags & HTTP_WRAPPER_REDIRECTED ) != 0 );
144
+ int redirect_keep_method = ((flags & HTTP_WRAPPER_KEEP_METHOD ) != 0 );
143
145
bool follow_location = 1 ;
144
146
php_stream_filter * transfer_encoding = NULL ;
145
147
int response_code ;
@@ -363,8 +365,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
363
365
if (context && (tmpzval = php_stream_context_get_option (context , "http" , "method" )) != NULL ) {
364
366
if (Z_TYPE_P (tmpzval ) == IS_STRING && Z_STRLEN_P (tmpzval ) > 0 ) {
365
367
/* As per the RFC, automatically redirected requests MUST NOT use other methods than
366
- * GET and HEAD unless it can be confirmed by the user */
367
- if (!redirected
368
+ * GET and HEAD unless it can be confirmed by the user. */
369
+ if (!redirected || redirect_keep_method
368
370
|| zend_string_equals_literal (Z_STR_P (tmpzval ), "GET" )
369
371
|| zend_string_equals_literal (Z_STR_P (tmpzval ), "HEAD" )
370
372
) {
@@ -458,7 +460,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
458
460
zend_str_tolower (ZSTR_VAL (tmp ), ZSTR_LEN (tmp ));
459
461
t = ZSTR_VAL (tmp );
460
462
461
- if (!header_init ) {
463
+ if (!header_init && ! redirect_keep_method ) {
462
464
/* strip POST headers on redirect */
463
465
strip_header (user_headers , t , "content-length:" );
464
466
strip_header (user_headers , t , "content-type:" );
@@ -606,7 +608,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
606
608
* see bug #44603 for details. Since Content-Type maybe part of user's headers we need to do this check first.
607
609
*/
608
610
if (
609
- header_init &&
611
+ ( header_init || redirect_keep_method ) &&
610
612
context &&
611
613
!(have_header & HTTP_HEADER_CONTENT_LENGTH ) &&
612
614
(tmpzval = php_stream_context_get_option (context , "http" , "content" )) != NULL &&
@@ -624,7 +626,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
624
626
}
625
627
626
628
/* Request content, such as for POST requests */
627
- if (header_init && context &&
629
+ if (( header_init || redirect_keep_method ) && context &&
628
630
(tmpzval = php_stream_context_get_option (context , "http" , "content" )) != NULL &&
629
631
Z_TYPE_P (tmpzval ) == IS_STRING && Z_STRLEN_P (tmpzval ) > 0 ) {
630
632
if (!(have_header & HTTP_HEADER_CONTENT_LENGTH )) {
@@ -913,9 +915,16 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
913
915
CHECK_FOR_CNTRL_CHARS (resource -> pass );
914
916
CHECK_FOR_CNTRL_CHARS (resource -> path );
915
917
}
918
+ int new_flags = HTTP_WRAPPER_REDIRECTED ;
919
+ if (response_code == 307 || response_code == 308 ) {
920
+ /* RFC 7538 specifies that status code 308 does not allow changing the request method from POST to GET.
921
+ * RFC 7231 does the same for status code 307.
922
+ * To keep consistency between POST and PATCH requests, we'll also not change the request method from PATCH to GET, even though it's allowed it's not mandated by the RFC. */
923
+ new_flags |= HTTP_WRAPPER_KEEP_METHOD ;
924
+ }
916
925
stream = php_stream_url_wrap_http_ex (
917
926
wrapper , new_path , mode , options , opened_path , context ,
918
- -- redirect_max , HTTP_WRAPPER_REDIRECTED , response_header STREAMS_CC );
927
+ -- redirect_max , new_flags , response_header STREAMS_CC );
919
928
} else {
920
929
php_stream_wrapper_log_error (wrapper , options , "HTTP request failed! %s" , tmp_line );
921
930
}
0 commit comments