7979
8080#define HTTP_WRAPPER_HEADER_INIT 1
8181#define HTTP_WRAPPER_REDIRECTED 2
82+ #define HTTP_WRAPPER_KEEP_METHOD 4
8283
8384static inline void strip_header (char * header_bag , char * lc_header_bag ,
8485 const char * lc_header_name )
@@ -140,6 +141,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
140141 char * user_headers = NULL ;
141142 int header_init = ((flags & HTTP_WRAPPER_HEADER_INIT ) != 0 );
142143 int redirected = ((flags & HTTP_WRAPPER_REDIRECTED ) != 0 );
144+ int redirect_keep_method = ((flags & HTTP_WRAPPER_KEEP_METHOD ) != 0 );
143145 bool follow_location = 1 ;
144146 php_stream_filter * transfer_encoding = NULL ;
145147 int response_code ;
@@ -363,8 +365,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
363365 if (context && (tmpzval = php_stream_context_get_option (context , "http" , "method" )) != NULL ) {
364366 if (Z_TYPE_P (tmpzval ) == IS_STRING && Z_STRLEN_P (tmpzval ) > 0 ) {
365367 /* 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
368370 || zend_string_equals_literal (Z_STR_P (tmpzval ), "GET" )
369371 || zend_string_equals_literal (Z_STR_P (tmpzval ), "HEAD" )
370372 ) {
@@ -458,7 +460,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
458460 zend_str_tolower (ZSTR_VAL (tmp ), ZSTR_LEN (tmp ));
459461 t = ZSTR_VAL (tmp );
460462
461- if (!header_init ) {
463+ if (!header_init && ! redirect_keep_method ) {
462464 /* strip POST headers on redirect */
463465 strip_header (user_headers , t , "content-length:" );
464466 strip_header (user_headers , t , "content-type:" );
@@ -606,7 +608,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
606608 * see bug #44603 for details. Since Content-Type maybe part of user's headers we need to do this check first.
607609 */
608610 if (
609- header_init &&
611+ ( header_init || redirect_keep_method ) &&
610612 context &&
611613 !(have_header & HTTP_HEADER_CONTENT_LENGTH ) &&
612614 (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,
624626 }
625627
626628 /* Request content, such as for POST requests */
627- if (header_init && context &&
629+ if (( header_init || redirect_keep_method ) && context &&
628630 (tmpzval = php_stream_context_get_option (context , "http" , "content" )) != NULL &&
629631 Z_TYPE_P (tmpzval ) == IS_STRING && Z_STRLEN_P (tmpzval ) > 0 ) {
630632 if (!(have_header & HTTP_HEADER_CONTENT_LENGTH )) {
@@ -913,9 +915,16 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
913915 CHECK_FOR_CNTRL_CHARS (resource -> pass );
914916 CHECK_FOR_CNTRL_CHARS (resource -> path );
915917 }
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+ }
916925 stream = php_stream_url_wrap_http_ex (
917926 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 );
919928 } else {
920929 php_stream_wrapper_log_error (wrapper , options , "HTTP request failed! %s" , tmp_line );
921930 }
0 commit comments