#include "php_http.h"
#include "php_http_cookie.h"
#include "php_http_encoding.h"
+#include "php_http_info.h"
+#include "php_http_message.h"
#include "php_http_env.h"
#include "php_http_env_request.h"
#include "php_http_env_response.h"
#include "php_http_filter.h"
#include "php_http_header_parser.h"
#include "php_http_headers.h"
-#include "php_http_info.h"
-#include "php_http_message.h"
#include "php_http_message_body.h"
#include "php_http_message_parser.h"
#include "php_http_negotiate.h"
return append_len;
}
-PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset)
-{
- if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, insert_len)) {
- return PHP_HTTP_BUFFER_NOMEM;
- }
- memmove(buf->data + offset + insert_len, buf->data + offset, insert_len);
- memcpy(buf->data + offset, insert, insert_len);
- buf->used += insert_len;
- buf->free -= insert_len;
- return insert_len;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...)
-{
- va_list argv;
- char *insert;
- size_t insert_len, alloc;
-
- va_start(argv, format);
- insert_len = vspprintf(&insert, 0, format, argv);
- va_end(argv);
-
- alloc = php_http_buffer_insert(buf, insert, insert_len, offset);
- efree(insert);
-
- if (PHP_HTTP_BUFFER_NOMEM == alloc) {
- return PHP_HTTP_BUFFER_NOMEM;
- }
- return insert_len;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len)
-{
- if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, prepend_len)) {
- return PHP_HTTP_BUFFER_NOMEM;
- }
- memmove(buf->data + prepend_len, buf->data, buf->used);
- memcpy(buf->data, prepend, prepend_len);
- buf->used += prepend_len;
- buf->free -= prepend_len;
- return prepend_len;
-}
-
-PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...)
-{
- va_list argv;
- char *prepend;
- size_t prepend_len, alloc;
-
- va_start(argv, format);
- prepend_len = vspprintf(&prepend, 0, format, argv);
- va_end(argv);
-
- alloc = php_http_buffer_prepend(buf, prepend, prepend_len);
- efree(prepend);
-
- if (PHP_HTTP_BUFFER_NOMEM == alloc) {
- return PHP_HTTP_BUFFER_NOMEM;
- }
- return prepend_len;
-}
-
PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer_t *buf, char **into, size_t *len)
{
char *copy = ecalloc(1, buf->used + 1);
return copy;
}
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to)
-{
- int free_to = !to;
-
- to = php_http_buffer_clone(from, to);
-
- if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(to, from->data, from->used)) {
- if (free_to) {
- php_http_buffer_free(&to);
- } else {
- php_http_buffer_dtor(to);
- }
- }
- return to;
-}
-
PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, size_t offset, size_t length)
{
if (offset > buf->used) {
return length;
}
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t length)
-{
- if (offset >= buf->used) {
- return NULL;
- } else {
- size_t need = 1 + ((length + offset) > buf->used ? (buf->used - offset) : (length - offset));
- php_http_buffer_t *sub = php_http_buffer_init_ex(NULL, need, PHP_HTTP_BUFFER_INIT_PREALLOC | (buf->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0));
- if (sub) {
- if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(sub, buf->data + offset, need)) {
- php_http_buffer_free(&sub);
- } else {
- sub->size = buf->size;
- }
- }
- return sub;
- }
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length)
-{
- if (length < buf->used) {
- return php_http_buffer_sub(buf, buf->used - length, length);
- } else {
- return php_http_buffer_sub(buf, 0, buf->used);
- }
-}
-
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv)
-{
- unsigned i = 0;
- buf = php_http_buffer_init(buf);
-
- if (buf) {
- while (argc > i++) {
- php_http_buffer_free_t f = va_arg(argv, php_http_buffer_free_t);
- php_http_buffer_t *current = va_arg(argv, php_http_buffer_t *);
- php_http_buffer_append(buf, current->data, current->used);
- FREE_PHP_HTTP_BUFFER(f, current);
- }
- }
-
- return buf;
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...)
-{
- va_list argv;
- php_http_buffer_t *ret;
-
- va_start(argv, argc);
- ret = php_http_buffer_merge_va(buf, argc, argv);
- va_end(argv);
- return ret;
-}
-
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...)
-{
- va_list argv;
- php_http_buffer_t *ret;
-
- va_start(argv, argc);
- ret = php_http_buffer_merge_va(NULL, argc, argv);
- va_end(argv);
- return ret;
-}
-
PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix(php_http_buffer_t *buf)
{
if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(buf, 1, 1, 0)) {
return buf;
}
-PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right)
-{
- if (left->used > right->used) {
- return -1;
- } else if (right->used > left->used) {
- return 1;
- } else {
- return memcmp(left->data, right->data, left->used);
- }
-}
-
PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer_t *buf)
{
buf->free += buf->used;
return passed;
}
+#ifdef PHP_HTTP_BUFFER_EXTENDED
+
+PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right)
+{
+ if (left->used > right->used) {
+ return -1;
+ } else if (right->used > left->used) {
+ return 1;
+ } else {
+ return memcmp(left->data, right->data, left->used);
+ }
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to)
+{
+ int free_to = !to;
+
+ to = php_http_buffer_clone(from, to);
+
+ if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(to, from->data, from->used)) {
+ if (free_to) {
+ php_http_buffer_free(&to);
+ } else {
+ php_http_buffer_dtor(to);
+ }
+ }
+ return to;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset)
+{
+ if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, insert_len)) {
+ return PHP_HTTP_BUFFER_NOMEM;
+ }
+ memmove(buf->data + offset + insert_len, buf->data + offset, insert_len);
+ memcpy(buf->data + offset, insert, insert_len);
+ buf->used += insert_len;
+ buf->free -= insert_len;
+ return insert_len;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...)
+{
+ va_list argv;
+ char *insert;
+ size_t insert_len, alloc;
+
+ va_start(argv, format);
+ insert_len = vspprintf(&insert, 0, format, argv);
+ va_end(argv);
+
+ alloc = php_http_buffer_insert(buf, insert, insert_len, offset);
+ efree(insert);
+
+ if (PHP_HTTP_BUFFER_NOMEM == alloc) {
+ return PHP_HTTP_BUFFER_NOMEM;
+ }
+ return insert_len;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len)
+{
+ if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, prepend_len)) {
+ return PHP_HTTP_BUFFER_NOMEM;
+ }
+ memmove(buf->data + prepend_len, buf->data, buf->used);
+ memcpy(buf->data, prepend, prepend_len);
+ buf->used += prepend_len;
+ buf->free -= prepend_len;
+ return prepend_len;
+}
+
+PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...)
+{
+ va_list argv;
+ char *prepend;
+ size_t prepend_len, alloc;
+
+ va_start(argv, format);
+ prepend_len = vspprintf(&prepend, 0, format, argv);
+ va_end(argv);
+
+ alloc = php_http_buffer_prepend(buf, prepend, prepend_len);
+ efree(prepend);
+
+ if (PHP_HTTP_BUFFER_NOMEM == alloc) {
+ return PHP_HTTP_BUFFER_NOMEM;
+ }
+ return prepend_len;
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t length)
+{
+ if (offset >= buf->used) {
+ return NULL;
+ } else {
+ size_t need = 1 + ((length + offset) > buf->used ? (buf->used - offset) : (length - offset));
+ php_http_buffer_t *sub = php_http_buffer_init_ex(NULL, need, PHP_HTTP_BUFFER_INIT_PREALLOC | (buf->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0));
+ if (sub) {
+ if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(sub, buf->data + offset, need)) {
+ php_http_buffer_free(&sub);
+ } else {
+ sub->size = buf->size;
+ }
+ }
+ return sub;
+ }
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length)
+{
+ if (length < buf->used) {
+ return php_http_buffer_sub(buf, buf->used - length, length);
+ } else {
+ return php_http_buffer_sub(buf, 0, buf->used);
+ }
+}
+
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv)
+{
+ unsigned i = 0;
+ buf = php_http_buffer_init(buf);
+
+ if (buf) {
+ while (argc > i++) {
+ php_http_buffer_free_t f = va_arg(argv, php_http_buffer_free_t);
+ php_http_buffer_t *current = va_arg(argv, php_http_buffer_t *);
+ php_http_buffer_append(buf, current->data, current->used);
+ FREE_PHP_HTTP_BUFFER(f, current);
+ }
+ }
+
+ return buf;
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...)
+{
+ va_list argv;
+ php_http_buffer_t *ret;
+
+ va_start(argv, argc);
+ ret = php_http_buffer_merge_va(buf, argc, argv);
+ va_end(argv);
+ return ret;
+}
+
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...)
+{
+ va_list argv;
+ php_http_buffer_t *ret;
+
+ va_start(argv, argc);
+ ret = php_http_buffer_merge_va(NULL, argc, argv);
+ va_end(argv);
+ return ret;
+}
+
+#endif
+
/*
* Local variables:
* tab-width: 4
PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf, const char *append, size_t append_len);
PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer_t *buf, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 2, 3);
-/* insert data at a specific position of the php_http_buffer_t */
-#define php_http_buffer_inserts(b, i, o) php_http_buffer_insert((b), (i), sizeof(i)-1, (o))
-#define php_http_buffer_insertl(b, i, o) php_http_buffer_insert((b), (i), strlen(i), (o))
-PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset);
-PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 3, 4);
-
-/* prepend data */
-#define php_http_buffer_prepends(b, p) php_http_buffer_prepend((b), (p), sizeof(p)-1)
-#define php_http_buffer_prependl(b, p) php_http_buffer_prepend((b), (p), strlen(p))
-PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len);
-PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 2, 3);
-
/* get a zero-terminated string */
PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer_t *buf, char **into, size_t *len);
-/* get a part of the php_http_buffer_t */
-#define php_http_buffer_mid(b, o, l) php_http_buffer_sub((b), (o), (l))
-#define php_http_buffer_left(b, l) php_http_buffer_sub((b), 0, (l))
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length);
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t len);
-
/* remove a substring */
PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, size_t offset, size_t length);
-/* get a complete php_http_buffer_t duplicate */
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to);
-
-/* merge several php_http_buffer_t objects
- use like:
-
- php_http_buffer_t *final = php_http_buffer_merge(3,
- PHP_HTTP_BUFFER_NOT_FREE(&keep),
- PHP_HTTP_BUFFER_ALL_FREE(middle_ptr),
- PHP_HTTP_BUFFER_VAL_FREE(&local);
-*/
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...);
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...);
-PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv);
-
/* sets a trailing NUL byte */
PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix(php_http_buffer_t *buf);
-/* memcmp for php_http_buffer_t objects */
-PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right);
-
/* reset php_http_buffer_t object */
PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer_t *buf);
/* write chunks directly into php_http_buffer_t buffer */
PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC);
-#endif
+
+# ifdef PHP_HTTP_BUFFER_EXTENDED
+
+/* memcmp for php_http_buffer_t objects */
+PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right);
+
+/* get a complete php_http_buffer_t duplicate */
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to);
+
+/* merge several php_http_buffer_t objects
+ use like:
+
+ php_http_buffer_t *final = php_http_buffer_merge(3,
+ PHP_HTTP_BUFFER_NOT_FREE(&keep),
+ PHP_HTTP_BUFFER_ALL_FREE(middle_ptr),
+ PHP_HTTP_BUFFER_VAL_FREE(&local);
+*/
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...);
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...);
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv);
+
+/* insert data at a specific position of the php_http_buffer_t */
+#define php_http_buffer_inserts(b, i, o) php_http_buffer_insert((b), (i), sizeof(i)-1, (o))
+#define php_http_buffer_insertl(b, i, o) php_http_buffer_insert((b), (i), strlen(i), (o))
+PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset);
+PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 3, 4);
+
+/* prepend data */
+#define php_http_buffer_prepends(b, p) php_http_buffer_prepend((b), (p), sizeof(p)-1)
+#define php_http_buffer_prependl(b, p) php_http_buffer_prepend((b), (p), strlen(p))
+PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len);
+PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 2, 3);
+
+/* get a part of the php_http_buffer_t */
+#define php_http_buffer_mid(b, o, l) php_http_buffer_sub((b), (o), (l))
+#define php_http_buffer_left(b, l) php_http_buffer_sub((b), 0, (l))
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length);
+PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t len);
+
+# endif /* PHP_HTTP_BUFFER_EXTENDED */
+
+#endif /* PHP_HTTP_BUFFER_H */
/*
}
}
-PHP_HTTP_API char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len TSRMLS_DC)
+PHP_HTTP_API char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request TSRMLS_DC)
{
- zval **zvalue;
+ HashTable *request_headers;
+ zval **zvalue = NULL;
char *val = NULL, *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
- php_http_env_get_request_headers(NULL TSRMLS_CC);
+ if (request) {
+ request_headers = &request->hdrs;
+ } else {
+ php_http_env_get_request_headers(NULL TSRMLS_CC);
+ request_headers = PHP_HTTP_G->env.request.headers;
+ }
- if (SUCCESS == zend_symtable_find(PHP_HTTP_G->env.request.headers, key, name_len + 1, (void *) &zvalue)) {
+ if (SUCCESS == zend_symtable_find(request_headers, key, name_len + 1, (void *) &zvalue)) {
zval *zcopy = php_http_ztyp(IS_STRING, *zvalue);
val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy));
return val;
}
-PHP_HTTP_API int php_http_env_got_request_header(const char *name_str, size_t name_len TSRMLS_DC)
+PHP_HTTP_API int php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request TSRMLS_DC)
{
+ HashTable *request_headers;
char *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1);
int got;
- php_http_env_get_request_headers(NULL TSRMLS_CC);
- got = zend_symtable_exists(PHP_HTTP_G->env.request.headers, key, name_len + 1);
+ if (request) {
+ request_headers = &request->hdrs;
+ } else {
+ php_http_env_get_request_headers(NULL TSRMLS_CC);
+ request_headers = PHP_HTTP_G->env.request.headers;
+ }
+ got = zend_symtable_exists(request_headers, key, name_len + 1);
efree(key);
return got;
return PHP_HTTP_G->env.request.body;
}
-PHP_HTTP_API php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t length TSRMLS_DC)
+PHP_HTTP_API const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC)
+{
+ const char *m;
+
+ if (PHP_HTTP_MESSAGE_TYPE(REQUEST, request)) {
+ m = request->http.info.request.method;
+ } else {
+ m = SG(request_info).request_method;
+ }
+
+ return m ? m : "GET";
+}
+
+PHP_HTTP_API php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t length, php_http_message_t *request TSRMLS_DC)
{
zval *zentry;
char *range, *rp, c;
long begin = -1, end = -1, *ptr;
- if (!(range = php_http_env_get_request_header(ZEND_STRL("Range"), NULL TSRMLS_CC))) {
+ if (!(range = php_http_env_get_request_header(ZEND_STRL("Range"), NULL, request TSRMLS_CC))) {
return PHP_HTTP_RANGE_NO;
}
if (strncmp(range, "bytes=", lenof("bytes="))) {
return ret;
}
-PHP_HTTP_API STATUS php_http_env_set_response_header_format(long http_code, zend_bool replace TSRMLS_DC, const char *fmt, ...)
+PHP_HTTP_API STATUS php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv TSRMLS_DC)
{
- va_list args;
STATUS ret = FAILURE;
sapi_header_line h = {NULL, 0, http_code};
- va_start(args, fmt);
- h.line_len = vspprintf(&h.line, 0, fmt, args);
- va_end(args);
+ h.line_len = vspprintf(&h.line, 0, fmt, argv);
if (h.line) {
if (h.line_len) {
return ret;
}
+PHP_HTTP_API STATUS php_http_env_set_response_header_format(long http_code, zend_bool replace TSRMLS_DC, const char *fmt, ...)
+{
+ STATUS ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = php_http_env_set_response_header_va(http_code, replace, fmt, args TSRMLS_CC);
+ va_end(args);
+
+ return ret;
+}
+
PHP_HTTP_API STATUS php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace TSRMLS_DC)
{
if (!value) {
if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
if (header_name_str && header_name_len) {
size_t header_length;
- char *header_value = php_http_env_get_request_header(header_name_str, header_name_len, &header_length TSRMLS_CC);
+ char *header_value = php_http_env_get_request_header(header_name_str, header_name_len, &header_length, NULL TSRMLS_CC);
if (header_value) {
RETURN_STRINGL(header_value, header_length, 0);
PHP_HTTP_RANGE_ERR
} php_http_range_status_t;
-PHP_HTTP_API php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t entity_length TSRMLS_DC);
+PHP_HTTP_API php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t entity_length, php_http_message_t *request TSRMLS_DC);
PHP_HTTP_API void php_http_env_get_request_headers(HashTable *headers TSRMLS_DC);
-PHP_HTTP_API char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len TSRMLS_DC);
-PHP_HTTP_API int php_http_env_got_request_header(const char *name_str, size_t name_len TSRMLS_DC);
+PHP_HTTP_API char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request TSRMLS_DC);
+PHP_HTTP_API int php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request TSRMLS_DC);
PHP_HTTP_API php_http_message_body_t *php_http_env_get_request_body(TSRMLS_D);
+PHP_HTTP_API const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC);
typedef enum php_http_content_disposition {
PHP_HTTP_CONTENT_DISPOSITION_NONE,
PHP_HTTP_API STATUS php_http_env_set_response_header(long http_code, const char *header_str, size_t header_len, zend_bool replace TSRMLS_DC);
PHP_HTTP_API STATUS php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace TSRMLS_DC);
PHP_HTTP_API STATUS php_http_env_set_response_header_format(long http_code, zend_bool replace TSRMLS_DC, const char *fmt, ...);
+PHP_HTTP_API STATUS php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv TSRMLS_DC);
PHP_HTTP_API zval *php_http_env_get_server_var(const char *key_str, size_t key_len, zend_bool check TSRMLS_DC);
#define php_http_env_got_server_var(v) (NULL != php_http_env_get_server_var((v), strlen(v), 1 TSRMLS_CC))
#include "php_http_api.h"
-static void set_option(zval *options, const char *name_str, size_t name_len, int type, const void *value_ptr, size_t value_len TSRMLS_DC)
+static void set_option(zval *options, const char *name_str, size_t name_len, int type, void *value_ptr, size_t value_len TSRMLS_DC)
{
if (Z_TYPE_P(options) == IS_OBJECT) {
/* stupid non-const api */
case IS_STRING:
zend_update_property_stringl(Z_OBJCE_P(options), options, name, name_len, value_ptr, value_len TSRMLS_CC);
break;
+ case IS_OBJECT:
+ zend_update_property(Z_OBJCE_P(options), options, name, name_len, value_ptr TSRMLS_CC);
+ break;
}
} else {
zend_update_property_null(Z_OBJCE_P(options), options, name, name_len TSRMLS_CC);
char *value = estrndup(value_ptr, value_len);
add_assoc_stringl_ex(options, name_str, name_len + 1, value, value_len, 0);
break;
+ case IS_OBJECT:
+ Z_ADDREF_P(value_ptr);
+ add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr);
+ break;
}
}
} else {
}
return val;
}
+static php_http_message_body_t *get_body(zval *options TSRMLS_DC)
+{
+ zval *zbody;
+ php_http_message_body_t *body = NULL;
+
+ if ((zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))) {
+ if ((Z_TYPE_P(zbody) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_get_class_entry() TSRMLS_CC)) {
+ php_http_message_body_object_t *body_obj = zend_object_store_get_object(zbody TSRMLS_CC);
+
+ body = body_obj->body;
+ }
+ zval_ptr_dtor(&zbody);
+ }
+
+ return body;
+}
+static php_http_message_t *get_request(zval *options TSRMLS_DC)
+{
+ zval *zrequest;
+ php_http_message_t *request = NULL;
+
+ if ((zrequest = get_option(options, ZEND_STRL("request") TSRMLS_CC))) {
+ if (Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_get_class_entry() TSRMLS_CC)) {
+ php_http_message_object_t *request_obj = zend_object_store_get_object(zrequest TSRMLS_CC);
+
+ request = request_obj->message;
+ }
+ zval_ptr_dtor(&zrequest);
+ }
+
+ return request;
+}
-PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len TSRMLS_DC)
+PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC)
{
php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
int free_etag = 0;
char *header = NULL, *etag;
- zval *zetag, *zbody = NULL;
+ php_http_message_body_t *body;
+ zval *zetag;
- if ( !(zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))
- || !(Z_TYPE_P(zbody) == IS_OBJECT)
- || !instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_get_class_entry() TSRMLS_CC)
- ) {
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
+
+ if (!(body = get_body(options TSRMLS_CC))) {
return ret;
}
if (zetag && Z_STRLEN_P(zetag)) {
etag = Z_STRVAL_P(zetag);
- } else if ((etag = php_http_message_body_etag(((php_http_message_body_object_t *) zend_object_store_get_object(zbody TSRMLS_CC))->body))) {
+ } else if ((etag = php_http_message_body_etag(body))) {
set_option(options, ZEND_STRL("etag"), IS_STRING, etag, strlen(etag) TSRMLS_CC);
free_etag = 1;
}
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
-
if (zetag) {
zval_ptr_dtor(&zetag);
}
- if (etag && (header = php_http_env_get_request_header(header_str, header_len, NULL TSRMLS_CC))) {
+ if (etag && (header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD) ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS;
}
if (free_etag) {
efree(etag);
}
- STR_FREE(header);
+ STR_FREE(header);
return ret;
}
-PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len TSRMLS_DC)
+PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC)
{
+ php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
char *header;
time_t ums, lm = 0;
- zval *zbody = NULL, *zlm;
+ php_http_message_body_t *body;
+ zval *zlm;
- if ( !(zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))
- || !(Z_TYPE_P(zbody) == IS_OBJECT)
- || !instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_get_class_entry() TSRMLS_CC)
- ) {
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
- return PHP_HTTP_CACHE_NO;
+ if (!(body = get_body(options TSRMLS_CC))) {
+ return ret;
}
if ((zlm = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
if (zlm && Z_LVAL_P(zlm) > 0) {
lm = Z_LVAL_P(zlm);
} else {
- lm = php_http_message_body_mtime(((php_http_message_body_object_t *) zend_object_store_get_object(zbody TSRMLS_CC))->body);
+ lm = php_http_message_body_mtime(body);
set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0 TSRMLS_CC);
}
- zval_ptr_dtor(&zbody);
if (zlm) {
zval_ptr_dtor(&zlm);
}
- if (!(header = php_http_env_get_request_header(header_str, header_len, NULL TSRMLS_CC))) {
- return PHP_HTTP_CACHE_NO;
+ if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
+ ums = php_parse_date(header, NULL);
+
+ if (ums > 0 && ums >= lm) {
+ ret = PHP_HTTP_CACHE_HIT;
+ } else {
+ ret = PHP_HTTP_CACHE_MISS;
+ }
+ }
+
+ STR_FREE(header);
+ return ret;
+}
+
+static zend_bool php_http_env_response_is_cacheable(php_http_env_response_t *r, php_http_message_t *request)
+{
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ if (r->ops->get_status(r) >= 400) {
+ return 0;
}
- ums = php_parse_date(header, NULL);
- efree(header);
+ if (php_http_env_got_request_header(ZEND_STRL("Authorizsation"), request TSRMLS_CC)) {
+ return 0;
+ }
- if (ums > 0 && ums >= lm) {
- return PHP_HTTP_CACHE_HIT;
- } else {
- return PHP_HTTP_CACHE_MISS;
+ if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC), 2, "HEAD", "GET")) {
+ return 0;
}
+
+ return 1;
}
static size_t output(void *context, char *buf, size_t len TSRMLS_DC)
{
php_http_env_response_t *r = context;
- PHPWRITE(buf, len);
+ r->ops->write(r, buf, len);
/* we really only need to flush when throttling is enabled,
because we push the data as fast as possible anyway if not */
if (r->throttle.delay >= PHP_HTTP_DIFFSEC) {
-#if PHP_VERSION_ID >= 50400
- if (php_output_get_level(TSRMLS_C)) {
- php_output_flush_all(TSRMLS_C);
- }
- if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) {
- sapi_flush(TSRMLS_C);
- }
-#else
- php_end_ob_buffer(1, 1 TSRMLS_CC);
- sapi_flush(TSRMLS_C);
-#endif
+ r->ops->flush(r);
php_http_sleep(r->throttle.delay);
}
return len;
return SUCCESS;
}
-PHP_HTTP_API php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options TSRMLS_DC)
+PHP_HTTP_API php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *init_arg TSRMLS_DC)
{
- php_http_env_response_t *free_r = NULL;
+ zend_bool free_r;
- if (!r) {
- r = free_r = emalloc(sizeof(*r));
+ if ((free_r = !r)) {
+ r = emalloc(sizeof(*r));
}
memset(r, 0, sizeof(*r));
+ if (ops) {
+ r->ops = ops;
+ } else {
+ r->ops = php_http_env_response_get_sapi_ops();
+ }
+
r->buffer = php_http_buffer_init(NULL);
Z_ADDREF_P(options);
TSRMLS_SET_CTX(r->ts);
+ if (r->ops->init && (SUCCESS != r->ops->init(r, init_arg))) {
+ if (free_r) {
+ php_http_env_response_free(&r);
+ } else {
+ php_http_env_response_dtor(r);
+ r = NULL;
+ }
+ }
+
return r;
}
PHP_HTTP_API void php_http_env_response_dtor(php_http_env_response_t *r)
{
+ if (r->ops->dtor) {
+ r->ops->dtor(r);
+ }
php_http_buffer_free(&r->buffer);
zval_ptr_dtor(&r->options);
STR_FREE(r->content.type);
}
}
-static STATUS php_http_env_response_send_head(php_http_env_response_t *r)
+static STATUS php_http_env_response_send_head(php_http_env_response_t *r, php_http_message_t *request)
{
STATUS ret = SUCCESS;
zval *zoption, *options = r->options;
zval_ptr_dtor(&zoption);
if (Z_LVAL_P(zoption_copy) > 0) {
- ret = php_http_env_set_response_code(Z_LVAL_P(zoption_copy) TSRMLS_CC);
+ ret = r->ops->set_status(r, Z_LVAL_P(zoption_copy));
}
zval_ptr_dtor(&zoption_copy);
}
zval_ptr_dtor(&zoption);
if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) {
- ret = php_http_env_set_response_protocol_version(&v TSRMLS_CC);
+ ret = r->ops->set_protocol_version(r, &v);
php_http_version_dtor(&v);
}
zval_ptr_dtor(&zoption_copy);
if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) {
if (Z_TYPE_P(zoption) == IS_ARRAY) {
- zval **val;
- HashPosition pos;
- php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
-
- FOREACH_KEYVAL(pos, zoption, key, val) {
- if (key.type == HASH_KEY_IS_STRING) {
- if (SUCCESS != (ret = php_http_env_set_response_header_value(0, key.str, key.len - 1, *val, 1 TSRMLS_CC))) {
- break;
- }
- }
- }
+ php_http_headers_to_callback(Z_ARRVAL_P(zoption), 0, r->ops->set_header, r TSRMLS_CC);
}
zval_ptr_dtor(&zoption);
}
zval_ptr_dtor(&zoption);
if (Z_STRLEN_P(zoption_copy)) {
PHP_HTTP_CHECK_CONTENT_TYPE(Z_STRVAL_P(zoption_copy), ret = FAILURE) else {
- if (SUCCESS == (ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)))) {
+ if (SUCCESS == (ret = r->ops->set_header(r, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)))) {
r->content.type = estrndup(Z_STRVAL_P(zoption_copy), Z_STRLEN_P(zoption_copy));
}
}
if ( 1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range)
&& 2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
) {
- ret = php_http_env_set_response_header_format(206, 1 TSRMLS_CC, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), r->content.length);
+ if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
+ ret = r->ops->set_header(r, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), r->content.length);
+ }
} else {
/* this should never happen */
zend_hash_destroy(&r->range.values);
- php_http_env_set_response_code(500 TSRMLS_CC);
ret = FAILURE;
}
} else {
php_http_boundary(r->range.boundary, sizeof(r->range.boundary) TSRMLS_CC);
- ret = php_http_env_set_response_header_format(206, 1 TSRMLS_CC, "Content-Type: multipart/byteranges; boundary=%s", r->range.boundary);
+ if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
+ ret = r->ops->set_header(r, "Content-Type: multipart/byteranges; boundary=%s", r->range.boundary);
+ }
}
} else {
if ((zoption = get_option(options, ZEND_STRL("cacheControl") TSRMLS_CC))) {
zval_ptr_dtor(&zoption);
if (Z_STRLEN_P(zoption_copy)) {
- ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy) TSRMLS_CC);
+ ret = r->ops->set_header(r, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy));
}
zval_ptr_dtor(&zoption_copy);
}
php_http_buffer_init(&buf);
if (php_http_params_to_string(&buf, Z_ARRVAL_P(zoption_copy), ZEND_STRL(","), ZEND_STRL(";"), ZEND_STRL("="), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC)) {
- ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Content-Disposition: %s", buf.data);
+ if (buf.used) {
+ ret = r->ops->set_header(r, "Content-Disposition: %.*s", buf.used, buf.data);
+ }
}
php_http_buffer_dtor(&buf);
add_next_index_stringl(&zsupported, ZEND_STRL("gzip"), 1);
add_next_index_stringl(&zsupported, ZEND_STRL("deflate"), 1);
- if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported) TSRMLS_CC))) {
+ if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request TSRMLS_CC))) {
char *key_str = NULL;
uint key_len = 0;
if (!strcmp(key_str, "gzip")) {
if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC))) {
ret = FAILURE;
- } else if (SUCCESS == (ret = php_http_env_set_response_header(0, ZEND_STRL("Content-Encoding: gzip"), 1 TSRMLS_CC))) {
+ } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: gzip"))) {
r->content.encoding = estrndup(key_str, key_len - 1);
}
} else if (!strcmp(key_str, "deflate")) {
if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC))) {
ret = FAILURE;
- } else if (SUCCESS == (ret = php_http_env_set_response_header(0, ZEND_STRL("Content-Encoding: deflate"), 1 TSRMLS_CC))) {
+ } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: deflate"))) {
r->content.encoding = estrndup(key_str, key_len - 1);
}
} else {
- ret = php_http_env_set_response_header_value(0, ZEND_STRL("Content-Encoding"), NULL, 0 TSRMLS_CC);
+ ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
}
if (SUCCESS == ret) {
- ret = php_http_env_set_response_header(0, ZEND_STRL("Vary: Accept-Encoding"), 0 TSRMLS_CC);
+ ret = r->ops->add_header(r, "Vary: Accept-Encoding");
}
}
case PHP_HTTP_CONTENT_ENCODING_NONE:
default:
- ret = php_http_env_set_response_header_value(0, ZEND_STRL("Content-Encoding"), NULL, 0 TSRMLS_CC);
+ ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
break;
}
zval_ptr_dtor(&zoption_copy);
return ret;
}
- if (php_http_env_get_response_code(TSRMLS_C) < 400 && !php_http_env_got_request_header(ZEND_STRL("Authorization") TSRMLS_CC)
- && ( !SG(request_info).request_method
- || !strcasecmp(SG(request_info).request_method, "GET")
- || !strcasecmp(SG(request_info).request_method, "HEAD")
- )
- ) {
- switch (php_http_env_is_response_cached_by_etag(options, ZEND_STRL("If-None-Match") TSRMLS_CC)) {
+ if (php_http_env_response_is_cacheable(r, request)) {
+ switch (php_http_env_is_response_cached_by_etag(options, ZEND_STRL("If-None-Match"), request TSRMLS_CC)) {
case PHP_HTTP_CACHE_MISS:
break;
case PHP_HTTP_CACHE_NO:
- if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since") TSRMLS_CC)) {
+ if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since"), request TSRMLS_CC)) {
break;
}
/* no break */
case PHP_HTTP_CACHE_HIT:
- ret = php_http_env_set_response_code(304 TSRMLS_CC);
+ ret = r->ops->set_status(r, 304);
r->done = 1;
break;
}
zval_ptr_dtor(&zoption);
if (*Z_STRVAL_P(zoption_copy) != '"' && strncmp(Z_STRVAL_P(zoption_copy), "W/\"", 3)) {
- ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "ETag: \"%s\"", Z_STRVAL_P(zoption_copy));
+ ret = r->ops->set_header(r, "ETag: \"%s\"", Z_STRVAL_P(zoption_copy));
} else {
- ret = php_http_env_set_response_header_value(0, ZEND_STRL("ETag"), zoption_copy, 1 TSRMLS_CC);
+ ret = r->ops->set_header(r, "ETag: %s", Z_STRVAL_P(zoption_copy));
}
zval_ptr_dtor(&zoption_copy);
}
if (Z_LVAL_P(zoption_copy)) {
char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), Z_LVAL_P(zoption_copy), 0 TSRMLS_CC);
if (date) {
- ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Last-Modified: %s", date);
+ ret = r->ops->set_header(r, "Last-Modified: %s", date);
efree(date);
}
}
static STATUS php_http_env_response_send_body(php_http_env_response_t *r)
{
STATUS ret = SUCCESS;
- zval *zbody, *zoption;
+ zval *zoption;
+ php_http_message_body_t *body;
TSRMLS_FETCH_FROM_CTX(r->ts);
if (r->done) {
return ret;
}
- if ( (zbody = get_option(r->options, ZEND_STRL("body") TSRMLS_CC))
- && (Z_TYPE_P(zbody) == IS_OBJECT)
- && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_get_class_entry() TSRMLS_CC)
- ) {
- php_http_message_body_object_t *obj = zend_object_store_get_object(zbody TSRMLS_CC);
-
+ if ((body = get_body(r->options TSRMLS_CC))) {
if ((zoption = get_option(r->options, ZEND_STRL("throttleDelay") TSRMLS_CC))) {
if (Z_TYPE_P(zoption) == IS_DOUBLE) {
r->throttle.delay = Z_DVAL_P(zoption);
&& 2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
) {
/* send chunk */
- php_http_message_body_to_callback(obj->body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
+ php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
php_http_env_response_send_done(r);
zend_hash_destroy(&r->range.values);
ret = SUCCESS;
} else {
- fprintf(stderr, "wut?");
/* this should never happen */
zend_hash_destroy(&r->range.values);
- php_http_env_set_response_code(500 TSRMLS_CC);
+ r->ops->set_status(r, 500);
ret = FAILURE;
}
Z_LVAL_PP(end),
r->content.length
);
- php_http_message_body_to_callback(obj->body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
+ php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
}
}
php_http_buffer_appendf(r->buffer, PHP_HTTP_CRLF "--%s--", r->range.boundary);
}
} else {
- php_http_message_body_to_callback(obj->body, (php_http_pass_callback_t) php_http_env_response_send_data, r, 0, 0);
+ php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, 0, 0);
php_http_env_response_send_done(r);
}
}
- if (zbody) {
- zval_ptr_dtor(&zbody);
- }
return ret;
}
PHP_HTTP_API STATUS php_http_env_response_send(php_http_env_response_t *r)
{
- zval *zbody;
+ php_http_message_t *request;
+ php_http_message_body_t *body;
TSRMLS_FETCH_FROM_CTX(r->ts);
- /* check for ranges */
- if ( (zbody = get_option(r->options, ZEND_STRL("body") TSRMLS_CC))
- && (Z_TYPE_P(zbody) == IS_OBJECT)
- && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_get_class_entry() TSRMLS_CC)
- ) {
- php_http_message_body_object_t *obj = zend_object_store_get_object(zbody TSRMLS_CC);
+ request = get_request(r->options TSRMLS_CC);
- r->content.length = php_http_message_body_size(obj->body);
- zval_ptr_dtor(&zbody);
+ /* check for ranges */
+ if ((body = get_body(r->options TSRMLS_CC))) {
+ r->content.length = php_http_message_body_size(body);
- if (SUCCESS != php_http_env_set_response_header(0, ZEND_STRL("Accept-Ranges: bytes"), 1 TSRMLS_CC)) {
+ if (SUCCESS != r->ops->set_header(r, "Accept-Ranges: bytes")) {
return FAILURE;
} else {
zend_hash_init(&r->range.values, 0, NULL, ZVAL_PTR_DTOR, 0);
- r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length TSRMLS_CC);
+ r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length, request TSRMLS_CC);
switch (r->range.status) {
case PHP_HTTP_RANGE_NO:
break;
case PHP_HTTP_RANGE_ERR:
- if (php_http_env_got_request_header(ZEND_STRL("If-Range") TSRMLS_CC)) {
+ if (php_http_env_got_request_header(ZEND_STRL("If-Range"), request TSRMLS_CC)) {
r->range.status = PHP_HTTP_RANGE_NO;
zend_hash_destroy(&r->range.values);
} else {
r->done = 1;
zend_hash_destroy(&r->range.values);
- if (SUCCESS != php_http_env_set_response_header_format(416, 1 TSRMLS_CC, "Content-Range: bytes */%zu", r->content.length)) {
+ if (SUCCESS != r->ops->set_status(r, 416)) {
+ return FAILURE;
+ }
+ if (SUCCESS != r->ops->set_header(r, "Content-Range: bytes */%zu", r->content.length)) {
return FAILURE;
}
}
break;
case PHP_HTTP_RANGE_OK:
- if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Range") TSRMLS_CC)
- || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Range") TSRMLS_CC)
+ if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
+ || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
) {
r->range.status = PHP_HTTP_RANGE_NO;
zend_hash_destroy(&r->range.values);
break;
}
- if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Match") TSRMLS_CC)
- || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Unmodified-Since") TSRMLS_CC)
- || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("Unless-Modified-Since") TSRMLS_CC)
+ if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Match"), request TSRMLS_CC)
+ || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Unmodified-Since"), request TSRMLS_CC)
+ || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("Unless-Modified-Since"), request TSRMLS_CC)
) {
r->done = 1;
zend_hash_destroy(&r->range.values);
- if (SUCCESS != php_http_env_set_response_code(412 TSRMLS_CC)) {
+ if (SUCCESS != r->ops->set_status(r, 412)) {
return FAILURE;
}
break;
break;
}
}
- } else if (zbody) {
- zval_ptr_dtor(&zbody);
}
- if (SUCCESS != php_http_env_response_send_head(r)) {
+ if (SUCCESS != php_http_env_response_send_head(r, request)) {
return FAILURE;
}
if (SUCCESS != php_http_env_response_send_body(r)) {
return FAILURE;
}
+
+ if (SUCCESS != r->ops->finish(r)) {
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+static long php_http_env_response_sapi_get_status(php_http_env_response_t *r)
+{
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ return php_http_env_get_response_code(TSRMLS_C);
+}
+static STATUS php_http_env_response_sapi_set_status(php_http_env_response_t *r, long http_code)
+{
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ return php_http_env_set_response_code(http_code TSRMLS_CC);
+}
+static STATUS php_http_env_response_sapi_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
+{
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ return php_http_env_set_response_protocol_version(v TSRMLS_CC);
+}
+static STATUS php_http_env_response_sapi_set_header(php_http_env_response_t *r, const char *fmt, ...)
+{
+ STATUS ret;
+ va_list args;
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ va_start(args, fmt);
+ ret = php_http_env_set_response_header_va(0, 1, fmt, args TSRMLS_CC);
+ va_end(args);
+
+ return ret;
+}
+static STATUS php_http_env_response_sapi_add_header(php_http_env_response_t *r, const char *fmt, ...)
+{
+ STATUS ret;
+ va_list args;
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ va_start(args, fmt);
+ ret = php_http_env_set_response_header_va(0, 0, fmt, args TSRMLS_CC);
+ va_end(args);
+
+ return ret;
+}
+static STATUS php_http_env_response_sapi_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
+{
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ return php_http_env_set_response_header_value(0, header_str, header_len, NULL, 1 TSRMLS_CC);
+}
+static STATUS php_http_env_response_sapi_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
+{
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ if (0 < PHPWRITE(data_str, data_len)) {
+ return SUCCESS;
+ }
+ return FAILURE;
+}
+static STATUS php_http_env_response_sapi_flush(php_http_env_response_t *r)
+{
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+#if PHP_VERSION_ID >= 50400
+ if (php_output_get_level(TSRMLS_C)) {
+ php_output_flush_all(TSRMLS_C);
+ }
+ if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) {
+ sapi_flush(TSRMLS_C);
+ }
+#else
+ php_end_ob_buffer(1, 1 TSRMLS_CC);
+ sapi_flush(TSRMLS_C);
+#endif
+
+ return SUCCESS;
+}
+static STATUS php_http_env_response_sapi_finish(php_http_env_response_t *r)
+{
return SUCCESS;
}
+static php_http_env_response_ops_t php_http_env_response_sapi_ops = {
+ NULL,
+ NULL,
+ php_http_env_response_sapi_get_status,
+ php_http_env_response_sapi_set_status,
+ php_http_env_response_sapi_set_protocol_version,
+ php_http_env_response_sapi_set_header,
+ php_http_env_response_sapi_add_header,
+ php_http_env_response_sapi_del_header,
+ php_http_env_response_sapi_write,
+ php_http_env_response_sapi_flush,
+ php_http_env_response_sapi_finish
+};
+
+PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void)
+{
+ return &php_http_env_response_sapi_ops;
+}
+
+typedef struct php_http_env_response_stream_ctx {
+ HashTable header;
+ php_http_version_t version;
+ long status_code;
+
+ php_stream *stream;
+
+ unsigned started:1;
+ unsigned finished:1;
+} php_http_env_response_stream_ctx_t;
+
+static STATUS php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg)
+{
+ php_http_env_response_stream_ctx_t *ctx;
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ ctx = ecalloc(1, sizeof(*ctx));
+
+ ctx->stream = init_arg;
+ if (SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) {
+ efree(ctx);
+ return FAILURE;
+ }
+ zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0);
+ php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC);
+ ctx->status_code = 200;
+
+ r->ctx = ctx;
+
+ return SUCCESS;
+}
+static void php_http_env_response_stream_dtor(php_http_env_response_t *r)
+{
+ php_http_env_response_stream_ctx_t *ctx = r->ctx;
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ zend_hash_destroy(&ctx->header);
+ zend_list_delete(ctx->stream->rsrc_id);
+ efree(ctx);
+ r->ctx = NULL;
+}
+static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header TSRMLS_DC)
+{
+ HashPosition pos;
+ zval **val;
+
+ FOREACH_HASH_VAL(pos, &ctx->header, val) {
+ if (Z_TYPE_PP(val) == IS_ARRAY) {
+ php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val) TSRMLS_CC);
+ } else {
+ php_stream_write(ctx->stream, Z_STRVAL_PP(val), Z_STRLEN_PP(val));
+ php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
+ }
+ }
+}
+static STATUS php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC)
+{
+ if (ctx->started || ctx->finished) {
+ return FAILURE;
+ }
+
+ php_stream_printf(ctx->stream TSRMLS_CC, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code));
+ php_http_env_response_stream_header(ctx, &ctx->header TSRMLS_CC);
+ php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
+ ctx->started = 1;
+ return SUCCESS;
+}
+static long php_http_env_response_stream_get_status(php_http_env_response_t *r)
+{
+ php_http_env_response_stream_ctx_t *ctx = r->ctx;
+
+ return ctx->status_code;
+}
+static STATUS php_http_env_response_stream_set_status(php_http_env_response_t *r, long http_code)
+{
+ php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+
+ if (stream_ctx->started || stream_ctx->finished) {
+ return FAILURE;
+ }
+
+ stream_ctx->status_code = http_code;
+
+ return SUCCESS;
+}
+static STATUS php_http_env_response_stream_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
+{
+ php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+
+ if (stream_ctx->started || stream_ctx->finished) {
+ return FAILURE;
+ }
+
+ memcpy(&stream_ctx->version, v, sizeof(stream_ctx->version));
+
+ return SUCCESS;
+}
+static STATUS php_http_env_response_stream_set_header_ex(php_http_env_response_t *r, zend_bool replace, const char *fmt, va_list argv)
+{
+ php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+ char *header_end, *header_str = NULL;
+ size_t header_len = 0;
+ zval *zheader, **zheader_ptr;
+
+ if (stream_ctx->started || stream_ctx->finished) {
+ return FAILURE;
+ }
+
+ header_len = vspprintf(&header_str, 0, fmt, argv);
+
+ if (!(header_end = strchr(header_str, ':'))) {
+ efree(header_str);
+ return FAILURE;
+ }
+
+ *header_end = '\0';
+
+ if (!replace && (SUCCESS == zend_hash_find(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader_ptr))) {
+ convert_to_array(*zheader_ptr);
+ *header_end = ':';
+ return add_next_index_stringl(*zheader_ptr, header_str, header_len, 0);
+ } else {
+ MAKE_STD_ZVAL(zheader);
+ ZVAL_STRINGL(zheader, header_str, header_len, 0);
+
+ if (SUCCESS != zend_hash_update(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader, sizeof(zval *), NULL)) {
+ zval_ptr_dtor(&zheader);
+ return FAILURE;
+ }
+
+ *header_end = ':';
+ return SUCCESS;
+ }
+}
+static STATUS php_http_env_response_stream_set_header(php_http_env_response_t *r, const char *fmt, ...)
+{
+ STATUS ret;
+ va_list argv;
+
+ va_start(argv, fmt);
+ ret = php_http_env_response_stream_set_header_ex(r, 1, fmt, argv);
+ va_end(argv);
+
+ return ret;
+}
+static STATUS php_http_env_response_stream_add_header(php_http_env_response_t *r, const char *fmt, ...)
+{
+ STATUS ret;
+ va_list argv;
+
+ va_start(argv, fmt);
+ ret = php_http_env_response_stream_set_header_ex(r, 0, fmt, argv);
+ va_end(argv);
+
+ return ret;
+}
+static STATUS php_http_env_response_stream_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
+{
+ php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+
+ if (stream_ctx->started || stream_ctx->finished) {
+ return FAILURE;
+ }
+
+ zend_hash_del(&stream_ctx->header, header_str, header_len + 1);
+ return SUCCESS;
+}
+static STATUS php_http_env_response_stream_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
+{
+ php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ if (stream_ctx->finished) {
+ return FAILURE;
+ }
+ if (!stream_ctx->started) {
+ if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
+ return FAILURE;
+ }
+ }
+
+ php_stream_write(stream_ctx->stream, data_str, data_len);
+
+ return SUCCESS;
+}
+static STATUS php_http_env_response_stream_flush(php_http_env_response_t *r)
+{
+ php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ if (stream_ctx->finished) {
+ return FAILURE;
+ }
+ if (!stream_ctx->started) {
+ if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
+ return FAILURE;
+ }
+ }
+
+ return php_stream_flush(stream_ctx->stream);
+}
+static STATUS php_http_env_response_stream_finish(php_http_env_response_t *r)
+{
+ php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
+ TSRMLS_FETCH_FROM_CTX(r->ts);
+
+ if (stream_ctx->finished) {
+ return FAILURE;
+ }
+ if (!stream_ctx->started) {
+ if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
+ return FAILURE;
+ }
+ }
+
+ stream_ctx->finished = 1;
+
+ return SUCCESS;
+}
+
+static php_http_env_response_ops_t php_http_env_response_stream_ops = {
+ php_http_env_response_stream_init,
+ php_http_env_response_stream_dtor,
+ php_http_env_response_stream_get_status,
+ php_http_env_response_stream_set_status,
+ php_http_env_response_stream_set_protocol_version,
+ php_http_env_response_stream_set_header,
+ php_http_env_response_stream_add_header,
+ php_http_env_response_stream_del_header,
+ php_http_env_response_stream_write,
+ php_http_env_response_stream_flush,
+ php_http_env_response_stream_finish
+};
+
+PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void)
+{
+ return &php_http_env_response_stream_ops;
+}
+
#undef PHP_HTTP_BEGIN_ARGS
#undef PHP_HTTP_EMPTY_ARGS
#define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpEnvResponse, method, 0, req_args)
PHP_HTTP_ARG_VAL(ob_flags, 0)
PHP_HTTP_END_ARGS;
+PHP_HTTP_BEGIN_ARGS(setEnvRequest, 1)
+ PHP_HTTP_ARG_OBJ(http\\Message, env_request, 1)
+PHP_HTTP_END_ARGS;
+
PHP_HTTP_BEGIN_ARGS(setContentType, 1)
PHP_HTTP_ARG_VAL(content_type, 0)
PHP_HTTP_END_ARGS;
static zend_function_entry php_http_env_response_method_entry[] = {
PHP_HTTP_ENV_RESPONSE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_HTTP_ENV_RESPONSE_ME(__invoke, ZEND_ACC_PUBLIC)
+ PHP_HTTP_ENV_RESPONSE_ME(setEnvRequest, ZEND_ACC_PUBLIC)
PHP_HTTP_ENV_RESPONSE_ME(setContentType, ZEND_ACC_PUBLIC)
PHP_HTTP_ENV_RESPONSE_ME(setContentDisposition, ZEND_ACC_PUBLIC)
PHP_HTTP_ENV_RESPONSE_ME(setContentEncoding, ZEND_ACC_PUBLIC)
}
}
+PHP_METHOD(HttpEnvResponse, setEnvRequest)
+{
+ zval *env_req = NULL;
+
+ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O", &env_req, php_http_message_get_class_entry())) {
+ set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0 TSRMLS_CC);
+ }
+}
+
PHP_METHOD(HttpEnvResponse, setContentType)
{
char *ct_str = NULL;
header_name_str = "If-Modified-Since";
header_name_len = lenof("If-Modified-Since");
}
- RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str, header_name_len TSRMLS_CC));
+
+ RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC));
}
RETURN_FALSE;
}
header_name_str = "If-None-Match";
header_name_len = lenof("If-None-Match");
}
- RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str, header_name_len TSRMLS_CC));
+ RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC));
}
RETURN_FALSE;
}
PHP_METHOD(HttpEnvResponse, send)
{
+ zval *zstream = NULL;
+ php_stream *s = NULL;
+
RETVAL_FALSE;
- if (SUCCESS == zend_parse_parameters_none()) {
- php_http_env_response_t *r = php_http_env_response_init(NULL, getThis() TSRMLS_CC);
+ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &zstream)) {
+ if (zstream) {
+ php_stream_from_zval_no_verify(s, &zstream);
- if (r) {
- RETVAL_SUCCESS(php_http_env_response_send(r));
- }
+ if (s) {
+ php_http_env_response_t *r;
+
+ if ((r = php_http_env_response_init(NULL, getThis(), php_http_env_response_get_stream_ops(), s TSRMLS_CC))) {
+ RETVAL_SUCCESS(php_http_env_response_send(r));
+ php_http_env_response_free(&r);
+ }
+ }
+ } else {
+ php_http_env_response_t r;
- php_http_env_response_free(&r);
+ if (php_http_env_response_init(&r, getThis(), NULL, NULL TSRMLS_CC)) {
+ RETVAL_SUCCESS(php_http_env_response_send(&r));
+ php_http_env_response_dtor(&r);
+ }
+ }
}
}
zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC);
zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC);
+ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC);
zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC);
#ifndef PHP_HTTP_ENV_RESPONSE_H
#define PHP_HTTP_ENV_RESPONSE_H
-typedef struct php_http_env_response {
+typedef struct php_http_env_response php_http_env_response_t;
+
+typedef struct php_http_env_response_ops {
+ STATUS (*init)(php_http_env_response_t *r, void *arg);
+ void (*dtor)(php_http_env_response_t *r);
+ long (*get_status)(php_http_env_response_t *r);
+ STATUS (*set_status)(php_http_env_response_t *r, long http_code);
+ STATUS (*set_protocol_version)(php_http_env_response_t *r, php_http_version_t *v);
+ STATUS (*set_header)(php_http_env_response_t *r, const char *fmt, ...);
+ STATUS (*add_header)(php_http_env_response_t *r, const char *fmt, ...);
+ STATUS (*del_header)(php_http_env_response_t *r, const char *header_str, size_t header_len);
+ STATUS (*write)(php_http_env_response_t *r, const char *data_str, size_t data_len);
+ STATUS (*flush)(php_http_env_response_t *r);
+ STATUS (*finish)(php_http_env_response_t *r);
+} php_http_env_response_ops_t;
+
+PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void);
+PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void);
+
+struct php_http_env_response {
+ void *ctx;
+ php_http_env_response_ops_t *ops;
+
php_http_buffer_t *buffer;
zval *options;
#ifdef ZTS
void ***ts;
#endif
-} php_http_env_response_t;
+};
-PHP_HTTP_API php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options TSRMLS_DC);
+PHP_HTTP_API php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *ops_ctx TSRMLS_DC);
PHP_HTTP_API STATUS php_http_env_response_send(php_http_env_response_t *r);
PHP_HTTP_API void php_http_env_response_dtor(php_http_env_response_t *r);
PHP_HTTP_API void php_http_env_response_free(php_http_env_response_t **r);
-PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len TSRMLS_DC);
-PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len TSRMLS_DC);
+PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC);
+PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC);
zend_class_entry *php_http_env_response_get_class_entry(void);
PHP_METHOD(HttpEnvResponse, __construct);
PHP_METHOD(HttpEnvResponse, __invoke);
+PHP_METHOD(HttpEnvResponse, setEnvRequest);
PHP_METHOD(HttpEnvResponse, setContentType);
PHP_METHOD(HttpEnvResponse, setContentDisposition);
PHP_METHOD(HttpEnvResponse, setContentEncoding);
return SUCCESS;
}
+PHP_HTTP_API void php_http_headers_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC)
+{
+ HashPosition pos1, pos2;
+ php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+ zval **header, **single_header;
+
+ FOREACH_HASH_KEYVAL(pos1, headers, key, header) {
+ if (key.type == HASH_KEY_IS_STRING) {
+ if (key.len == sizeof("Set-Cookie") && !strcasecmp(key.str, "Set-Cookie") && Z_TYPE_PP(header) == IS_ARRAY) {
+ FOREACH_VAL(pos2, *header, single_header) {
+ if (Z_TYPE_PP(single_header) == IS_ARRAY) {
+ php_http_cookie_list_t *cookie = php_http_cookie_list_from_struct(NULL, *single_header TSRMLS_CC);
+
+ if (cookie) {
+ char *buf;
+ size_t len;
+
+ php_http_cookie_list_to_string(cookie, &buf, &len);
+ cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", buf);
+ php_http_cookie_list_free(&cookie);
+ efree(buf);
+ }
+ } else {
+ zval *strval = php_http_header_value_to_string(*single_header TSRMLS_CC);
+
+ cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", Z_STRVAL_P(strval));
+ zval_ptr_dtor(&strval);
+ }
+ }
+ } else {
+ zval *strval = php_http_header_value_to_string(*header TSRMLS_CC);
+
+ cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.str, Z_STRVAL_P(strval));
+ zval_ptr_dtor(&strval);
+ }
+ }
+ }
+}
+
+PHP_HTTP_API void php_http_headers_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC)
+{
+ php_http_headers_to_callback(headers, 1, php_http_buffer_appendf, str TSRMLS_CC);
+}
+
+PHP_HTTP_API zval *php_http_header_value_to_string(zval *header TSRMLS_DC)
+{
+ zval *ret;
+
+ if (Z_TYPE_P(header) == IS_BOOL) {
+ MAKE_STD_ZVAL(ret);
+ ZVAL_STRING(ret, Z_BVAL_P(header) ? "true" : "false", 1);
+ } else if (Z_TYPE_P(header) == IS_ARRAY) {
+ zval **val;
+ HashPosition pos;
+ php_http_buffer_t str;
+
+ php_http_buffer_init(&str);
+ MAKE_STD_ZVAL(ret);
+ FOREACH_VAL(pos,header, val) {
+ zval *strval = php_http_header_value_to_string(*val TSRMLS_CC);
+
+ php_http_buffer_appendf(&str, str.used ? ", %s":"%s", Z_STRVAL_P(strval));
+ zval_ptr_dtor(&strval);
+ }
+ php_http_buffer_fix(&str);
+ ZVAL_STRINGL(ret, str.data, str.used, 0);
+ } else {
+ ret = php_http_zsep(1, IS_STRING, header);
+ }
+
+ return ret;
+}
+
#define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpHeader, method, 0, req_args)
#define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpHeader, method, 0)
#define PHP_HTTP_HEADER_ME(method, v) PHP_ME(HttpHeader, method, PHP_HTTP_ARGS(HttpHeader, method), v)
PHP_HTTP_API STATUS php_http_headers_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data TSRMLS_DC);
+PHP_HTTP_API void php_http_headers_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC);
+PHP_HTTP_API void php_http_headers_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC);
+
+PHP_HTTP_API zval *php_http_header_value_to_string(zval *header TSRMLS_DC);
+
zend_class_entry *php_http_header_get_class_entry(void);
PHP_METHOD(HttpHeader, __construct);
#include "php_http_api.h"
-static zval *message_header_strval(zval **header TSRMLS_DC);
static void message_headers(php_http_message_t *msg, php_http_buffer_t *str);
PHP_HTTP_API zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info TSRMLS_DC)
return msg;
}
-PHP_HTTP_API zval *php_http_message_header(php_http_message_t *msg, char *key_str, size_t key_len, int join)
+PHP_HTTP_API zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len, int join)
{
zval *ret = NULL, **header;
char *key = php_http_pretty_key(estrndup(key_str, key_len), key_len, 1, 1);
if (join && Z_TYPE_PP(header) == IS_ARRAY) {
TSRMLS_FETCH_FROM_CTX(msg->ts);
- ret = message_header_strval(header TSRMLS_CC);
+ ret = php_http_header_value_to_string(*header TSRMLS_CC);
} else {
Z_ADDREF_PP(header);
ret = *header;
}
}
-static zval *message_header_strval(zval **header TSRMLS_DC)
-{
- zval *ret;
-
- if (Z_TYPE_PP(header) == IS_BOOL) {
- MAKE_STD_ZVAL(ret);
- ZVAL_STRING(ret, Z_BVAL_PP(header) ? "true" : "false", 1);
- } else if (Z_TYPE_PP(header) == IS_ARRAY) {
- zval **val;
- HashPosition pos;
- php_http_buffer_t str;
-
- php_http_buffer_init(&str);
- MAKE_STD_ZVAL(ret);
- FOREACH_VAL(pos, *header, val) {
- zval *strval = message_header_strval(val TSRMLS_CC);
-
- php_http_buffer_appendf(&str, str.used ? ", %s":"%s", Z_STRVAL_P(strval));
- zval_ptr_dtor(&strval);
- }
- php_http_buffer_fix(&str);
- ZVAL_STRINGL(ret, str.data, str.used, 0);
- } else {
- ret = php_http_zsep(1, IS_STRING, *header);
- }
-
- return ret;
-}
-
static void message_headers(php_http_message_t *msg, php_http_buffer_t *str)
{
- php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
- HashPosition pos1;
- zval **header;
TSRMLS_FETCH_FROM_CTX(msg->ts);
switch (msg->type) {
}
php_http_message_update_headers(msg);
-
- FOREACH_HASH_KEYVAL(pos1, &msg->hdrs, key, header) {
- if (key.type == HASH_KEY_IS_STRING) {
- if (key.len == sizeof("Set-Cookie") && !strcasecmp(key.str, "Set-Cookie") && Z_TYPE_PP(header) == IS_ARRAY) {
- HashPosition pos2;
- zval **single_header;
-
- FOREACH_VAL(pos2, *header, single_header) {
- if (Z_TYPE_PP(single_header) == IS_ARRAY) {
- php_http_cookie_list_t *cookie = php_http_cookie_list_from_struct(NULL, *single_header TSRMLS_CC);
-
- if (cookie) {
- char *buf;
- size_t len;
-
- php_http_cookie_list_to_string(cookie, &buf, &len);
- php_http_buffer_appendf(str, "Set-Cookie: %s" PHP_HTTP_CRLF, buf);
- php_http_cookie_list_free(&cookie);
- efree(buf);
- }
- } else {
- zval *strval = message_header_strval(single_header TSRMLS_CC);
-
- php_http_buffer_appendf(str, "Set-Cookie: %s" PHP_HTTP_CRLF, Z_STRVAL_P(strval));
- zval_ptr_dtor(&strval);
- }
- }
- } else {
- zval *strval = message_header_strval(header TSRMLS_CC);
-
- php_http_buffer_appendf(str, "%s: %s" PHP_HTTP_CRLF, key.str, Z_STRVAL_P(strval));
- zval_ptr_dtor(&strval);
- }
- }
- }
+ php_http_headers_to_string(str, &msg->hdrs TSRMLS_CC);
}
PHP_HTTP_API void php_http_message_to_callback(php_http_message_t *msg, php_http_pass_callback_t cb, void *cb_arg)
}
if (msg) {
+ php_http_message_body_object_t *body_obj;
+
o->message = msg;
if (msg->parent) {
o->parent = php_http_message_object_new_ex(ce, msg->parent, NULL TSRMLS_CC);
}
- o->body = php_http_message_body_object_new_ex(php_http_message_body_get_class_entry(), php_http_message_body_copy(&msg->body, NULL, 0), NULL TSRMLS_CC);
+ o->body = php_http_message_body_object_new_ex(php_http_message_body_get_class_entry(), &msg->body, &body_obj TSRMLS_CC);
+ body_obj->shared = 1;
}
ov.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_object_free, NULL TSRMLS_CC);
if (!obj->message) {
obj->message = php_http_message_init(NULL, 0 TSRMLS_CC);
}
-
- if (obj->body.handle || SUCCESS == php_http_new(&obj->body, php_http_message_body_get_class_entry(), (php_http_new_t) php_http_message_body_object_new_ex, NULL, (void *) php_http_message_body_copy(&obj->message->body, NULL, 0), NULL TSRMLS_CC)) {
+ if (!obj->body.handle) {
+ php_http_message_body_object_t *body_obj;
+ obj->body = php_http_message_body_object_new_ex(php_http_message_body_get_class_entry(), &obj->message->body, &body_obj TSRMLS_CC);
+ body_obj->shared = 1;
+ }
+ if (obj->body.handle) {
RETVAL_OBJVAL(obj->body, 1);
}
}
obj->message = php_http_message_init(NULL, 0 TSRMLS_CC);
}
- RETVAL_OBJVAL(php_http_message_object_new_ex(obj->zo.ce, php_http_message_copy(obj->message, NULL), NULL TSRMLS_CC), 0);
+ RETVAL_OBJVAL(php_http_message_object_new_ex(obj->zo.ce, php_http_message_copy_ex(obj->message, NULL, 0), NULL TSRMLS_CC), 0);
}
} end_error_handling();
}
PHP_HTTP_API void php_http_message_update_headers(php_http_message_t *msg);
-PHP_HTTP_API zval *php_http_message_header(php_http_message_t *msg, char *key_str, size_t key_len, int join);
+PHP_HTTP_API zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len, int join);
PHP_HTTP_API zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary);
PHP_HTTP_API void php_http_message_to_string(php_http_message_t *msg, char **string, size_t *length);
{
php_http_message_body_object_t *obj = object;
- php_http_message_body_free(&obj->body);
-
+ if (!obj->shared) {
+ php_http_message_body_free(&obj->body);
+ }
zend_object_std_dtor((zend_object *) obj TSRMLS_CC);
efree(obj);
}
php_stream_from_zval(stream, &zstream);
if (stream) {
- if (obj->body) {
+ if (obj->body && !obj->shared) {
php_http_message_body_dtor(obj->body);
}
obj->body = php_http_message_body_init(obj->body, stream TSRMLS_CC);
typedef struct php_http_message_body_object {
zend_object zo;
php_http_message_body_t *body;
+ unsigned shared:1;
} php_http_message_body_object_t;
zend_class_entry *php_http_message_body_get_class_entry(void);
typedef size_t (*php_http_pass_callback_t)(void *cb_arg, const char *str, size_t len);
typedef size_t (*php_http_pass_php_http_buffer_callback_t)(void *cb_arg, php_http_buffer_t *str);
+typedef size_t (*php_http_pass_format_callback_t)(void *cb_arg, const char *fmt, ...);
typedef struct php_http_pass_fcall_arg {
zval *fcz;
PHP_HTTP_API HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len TSRMLS_DC);
-static inline HashTable *php_http_negotiate_language(HashTable *supported TSRMLS_DC)
+static inline HashTable *php_http_negotiate_language(HashTable *supported, php_http_message_t *request TSRMLS_DC)
{
HashTable *result = NULL;
size_t length;
- char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Language"), &length TSRMLS_CC);
+ char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Language"), &length, request TSRMLS_CC);
if (value) {
result = php_http_negotiate(value, length, supported, "-", 1 TSRMLS_CC);
return result;
}
-static inline HashTable *php_http_negotiate_encoding(HashTable *supported TSRMLS_DC)
+static inline HashTable *php_http_negotiate_encoding(HashTable *supported, php_http_message_t *request TSRMLS_DC)
{
HashTable *result = NULL;
size_t length;
- char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Encoding"), &length TSRMLS_CC);
+ char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Encoding"), &length, request TSRMLS_CC);
if (value) {
result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC);
return result;
}
-static inline HashTable *php_http_negotiate_charset(HashTable *supported TSRMLS_DC)
+static inline HashTable *php_http_negotiate_charset(HashTable *supported, php_http_message_t *request TSRMLS_DC)
{
HashTable *result = NULL;
size_t length;
- char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Charset"), &length TSRMLS_CC);
+ char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Charset"), &length, request TSRMLS_CC);
if (value) {
result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC);
return result;
}
-static inline HashTable *php_http_negotiate_content_type(HashTable *supported TSRMLS_DC)
+static inline HashTable *php_http_negotiate_content_type(HashTable *supported, php_http_message_t *request TSRMLS_DC)
{
HashTable *result = NULL;
size_t length;
- char *value = php_http_env_get_request_header(ZEND_STRL("Accept"), &length TSRMLS_CC);
+ char *value = php_http_env_get_request_header(ZEND_STRL("Accept"), &length, request TSRMLS_CC);
if (value) {
result = php_http_negotiate(value, length, supported, "/", 1 TSRMLS_CC);
#define PHP_HTTP_DO_NEGOTIATE(type, supported, rs_array) \
{ \
HashTable *result; \
- if ((result = php_http_negotiate_ ##type(supported TSRMLS_CC))) { \
+ if ((result = php_http_negotiate_ ##type(supported, NULL TSRMLS_CC))) { \
PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array); \
} else { \
PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array); \
php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
zval **val;
+ shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC);
FOREACH_KEYVAL(pos, zvalue, key, val) {
/* did you mean recursion? */
php_http_array_hashkey_stringify(&key);
$m->toString()
);
}
+
+ function testDetach() {
+ $m = new http\Message(
+ "HTTP/1.1 200 Ok\r\n".
+ "HTTP/1.1 201 Created\n".
+ "HTTP/1.1 302 Found\r\n"
+ );
+
+ $this->assertCount(3, $m);
+ $d = $m->detach();
+ $this->assertCount(3, $m);
+ $this->assertCount(1, $d);
+
+ $this->assertEquals("HTTP/1.1 302 Found\r\n\r\n", $d->toString(true));
+ }
+
+ function testPrepend() {
+ for ($i=0; $i<9; ++$i) {
+ $a[] = new http\Message("HTTP/1.1 ".($i+200));
+ }
+
+ foreach ($a as $m) {
+ if (isset($p)) $m->prepend($p);
+ $p = $m;
+ }
+
+ $this->assertEquals(
+ "HTTP/1.1 200\r\n\r\n".
+ "HTTP/1.1 201\r\n\r\n".
+ "HTTP/1.1 202\r\n\r\n".
+ "HTTP/1.1 203\r\n\r\n".
+ "HTTP/1.1 204\r\n\r\n".
+ "HTTP/1.1 205\r\n\r\n".
+ "HTTP/1.1 206\r\n\r\n".
+ "HTTP/1.1 207\r\n\r\n".
+ "HTTP/1.1 208\r\n\r\n",
+ $m->toString(true)
+ );
+ }
+
+ function testPrependError() {
+ $m = new http\Message("HTTP/1.1 200\r\nHTTP/1.1 201");
+ try {
+ $m->prepend($m->getParentMessage());
+ $this->assertFalse("this code should not be reached");
+ } catch (http\Exception $e) {
+ $this->assertEquals("Cannot prepend a message located within the same message chain", $e->getMessage());
+ }
+ }
+
+ function testToCallback() {
+ $m = new http\Message("HTTP/1.1 200 Ok");
+ $m->addHeader("Content-Type", "text/plain");
+ $m->getBody()->append("this\nis\nthe\ntext");
+
+ $d = new http\Encoding\Stream\Deflate;
+ $s = "";
+ $m->toCallback(function ($m, $data) use ($d) {
+ $s.=$d->update($data);
+ });
+ $s.=$d->finish();
+ $this->assertEquals($m->toString(), http\Encoding\Stream\Inflate::decode($s));
+ }
+
+ function testToStream() {
+ $m = new http\Message("HTTP/1.1 200 Ok");
+ $m->addHeader("Content-Type", "text/plain");
+ $m->getBody()->append("this\nis\nthe\ntext");
+
+ $f = tmpfile();
+ $m->toStream($f);
+ rewind($f);
+ $this->assertEquals((string) $m, stream_get_contents($f));
+ fclose($f);
+ }
+
+ function testBoundary() {
+ $p = new http\Message;
+ $p->addHeader("Content-Type", "text/plain");
+ $p->getBody()->append("data");
+
+ $m = new http\Message("HTTP/1.1 200");
+ $m->getBody()->addPart($p);
+ $this->assertStringMatchesFormat(
+ "HTTP/1.1 200\r\n".
+ "Content-Length: 97\r\n".
+ "Content-Type: multipart/form-data; boundary=\"%x.%x\"\r\n".
+ "\r\n".
+ "--%x.%x\r\n".
+ "Content-Type: text/plain\r\n".
+ "Content-Length: 4\r\n".
+ "\r\n".
+ "data\r\n".
+ "--%x.%x--\r\n".
+ "",
+ str_replace("\r", "", $m->toString()) // phpunit replaces \r\n with \n
+ );
+ }
}
--- /dev/null
+--TEST--
+env response body
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--INI--
+output_buffering=1
+--FILE--
+<?php
+echo "Test\n";
+$r = new http\Env\Response;
+var_dump((string) $r->getBody());
+?>
+Done
+--EXPECTF--
+Test
+string(5) "Test
+"
+Done
--- /dev/null
+--TEST--
+env response body error
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--INI--
+output_buffering=1
+--FILE--
+<?php
+echo "Test\n";
+ob_flush();
+try {
+ $r = new http\Env\Response;
+ var_dump((string) $r->getBody());
+} catch (http\Exception $e) {
+ echo $e->getMessage(),"\n";
+}
+?>
+Done
+--EXPECTF--
+Test
+Could not fetch response body, output has already been sent at %senvresponsebody002.php:3
+Done
if ($m->isMultipart($boundary)) {
var_dump($boundary);
- foreach ($m->splitMultipartBody() as $mm) {
- echo "===\n",$mm,"===\n";
+ foreach ($m->splitMultipartBody() as $i => $mm) {
+ echo "==$i==\n",$mm,"===\n";
}
}
?>
DONE
--EXPECTF--
string(40) "----------------------------6e182425881c"
-===
+==%d==
Content-Disposition: form-data; name="composer"; filename="composer.json"
Content-Type: application/octet-stream
Content-Length: 567
}
===
-===
+==%d==
Content-Disposition: form-data; name="LICENSE"; filename="LICENSE"
Content-Type: application/octet-stream
Content-Length: 1354
<?php
$msg = new http\Message("
+HTTP/1.1 201 Created
HTTP/1.1 200 Ok
String: foobar
");
--- /dev/null
+--TEST--
+http response stream
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+$f = tmpfile();
+
+$r = new http\Env\Response;
+$r->addHeader("foo", array("bar","baz"));
+$r->getBody()->append("foo");
+
+$r->send($f);
+
+rewind($f);
+var_dump(stream_get_contents($f));
+?>
+Done
+--EXPECT--
+Test
+string(77) "HTTP/1.1 200 OK
+Accept-Ranges: bytes
+Foo: bar, baz
+ETag: "8c736521"
+
+foo"
+Done
--- /dev/null
+--TEST--
+response env request
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--GET--
+dummy=1
+--FILE--
+<?php
+echo "Test\n";
+
+$tmp = tmpfile();
+
+// modify HTTP env
+$req = new http\Env\Request;
+$req->setHeader("Range", "bytes=2-4");
+
+$res = new http\Env\Response;
+$res->setEnvRequest($req);
+$res->setContentType("text/plain");
+$res->getBody()->append("012345679");
+$res->send($tmp);
+
+rewind($tmp);
+var_dump(stream_get_contents($tmp));
+
+?>
+Done
+--EXPECTF--
+Test
+string(141) "HTTP/1.1 206 Partial Content%c
+Accept-Ranges: bytes%c
+X-Powered-By: PHP/5.4.11-dev%c
+Content-Type: text/plain%c
+Content-Range: bytes 2-4/9%c
+%c
+234"
+Done
--- /dev/null
+--TEST--
+env response stream message
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--ENV--
+HTTP_ACCEPT_ENCODING=gzip
+--FILE--
+<?php
+
+$f = tmpfile();
+
+$r = new http\env\Response;
+$r->setHeader("foo","bar");
+$r->setContentEncoding(http\env\Response::CONTENT_ENCODING_GZIP);
+$r->setBody(new http\message\Body(fopen(__FILE__,"r")));
+$r->send($f);
+
+rewind($f);
+var_dump(stream_get_contents($f));
+
+--EXPECTREGEX--
+string\(\d+\) "HTTP\/1\.1 200 OK
+Accept-Ranges: bytes
+Foo: bar
+Content-Encoding: gzip
+Vary: Accept-Encoding
+ETag: "\w+-\w+-\w+"
+Last-Modified: \w+, \d+ \w+ \d{4} \d\d:\d\d:\d\d GMT
+
+\x1f\x8b\x08.+
--- /dev/null
+--TEST--
+http response stream cache negative
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--GET--
+a=b
+--ENV--
+HTTP_IF_MODIFIED_SINCE=Fri, 13 Feb 2009 23:31:30 GMT
+HTTP_IF_NONE_MATCH=0000-00-0000
+--FILE--
+<?php
+$f = tmpfile();
+$r = new http\Env\Response;
+$r->setBody(new http\Message\Body(fopen(__FILE__,"rb")));
+$r->setEtag("abc");
+$r->setLastModified(1234567891);
+$r->send($f);
+rewind($f);
+var_dump(stream_get_contents($f));
+?>
+--EXPECTF--
+string(355) "HTTP/1.1 200 OK%c
+Accept-Ranges: bytes%c
+X-Powered-By: %s%c
+ETag: "abc"%c
+Last-Modified: %s%c
+%c
+<?php
+$f = tmpfile();
+$r = new http\Env\Response;
+$r->setBody(new http\Message\Body(fopen(__FILE__,"rb")));
+$r->setEtag("abc");
+$r->setLastModified(1234567891);
+$r->send($f);
+rewind($f);
+var_dump(stream_get_contents($f));
+?>
+"
+
--- /dev/null
+--TEST--
+response stream ranges
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--ENV--
+HTTP_RANGE=bytes=2-4
+--GET--
+a=b
+--FILE--
+<?php
+$f = tmpfile();
+$r = new http\Env\Response;
+$r->setContentType("text/plain");
+$r->setContentDisposition(
+ array("attachment" => array(array("filename" => basename(__FILE__))))
+);
+$r->setBody(new http\Message\Body(fopen(__FILE__, "rb")));
+$r->send($f);
+rewind($f);
+var_dump(stream_get_contents($f));
+?>
+--EXPECTF--
+string(%i) "HTTP/1.1 206 Partial Content%c
+Accept-Ranges: bytes%c
+X-Powered-By: PHP/%s%c
+Content-Type: text/plain%c
+Content-Range: bytes 2-4/311%c
+%c
+php"
--- /dev/null
+--TEST--
+http response cache positive with env message
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--GET--
+dummy=1
+--FILE--
+<?php
+$e = new http\Env\Request;
+$e->setHeader("If-Modified-Since", "Fri, 13 Feb 2009 23:31:32 GMT");
+$r = new http\Env\Response;
+$r->setEnvRequest($e);
+$r->setBody(new http\Message\Body(fopen(__FILE__,"rb")));
+$r->setEtag("abc");
+$r->setLastModified(1234567891);
+$r->isCachedByEtag("If-None-Match") and die("Huh? etag? really?\n");
+$r->isCachedByLastModified("If-Modified-Since") or die("yep, I should be cached");
+$r->send();
+?>
+--EXPECTHEADERS--
+HTTP/1.1 304 Not Modified
+ETag: "abc"
+Last-Modified: Fri, 13 Feb 2009 23:31:31 GMT
+--EXPECT--
--- /dev/null
+--TEST--
+response content disposition
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--GET--
+dummy=1
+--FILE--
+<?php
+
+$r = new http\Env\Response;
+$r->setContentDisposition(array("attachment"=>array("filename"=>basename(__FILE__))));
+$r->setBody(new http\Message\Body(fopen(__FILE__,"r")));
+$r->send();
+
+?>
+--EXPECTHEADERS--
+Content-Disposition: attachment;filename=response012.php
+--EXPECT--
+<?php
+
+$r = new http\Env\Response;
+$r->setContentDisposition(array("attachment"=>array("filename"=>basename(__FILE__))));
+$r->setBody(new http\Message\Body(fopen(__FILE__,"r")));
+$r->send();
+
+?>
+
--- /dev/null
+--TEST--
+response deflate
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--GET--
+dummy=1
+--FILE--
+<?php
+
+$req = new http\Env\Request;
+$req->setHeader("Accept-Encoding", "deflate");
+
+$res = new http\Env\Response;
+$res->setCacheControl("public, max-age=3600");
+$res->setContentEncoding(http\Env\Response::CONTENT_ENCODING_GZIP);
+$res->getBody()->append("foobar");
+
+$res->setEnvRequest($req);
+$res->send();
+?>
+--EXPECTHEADERS--
+Content-Encoding: deflate
+Vary: Accept-Encoding
+Cache-Control: public, max-age=3600
+--EXPECTREGEX--
+^\x78\x9c.+
+
--- /dev/null
+--TEST--
+response invalid ranges
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+$f = tmpfile();
+$req = new http\Env\Request;
+$req->setHeader("Range", "bytes=321-123,123-0");
+$res = new http\Env\Response;
+$res->getBody()->append("foobar");
+$res->setEnvRequest($req);
+$res->send($f);
+rewind($f);
+var_dump(stream_get_contents($f));
+--EXPECTF--
+string(96) "HTTP/1.1 416 Requested Range Not Satisfiable
+Accept-Ranges: bytes
+Content-Range: bytes */6
+
+"
+
--- /dev/null
+--TEST--
+version parse error
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+$m = new http\Message;
+$m->setHttpVersion("1-1");
+$m->setHttpVersion("one.one");
+?>
+--EXPECTF--
+Notice: http\Message::setHttpVersion(): Non-standard version separator '-' in HTTP protocol version '1-1' in %s
+
+Warning: http\Message::setHttpVersion(): Could not parse HTTP protocol version 'one.one' in %s
+