X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;ds=inline;f=http_api.c;h=d48770c0a43267352dc2977a7126ee3ec1eecf16;hb=fc73263920c3b5af2f0266d51cd80c0a50017807;hp=317e902ccac993dfc5dd6c9f338ced5255a08a73;hpb=f6e558f7886fff36d239ae2fae69d955ac6925ee;p=m6w6%2Fext-http diff --git a/http_api.c b/http_api.c index 317e902..d48770c 100644 --- a/http_api.c +++ b/http_api.c @@ -15,20 +15,23 @@ /* $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 "spprintf.h" #include "ext/standard/md5.h" #include "ext/standard/url.h" #include "ext/standard/base64.h" @@ -154,18 +157,20 @@ static int http_sort_q(const void *a, const void *b TSRMLS_DC) static STATUS _http_send_chunk(const void *data, const size_t begin, const size_t end, const 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) { @@ -177,6 +182,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 */ @@ -190,15 +198,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; @@ -585,6 +617,15 @@ PHP_HTTP_API zval *_http_get_server_var(const char *key TSRMLS_DC) } /* }}} */ +/* {{{ int http_got_server_var(char *) */ +PHP_HTTP_API int _http_got_server_var(const char *key TSRMLS_DC) +{ + zval *dummy; + HTTP_GSC(dummy, key, 0); + return 1; +} +/* }}} */ + /* {{{ void http_ob_etaghandler(char *, uint, char **, uint *, int) */ PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC) @@ -607,6 +648,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); } @@ -662,14 +704,15 @@ 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) +/* {{{ int http_modified_match(char *, time_t) */ +PHP_HTTP_API int _http_modified_match_ex(const char *entry, const time_t t, + const int enforce_presence TSRMLS_DC) { int 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, ';')) { @@ -682,13 +725,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) +PHP_HTTP_API int _http_etag_match_ex(const char *entry, const char *etag, + const int enforce_presence TSRMLS_DC) { zval *zetag; char *quoted_etag; STATUS result; - HTTP_GSC(zetag, entry, 0); + HTTP_GSC(zetag, entry, !enforce_presence); if (NULL != strchr(Z_STRVAL_P(zetag), '*')) { return 1; @@ -887,8 +931,10 @@ PHP_HTTP_API char *_http_absolute_uri_ex( const char *host, size_t host_len, unsigned port TSRMLS_DC) { - php_url *purl, furl = {NULL}; +#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); @@ -909,14 +955,17 @@ PHP_HTTP_API char *_http_absolute_uri_ex( furl.user = purl->user; furl.pass = purl->pass; furl.path = purl->path; + furl.query = purl->query; furl.fragment = purl->fragment; - if (proto) { + if (proto && proto_len) { furl.scheme = scheme = estrdup(proto); } else if (purl->scheme) { furl.scheme = purl->scheme; - } else if (port && (se = getservbyport(htons(port), "tcp"))) { +#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"; } @@ -925,10 +974,14 @@ PHP_HTTP_API char *_http_absolute_uri_ex( furl.port = port; } else if (purl->port) { furl.port = purl->port; - } else if (strncmp(furl.scheme, "http", 4) && (se = getservbyname(furl.scheme, "tcp"))) { - furl.port = ntohs(se->s_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; + } +#endif } else { - furl.port = furl.scheme[5] ? 443 : 80; + furl.port = (furl.scheme[4] == 's') ? 443 : 80; } if (host) { @@ -973,23 +1026,30 @@ PHP_HTTP_API char *_http_absolute_uri_ex( HTTP_URI_STRLCATL(URL, full_len, furl.host); - if ( (strcmp(furl.scheme, "http") && (furl.port != 80)) || - (strcmp(furl.scheme, "https") && (furl.port != 443))) { + 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); } if (furl.path) { - HTTP_URI_STRLCATL(URL, full_len, furl.path); - if (furl.query) { - HTTP_URI_STRLCATS(URL, full_len, "?"); - HTTP_URI_STRLCATL(URL, full_len, furl.query); - } - if (furl.fragment) { - HTTP_URI_STRLCATS(URL, full_len, "#"); - HTTP_URI_STRLCATL(URL, full_len, furl.fragment); + if (furl.path[0] != '/') { + HTTP_URI_STRLCATS(URL, full_len, "/"); } + HTTP_URI_STRLCATL(URL, full_len, furl.path); + } else { + HTTP_URI_STRLCATS(URL, full_len, "/"); + } + + if (furl.query) { + HTTP_URI_STRLCATS(URL, full_len, "?"); + HTTP_URI_STRLCATL(URL, full_len, furl.query); + } + + if (furl.fragment) { + HTTP_URI_STRLCATS(URL, full_len, "#"); + HTTP_URI_STRLCATL(URL, full_len, furl.fragment); } if (scheme) { @@ -1133,7 +1193,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; @@ -1141,11 +1201,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)" */ @@ -1154,7 +1214,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; @@ -1162,9 +1222,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; @@ -1275,7 +1335,8 @@ PHP_HTTP_API STATUS _http_send_ranges(HashTable *ranges, const void *data, const PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size, const http_send_mode data_mode TSRMLS_DC) { - int is_range_request = http_is_range_request(); + HashTable ranges; + http_range_status range_status; if (!data_ptr) { return FAILURE; @@ -1284,19 +1345,34 @@ PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size, return SUCCESS; } + zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0); + range_status = http_get_request_ranges(&ranges, data_size); + + if (range_status != RANGE_OK) { + zend_hash_destroy(&ranges); + } + + if (range_status == RANGE_ERR) { + http_send_status(416); + return FAILURE; + } + /* etag handling */ + if (HTTP_G(etag_started)) { char *etag; + /* 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)) { + if ((range_status == RANGE_NO) && http_etag_match("HTTP_IF_NONE_MATCH", etag)) { efree(etag); return http_send_status(304); } @@ -1305,53 +1381,26 @@ PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size, efree(etag); } - /* 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); - } - - if (is_range_request) { - - /* 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))) - ) { - - STATUS result = FAILURE; - HashTable ranges; - zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0); - - switch (http_get_request_ranges(&ranges, data_size)) - { - case RANGE_NO: - zend_hash_destroy(&ranges); - /* go ahead and send all */ - break; + switch (range_status) { + /* breaks intentionally left out */ - case RANGE_OK: - result = http_send_ranges(&ranges, data_ptr, data_size, data_mode); - zend_hash_destroy(&ranges); - return result; - break; - - case RANGE_ERR: - zend_hash_destroy(&ranges); - http_send_status(416); - return FAILURE; - break; - - default: - return FAILURE; - break; + case RANGE_OK: + /* only send ranges if entity hasn't changed */ + if ( 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; + } else { + zend_hash_destroy(&ranges); } - } + + case RANGE_NO: + if (http_modified_match("HTTP_IF_MODIFIED_SINCE", HTTP_G(lmod))) { + return http_send_status(304); + } + return http_send_chunk(data_ptr, 0, data_size, data_mode); } - /* send all */ - return http_send_chunk(data_ptr, 0, data_size, data_mode); } /* }}} */ @@ -1375,7 +1424,7 @@ PHP_HTTP_API STATUS _http_send_stream_ex(php_stream *file, } /* }}} */ -/* {{{ proto STATUS http_chunked_decode(char *, size_t, char **, size_t *) */ +/* {{{ 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) { @@ -1439,33 +1488,45 @@ PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded, } /* }}} */ -/* {{{ proto STATUS http_split_response_ex(char *, size_t, zval *, zval *) */ +/* {{{ 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 *header = response; - *body = NULL; + 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 && (*body_len = response_len - (*body - header))) { - *body = estrndup(*body, *body_len - 1); + 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, headers); + 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, HashTable *headers TSRMLS_DC) +PHP_HTTP_API STATUS _http_parse_headers_ex(char *header, int header_len, + HashTable *headers, zend_bool prettify TSRMLS_DC) { char *colon = NULL, *line = NULL, *begin = header; zval array; @@ -1500,6 +1561,11 @@ PHP_HTTP_API STATUS _http_parse_headers(char *header, int header_len, HashTable /* 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 */ @@ -1533,17 +1599,26 @@ PHP_HTTP_API STATUS _http_parse_headers(char *header, int header_len, HashTable } /* }}} */ -/* {{{ 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; + zval array; + + Z_ARRVAL(array) = headers; FOREACH_HASH_KEY(HTTP_SERVER_VARS, key, idx) { if (key && !strncmp(key, "HTTP_", 5)) { zval **header; + + if (prettify) { + key = pretty_key(key + 5, strlen(key) - 5, 1, 1); + } + 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); + add_assoc_stringl(&array, key, Z_STRVAL_PP(header), Z_STRLEN_PP(header), 1); + key = NULL; } } }