@@ -352,6 +352,43 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w
352352 return NULL ;
353353}
354354
355+ static bool php_stream_unwrap_content (php_stream_context * context , zend_string * * str_out , php_stream * * stream_out )
356+ {
357+ zval * content = php_stream_context_get_option (context , "http" , "content" );
358+ if (content ) {
359+ if (Z_TYPE_P (content ) == IS_STRING && Z_STRLEN_P (content ) > 0 ) {
360+ * str_out = Z_STR_P (content );
361+ return true;
362+ } else if (Z_TYPE_P (content ) == IS_RESOURCE ) {
363+ if ((php_stream_from_zval_no_verify (* stream_out , content ))) {
364+ return true;
365+ }
366+ }
367+ }
368+
369+ return false;
370+ }
371+
372+ static bool php_stream_append_content_length (smart_str * req_buf , zend_string * content_str , php_stream * content_stream )
373+ {
374+ smart_str_appends (req_buf , "Content-Length: " );
375+ if (content_str ) {
376+ smart_str_append_unsigned (req_buf , ZSTR_LEN (content_str ));
377+ } else {
378+ zend_off_t current_position = php_stream_tell (content_stream );
379+ if (php_stream_seek (content_stream , 0 , SEEK_END ) < 0 ) {
380+ return false;
381+ }
382+ zend_off_t end_position = php_stream_tell (content_stream );
383+ if (php_stream_seek (content_stream , current_position , SEEK_SET ) < 0 ) {
384+ return false;
385+ }
386+ smart_str_append_unsigned (req_buf , end_position - current_position );
387+ }
388+ smart_str_appends (req_buf , "\r\n" );
389+ return true;
390+ }
391+
355392static php_stream * php_stream_url_wrap_http_ex (php_stream_wrapper * wrapper ,
356393 const char * path , const char * mode , int options , zend_string * * opened_path ,
357394 php_stream_context * context , int redirect_max , int flags ,
@@ -832,6 +869,9 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
832869 }
833870 }
834871
872+ zend_string * content_str = NULL ;
873+ php_stream * content_stream = NULL ;
874+
835875 if (user_headers ) {
836876 /* A bit weird, but some servers require that Content-Length be sent prior to Content-Type for POST
837877 * see bug #44603 for details. Since Content-Type maybe part of user's headers we need to do this check first.
@@ -840,42 +880,71 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
840880 (header_init || redirect_keep_method ) &&
841881 context &&
842882 !(have_header & HTTP_HEADER_CONTENT_LENGTH ) &&
843- (tmpzval = php_stream_context_get_option (context , "http" , "content" )) != NULL &&
844- Z_TYPE_P (tmpzval ) == IS_STRING && Z_STRLEN_P (tmpzval ) > 0
883+ php_stream_unwrap_content (context , & content_str , & content_stream )
845884 ) {
846- smart_str_appends (& req_buf , "Content-Length: " );
847- smart_str_append_unsigned (& req_buf , Z_STRLEN_P (tmpzval ));
848- smart_str_appends (& req_buf , "\r\n" );
885+ if (!php_stream_append_content_length (& req_buf , content_str , content_stream )) {
886+ php_stream_close (stream );
887+ stream = NULL ;
888+ efree (user_headers );
889+ php_stream_wrapper_log_error (wrapper , options , "Unable to determine length of \"content\" stream!" );
890+ goto out ;
891+ }
849892 have_header |= HTTP_HEADER_CONTENT_LENGTH ;
850893 }
851894
852895 smart_str_appends (& req_buf , user_headers );
853896 smart_str_appends (& req_buf , "\r\n" );
854897 efree (user_headers );
898+
899+ /* php_stream_unwrap_content() may throw a TypeError for non-stream resources */
900+ if (UNEXPECTED (EG (exception ))) {
901+ php_stream_close (stream );
902+ stream = NULL ;
903+ goto out ;
904+ }
855905 }
856906
857907 /* Request content, such as for POST requests */
858908 if ((header_init || redirect_keep_method ) && context &&
859- (tmpzval = php_stream_context_get_option (context , "http" , "content" )) != NULL &&
860- Z_TYPE_P (tmpzval ) == IS_STRING && Z_STRLEN_P (tmpzval ) > 0 ) {
909+ (content_str || content_stream || php_stream_unwrap_content (context , & content_str , & content_stream ))) {
861910 if (!(have_header & HTTP_HEADER_CONTENT_LENGTH )) {
862- smart_str_appends (& req_buf , "Content-Length: " );
863- smart_str_append_unsigned (& req_buf , Z_STRLEN_P (tmpzval ));
864- smart_str_appends (& req_buf , "\r\n" );
911+ if (!php_stream_append_content_length (& req_buf , content_str , content_stream )) {
912+ php_stream_close (stream );
913+ stream = NULL ;
914+ php_stream_wrapper_log_error (wrapper , options , "Unable to determine length of \"content\" stream!" );
915+ goto out ;
916+ }
865917 }
866918 if (!(have_header & HTTP_HEADER_TYPE )) {
867919 smart_str_appends (& req_buf , "Content-Type: application/x-www-form-urlencoded\r\n" );
868920 php_error_docref (NULL , E_NOTICE , "Content-type not specified assuming application/x-www-form-urlencoded" );
869921 }
870922 smart_str_appends (& req_buf , "\r\n" );
871- smart_str_appendl (& req_buf , Z_STRVAL_P (tmpzval ), Z_STRLEN_P (tmpzval ));
923+ if (content_str ) {
924+ smart_str_append (& req_buf , content_str );
925+ php_stream_write (stream , ZSTR_VAL (req_buf .s ), ZSTR_LEN (req_buf .s ));
926+ } else {
927+ php_stream_write (stream , ZSTR_VAL (req_buf .s ), ZSTR_LEN (req_buf .s ));
928+
929+ if (SUCCESS != php_stream_copy_to_stream_ex (content_stream , stream , PHP_STREAM_COPY_ALL , NULL )) {
930+ php_stream_close (stream );
931+ stream = NULL ;
932+ php_stream_wrapper_log_error (wrapper , options , "Unable to copy \"content\" stream!" );
933+ goto out ;
934+ }
935+ }
872936 } else {
937+ /* php_stream_unwrap_content() may throw a TypeError for non-stream resources */
938+ if (UNEXPECTED (EG (exception ))) {
939+ php_stream_close (stream );
940+ stream = NULL ;
941+ goto out ;
942+ }
943+
873944 smart_str_appends (& req_buf , "\r\n" );
945+ php_stream_write (stream , ZSTR_VAL (req_buf .s ), ZSTR_LEN (req_buf .s ));
874946 }
875947
876- /* send it */
877- php_stream_write (stream , ZSTR_VAL (req_buf .s ), ZSTR_LEN (req_buf .s ));
878-
879948 if (Z_ISUNDEF_P (response_header )) {
880949 array_init (response_header );
881950 }
0 commit comments