From 7dedd1935ab8400dfbd49fe63c83b74b0d0b68e6 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 8 Feb 2005 11:52:24 +0000 Subject: [PATCH] * fixed http_cache_last_modified(): if parameter was omitted, would have always sent 304 Not Modified * we do not need to suggest closing the connection in http_cache_etag() as we'll end the ob_buffer * made http_redirect() really RFC compliant by writing out "Redirecting to URI." * separate params argument in http_redirect() * remember sent etag and last-modified as globals * new HTTP_SERVER_VARS define * only try to match the etag in http_ob_etag_handler() if we have not been stopped so far * ignore spaces in range request of IE * free zranges where they've been initialized * caching handling in http_send() * update package.xml --- http.c | 31 +++++++--- http_api.c | 162 ++++++++++++++++++++++++++++++------------------- package.xml | 14 +++-- php_http.h | 3 + php_http_api.h | 6 +- 5 files changed, 140 insertions(+), 76 deletions(-) diff --git a/http.c b/http.c index 9a3ed65..e81deff 100644 --- a/http.c +++ b/http.c @@ -347,12 +347,12 @@ PHP_FUNCTION(http_cache_last_modified) /* 0 or omitted */ if (!last_modified) { - /* does the client have? */ + /* does the client have? (att: caching "forever") */ if (zlm = http_get_server_var("HTTP_IF_MODIFIED_SINCE")) { last_modified = send_modified = http_parse_date(Z_STRVAL_P(zlm)); - /* use current time */ + /* send current time */ } else { - last_modified = send_modified = t; + send_modified = t; } /* negative value is supposed to be expiration time */ } else if (last_modified < 0) { @@ -397,8 +397,7 @@ PHP_FUNCTION(http_cache_etag) RETURN_FALSE; } - /* send remaining data to nirvana */ - http_send_header("Connection: close"); + php_end_ob_buffers(0 TSRMLS_CC); http_send_header("Cache-Control: private, must-revalidate, max-age=0"); /* if no etag is given and we didn't already @@ -434,6 +433,8 @@ PHP_FUNCTION(http_cache_etag) * ("301 Moved Permanently") or a temporary ("302 Found") redirection * status code. * + * To be RFC compliant, "Redirecting to URI." will be displayed, + * if the client doesn't redirect immediatly. */ PHP_FUNCTION(http_redirect) { @@ -441,9 +442,9 @@ PHP_FUNCTION(http_redirect) zend_bool session = 0, permanent = 0; zval *params = NULL; smart_str qstr = {0}; - char *url, *URI, LOC[HTTP_URI_MAXLEN + 9]; + char *url, *URI, LOC[HTTP_URI_MAXLEN + 9], RED[HTTP_URI_MAXLEN * 2 + 34]; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!bb", &url, &url_len, ¶ms, &session, &permanent) != SUCCESS) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bb", &url, &url_len, ¶ms, &session, &permanent) != SUCCESS) { RETURN_FALSE; } @@ -473,13 +474,19 @@ PHP_FUNCTION(http_redirect) URI = http_absolute_uri(url, NULL); if (qstr.c) { snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s?%s", URI, qstr.c); + sprintf(RED, "Redirecting to %s?%s.\n", URI, qstr.c, URI, qstr.c); efree(qstr.c); } else { snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s", URI); + sprintf(RED, "Redirecting to %s.\n", URI, URI); } efree(URI); - - RETVAL_BOOL((SUCCESS == http_send_header(LOC)) && (SUCCESS == http_send_status((permanent ? 301 : 302)))); + + if ((SUCCESS == http_send_header(LOC)) && (SUCCESS == http_send_status((permanent ? 301 : 302)))) { + php_body_write(RED, strlen(RED) TSRMLS_CC); + RETURN_TRUE; + } + RETURN_FALSE; } /* }}} */ @@ -933,6 +940,8 @@ static void php_http_init_globals(zend_http_globals *http_globals) { http_globals->etag_started = 0; http_globals->ctype = NULL; + http_globals->etag = NULL; + http_globals->lmod = 0; #if defined(HAVE_CURL) && HAVE_CURL http_globals->curlbuf.body.data = NULL; http_globals->curlbuf.body.used = 0; @@ -963,6 +972,9 @@ PHP_RSHUTDOWN_FUNCTION(http) if (HTTP_G(ctype)) { efree(HTTP_G(ctype)); } + if (HTTP_G(etag)) { + efree(HTTP_G(etag)); + } #if defined(HAVE_CURL) && HAVE_CURL if (HTTP_G(curlbuf).body.data) { efree(HTTP_G(curlbuf).body.data); @@ -1000,3 +1012,4 @@ PHP_MINFO_FUNCTION(http) * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ + diff --git a/http_api.c b/http_api.c index 32dc183..210b930 100644 --- a/http_api.c +++ b/http_api.c @@ -560,7 +560,7 @@ static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *opti } } smart_str_0(&qstr); - + if (qstr.c) { curl_easy_setopt(ch, CURLOPT_COOKIE, qstr.c); } @@ -578,7 +578,7 @@ static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *opti char *header_key, header[1024] = {0}; zval **header_val; struct curl_slist *headers = NULL; - + zend_hash_internal_pointer_reset(Z_ARRVAL_P(zoption)); while (HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_type(Z_ARRVAL_P(zoption)))) { if (key_type == HASH_KEY_IS_STRING) { @@ -1023,7 +1023,7 @@ PHP_HTTP_API inline zval *_http_get_server_var(const char *key TSRMLS_DC) { zval **var; if (SUCCESS == zend_hash_find( - Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), + HTTP_SERVER_VARS, (char *) key, strlen(key) + 1, (void **) &var)) { return *var; } @@ -1046,12 +1046,16 @@ PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len, if (mode & PHP_OUTPUT_HANDLER_END) { PHP_MD5Final(digest, &HTTP_G(etag_md5)); - make_digest(etag, digest); - - if (http_etag_match("HTTP_IF_NONE_MATCH", etag)) { - http_send_status(304); - } else { - http_send_etag(etag, 32); + + /* just do that if desired */ + if (HTTP_G(etag_started)) { + make_digest(etag, digest); + + if (http_etag_match("HTTP_IF_NONE_MATCH", etag)) { + http_send_status(304); + } else { + http_send_etag(etag, 32); + } } } @@ -1074,6 +1078,12 @@ PHP_HTTP_API int _http_modified_match(const char *entry, const time_t t TSRMLS_D chr_ptr = 0; } retval = (t <= http_parse_date(modified)); +#if defined(PHP_DEBUG) + fprintf(stderr, + "\nComparing Last-Modified %s(%s)==%d:\n\t%d\n\t%d\n\n", + get_active_function_name(TSRMLS_C), entry, retval, t, + http_parse_date(modified)); +#endif efree(modified); return retval; } @@ -1100,19 +1110,28 @@ PHP_HTTP_API int _http_etag_match(const char *entry, const char *etag TSRMLS_DC) } else { result = (NULL != strstr(Z_STRVAL_P(zetag), quoted_etag)); } - +#if defined(PHP_DEBUG) + fprintf(stderr, + "\nComparing E-Tag %s(%s)==%d:\n\t<%s>\n\t<%s>\n\n", + get_active_function_name(TSRMLS_C), entry, result, + Z_STRVAL_P(zetag), quoted_etag); +#endif efree(quoted_etag); return result; } /* }}} */ /* {{{ STATUS http_send_last_modified(int) */ -PHP_HTTP_API STATUS _http_send_last_modified(const int t TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_last_modified(const time_t t TSRMLS_DC) { char modified[96] = "Last-Modified: ", *date; date = http_date(t); strcat(modified, date); efree(date); + + /* remember */ + HTTP_G(lmod) = t; + return http_send_header(modified); } /* }}} */ @@ -1130,6 +1149,13 @@ PHP_HTTP_API STATUS _http_send_etag(const char *etag, snprintf(etag_header, header_len, "ETag: \"%s\"", etag); ret = http_send_header(etag_header); efree(etag_header); + + /* remember */ + if (HTTP_G(etag)) { + efree(HTTP_G(etag)); + } + HTTP_G(etag) = estrdup(etag); + return ret; } /* }}} */ @@ -1319,6 +1345,10 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(zval *zranges, ptr = &end; break; + case ' ': + /* IE - ignore for now */ + break; + case 0: case ',': @@ -1394,7 +1424,7 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(zval *zranges, PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const size_t size, const http_send_mode mode TSRMLS_DC) { zval **zrange; - long b, e, **begin, **end; + long **begin, **end; char range_header[255], multi_header[68] = "Content-Type: multipart/byteranges; boundary=", bound[23], preface[1024]; int i, c; @@ -1407,20 +1437,12 @@ PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const siz zend_hash_index_find(Z_ARRVAL_PP(zrange), 0, (void **) &begin); zend_hash_index_find(Z_ARRVAL_PP(zrange), 1, (void **) &end); - /* so zranges can be freed */ - b = **begin; - e = **end; - - /* free zranges */ - zval_dtor(zranges); - efree(zranges); - /* send content range header */ - snprintf(range_header, 255, "Content-Range: bytes %d-%d/%d", b, e, size); + snprintf(range_header, 255, "Content-Range: bytes %d-%d/%d", **begin, **end, size); http_send_header(range_header); /* send requested chunk */ - return http_send_chunk(data, b, e + 1, mode); + return http_send_chunk(data, **begin, **end + 1, mode); } /* multi range */ @@ -1448,15 +1470,10 @@ PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const siz **begin, **end, size); php_body_write(preface, strlen(preface) TSRMLS_CC); http_send_chunk(data, **begin, **end + 1, mode); - } - /* free zranges */ - zval_dtor(zranges); - efree(zranges); - /* write boundary once more */ - php_body_write("\n", 1 TSRMLS_CC); + php_body_write("\r\n", 1 TSRMLS_CC); php_body_write(bound, strlen(bound) TSRMLS_CC); return SUCCESS; @@ -1467,16 +1484,19 @@ PHP_HTTP_API STATUS _http_send_ranges(zval *zranges, const void *data, const siz PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size, const http_send_mode data_mode TSRMLS_DC) { - zval *zranges = NULL; - + char *new_etag = NULL; + int is_range_request = http_is_range_request(); + if (!data_ptr) { return FAILURE; } - /* never ever use the output to compute the ETag if http_send() is used */ + /* etag handling */ if (HTTP_G(etag_started)) { - char *new_etag = (char *) emalloc(33); - + new_etag = (char *) emalloc(33); + + /* never ever use the output to compute the ETag if http_send() is used */ + HTTP_G(etag_started) = 0; php_end_ob_buffer(0, 0 TSRMLS_CC); if (NULL == http_etag(&new_etag, data_ptr, data_size, data_mode)) { efree(new_etag); @@ -1484,7 +1504,7 @@ PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size, } /* send 304 Not Modified if etag matches */ - if (http_etag_match("HTTP_IF_NONE_MATCH", new_etag)) { + if ((!is_range_request) && http_etag_match("HTTP_IF_NONE_MATCH", new_etag)) { efree(new_etag); return http_send_status(304); } @@ -1492,34 +1512,54 @@ PHP_HTTP_API STATUS _http_send(const void *data_ptr, const size_t data_size, http_send_etag(new_etag, 32); efree(new_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; + zval *zranges = NULL; + MAKE_STD_ZVAL(zranges); + array_init(zranges); + + switch (http_get_request_ranges(zranges, data_size)) + { + case RANGE_NO: + zval_dtor(zranges); + efree(zranges); + /* go ahead and send all */ + break; - if (http_is_range_request()) { - - MAKE_STD_ZVAL(zranges); - array_init(zranges); - - switch (http_get_request_ranges(zranges, data_size)) - { - case RANGE_NO: - zval_dtor(zranges); - efree(zranges); - /* go ahead and send all */ - break; - - case RANGE_OK: - return http_send_ranges(zranges, data_ptr, data_size, data_mode); - break; + case RANGE_OK: + result = http_send_ranges(zranges, data_ptr, data_size, data_mode); + zval_dtor(zranges); + efree(zranges); + return result; + break; - case RANGE_ERR: - zval_dtor(zranges); - efree(zranges); - http_send_status(416); - return FAILURE; - break; + case RANGE_ERR: + zval_dtor(zranges); + efree(zranges); + http_send_status(416); + return FAILURE; + break; - default: - return FAILURE; - break; + default: + return FAILURE; + break; + } } } /* send all */ @@ -1537,7 +1577,7 @@ PHP_HTTP_API STATUS _http_send_data(const zval *zdata TSRMLS_DC) return FAILURE; } - return http_send((void *) zdata, Z_STRLEN_P(zdata), SEND_DATA); + return http_send(zdata, Z_STRLEN_P(zdata), SEND_DATA); } /* }}} */ @@ -1548,7 +1588,7 @@ PHP_HTTP_API STATUS _http_send_stream(const php_stream *file TSRMLS_DC) return FAILURE; } - return http_send((void *) file, HTTP_G(ssb).sb.st_size, SEND_RSRC); + return http_send(file, HTTP_G(ssb).sb.st_size, SEND_RSRC); } /* }}} */ diff --git a/package.xml b/package.xml index a1ef2a2..82d022d 100644 --- a/package.xml +++ b/package.xml @@ -5,15 +5,15 @@ Extended HTTP support PHP License 3.0 @@ -33,7 +33,11 @@ Currently implemented features: alpha URI." in http_redirect() for full RFC compliance + * fixed http_cache_last_modified(): if parameter was omitted, would have always sent 304 Not Modified ]]> diff --git a/php_http.h b/php_http.h index 4980424..2f2b7ee 100644 --- a/php_http.h +++ b/php_http.h @@ -70,6 +70,8 @@ ZEND_BEGIN_MODULE_GLOBALS(http) PHP_MD5_CTX etag_md5; php_stream_statbuf ssb; char *ctype; + char *etag; + time_t lmod; #if defined(HAVE_CURL) && HAVE_CURL struct { struct { @@ -96,3 +98,4 @@ ZEND_END_MODULE_GLOBALS(http) * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ + diff --git a/php_http_api.h b/php_http_api.h index 53198e1..34a0159 100644 --- a/php_http_api.h +++ b/php_http_api.h @@ -48,6 +48,9 @@ typedef enum { /* buffer size */ #define HTTP_BUF_SIZE 2097152 +/* server vars shorthand */ +#define HTTP_SERVER_VARS Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]) + /* {{{ HAVE_CURL */ #if defined(HAVE_CURL) && HAVE_CURL @@ -108,7 +111,7 @@ PHP_HTTP_API int _http_modified_match(const char *entry, const time_t t TSRMLS_D PHP_HTTP_API int _http_etag_match(const char *entry, const char *etag TSRMLS_DC); #define http_send_last_modified(t) _http_send_last_modified((t) TSRMLS_CC) -PHP_HTTP_API STATUS _http_send_last_modified(const int t TSRMLS_DC); +PHP_HTTP_API STATUS _http_send_last_modified(const time_t t TSRMLS_DC); #define http_send_etag(e, l) _http_send_etag((e), (l) TSRMLS_CC) PHP_HTTP_API STATUS _http_send_etag(const char *etag, const int etag_len TSRMLS_DC); @@ -181,3 +184,4 @@ PHP_HTTP_API void _http_auth_header(const char *type, const char *realm TSRMLS_D * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ + -- 2.30.2