pollita Tue Sep 7 15:27:11 2004 EDT Modified files: /php-src/ext/standard http_fopen_wrapper.c /php-src NEWS Log: Protocol version context option and chunked transfer encoding
http://cvs.php.net/diff.php/php-src/ext/standard/http_fopen_wrapper.c?r1=1.89&r2=1.90&ty=u Index: php-src/ext/standard/http_fopen_wrapper.c diff -u php-src/ext/standard/http_fopen_wrapper.c:1.89 php-src/ext/standard/http_fopen_wrapper.c:1.90 --- php-src/ext/standard/http_fopen_wrapper.c:1.89 Sat Jul 24 00:01:48 2004 +++ php-src/ext/standard/http_fopen_wrapper.c Tue Sep 7 15:27:11 2004 @@ -16,9 +16,10 @@ | Jim Winstead <[EMAIL PROTECTED]> | | Hartmut Holzgraefe <[EMAIL PROTECTED]> | | Wez Furlong <[EMAIL PROTECTED]> | + | Sara Golemon <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ */ -/* $Id: http_fopen_wrapper.c,v 1.89 2004/07/24 04:01:48 pollita Exp $ */ +/* $Id: http_fopen_wrapper.c,v 1.90 2004/09/07 19:27:11 pollita Exp $ */ #include "php.h" #include "php_globals.h" @@ -85,9 +86,182 @@ #define HTTP_HEADER_HOST 2 #define HTTP_HEADER_AUTH 4 #define HTTP_HEADER_FROM 8 -#define HTTP_HEADER_CONTENT_LENGTH 16 +#define HTTP_HEADER_CONTENT_LENGTH 16 #define HTTP_HEADER_TYPE 32 +/* 8 hexits plus \r\n\0 */ +#define HTTP_CHUNK_SIZE_MAXLEN 11 +#define HTTP_CHUNKED_ENCODING_BUFFER_LEN (HTTP_CHUNK_SIZE_MAXLEN + 1) + +typedef struct _php_http_chunked_encoding_data { + int is_persistent; + size_t chunk_remaining; + char chunksize_buffer[HTTP_CHUNKED_ENCODING_BUFFER_LEN]; + char *chunksize_buffer_pos; +} php_http_chunked_encoding_data; + +static php_stream_filter_status_t php_http_chunked_encoding_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC) +{ + php_stream_bucket *bucket; + php_http_chunked_encoding_data *data = (php_http_chunked_encoding_data*)thisfilter->abstract; + size_t consumed = 0; + char *buf; + size_t buflen; + + while (buckets_in->head) { + char *e = NULL; + size_t chunk_remaining; + + bucket = buckets_in->head; + php_stream_bucket_unlink(bucket TSRMLS_CC); + + buf = bucket->buf; + buflen = bucket->buflen; + +continue_bucket: + + if (data->chunk_remaining > 0) { + if ((data->chunk_remaining > buflen) && (bucket->buf == buf)) { + /* This bucket is smaller than our remaining chunksize, + Pass it on unmolested */ + consumed += buflen; + data->chunk_remaining -= buflen; + php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); + + /* Next bucket please */ + continue; + } else if (data->chunk_remaining > buflen) { + php_stream_bucket *newbucket; + char *newbuf; + + /* Previously split bucket can be used en toto */ + consumed += buflen; + data->chunk_remaining -= buflen; + + newbuf = estrndup(buf, buflen); + newbucket = php_stream_bucket_new(stream, newbuf, buflen, 1, stream->is_persistent TSRMLS_CC); + php_stream_bucket_append(buckets_out, newbucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + /* Next bucket please */ + continue; + } else { + php_stream_bucket *newbucket; + char *newbuf; + + /* Consume enough of this bucket to satisfy the current chunk */ + newbuf = pemalloc(data->chunk_remaining, stream->is_persistent); + memcpy(newbuf, buf, data->chunk_remaining); + + newbucket = php_stream_bucket_new(stream, newbuf, data->chunk_remaining, 1, stream->is_persistent TSRMLS_CC); + php_stream_bucket_append(buckets_out, newbucket TSRMLS_CC); + buf += data->chunk_remaining; + buflen -= data->chunk_remaining; + consumed += data->chunk_remaining; + data->chunk_remaining = 0; + /* Fall Through */ + } + } + + while (buflen > 0 && (*buf == '\r' || *buf == '\n')) { + buf++; + buflen--; + } + + if (buflen <= 0) { + php_stream_bucket_delref(bucket TSRMLS_CC); + continue; + } + + if ((data->chunksize_buffer_pos - data->chunksize_buffer) < HTTP_CHUNK_SIZE_MAXLEN) { + /* Copy buf into chunksize_buffer */ + if ((HTTP_CHUNKED_ENCODING_BUFFER_LEN - (data->chunksize_buffer_pos - data->chunksize_buffer)) > buflen) { + /* Possible split chunklen */ + memcpy(data->chunksize_buffer_pos, buf, buflen); + memset(data->chunksize_buffer_pos + buflen, 0, HTTP_CHUNK_SIZE_MAXLEN - buflen - (data->chunksize_buffer_pos - data->chunksize_buffer)); + chunk_remaining = strtoul(data->chunksize_buffer, &e, 16); + if (*e != '\r') { + if (!((*e >= '0' && *e <= '9') || + (*e >= 'a' && *e <= 'f') || + (*e >= 'A' && *e <= 'F'))) { + /* Unexpected character */ + return PSFS_ERR_FATAL; + } + + /* Not enough data to know the chunksize yet */ + php_stream_bucket_delref(bucket TSRMLS_CC); + continue; + } + } else { + memcpy(data->chunksize_buffer_pos, buf, HTTP_CHUNK_SIZE_MAXLEN - (data->chunksize_buffer_pos - data->chunksize_buffer)); + chunk_remaining = strtoul(data->chunksize_buffer, &e, 16); + if (*e != '\r') { + /* Invalid chunksize */ + return PSFS_ERR_FATAL; + } + } + } + + data->chunk_remaining = chunk_remaining; + /* Skip past the chunksize itself */ + buf += (e - data->chunksize_buffer) + sizeof("\r\n") - 1; + buflen -= (e - data->chunksize_buffer) + sizeof("\r\n") - 1; + consumed += (e - data->chunksize_buffer) + sizeof("\r\n") - 1; + memset(data->chunksize_buffer, 0, sizeof(data->chunksize_buffer)); + data->chunksize_buffer_pos = data->chunksize_buffer; + + if (buflen > 0) { + goto continue_bucket; + } + php_stream_bucket_delref(bucket TSRMLS_CC); + } + + if (bytes_consumed) { + *bytes_consumed = consumed; + } + + if (consumed > 0) { + return PSFS_PASS_ON; + } + + return PSFS_FEED_ME; +} + +static void php_http_chunked_encoding_dtor(php_stream_filter *thisfilter TSRMLS_DC) +{ + if (thisfilter && thisfilter->abstract) { + php_http_chunked_encoding_data *data = (php_http_chunked_encoding_data*)thisfilter->abstract; + + pefree(data, data->is_persistent); + thisfilter->abstract = NULL; + } +} + +static php_stream_filter_ops php_http_chunked_encoding_filter_ops = { + php_http_chunked_encoding_filter, + php_http_chunked_encoding_dtor, + "http.chunked.decode" +}; + +static php_stream_filter *php_http_chunked_encoding_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) +{ + php_http_chunked_encoding_data *data; + + data = pemalloc(sizeof(php_http_chunked_encoding_data), persistent); + data->chunk_remaining = 0; + data->is_persistent = persistent; + memset(data->chunksize_buffer, 0, sizeof(data->chunksize_buffer)); + data->chunksize_buffer_pos = data->chunksize_buffer; + + return php_stream_filter_alloc(&php_http_chunked_encoding_filter_ops, data, persistent); +} + php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context, int redirect_max, int header_init STREAMS_DC TSRMLS_DC) { php_stream *stream = NULL; @@ -108,6 +282,9 @@ int eol_detect = 0; char *transport_string, *errstr = NULL; int transport_len, have_header = 0, request_fulluri = 0; + char *protocol_version = NULL; + int protocol_version_len = 3; /* Default: "1.0" */ + int autofilter_chunked_encoding = 0; if (redirect_max < 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Circular redirect, aborting."); @@ -198,8 +375,16 @@ } } + if (context && + php_stream_context_get_option(context, "http", "protocol_version", &tmpzval) == SUCCESS) { + SEPARATE_ZVAL(tmpzval); + convert_to_double_ex(tmpzval); + protocol_version_len = spprintf(&protocol_version, 0, "%.1f", Z_DVAL_PP(tmpzval)); + zval_ptr_dtor(tmpzval); + } + if (!scratch) { - scratch_len = strlen(path) + 32; + scratch_len = strlen(path) + 29 + protocol_version_len; scratch = emalloc(scratch_len); strcpy(scratch, "GET "); } @@ -236,7 +421,16 @@ } /* protocol version we are speaking */ - strlcat(scratch, " HTTP/1.0\r\n", scratch_len); + if (protocol_version) { + strlcat(scratch, " HTTP/", scratch_len); + strlcat(scratch, protocol_version, scratch_len); + strlcat(scratch, "\r\n", scratch_len); + efree(protocol_version); + protocol_version = NULL; + } else { + strlcat(scratch, " HTTP/1.0\r\n", scratch_len); + } + /* send it */ php_stream_write(stream, scratch, strlen(scratch)); @@ -471,6 +665,9 @@ } else if (!strncasecmp(http_header_line, "Content-Length: ", 16)) { file_size = atoi(http_header_line + 16); php_stream_notify_file_size(context, file_size, http_header_line, 0); + } else if ((strncasecmp(http_header_line, "Transfer-Encoding: ", sizeof("Transfer-Encoding: ") - 1) == 0) && + (strstr(http_header_line + sizeof("Transfer-Encoding: ") - 1, "chunked") != NULL)) { + autofilter_chunked_encoding = 1; } if (http_header_line[0] == '\0') { @@ -560,10 +757,18 @@ } } out: - if (http_header_line) + if (protocol_version) { + efree(protocol_version); + } + + if (http_header_line) { efree(http_header_line); - if (scratch) + } + + if (scratch) { efree(scratch); + } + php_url_free(resource); if (stream) { @@ -582,6 +787,19 @@ /* as far as streams are concerned, we are now at the start of * the stream */ stream->position = 0; + + if (autofilter_chunked_encoding) { + php_stream_filter *filter; + + filter = php_http_chunked_encoding_filter_create("http.chunked.decode", NULL, stream->is_persistent TSRMLS_CC); + if (!filter) { + /* Never actually happens */ + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to apply chunked content decoding"); + } else { + php_stream_filter_append(&stream->readfilters, filter); + } + } + } return stream; http://cvs.php.net/diff.php/php-src/NEWS?r1=1.1806&r2=1.1807&ty=u Index: php-src/NEWS diff -u php-src/NEWS:1.1806 php-src/NEWS:1.1807 --- php-src/NEWS:1.1806 Wed Aug 25 02:37:47 2004 +++ php-src/NEWS Tue Sep 7 15:27:11 2004 @@ -26,6 +26,7 @@ - Added MDTM support to ftp_url_stat. (Sara) - Added zlib stream filter support. (Sara) - Added bz2 stream filter support. (Sara) +- Added HTTP/1.1 and chunked encoding support to http:// wrapper. (Sara) - Added support of parameter->value arrays to xsl_xsltprocessor_set_parameter() (Tony) - Fixed bug with raw_post_data not getting set. (Brian)
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php