X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=php_http_env_response.c;h=a4431b60af08c9be636272ea6110220aaae6ae78;hp=52d561ac19eeaae62417e1e3840afdb166a0a6e8;hb=791511f3bc18cdc68b3f27b43d9396cf56d99e5a;hpb=5e39218b5ee82680410b3e170ab7581ba87ff89e diff --git a/php_http_env_response.c b/php_http_env_response.c index 52d561a..a4431b6 100644 --- a/php_http_env_response.c +++ b/php_http_env_response.c @@ -883,9 +883,11 @@ typedef struct php_http_env_response_stream_ctx { long status_code; php_stream *stream; + php_http_message_t *request; unsigned started:1; unsigned finished:1; + unsigned chunked:1; } php_http_env_response_stream_ctx_t; static STATUS php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg) @@ -903,6 +905,13 @@ static STATUS php_http_env_response_stream_init(php_http_env_response_t *r, void zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0); php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC); ctx->status_code = 200; + ctx->chunked = 1; + ctx->request = get_request(r->options TSRMLS_CC); + + /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */ + if (ctx->request && ctx->request->http.version.major == 1 && ctx->request->http.version.minor == 0) { + ctx->version.minor = 0; + } r->ctx = ctx; @@ -929,6 +938,12 @@ static void php_http_env_response_stream_header(php_http_env_response_stream_ctx } else { zval *tmp = php_http_ztyp(IS_STRING, *val); + if (ctx->chunked) { + /* disable chunked transfer encoding if we've got an explicit content-length */ + if (!strncasecmp(Z_STRVAL_P(tmp), "Content-Length:", lenof("Content-Length:"))) { + ctx->chunked = 0; + } + } php_stream_write(ctx->stream, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); php_stream_write_string(ctx->stream, PHP_HTTP_CRLF); zval_ptr_dtor(&tmp); @@ -942,9 +957,26 @@ static STATUS php_http_env_response_stream_start(php_http_env_response_stream_ct } php_stream_printf(ctx->stream TSRMLS_CC, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code)); + + /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */ + if (ctx->version.major == 1 && ctx->version.minor == 0) { + ctx->chunked = 0; + } else if (ctx->status_code == 204 || ctx->status_code/100 == 1) { + ctx->chunked = 0; + } else if (ctx->request && ctx->status_code/100 == 2 && !strcasecmp(ctx->request->http.info.request.method, "CONNECT")) { + ctx->chunked = 0; + } + php_http_env_response_stream_header(ctx, &ctx->header TSRMLS_CC); + + /* enable chunked transfer encoding */ + if (ctx->chunked) { + php_stream_write_string(ctx->stream, "Transfer-Encoding: chunked" PHP_HTTP_CRLF); + } php_stream_write_string(ctx->stream, PHP_HTTP_CRLF); + ctx->started = 1; + return SUCCESS; } static long php_http_env_response_stream_get_status(php_http_env_response_t *r) @@ -1061,10 +1093,18 @@ static STATUS php_http_env_response_stream_write(php_http_env_response_t *r, con } } + if (stream_ctx->chunked && 0 == php_stream_printf(stream_ctx->stream TSRMLS_CC, "%lx" PHP_HTTP_CRLF, (unsigned long) data_len)) { + return FAILURE; + } + if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) { return FAILURE; } + if (stream_ctx->chunked && 2 != php_stream_write_string(stream_ctx->stream, PHP_HTTP_CRLF)) { + return FAILURE; + } + return SUCCESS; } static STATUS php_http_env_response_stream_flush(php_http_env_response_t *r) @@ -1097,6 +1137,10 @@ static STATUS php_http_env_response_stream_finish(php_http_env_response_t *r) } } + if (stream_ctx->chunked && 5 != php_stream_write_string(stream_ctx->stream, "0" PHP_HTTP_CRLF PHP_HTTP_CRLF)) { + return FAILURE; + } + stream_ctx->finished = 1; return SUCCESS; @@ -1367,6 +1411,7 @@ static PHP_METHOD(HttpEnvResponse, send) #else php_end_ob_buffers(1 TSRMLS_CC); #endif + if (zstream) { php_http_env_response_t *r;