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