LCOV - code coverage report
Current view: top level - ext/http - php_http_filter.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 130 149 87.2 %
Date: 2015-02-17 20:30:22 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :     +--------------------------------------------------------------------+
       3             :     | PECL :: http                                                       |
       4             :     +--------------------------------------------------------------------+
       5             :     | Redistribution and use in source and binary forms, with or without |
       6             :     | modification, are permitted provided that the conditions mentioned |
       7             :     | in the accompanying LICENSE file are met.                          |
       8             :     +--------------------------------------------------------------------+
       9             :     | Copyright (c) 2004-2014, Michael Wallner <mike@php.net>            |
      10             :     +--------------------------------------------------------------------+
      11             : */
      12             : 
      13             : #include "php_http_api.h"
      14             : 
      15             : #ifndef DBG_FILTER
      16             : #       define DBG_FILTER 0
      17             : #endif
      18             : 
      19         374 : PHP_MINIT_FUNCTION(http_filter)
      20             : {
      21         374 :         php_stream_filter_register_factory("http.*", &php_http_filter_factory TSRMLS_CC);
      22         374 :         return SUCCESS;
      23             : }
      24             : 
      25             : #define PHP_HTTP_FILTER_PARAMS \
      26             :         php_stream *stream, \
      27             :         php_stream_filter *this, \
      28             :         php_stream_bucket_brigade *buckets_in, \
      29             :         php_stream_bucket_brigade *buckets_out, \
      30             :         size_t *bytes_consumed, int flags \
      31             :         TSRMLS_DC
      32             : #define PHP_HTTP_FILTER_OP(filter) \
      33             :         http_filter_op_ ##filter
      34             : #define PHP_HTTP_FILTER_OPS(filter) \
      35             :         php_stream_filter_ops PHP_HTTP_FILTER_OP(filter)
      36             : #define PHP_HTTP_FILTER_DTOR(filter) \
      37             :         http_filter_ ##filter## _dtor
      38             : #define PHP_HTTP_FILTER_DESTRUCTOR(filter) \
      39             :         void PHP_HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC)
      40             : #define PHP_HTTP_FILTER_FUNC(filter) \
      41             :         http_filter_ ##filter
      42             : #define PHP_HTTP_FILTER_FUNCTION(filter) \
      43             :         php_stream_filter_status_t PHP_HTTP_FILTER_FUNC(filter)(PHP_HTTP_FILTER_PARAMS)
      44             : #define PHP_HTTP_FILTER_BUFFER(filter) \
      45             :         http_filter_ ##filter## _buffer
      46             : 
      47             : #define PHP_HTTP_FILTER_IS_CLOSING(stream, flags) \
      48             :         (       (flags & PSFS_FLAG_FLUSH_CLOSE) \
      49             :         ||      php_stream_eof(stream) \
      50             :         || ((stream->ops == &php_stream_temp_ops || stream->ops == &php_stream_memory_ops) && stream->eof) \
      51             :         )
      52             : 
      53             : #define NEW_BUCKET(data, length) \
      54             :         { \
      55             :                 char *__data; \
      56             :                 php_stream_bucket *__buck; \
      57             :                 \
      58             :                 __data = pemalloc(length, this->is_persistent); \
      59             :                 if (!__data) { \
      60             :                         return PSFS_ERR_FATAL; \
      61             :                 } \
      62             :                 memcpy(__data, data, length); \
      63             :                 \
      64             :                 __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \
      65             :                 if (!__buck) { \
      66             :                         pefree(__data, this->is_persistent); \
      67             :                         return PSFS_ERR_FATAL; \
      68             :                 } \
      69             :                 \
      70             :                 php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \
      71             :         }
      72             : 
      73             : typedef struct _http_chunked_decode_filter_buffer_t {
      74             :         php_http_buffer_t       buffer;
      75             :         ulong   hexlen;
      76             : } PHP_HTTP_FILTER_BUFFER(chunked_decode);
      77             : 
      78             : typedef php_http_encoding_stream_t PHP_HTTP_FILTER_BUFFER(zlib);
      79             : 
      80           1 : static PHP_HTTP_FILTER_FUNCTION(chunked_decode)
      81             : {
      82           1 :         int out_avail = 0;
      83             :         php_stream_bucket *ptr, *nxt;
      84           1 :         PHP_HTTP_FILTER_BUFFER(chunked_decode) *buffer = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
      85             :         
      86           1 :         if (bytes_consumed) {
      87           0 :                 *bytes_consumed = 0;
      88             :         }
      89             :         
      90             :         /* fetch available bucket data */
      91           2 :         for (ptr = buckets_in->head; ptr; ptr = nxt) {
      92           1 :                 if (bytes_consumed) {
      93           0 :                         *bytes_consumed += ptr->buflen;
      94             :                 }
      95             : 
      96           1 :                 if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(PHP_HTTP_BUFFER(buffer), ptr->buf, ptr->buflen)) {
      97           0 :                         return PSFS_ERR_FATAL;
      98             :                 }
      99             : 
     100           1 :                 nxt = ptr->next;
     101           1 :                 php_stream_bucket_unlink(ptr TSRMLS_CC);
     102           1 :                 php_stream_bucket_delref(ptr TSRMLS_CC);
     103             :         }
     104             :         
     105           1 :         if (!php_http_buffer_fix(PHP_HTTP_BUFFER(buffer))) {
     106           0 :                 return PSFS_ERR_FATAL;
     107             :         }
     108             : 
     109             :         /* we have data in our buffer */
     110          46 :         while (PHP_HTTP_BUFFER(buffer)->used) {
     111             :         
     112             :                 /* we already know the size of the chunk and are waiting for data */
     113          45 :                 if (buffer->hexlen) {
     114             :                 
     115             :                         /* not enough data buffered */
     116          22 :                         if (PHP_HTTP_BUFFER(buffer)->used < buffer->hexlen) {
     117             :                         
     118             :                                 /* flush anyway? */
     119           0 :                                 if (flags & PSFS_FLAG_FLUSH_INC) {
     120             :                                 
     121             :                                         /* flush all data (should only be chunk data) */
     122           0 :                                         out_avail = 1;
     123           0 :                                         NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used);
     124             :                                         
     125             :                                         /* waiting for less data now */
     126           0 :                                         buffer->hexlen -= PHP_HTTP_BUFFER(buffer)->used;
     127             :                                         /* no more buffered data */
     128           0 :                                         php_http_buffer_reset(PHP_HTTP_BUFFER(buffer));
     129             :                                         /* break */
     130             :                                 } 
     131             :                                 
     132             :                                 /* we have too less data and don't need to flush */
     133             :                                 else {
     134           0 :                                         break;
     135             :                                 }
     136             :                         } 
     137             :                         
     138             :                         /* we seem to have all data of the chunk */
     139             :                         else {
     140          22 :                                 out_avail = 1;
     141          22 :                                 NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, buffer->hexlen);
     142             :                                 
     143             :                                 /* remove outgoing data from the buffer */
     144          22 :                                 php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, buffer->hexlen);
     145             :                                 /* reset hexlen */
     146          22 :                                 buffer->hexlen = 0;
     147             :                                 /* continue */
     148             :                         }
     149             :                 } 
     150             :                 
     151             :                 /* we don't know the length of the chunk yet */
     152             :                 else {
     153          23 :                         size_t off = 0;
     154             :                         
     155             :                         /* ignore preceeding CRLFs (too loose?) */
     156         157 :                         while (off < PHP_HTTP_BUFFER(buffer)->used && (
     157         112 :                                         PHP_HTTP_BUFFER(buffer)->data[off] == '\n' || 
     158          45 :                                         PHP_HTTP_BUFFER(buffer)->data[off] == '\r')) {
     159          44 :                                 ++off;
     160             :                         }
     161          23 :                         if (off) {
     162          22 :                                 php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, off);
     163             :                         }
     164             :                         
     165             :                         /* still data there? */
     166          23 :                         if (PHP_HTTP_BUFFER(buffer)->used) {
     167             :                                 int eollen;
     168             :                                 const char *eolstr;
     169             :                                 
     170             :                                 /* we need eol, so we can be sure we have all hex digits */
     171          23 :                                 php_http_buffer_fix(PHP_HTTP_BUFFER(buffer));
     172          23 :                                 if ((eolstr = php_http_locate_bin_eol(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used, &eollen))) {
     173          23 :                                         char *stop = NULL;
     174             :                                         
     175             :                                         /* read in chunk size */
     176          23 :                                         buffer->hexlen = strtoul(PHP_HTTP_BUFFER(buffer)->data, &stop, 16);
     177             :                                         
     178             :                                         /*      if strtoul() stops at the beginning of the buffered data
     179             :                                                 there's something oddly wrong, i.e. bad input */
     180          23 :                                         if (stop == PHP_HTTP_BUFFER(buffer)->data) {
     181           0 :                                                 return PSFS_ERR_FATAL;
     182             :                                         }
     183             :                                         
     184             :                                         /* cut out <chunk size hex><chunk extension><eol> */
     185          23 :                                         php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, eolstr + eollen - PHP_HTTP_BUFFER(buffer)->data);
     186             :                                         /* buffer->hexlen is 0 now or contains the size of the next chunk */
     187          23 :                                         if (!buffer->hexlen) {
     188           1 :                                                 php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_COMPLETED, NULL, 0);
     189           1 :                                                 break;
     190             :                                         }
     191             :                                         /* continue */
     192             :                                 } else {
     193             :                                         /* we have not enough data buffered to read in chunk size */
     194           0 :                                         break;
     195             :                                 }
     196             :                         }
     197             :                         /* break */
     198             :                 }
     199             :         }
     200             :         
     201             :         /* flush before close, but only if we are already waiting for more data */
     202           1 :         if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags) && buffer->hexlen && PHP_HTTP_BUFFER(buffer)->used) {
     203           0 :                 out_avail = 1;
     204           0 :                 NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used);
     205           0 :                 php_http_buffer_reset(PHP_HTTP_BUFFER(buffer));
     206           0 :                 buffer->hexlen = 0;
     207             :         }
     208             :         
     209           1 :         return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
     210             : }
     211             : 
     212           1 : static PHP_HTTP_FILTER_DESTRUCTOR(chunked_decode)
     213             : {
     214           1 :         PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
     215             :         
     216           1 :         php_http_buffer_dtor(PHP_HTTP_BUFFER(b));
     217           1 :         pefree(b, this->is_persistent);
     218           1 : }
     219             : 
     220        3349 : static PHP_HTTP_FILTER_FUNCTION(chunked_encode)
     221             : {
     222             :         php_http_buffer_t buf;
     223             :         php_stream_bucket *ptr, *nxt;
     224             :         
     225        3349 :         if (bytes_consumed) {
     226        3339 :                 *bytes_consumed = 0;
     227             :         }
     228             :         
     229             :         /* new data available? */
     230        3349 :         php_http_buffer_init(&buf);
     231             : 
     232             :         /* fetch available bucket data */
     233        6655 :         for (ptr = buckets_in->head; ptr; ptr = nxt) {
     234        3306 :                 if (bytes_consumed) {
     235        3306 :                         *bytes_consumed += ptr->buflen;
     236             :                 }
     237             : #if DBG_FILTER
     238             :                 fprintf(stderr, "update: chunked (-> %zu) (w: %zu, r: %zu)\n", ptr->buflen, stream->writepos, stream->readpos);
     239             : #endif
     240             :                 
     241        3306 :                 nxt = ptr->next;
     242        3306 :                 php_stream_bucket_unlink(ptr TSRMLS_CC);
     243        3306 :                 php_http_buffer_appendf(&buf, "%lx" PHP_HTTP_CRLF, (long unsigned int) ptr->buflen);
     244        3306 :                 php_http_buffer_append(&buf, ptr->buf, ptr->buflen);
     245        3306 :                 php_http_buffer_appends(&buf, PHP_HTTP_CRLF);
     246             : 
     247             :                 /* pass through */
     248        3306 :                 NEW_BUCKET(buf.data, buf.used);
     249             :                 /* reset */
     250        3306 :                 php_http_buffer_reset(&buf);
     251        3306 :                 php_stream_bucket_delref(ptr TSRMLS_CC);
     252             :         }
     253             : 
     254             :         /* free buffer */
     255        3349 :         php_http_buffer_dtor(&buf);
     256             :         
     257             :         /* terminate with "0" */
     258        3349 :         if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) {
     259             : #if DBG_FILTER
     260             :                 fprintf(stderr, "finish: chunked\n");
     261             : #endif
     262             :                 
     263          11 :                 NEW_BUCKET("0" PHP_HTTP_CRLF PHP_HTTP_CRLF, lenof("0" PHP_HTTP_CRLF PHP_HTTP_CRLF));
     264             :         }
     265             :         
     266        3349 :         return PSFS_PASS_ON;
     267             : }
     268             : 
     269             : static PHP_HTTP_FILTER_OPS(chunked_decode) = {
     270             :         PHP_HTTP_FILTER_FUNC(chunked_decode),
     271             :         PHP_HTTP_FILTER_DTOR(chunked_decode),
     272             :         "http.chunked_decode"
     273             : };
     274             : 
     275             : static PHP_HTTP_FILTER_OPS(chunked_encode) = {
     276             :         PHP_HTTP_FILTER_FUNC(chunked_encode),
     277             :         NULL,
     278             :         "http.chunked_encode"
     279             : };
     280             : 
     281          46 : static PHP_HTTP_FILTER_FUNCTION(zlib)
     282             : {
     283             :         php_stream_bucket *ptr, *nxt;
     284          46 :         PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract;
     285             :         
     286          46 :         if (bytes_consumed) {
     287          45 :                 *bytes_consumed = 0;
     288             :         }
     289             :         
     290             :         /* fetch available bucket data */
     291         138 :         for (ptr = buckets_in->head; ptr; ptr = nxt) {
     292          23 :                 char *encoded = NULL;
     293          23 :                 size_t encoded_len = 0;
     294             : 
     295          23 :                 if (bytes_consumed) {
     296          22 :                         *bytes_consumed += ptr->buflen;
     297             :                 }
     298             : 
     299             : #if DBG_FILTER
     300             :                 fprintf(stderr, "bucket: b=%p p=%p p=%p\n", ptr->brigade, ptr->prev,  ptr->next);
     301             : #endif
     302             :                 
     303          23 :                 nxt = ptr->next;
     304          23 :                 php_stream_bucket_unlink(ptr TSRMLS_CC);
     305          23 :                 php_http_encoding_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len);
     306             :                 
     307             : #if DBG_FILTER
     308             :                 fprintf(stderr, "update: deflate (-> %zu) (w: %zu, r: %zu)\n", encoded_len, stream->writepos, stream->readpos);
     309             : #endif
     310             :                 
     311          23 :                 if (encoded) {
     312          23 :                         if (encoded_len) {
     313           2 :                                 NEW_BUCKET(encoded, encoded_len);
     314             :                         }
     315          23 :                         efree(encoded);
     316             :                 }
     317          23 :                 php_stream_bucket_delref(ptr TSRMLS_CC);
     318             :         }
     319             : 
     320             :         /* flush & close */
     321          46 :         if (flags & PSFS_FLAG_FLUSH_INC) {
     322          22 :                 char *encoded = NULL;
     323          22 :                 size_t encoded_len = 0;
     324             :                 
     325          22 :                 php_http_encoding_stream_flush(buffer, &encoded, &encoded_len);
     326             :                 
     327             : #if DBG_FILTER
     328             :                 fprintf(stderr, "flush: deflate (-> %zu)\n", encoded_len);
     329             : #endif
     330             :                 
     331          22 :                 if (encoded) {
     332          22 :                         if (encoded_len) {
     333          22 :                                 NEW_BUCKET(encoded, encoded_len);
     334             :                         }
     335          22 :                         efree(encoded);
     336             :                 }
     337             :         }
     338             :         
     339          46 :         if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) {
     340           2 :                 char *encoded = NULL;
     341           2 :                 size_t encoded_len = 0;
     342             :                 
     343           2 :                 php_http_encoding_stream_finish(buffer, &encoded, &encoded_len);
     344             :                 
     345             : #if DBG_FILTER
     346             :                 fprintf(stderr, "finish: deflate (-> %zu)\n", encoded_len);
     347             : #endif
     348             :                 
     349           2 :                 if (encoded) {
     350           1 :                         if (encoded_len) {
     351           1 :                                 NEW_BUCKET(encoded, encoded_len);
     352             :                         }
     353           1 :                         efree(encoded);
     354             :                 }
     355             :         }
     356             :         
     357          46 :         return PSFS_PASS_ON;
     358             : }
     359           2 : static PHP_HTTP_FILTER_DESTRUCTOR(zlib)
     360             : {
     361           2 :         PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract;
     362           2 :         php_http_encoding_stream_free(&buffer);
     363           2 : }
     364             : 
     365             : static PHP_HTTP_FILTER_OPS(deflate) = {
     366             :         PHP_HTTP_FILTER_FUNC(zlib),
     367             :         PHP_HTTP_FILTER_DTOR(zlib),
     368             :         "http.deflate"
     369             : };
     370             : 
     371             : static PHP_HTTP_FILTER_OPS(inflate) = {
     372             :         PHP_HTTP_FILTER_FUNC(zlib),
     373             :         PHP_HTTP_FILTER_DTOR(zlib),
     374             :         "http.inflate"
     375             : };
     376             : 
     377          14 : static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
     378             : {
     379          14 :         zval **tmp = &params;
     380          14 :         php_stream_filter *f = NULL;
     381          14 :         int flags = p ? PHP_HTTP_ENCODING_STREAM_PERSISTENT : 0;
     382             :         
     383          14 :         if (params) {
     384           2 :                 switch (Z_TYPE_P(params)) {
     385             :                         case IS_ARRAY:
     386             :                         case IS_OBJECT:
     387           1 :                                 if (SUCCESS != zend_hash_find(HASH_OF(params), "flags", sizeof("flags"), (void *) &tmp)) {
     388           1 :                                         break;
     389             :                                 }
     390             :                                 /* no break */
     391             :                         default:
     392             :                         {
     393           1 :                                 zval *num = php_http_ztyp(IS_LONG, *tmp);
     394             :                                 
     395           1 :                                 flags |= (Z_LVAL_P(num) & 0x0fffffff);
     396           1 :                                 zval_ptr_dtor(&num);
     397             : 
     398             :                         }
     399           1 :                         break;
     400             :                 }
     401             :         }
     402             : 
     403          14 :         if (!strcasecmp(name, "http.chunked_decode")) {
     404           1 :                 PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = NULL;
     405             :                 
     406           1 :                 if ((b = pecalloc(1, sizeof(PHP_HTTP_FILTER_BUFFER(chunked_decode)), p))) {
     407           1 :                         php_http_buffer_init_ex(PHP_HTTP_BUFFER(b), 4096, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0);
     408           1 :                         if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_decode), b, p))) {
     409           0 :                                 pefree(b, p);
     410             :                         }
     411             :                 }
     412             :         } else
     413             :         
     414          13 :         if (!strcasecmp(name, "http.chunked_encode")) {
     415          11 :                 f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_encode), NULL, p);
     416             :         } else
     417             :         
     418           2 :         if (!strcasecmp(name, "http.inflate")) {
     419           1 :                 PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
     420             :                 
     421           1 :                 if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags TSRMLS_CC))) {
     422           1 :                         if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(inflate), b, p))) {
     423           0 :                                 php_http_encoding_stream_free(&b);
     424             :                         }
     425             :                 }
     426             :         } else
     427             :         
     428           1 :         if (!strcasecmp(name, "http.deflate")) {
     429           1 :                 PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL;
     430             :                 
     431           1 :                 if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags TSRMLS_CC))) {
     432           1 :                         if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(deflate), b, p))) {
     433           0 :                                 php_http_encoding_stream_free(&b);
     434             :                         }
     435             :                 }
     436             :         }
     437             :         
     438          14 :         return f;
     439             : }
     440             : 
     441             : php_stream_filter_factory php_http_filter_factory = {
     442             :         http_filter_create
     443             : };
     444             : 
     445             : 
     446             : /*
     447             :  * Local variables:
     448             :  * tab-width: 4
     449             :  * c-basic-offset: 4
     450             :  * End:
     451             :  * vim600: noet sw=4 ts=4 fdm=marker
     452             :  * vim<600: noet sw=4 ts=4
     453             :  */

Generated by: LCOV version 1.11