* fixed http_cache_last_modified(): if parameter was omitted, would have always sent...
authorMichael Wallner <mike@php.net>
Tue, 8 Feb 2005 11:52:24 +0000 (11:52 +0000)
committerMichael Wallner <mike@php.net>
Tue, 8 Feb 2005 11:52:24 +0000 (11:52 +0000)
* 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 <link>URI</link>."
* 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
http_api.c
package.xml
php_http.h
php_http_api.h

diff --git a/http.c b/http.c
index 9a3ed65c224e81ba4b5ee9981721e0517d6fee3d..e81deffd554b40bfeeb90e4650df77c283997858 100644 (file)
--- 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 <a>URI</a>." 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, &params, &session, &permanent) != SUCCESS) {
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bb", &url, &url_len, &params, &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 <a href=\"%s?%s\">%s?%s</a>.\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 <a href=\"%s\">%s</a>.\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
  */
+
index 32dc183077ac2639b83bcf2efbb24da08cca61d8..210b9301a339b57d477371c445ef25a5e3ed39ab 100644 (file)
@@ -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);
 }
 /* }}} */
 
index a1ef2a2ea670441a660d143a74ca40a4485bd838..82d022da04f3c61d100acf5af874533a4536930d 100644 (file)
@@ -5,15 +5,15 @@
   <summary>Extended HTTP support</summary>
   <description>
 <![CDATA[
-Currently implemented features:
-===============================
-
 * Building absolute URIs
 * RCF compliant HTTP redirects
-* Caching by "Last-Modified" and/or ETag (with 'on the fly' option)
+* RFC compliant HTTP date handling
+* Caching by "Last-Modified" and/or ETag
+  (with 'on the fly' option for ETag generation from buffered output)
 * Sending data/files/streams with (multiple) ranges support
 * Negotiating user preferred language/charset
 * Convenient request functions to HEAD/GET/POST if libcurl is available
+* HTTP auth hooks (Basic)
 ]]>
   </description>
   <license>PHP License 3.0</license>
@@ -33,7 +33,11 @@ Currently implemented features:
     <state>alpha</state>
     <notes><![CDATA[
  * ctype is now mandatory
- * replaced calls to php_parse_date() to http_parse_date() (original by libcurls Curl_parsedate())
+ * implemented additional third/fourth informational parameter to http_(get|head|post*)
+   which will be filled with an associative array containing request/response information
+ * removed http_accept_ranges()
+ * output "Redirecting to <a>URI</a>." in http_redirect() for full RFC compliance
+ * fixed http_cache_last_modified(): if parameter was omitted, would have always sent 304 Not Modified
 ]]>
     </notes>
   </release>
index 49804244b3048e2aed92a9436e1a6f4085c0f087..2f2b7eef4ce732567d91ee29cd02aa99b6c72354 100644 (file)
@@ -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
  */
+
index 53198e1b53e4799d843a716acb9e04664df51149..34a01597587f25aad7ec29de69c2de536ce28833 100644 (file)
@@ -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
  */
+