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

Reply via email to