X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=http_api.c;h=ff7468d066626358a5423a856b0ac9f75935f29b;hp=e1d6e1b43bd9be29c9dbb6a63dd7e34096e664c5;hb=af674f03c32f0f56b7f8c67e61c7e86b3ea23be5;hpb=6dd728f3faa5dcedf81f3f9a6cfe2e41993efc0b diff --git a/http_api.c b/http_api.c index e1d6e1b..ff7468d 100644 --- a/http_api.c +++ b/http_api.c @@ -15,24 +15,27 @@ /* $Id$ */ -#define _WINSOCKAPI_ -#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS - #ifdef HAVE_CONFIG_H # include "config.h" #endif #include +#ifdef PHP_WIN32 +# include +#elif defined(HAVE_NETDB_H) +# include +#endif + #include "php.h" #include "php_version.h" #include "php_streams.h" +#include "php_output.h" #include "snprintf.h" #include "ext/standard/md5.h" #include "ext/standard/url.h" #include "ext/standard/base64.h" #include "ext/standard/php_string.h" -#include "ext/standard/php_smart_str.h" #include "ext/standard/php_lcg.h" #include "SAPI.h" @@ -121,7 +124,7 @@ static const struct time_zone { static int http_sort_q(const void *a, const void *b TSRMLS_DC); #define http_send_chunk(d, b, e, m) _http_send_chunk((d), (b), (e), (m) TSRMLS_CC) -static STATUS _http_send_chunk(const void *data, const size_t begin, const size_t end, const http_send_mode mode TSRMLS_DC); +static STATUS _http_send_chunk(const void *data, size_t begin, size_t end, http_send_mode mode TSRMLS_DC); static int check_day(char *day, size_t len); static int check_month(char *month); @@ -150,21 +153,22 @@ static int http_sort_q(const void *a, const void *b TSRMLS_DC) /* {{{ static STATUS http_send_chunk(const void *, size_t, size_t, http_send_mode) */ -static STATUS _http_send_chunk(const void *data, const size_t begin, - const size_t end, const http_send_mode mode TSRMLS_DC) +static STATUS _http_send_chunk(const void *data, size_t begin, size_t end, http_send_mode mode TSRMLS_DC) { - char *buf; - size_t read = 0; long len = end - begin; - php_stream *s; switch (mode) { case SEND_RSRC: - s = (php_stream *) data; + { + char *buf; + size_t read = 0; + php_stream *s = (php_stream *) data; + if (php_stream_seek(s, begin, SEEK_SET)) { return FAILURE; } + buf = (char *) ecalloc(1, HTTP_SENDBUF_SIZE); /* read into buf and write out */ while ((len -= HTTP_SENDBUF_SIZE) >= 0) { @@ -176,6 +180,9 @@ static STATUS _http_send_chunk(const void *data, const size_t begin, efree(buf); return FAILURE; } + /* ob_flush() && flush() */ + php_end_ob_buffer(1, 1 TSRMLS_CC); + sapi_flush(TSRMLS_C); } /* read & write left over */ @@ -189,15 +196,39 @@ static STATUS _http_send_chunk(const void *data, const size_t begin, efree(buf); return FAILURE; } + /* ob_flush() & flush() */ + php_end_ob_buffer(1, 1 TSRMLS_CC); + sapi_flush(TSRMLS_C); } efree(buf); return SUCCESS; - break; + } case SEND_DATA: - return len == php_body_write(((char *)data) + begin, len TSRMLS_CC) - ? SUCCESS : FAILURE; - break; + { + char *s = (char *) data + begin; + + while ((len -= HTTP_SENDBUF_SIZE) >= 0) { + if (HTTP_SENDBUF_SIZE - php_body_write(s, HTTP_SENDBUF_SIZE TSRMLS_CC)) { + return FAILURE; + } + s += HTTP_SENDBUF_SIZE; + /* ob_flush() & flush() */ + php_end_ob_buffer(1, 1 TSRMLS_CC); + sapi_flush(TSRMLS_C); + } + + /* write left over */ + if (len) { + if (HTTP_SENDBUF_SIZE + len - php_body_write(s, HTTP_SENDBUF_SIZE + len TSRMLS_CC)) { + return FAILURE; + } + /* ob_flush() & flush() */ + php_end_ob_buffer(1, 1 TSRMLS_CC); + sapi_flush(TSRMLS_C); + } + return SUCCESS; + } default: return FAILURE; @@ -251,8 +282,8 @@ static int check_tzone(char *tzone) } /* }}} */ -/* char *pretty_key(char *, int, int, int) */ -char *pretty_key(char *key, int key_len, int uctitle, int xhyphen) +/* char *pretty_key(char *, size_t, zend_bool, zebd_bool) */ +char *pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen) { if (key && key_len) { int i, wasalpha; @@ -487,8 +518,7 @@ PHP_HTTP_API time_t _http_parse_date(const char *date) /* }}} */ /* {{{ char *http_etag(void *, size_t, http_send_mode) */ -PHP_HTTP_API char *_http_etag(const void *data_ptr, const size_t data_len, - const http_send_mode data_mode TSRMLS_DC) +PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC) { char ssb_buf[128] = {0}; unsigned char digest[16]; @@ -531,7 +561,7 @@ PHP_HTTP_API char *_http_etag(const void *data_ptr, const size_t data_len, /* }}} */ /* {{{ time_t http_lmod(void *, http_send_mode) */ -PHP_HTTP_API time_t _http_lmod(const void *data_ptr, const http_send_mode data_mode TSRMLS_DC) +PHP_HTTP_API time_t _http_lmod(const void *data_ptr, http_send_mode data_mode TSRMLS_DC) { switch (data_mode) { @@ -564,21 +594,23 @@ PHP_HTTP_API time_t _http_lmod(const void *data_ptr, const http_send_mode data_m /* }}} */ /* {{{ STATUS http_send_status_header(int, char *) */ -PHP_HTTP_API STATUS _http_send_status_header(const int status, const char *header TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_status_header(int status, const char *header TSRMLS_DC) { sapi_header_line h = {(char *) header, strlen(header), status}; return sapi_header_op(SAPI_HEADER_REPLACE, &h TSRMLS_CC); } /* }}} */ -/* {{{ zval *http_get_server_var(char *) */ -PHP_HTTP_API zval *_http_get_server_var(const char *key TSRMLS_DC) +/* {{{ zval *http_get_server_var_ex(char *, size_t) */ +PHP_HTTP_API zval *_http_get_server_var_ex(const char *key, size_t key_size, zend_bool check TSRMLS_DC) { zval **var; - if (SUCCESS == zend_hash_find( - HTTP_SERVER_VARS, - (char *) key, strlen(key) + 1, (void **) &var)) { - return *var; + if (SUCCESS == zend_hash_find(HTTP_SERVER_VARS, (char *) key, key_size, (void **) &var)) { + if (check) { + return Z_STRVAL_PP(var) && Z_STRLEN_PP(var) ? *var : NULL; + } else { + return *var; + } } return NULL; } @@ -606,6 +638,7 @@ PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len, if (http_etag_match("HTTP_IF_NONE_MATCH", etag)) { http_send_status(304); + zend_bailout(); } else { http_send_etag(etag, 32); } @@ -661,14 +694,14 @@ PHP_HTTP_API STATUS _http_start_ob_handler(php_output_handler_func_t handler_fun } /* }}} */ -/* {{{ int http_modified_match(char *, int) */ -PHP_HTTP_API int _http_modified_match(const char *entry, const time_t t TSRMLS_DC) +/* {{{ zend_bool http_modified_match(char *, time_t) */ +PHP_HTTP_API zend_bool _http_modified_match_ex(const char *entry, time_t t, zend_bool enforce_presence TSRMLS_DC) { - int retval; + zend_bool retval; zval *zmodified; char *modified, *chr_ptr; - HTTP_GSC(zmodified, entry, 0); + HTTP_GSC(zmodified, entry, !enforce_presence); modified = estrndup(Z_STRVAL_P(zmodified), Z_STRLEN_P(zmodified)); if (chr_ptr = strrchr(modified, ';')) { @@ -680,14 +713,14 @@ PHP_HTTP_API int _http_modified_match(const char *entry, const time_t t TSRMLS_D } /* }}} */ -/* {{{ int http_etag_match(char *, char *) */ -PHP_HTTP_API int _http_etag_match(const char *entry, const char *etag TSRMLS_DC) +/* {{{ zend_bool http_etag_match(char *, char *) */ +PHP_HTTP_API zend_bool _http_etag_match_ex(const char *entry, const char *etag, zend_bool enforce_presence TSRMLS_DC) { zval *zetag; char *quoted_etag; - STATUS result; + zend_bool result; - HTTP_GSC(zetag, entry, 0); + HTTP_GSC(zetag, entry, !enforce_presence); if (NULL != strchr(Z_STRVAL_P(zetag), '*')) { return 1; @@ -707,7 +740,7 @@ PHP_HTTP_API int _http_etag_match(const char *entry, const char *etag TSRMLS_DC) /* }}} */ /* {{{ STATUS http_send_last_modified(int) */ -PHP_HTTP_API STATUS _http_send_last_modified(const time_t t TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_last_modified(time_t t TSRMLS_DC) { char *date = NULL; if (date = http_date(t)) { @@ -724,9 +757,8 @@ PHP_HTTP_API STATUS _http_send_last_modified(const time_t t TSRMLS_DC) } /* }}} */ -/* {{{ static STATUS http_send_etag(char *, int) */ -PHP_HTTP_API STATUS _http_send_etag(const char *etag, - const int etag_len TSRMLS_DC) +/* {{{ STATUS http_send_etag(char *, size_t) */ +PHP_HTTP_API STATUS _http_send_etag(const char *etag, size_t etag_len TSRMLS_DC) { STATUS status; char *etag_header; @@ -754,8 +786,7 @@ PHP_HTTP_API STATUS _http_send_etag(const char *etag, /* }}} */ /* {{{ STATUS http_send_cache_control(char *, size_t) */ -PHP_HTTP_API STATUS _http_send_cache_control(const char *cache_control, - const size_t cc_len TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_cache_control(const char *cache_control, size_t cc_len TSRMLS_DC) { STATUS status; char *cc_header = ecalloc(1, sizeof("Cache-Control: ") + cc_len); @@ -771,8 +802,7 @@ PHP_HTTP_API STATUS _http_send_cache_control(const char *cache_control, /* }}} */ /* {{{ STATUS http_send_content_type(char *, size_t) */ -PHP_HTTP_API STATUS _http_send_content_type(const char *content_type, - const size_t ct_len TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_content_type(const char *content_type, size_t ct_len TSRMLS_DC) { STATUS status; char *ct_header; @@ -803,8 +833,7 @@ PHP_HTTP_API STATUS _http_send_content_type(const char *content_type, /* }}} */ /* {{{ STATUS http_send_content_disposition(char *, size_t, zend_bool) */ -PHP_HTTP_API STATUS _http_send_content_disposition(const char *filename, - const size_t f_len, const int send_inline TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_content_disposition(const char *filename, size_t f_len, zend_bool send_inline TSRMLS_DC) { STATUS status; char *cd_header; @@ -826,8 +855,8 @@ PHP_HTTP_API STATUS _http_send_content_disposition(const char *filename, /* }}} */ /* {{{ STATUS http_cache_last_modified(time_t, time_t, char *, size_t) */ -PHP_HTTP_API STATUS _http_cache_last_modified(const time_t last_modified, - const time_t send_modified, const char *cache_control, const size_t cc_len TSRMLS_DC) +PHP_HTTP_API STATUS _http_cache_last_modified(time_t last_modified, + time_t send_modified, const char *cache_control, size_t cc_len TSRMLS_DC) { if (cc_len) { http_send_cache_control(cache_control, cc_len); @@ -846,8 +875,8 @@ PHP_HTTP_API STATUS _http_cache_last_modified(const time_t last_modified, /* }}} */ /* {{{ STATUS http_cache_etag(char *, size_t, char *, size_t) */ -PHP_HTTP_API STATUS _http_cache_etag(const char *etag, const size_t etag_len, - const char *cache_control, const size_t cc_len TSRMLS_DC) +PHP_HTTP_API STATUS _http_cache_etag(const char *etag, size_t etag_len, + const char *cache_control, size_t cc_len TSRMLS_DC) { if (cc_len) { http_send_cache_control(cache_control, cc_len); @@ -879,135 +908,195 @@ PHP_HTTP_API STATUS _http_cache_etag(const char *etag, const size_t etag_len, } /* }}} */ -/* {{{ char *http_absolute_uri(char *, char *) */ -PHP_HTTP_API char *_http_absolute_uri(const char *url, - const char *proto TSRMLS_DC) +/* {{{ char *http_absolute_uri(char *) */ +PHP_HTTP_API char *_http_absolute_uri_ex( + const char *url, size_t url_len, + const char *proto, size_t proto_len, + const char *host, size_t host_len, + unsigned port TSRMLS_DC) { - char *proto_ptr, *host, *path, *PTR, *URI = ecalloc(1, HTTP_URI_MAXLEN + 1); - zval *zhost; +#if defined(PHP_WIN32) || defined(HAVE_NETDB_H) + struct servent *se; +#endif + php_url *purl, furl = {NULL}; + size_t full_len = 0; + zval *zhost = NULL; + char *scheme = NULL, *URL = ecalloc(1, HTTP_URI_MAXLEN + 1); + + if ((!url || !url_len) && ( + (!(url = SG(request_info).request_uri)) || + (!(url_len = strlen(SG(request_info).request_uri))))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Cannot build an absolute URI if supplied URL and REQUEST_URI is empty"); + return NULL; + } - if (!url || !strlen(url)) { - if (!SG(request_info).request_uri) { - return NULL; + if (!(purl = php_url_parse((char *) url))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse supplied URL"); + return NULL; + } + + furl.user = purl->user; + furl.pass = purl->pass; + furl.path = purl->path; + furl.query = purl->query; + furl.fragment = purl->fragment; + + if (proto && proto_len) { + furl.scheme = scheme = estrdup(proto); + } else if (purl->scheme) { + furl.scheme = purl->scheme; +#if defined(PHP_WIN32) || defined(HAVE_NETDB_H) + } else if (port && (se = getservbyport(port, "tcp"))) { + furl.scheme = (scheme = estrdup(se->s_name)); +#endif + } else { + furl.scheme = "http"; + } + + if (port) { + furl.port = port; + } else if (purl->port) { + furl.port = purl->port; + } else if (strncmp(furl.scheme, "http", 4)) { +#if defined(PHP_WIN32) || defined(HAVE_NETDB_H) + if (se = getservbyname(furl.scheme, "tcp")) { + furl.port = se->s_port; } - url = SG(request_info).request_uri; +#endif + } else { + furl.port = (furl.scheme[4] == 's') ? 443 : 80; } - /* Mess around with already absolute URIs */ - else if (proto_ptr = strstr(url, "://")) { - if (!proto || !strncmp(url, proto, strlen(proto))) { - strncpy(URI, url, HTTP_URI_MAXLEN); - return URI; - } else { - snprintf(URI, HTTP_URI_MAXLEN, "%s%s", proto, proto_ptr + 3); - return URI; + + if (host) { + furl.host = (char *) host; + } else if (purl->host) { + furl.host = purl->host; + } else if ( (zhost = http_get_server_var("HTTP_HOST")) || + (zhost = http_get_server_var("SERVER_NAME"))) { + furl.host = Z_STRVAL_P(zhost); + } else { + furl.host = "localhost"; + } + +#define HTTP_URI_STRLCATS(URL, full_len, add_string) HTTP_URI_STRLCAT(URL, full_len, add_string, sizeof(add_string)-1) +#define HTTP_URI_STRLCATL(URL, full_len, add_string) HTTP_URI_STRLCAT(URL, full_len, add_string, strlen(add_string)) +#define HTTP_URI_STRLCAT(URL, full_len, add_string, add_len) \ + if ((full_len += add_len) > HTTP_URI_MAXLEN) { \ + php_error_docref(NULL TSRMLS_CC, E_NOTICE, \ + "Absolute URI would have exceeded max URI length (%d bytes) - " \ + "tried to add %d bytes ('%s')", \ + HTTP_URI_MAXLEN, add_len, add_string); \ + if (scheme) { \ + efree(scheme); \ + } \ + php_url_free(purl); \ + return URL; \ + } else { \ + strcat(URL, add_string); \ + } + + HTTP_URI_STRLCATL(URL, full_len, furl.scheme); + HTTP_URI_STRLCATS(URL, full_len, "://"); + + if (furl.user) { + HTTP_URI_STRLCATL(URL, full_len, furl.user); + if (furl.pass) { + HTTP_URI_STRLCATS(URL, full_len, ":"); + HTTP_URI_STRLCATL(URL, full_len, furl.pass); } + HTTP_URI_STRLCATS(URL, full_len, "@"); } - /* protocol defaults to http */ - if (!proto || !strlen(proto)) { - proto = "http"; + HTTP_URI_STRLCATL(URL, full_len, furl.host); + + if ( (!strcmp(furl.scheme, "http") && (furl.port != 80)) || + (!strcmp(furl.scheme, "https") && (furl.port != 443))) { + char port_string[8] = {0}; + snprintf(port_string, 7, ":%u", furl.port); + HTTP_URI_STRLCATL(URL, full_len, port_string); } - /* get host name */ - if ( (zhost = http_get_server_var("HTTP_HOST")) || - (zhost = http_get_server_var("SERVER_NAME"))) { - host = Z_STRVAL_P(zhost); + if (furl.path) { + if (furl.path[0] != '/') { + HTTP_URI_STRLCATS(URL, full_len, "/"); + } + HTTP_URI_STRLCATL(URL, full_len, furl.path); } else { - host = "localhost"; + HTTP_URI_STRLCATS(URL, full_len, "/"); } + if (furl.query) { + HTTP_URI_STRLCATS(URL, full_len, "?"); + HTTP_URI_STRLCATL(URL, full_len, furl.query); + } - /* glue together */ - if (url[0] == '/') { - snprintf(URI, HTTP_URI_MAXLEN, "%s://%s%s", proto, host, url); - } else if (SG(request_info).request_uri) { - path = estrdup(SG(request_info).request_uri); - php_dirname(path, strlen(path)); - snprintf(URI, HTTP_URI_MAXLEN, "%s://%s%s/%s", proto, host, path, url); - efree(path); - } else { - snprintf(URI, HTTP_URI_MAXLEN, "%s://%s/%s", proto, host, url); + if (furl.fragment) { + HTTP_URI_STRLCATS(URL, full_len, "#"); + HTTP_URI_STRLCATL(URL, full_len, furl.fragment); } - /* strip everything after a new line */ - if ((PTR = strchr(URI, '\r')) || (PTR = strchr(URI, '\n'))) { - PTR = 0; + if (scheme) { + efree(scheme); } + php_url_free(purl); - return URI; + return URL; } /* }}} */ -/* {{{ char *http_negotiate_q(char *, zval *, char *, hash_entry_type) */ -PHP_HTTP_API char *_http_negotiate_q(const char *entry, const zval *supported, - const char *def TSRMLS_DC) +/* {{{ char *http_negotiate_q(char *, HashTable *, char *) */ +PHP_HTTP_API char *_http_negotiate_q(const char *entry, const HashTable *supported, const char *def TSRMLS_DC) { - zval *zaccept, *zarray, *zdelim, **zentry, *zentries, **zsupp; - char *q_ptr, *result; - int i, c; + zval *zaccept, zdelim, zarray, zentries, **zentry, **zsupp; + char *q_ptr = NULL, *key = NULL; + int i = 0, idx = 0; double qual; HTTP_GSC(zaccept, entry, estrdup(def)); - MAKE_STD_ZVAL(zarray); - array_init(zarray); - - MAKE_STD_ZVAL(zdelim); - ZVAL_STRING(zdelim, ",", 0); - php_explode(zdelim, zaccept, zarray, -1); - efree(zdelim); + array_init(&zarray); + array_init(&zentries); - MAKE_STD_ZVAL(zentries); - array_init(zentries); + Z_STRVAL(zdelim) = ","; + Z_STRLEN(zdelim) = 1; - c = zend_hash_num_elements(Z_ARRVAL_P(zarray)); - for (i = 0; i < c; i++, zend_hash_move_forward(Z_ARRVAL_P(zarray))) { + php_explode(&zdelim, zaccept, &zarray, -1); - if (SUCCESS != zend_hash_get_current_data( - Z_ARRVAL_P(zarray), (void **) &zentry)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Cannot parse %s header: %s", entry, Z_STRVAL_P(zaccept)); - break; - } - - /* check for qualifier */ - if (NULL != (q_ptr = strrchr(Z_STRVAL_PP(zentry), ';'))) { + FOREACH_HASH_VAL(Z_ARRVAL(zarray), zentry) { + if (q_ptr = strrchr(Z_STRVAL_PP(zentry), ';')) { qual = strtod(q_ptr + 3, NULL); + *q_ptr = 0; + q_ptr = NULL; } else { - qual = 1000.0 - i; + qual = 1000.0 - i++; } - - /* walk through the supported array */ - FOREACH_VAL(supported, zsupp) { + FOREACH_HASH_VAL((HashTable *)supported, zsupp) { if (!strcasecmp(Z_STRVAL_PP(zsupp), Z_STRVAL_PP(zentry))) { - add_assoc_double(zentries, Z_STRVAL_PP(zsupp), qual); + add_assoc_double(&zentries, Z_STRVAL_PP(zsupp), qual); break; } } } + zval_dtor(&zarray); - zval_dtor(zarray); - efree(zarray); - - zend_hash_internal_pointer_reset(Z_ARRVAL_P(zentries)); + zend_hash_sort(Z_ARRVAL(zentries), zend_qsort, http_sort_q, 0 TSRMLS_CC); - if ( (SUCCESS != zend_hash_sort(Z_ARRVAL_P(zentries), zend_qsort, - http_sort_q, 0 TSRMLS_CC)) || - (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key( - Z_ARRVAL_P(zentries), &result, 0, 1))) { - result = estrdup(def); + FOREACH_HASH_KEY(Z_ARRVAL(zentries), key, idx) { + if (key) { + key = estrdup(key); + zval_dtor(&zentries); + return key; + } } + zval_dtor(&zentries); - zval_dtor(zentries); - efree(zentries); - - return result; + return estrdup(def); } /* }}} */ /* {{{ http_range_status http_get_request_ranges(HashTable *ranges, size_t) */ -PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, - const size_t length TSRMLS_DC) +PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_t length TSRMLS_DC) { zval *zrange; char *range, c; @@ -1070,7 +1159,7 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, { /* "0-12345" */ case -10: - if ((length - end) < 1) { + if (length <= end) { return RANGE_ERR; } begin = 0; @@ -1078,11 +1167,11 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, /* "-12345" */ case -1: - if ((length - end) < 1) { + if (length <= end) { return RANGE_ERR; } begin = length - end; - end = length; + end = length - 1; break; /* "12345-(xxx)" */ @@ -1091,7 +1180,7 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, { /* "12345-" */ case -1: - if ((length - begin) < 1) { + if (length <= begin) { return RANGE_ERR; } end = length - 1; @@ -1099,9 +1188,9 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, /* "12345-67890" */ default: - if ( ((length - begin) < 1) || - ((length - end) < 1) || - ((begin - end) >= 0)) { + if ( (length <= begin) || + (length <= end) || + (end < begin)) { return RANGE_ERR; } break; @@ -1134,7 +1223,7 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, /* }}} */ /* {{{ STATUS http_send_ranges(HashTable *, void *, size_t, http_send_mode) */ -PHP_HTTP_API STATUS _http_send_ranges(HashTable *ranges, const void *data, const size_t size, const http_send_mode mode TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_ranges(HashTable *ranges, const void *data, size_t size, http_send_mode mode TSRMLS_DC) { long **begin, **end; zval **zrange; @@ -1209,10 +1298,11 @@ PHP_HTTP_API STATUS _http_send_ranges(HashTable *ranges, const void *data, const /* }}} */ /* {{{ STATUS http_send(void *, size_t, http_send_mode) */ -PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size, - const http_send_mode data_mode TSRMLS_DC) +PHP_HTTP_API STATUS _http_send(const void *data_ptr, size_t data_size, http_send_mode data_mode TSRMLS_DC) { - int is_range_request = http_is_range_request(); + HashTable ranges; + http_range_status range_status; + int cache_etag = 0; if (!data_ptr) { return FAILURE; @@ -1221,80 +1311,64 @@ PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size, return SUCCESS; } - /* etag handling */ - if (HTTP_G(etag_started)) { - char *etag; + /* stop on-the-fly etag generation */ + if (cache_etag = HTTP_G(etag_started)) { /* interrupt */ HTTP_G(etag_started) = 0; /* never ever use the output to compute the ETag if http_send() is used */ - php_end_ob_buffer(0, 0 TSRMLS_CC); - if (!(etag = http_etag(data_ptr, data_size, data_mode))) { - return FAILURE; - } - - /* send 304 Not Modified if etag matches */ - if ((!is_range_request) && http_etag_match("HTTP_IF_NONE_MATCH", etag)) { - efree(etag); - return http_send_status(304); - } - - http_send_etag(etag, 32); - efree(etag); + php_end_ob_buffers(0 TSRMLS_CC); } - /* send 304 Not Modified if last-modified matches*/ - if ((!is_range_request) && http_modified_match("HTTP_IF_MODIFIED_SINCE", HTTP_G(lmod))) { - return http_send_status(304); - } + zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0); + range_status = http_get_request_ranges(&ranges, data_size); - if (is_range_request) { + if (range_status == RANGE_ERR) { + zend_hash_destroy(&ranges); + http_send_status(416); + return FAILURE; + } - /* only send ranges if entity hasn't changed */ - if ( - ((!zend_hash_exists(HTTP_SERVER_VARS, "HTTP_IF_MATCH", 13)) || - http_etag_match("HTTP_IF_MATCH", HTTP_G(etag))) - && - ((!zend_hash_exists(HTTP_SERVER_VARS, "HTTP_IF_UNMODIFIED_SINCE", 25)) || - http_modified_match("HTTP_IF_UNMODIFIED_SINCE", HTTP_G(lmod))) - ) { + /* Range Request - only send ranges if entity hasn't changed */ + if ( range_status == RANGE_OK && + http_etag_match_ex("HTTP_IF_MATCH", HTTP_G(etag), 0) && + http_modified_match_ex("HTTP_IF_UNMODIFIED_SINCE", HTTP_G(lmod), 0)) { + STATUS result = http_send_ranges(&ranges, data_ptr, data_size, data_mode); + zend_hash_destroy(&ranges); + return result; + } - STATUS result = FAILURE; - HashTable ranges; - zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_destroy(&ranges); - switch (http_get_request_ranges(&ranges, data_size)) - { - case RANGE_NO: - zend_hash_destroy(&ranges); - /* go ahead and send all */ - break; + /* send 304 Not Modified if etag matches */ + if (cache_etag) { + char *etag = NULL; + int etag_match = 0; - case RANGE_OK: - result = http_send_ranges(&ranges, data_ptr, data_size, data_mode); - zend_hash_destroy(&ranges); - return result; - break; + if (!(etag = http_etag(data_ptr, data_size, data_mode))) { + return FAILURE; + } - case RANGE_ERR: - zend_hash_destroy(&ranges); - http_send_status(416); - return FAILURE; - break; + http_send_etag(etag, 32); + etag_match = http_etag_match("HTTP_IF_NONE_MATCH", etag); + efree(etag); - default: - return FAILURE; - break; - } + if (etag_match) { + return http_send_status(304); } } - /* send all */ + + /* send 304 Not Modified if last modified matches */ + if (http_modified_match("HTTP_IF_MODIFIED_SINCE", HTTP_G(lmod))) { + return http_send_status(304); + } + + /* send full entity */ return http_send_chunk(data_ptr, 0, data_size, data_mode); } /* }}} */ /* {{{ STATUS http_send_stream(php_stream *) */ -PHP_HTTP_API STATUS _http_send_stream_ex(php_stream *file, - zend_bool close_stream TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_stream_ex(php_stream *file, zend_bool close_stream TSRMLS_DC) { STATUS status; @@ -1312,9 +1386,9 @@ PHP_HTTP_API STATUS _http_send_stream_ex(php_stream *file, } /* }}} */ -/* {{{ proto STATUS http_chunked_decode(char *, size_t, char **, size_t *) */ -PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded, - const size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC) +/* {{{ STATUS http_chunked_decode(char *, size_t, char **, size_t *) */ +PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded, size_t encoded_len, + char **decoded, size_t *decoded_len TSRMLS_DC) { const char *e_ptr; char *d_ptr; @@ -1376,37 +1450,50 @@ PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded, } /* }}} */ -/* {{{ proto STATUS http_split_response_ex(char *, size_t, zval *, zval *) */ -PHP_HTTP_API STATUS _http_split_response_ex(char *response, - size_t response_len, zval *zheaders, zval *zbody TSRMLS_DC) +/* {{{ STATUS http_split_response(zval *, zval *, zval *) */ +PHP_HTTP_API STATUS _http_split_response(zval *response, zval *headers, zval *body TSRMLS_DC) +{ + char *b = NULL; + size_t l = 0; + STATUS status = http_split_response_ex(Z_STRVAL_P(response), Z_STRLEN_P(response), Z_ARRVAL_P(headers), &b, &l); + ZVAL_STRINGL(body, b, l, 0); + return status; +} +/* }}} */ + +/* {{{ STATUS http_split_response(char *, size_t, HashTable *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_split_response_ex(char *response, size_t response_len, + HashTable *headers, char **body, size_t *body_len TSRMLS_DC) { - char *body = NULL; - char *header = response; + char *header = response, *real_body = NULL; while (0 < (response_len - (response - header + 4))) { if ( (*response++ == '\r') && (*response++ == '\n') && (*response++ == '\r') && (*response++ == '\n')) { - body = response; + real_body = response; break; } } - if (body && (response_len - (body - header))) { - ZVAL_STRINGL(zbody, body, response_len - (body - header) - 1, 1); - } else { - Z_TYPE_P(zbody) = IS_NULL; + if (real_body && (*body_len = (response_len - (real_body - header)))) { + *body = ecalloc(1, *body_len + 1); + memcpy(*body, real_body, *body_len); } - return http_parse_headers(header, body ? body - header : response_len, zheaders); + return http_parse_headers_ex(header, real_body ? response_len - *body_len : response_len, headers, 1); } /* }}} */ -/* {{{ STATUS http_parse_headers(char *, long, zval *) */ -PHP_HTTP_API STATUS _http_parse_headers(char *header, int header_len, zval *array TSRMLS_DC) +/* {{{ STATUS http_parse_headers(char *, size_t, HashTable *, zend_bool) */ +PHP_HTTP_API STATUS _http_parse_headers_ex(char *header, size_t header_len, + HashTable *headers, zend_bool prettify TSRMLS_DC) { char *colon = NULL, *line = NULL, *begin = header; + zval array; + + Z_ARRVAL(array) = headers; if (header_len < 2) { return FAILURE; @@ -1415,7 +1502,7 @@ PHP_HTTP_API STATUS _http_parse_headers(char *header, int header_len, zval *arra /* status code */ if (!strncmp(header, "HTTP/1.", 7)) { char *end = strstr(header, HTTP_CRLF); - add_assoc_stringl(array, "Status", + add_assoc_stringl(&array, "Status", header + sizeof("HTTP/1.x ") - 1, end - (header + sizeof("HTTP/1.x ") - 1), 1); header = end + 2; @@ -1436,6 +1523,11 @@ PHP_HTTP_API STATUS _http_parse_headers(char *header, int header_len, zval *arra /* skip empty key */ if (header != colon) { char *key = estrndup(header, colon - header); + + if (prettify) { + key = pretty_key(key, colon - header, 1, 1); + } + value_len += line - colon - 1; /* skip leading ws */ @@ -1445,9 +1537,9 @@ PHP_HTTP_API STATUS _http_parse_headers(char *header, int header_len, zval *arra if (value_len < 1) { /* hm, empty header? */ - add_assoc_stringl(array, key, "", 0, 1); + add_assoc_stringl(&array, key, "", 0, 1); } else { - add_assoc_stringl(array, key, colon, value_len, 1); + add_assoc_stringl(&array, key, colon, value_len, 1); } efree(key); } @@ -1469,58 +1561,56 @@ PHP_HTTP_API STATUS _http_parse_headers(char *header, int header_len, zval *arra } /* }}} */ -/* {{{ void http_get_request_headers(zval *) */ -PHP_HTTP_API void _http_get_request_headers(zval *array TSRMLS_DC) +/* {{{ void http_get_request_headers_ex(HashTable *, zend_bool) */ +PHP_HTTP_API void _http_get_request_headers_ex(HashTable *headers, zend_bool prettify TSRMLS_DC) { - char *key = NULL; - long idx = 0; - - FOREACH_HASH_KEY(HTTP_SERVER_VARS, key, idx) { - if (key && !strncmp(key, "HTTP_", 5)) { - zval **header; - zend_hash_get_current_data(HTTP_SERVER_VARS, (void **) &header); - add_assoc_stringl(array, pretty_key(key + 5, strlen(key) - 5, 1, 1), Z_STRVAL_PP(header), Z_STRLEN_PP(header), 1); - } - } + char *key = NULL; + long idx = 0; + zval array; + + Z_ARRVAL(array) = headers; + + FOREACH_HASH_KEY(HTTP_SERVER_VARS, key, idx) { + if (key && !strncmp(key, "HTTP_", 5)) { + zval **header; + + key += 5; + if (prettify) { + key = pretty_key(key, strlen(key), 1, 1); + } + + zend_hash_get_current_data(HTTP_SERVER_VARS, (void **) &header); + add_assoc_stringl(&array, key, Z_STRVAL_PP(header), Z_STRLEN_PP(header), 1); + key = NULL; + } + } } /* }}} */ -/* {{{ STATUS http_urlencode_hash_ex(HashTable *, int, char **, size_t *) */ -PHP_HTTP_API STATUS _http_urlencode_hash_ex(HashTable *hash, int override_argsep, +/* {{{ STATUS http_urlencode_hash_ex(HashTable *, zend_bool, char *, size_t, char **, size_t *) */ +PHP_HTTP_API STATUS _http_urlencode_hash_ex(HashTable *hash, zend_bool override_argsep, char *pre_encoded_data, size_t pre_encoded_len, char **encoded_data, size_t *encoded_len TSRMLS_DC) { - smart_str qstr = {0}; + char *arg_sep; + phpstr *qstr = phpstr_new(); - if (override_argsep) { - HTTP_URL_ARGSEP_OVERRIDE; + if (override_argsep || !strlen(arg_sep = INI_STR("arg_separator.output"))) { + arg_sep = HTTP_URL_ARGSEP_DEFAULT; } if (pre_encoded_len && pre_encoded_data) { - smart_str_appendl(&qstr, pre_encoded_data, pre_encoded_len); + phpstr_append(qstr, pre_encoded_data, pre_encoded_len); } - if (SUCCESS != php_url_encode_hash_ex(hash, &qstr, NULL, 0, NULL, 0, NULL, 0, NULL TSRMLS_CC)) { + if (SUCCESS != http_urlencode_hash_implementation(hash, qstr, arg_sep)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't encode query data"); - if (qstr.c) { - efree(qstr.c); - } - if (override_argsep) { - HTTP_URL_ARGSEP_RESTORE; - } + phpstr_free(qstr); return FAILURE; } - if (override_argsep) { - HTTP_URL_ARGSEP_RESTORE; - } - - smart_str_0(&qstr); - - *encoded_data = qstr.c; - if (encoded_len) { - *encoded_len = qstr.len; - } + phpstr_data(qstr, encoded_data, encoded_len); + phpstr_free(qstr); return SUCCESS; } @@ -1570,21 +1660,21 @@ PHP_HTTP_API STATUS _http_auth_credentials(char **user, char **pass TSRMLS_DC) } /* }}} */ -#ifndef ZEND_ENGINE_2 -/* {{{ php_url_encode_hash - Author: Sarah Golemon */ -PHP_HTTP_API STATUS php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, +/* {{{ http_urlencode_hash_implementation + Original Author: Sara Golemon */ +PHP_HTTP_API STATUS _http_urlencode_hash_implementation_ex( + HashTable *ht, phpstr *formstr, char *arg_sep, const char *num_prefix, int num_prefix_len, const char *key_prefix, int key_prefix_len, const char *key_suffix, int key_suffix_len, zval *type TSRMLS_DC) { - char *arg_sep = NULL, *key = NULL, *ekey, *newprefix, *p; + char *key = NULL, *ekey, *newprefix, *p; int arg_sep_len, key_len, ekey_len, key_type, newprefix_len; ulong idx; zval **zdata = NULL, *copyzval; - if (!ht) { + if (!ht || !formstr) { return FAILURE; } @@ -1593,7 +1683,6 @@ PHP_HTTP_API STATUS php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, return SUCCESS; } - arg_sep = INI_STR("arg_separator.output"); if (!arg_sep || !strlen(arg_sep)) { arg_sep = HTTP_URL_ARGSEP_DEFAULT; } @@ -1677,33 +1766,34 @@ PHP_HTTP_API STATUS php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, *p = '\0'; } ht->nApplyCount++; - php_url_encode_hash_ex(HASH_OF(*zdata), formstr, NULL, 0, newprefix, newprefix_len, "]", 1, (Z_TYPE_PP(zdata) == IS_OBJECT ? *zdata : NULL) TSRMLS_CC); + http_urlencode_hash_implementation_ex(HASH_OF(*zdata), formstr, arg_sep, + NULL, 0, newprefix, newprefix_len, "]", 1, (Z_TYPE_PP(zdata) == IS_OBJECT ? *zdata : NULL)); ht->nApplyCount--; efree(newprefix); } else if (Z_TYPE_PP(zdata) == IS_NULL || Z_TYPE_PP(zdata) == IS_RESOURCE) { /* Skip these types */ continue; } else { - if (formstr->len) { - smart_str_appendl(formstr, arg_sep, arg_sep_len); + if (formstr->used) { + phpstr_append(formstr, arg_sep, arg_sep_len); } /* Simple key=value */ - smart_str_appendl(formstr, key_prefix, key_prefix_len); + phpstr_append(formstr, key_prefix, key_prefix_len); if (key_type == HASH_KEY_IS_STRING) { ekey = php_url_encode(key, key_len, &ekey_len); - smart_str_appendl(formstr, ekey, ekey_len); + phpstr_append(formstr, ekey, ekey_len); efree(ekey); } else { /* Numeric key */ if (num_prefix) { - smart_str_appendl(formstr, num_prefix, num_prefix_len); + phpstr_append(formstr, num_prefix, num_prefix_len); } ekey_len = spprintf(&ekey, 12, "%ld", idx); - smart_str_appendl(formstr, ekey, ekey_len); + phpstr_append(formstr, ekey, ekey_len); efree(ekey); } - smart_str_appendl(formstr, key_suffix, key_suffix_len); - smart_str_appendl(formstr, "=", 1); + phpstr_append(formstr, key_suffix, key_suffix_len); + phpstr_appends(formstr, "="); switch (Z_TYPE_PP(zdata)) { case IS_STRING: ekey = php_url_encode(Z_STRVAL_PP(zdata), Z_STRLEN_PP(zdata), &ekey_len); @@ -1724,7 +1814,7 @@ PHP_HTTP_API STATUS php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, ekey = php_url_encode(Z_STRVAL_P(copyzval), Z_STRLEN_P(copyzval), &ekey_len); zval_ptr_dtor(©zval); } - smart_str_appendl(formstr, ekey, ekey_len); + phpstr_append(formstr, ekey, ekey_len); efree(ekey); } } @@ -1732,7 +1822,6 @@ PHP_HTTP_API STATUS php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, return SUCCESS; } /* }}} */ -#endif /* !ZEND_ENDGINE_2 */ /* }}} public API */ @@ -1743,4 +1832,5 @@ PHP_HTTP_API STATUS php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 - */ \ No newline at end of file + */ +