better reporting whether the response was sent out completely
authorMichael Wallner <mike@php.net>
Thu, 10 Jul 2014 19:36:41 +0000 (21:36 +0200)
committerMichael Wallner <mike@php.net>
Thu, 10 Jul 2014 19:36:41 +0000 (21:36 +0200)
php_http_buffer.c
php_http_buffer.h
php_http_env_response.c
tests/envresponse016.phpt [new file with mode: 0644]

index 1a4fdc4..35155e7 100644 (file)
@@ -222,13 +222,17 @@ PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, c
        return 0;
 }
 
-PHP_HTTP_BUFFER_API void php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_len, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC)
+PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_len, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC)
 {
        char *chunk = NULL;
-       size_t got = 0;
+       size_t passed = 0, got = 0;
 
        while ((got = php_http_buffer_chunk_buffer(s, data, data_len, &chunk, chunk_len))) {
-               passout(opaque, chunk, got TSRMLS_CC);
+               if (PHP_HTTP_BUFFER_PASS0 == passout(opaque, chunk, got TSRMLS_CC)) {
+                       STR_SET(chunk, NULL);
+                       return PHP_HTTP_BUFFER_PASS0;
+               }
+               ++passed;
                if (!chunk_len) {
                        /*      we already got the last chunk,
                                and freed all resources */
@@ -239,6 +243,7 @@ PHP_HTTP_BUFFER_API void php_http_buffer_chunked_output(php_http_buffer_t **s, c
                STR_SET(chunk, NULL);
        }
        STR_FREE(chunk);
+       return passed;
 }
 
 PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC)
index bc2a7f2..cf9b458 100644 (file)
@@ -191,7 +191,7 @@ typedef size_t (*php_http_buffer_pass_func_t)(void *opaque, char *, size_t TSRML
 PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC);
 
 /* wrapper around php_http_buffer_chunk_buffer, which passes available chunks to passthru() */
-PHP_HTTP_BUFFER_API void php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_size, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC);
+PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_size, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC);
 
 /* write chunks directly into php_http_buffer_t buffer */
 PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC);
index 275b91b..4d6ef03 100644 (file)
@@ -217,7 +217,9 @@ static size_t output(void *context, char *buf, size_t len TSRMLS_DC)
 {
        php_http_env_response_t *r = context;
 
-       r->ops->write(r, buf, len);
+       if (SUCCESS != r->ops->write(r, buf, len)) {
+               return (size_t) -1;
+       }
 
        /*      we really only need to flush when throttling is enabled,
                because we push the data as fast as possible anyway if not */
@@ -231,7 +233,7 @@ static size_t output(void *context, char *buf, size_t len TSRMLS_DC)
 #define php_http_env_response_send_done(r) php_http_env_response_send_data((r), NULL, 0)
 static STATUS php_http_env_response_send_data(php_http_env_response_t *r, const char *buf, size_t len)
 {
-       size_t chunk = r->throttle.chunk ? r->throttle.chunk : PHP_HTTP_SENDBUF_SIZE;
+       size_t chunks_sent, chunk = r->throttle.chunk ? r->throttle.chunk : PHP_HTTP_SENDBUF_SIZE;
        TSRMLS_FETCH_FROM_CTX(r->ts);
 
        if (r->content.encoder) {
@@ -248,15 +250,16 @@ static STATUS php_http_env_response_send_data(php_http_env_response_t *r, const
                        }
                }
 
-               if (enc_str) {
-                       php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC);
-                       STR_FREE(enc_str);
+               if (!enc_str) {
+                       return SUCCESS;
                }
+               chunks_sent = php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC);
+               STR_FREE(enc_str);
        } else {
-               php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC);
+               chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC);
        }
 
-       return SUCCESS;
+       return chunks_sent != (size_t) -1 ? SUCCESS : FAILURE;
 }
 
 php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *init_arg TSRMLS_DC)
@@ -996,7 +999,9 @@ static STATUS php_http_env_response_stream_write(php_http_env_response_t *r, con
                }
        }
 
-       php_stream_write(stream_ctx->stream, data_str, data_len);
+       if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) {
+               return FAILURE;
+       }
 
        return SUCCESS;
 }
diff --git a/tests/envresponse016.phpt b/tests/envresponse016.phpt
new file mode 100644 (file)
index 0000000..67595b4
--- /dev/null
@@ -0,0 +1,34 @@
+--TEST--
+env response send failure
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+echo "Test\n";
+
+class closer extends php_user_filter {
+       function filter ($in, $out, &$consumed, $closing) {
+               while ($bucket = stream_bucket_make_writeable($in)) {
+                       stream_bucket_append($out, $bucket);
+               }
+               return PSFS_ERR_FATAL;
+       }
+}
+
+stream_filter_register("closer", "closer");
+
+$r = new http\Env\Response;
+$r->getBody()->append(str_repeat("a", 16*1024*4));
+$s = fopen("php://temp", "w");
+stream_filter_append($s, "closer");
+var_dump($r->send($s));
+?>
+DONE
+--EXPECTF--
+Test
+Warning: http\Env\Response::send(): Failed to send response body in %s on line %d
+bool(false)
+DONE