/* 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) {
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
* ("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)
{
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;
}
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;
}
/* }}} */
{
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;
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);
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/
+
}
}
smart_str_0(&qstr);
-
+
if (qstr.c) {
curl_easy_setopt(ch, CURLOPT_COOKIE, qstr.c);
}
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) {
{
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;
}
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);
+ }
}
}
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;
}
} 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);
}
/* }}} */
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;
}
/* }}} */
ptr = &end;
break;
+ case ' ':
+ /* IE - ignore for now */
+ break;
+
case 0:
case ',':
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;
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 */
**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;
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);
}
/* 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);
}
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 */
return FAILURE;
}
- return http_send((void *) zdata, Z_STRLEN_P(zdata), SEND_DATA);
+ return http_send(zdata, Z_STRLEN_P(zdata), SEND_DATA);
}
/* }}} */
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);
}
/* }}} */