- message parser now recognizes nested messages if there's a content length header or chunked transfer encoding
- added (still crashing) HttpMessage::getMessage()
- some other fixes & adjustments
efree(HTTP_G(ctype));
HTTP_G(ctype) = NULL;
}
-
+
#ifdef HTTP_HAVE_CURL
# if LIBCURL_VERSION_NUM < 0x070c00
memset(&HTTP_G(curlerr), 0, sizeof(HTTP_G(curlerr)));
#define http_check_allowed_methods(m, l) _http_check_allowed_methods((m), (l) TSRMLS_CC)
static inline void _http_check_allowed_methods(char *methods, int length TSRMLS_DC)
{
- char *found, *header;
-
- if (!length || !SG(request_info).request_method) {
- return;
+ if (length && SG(request_info).request_method) {
+ if (SUCCESS != http_check_method(SG(request_info).request_method, methods)) {
+ char *header = emalloc(length + sizeof("Allow: "));
+ sprintf(header, "Allow: %s", methods);
+ http_exit(405, header);
+ }
}
-
- if ( (found = strstr(methods, SG(request_info).request_method)) &&
- (found == SG(request_info).request_method || !isalpha(found[-1])) &&
- (!isalpha(found[strlen(SG(request_info).request_method) + 1]))) {
- return;
- }
-
- header = emalloc(length + sizeof("Allow: "));
- sprintf(header, "Allow: %s", methods);
- http_exit(405, header);
}
/* }}} */
}
/* }}} */
+/* {{{ STATUS http_check_method(char *) */
+STATUS _http_check_method_ex(const char *method, const char *methods)
+{
+ const char *found;
+
+ if ( (found = strstr(methods, method)) &&
+ (found == method || !isalpha(found[-1])) &&
+ (!isalpha(found[strlen(method) + 1]))) {
+ return SUCCESS;
+ }
+ return FAILURE;
+}
+/* }}} */
+
/* {{{ zval *http_get_server_var_ex(char *, size_t) */
PHP_HTTP_API zval *_http_get_server_var_ex(const char *key, size_t key_size, zend_bool check TSRMLS_DC)
{
/* }}} */
-/* {{{ STATUS http_chunked_decode(char *, size_t, char **, size_t *) */
-PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded, size_t encoded_len,
+/* {{{ char *http_chunked_decode(char *, size_t, char **, size_t *) */
+PHP_HTTP_API char *_http_chunked_decode(const char *encoded, size_t encoded_len,
char **decoded, size_t *decoded_len TSRMLS_DC)
{
const char *e_ptr;
if (i == 9) {
http_error_ex(E_WARNING, HTTP_E_PARSE, "Chunk size is too long: 0x%s...", hex_len);
efree(*decoded);
- return FAILURE;
+ return NULL;
}
hex_len[i++] = *e_ptr++;
}
/* new line */
if (strncmp(e_ptr, HTTP_CRLF, 2)) {
- http_error_ex(E_WARNING, HTTP_E_PARSE, "Invalid character (expected 0x0D 0x0A; got: %x %x)", *e_ptr, *(e_ptr + 1));
+ http_error_ex(E_WARNING, HTTP_E_PARSE,
+ "Invalid character (expected 0x0D 0x0A; got: 0x%x(%c) 0x%x(%c))",
+ *e_ptr, *e_ptr, *(e_ptr + 1), *(e_ptr + 1));
efree(*decoded);
- return FAILURE;
+ return NULL;
}
/* hex to long */
if (error == hex_len) {
http_error_ex(E_WARNING, HTTP_E_PARSE, "Invalid chunk size string: '%s'", hex_len);
efree(*decoded);
- return FAILURE;
+ return NULL;
}
}
*decoded_len += chunk_len;
}
- return SUCCESS;
+ return e_ptr;
}
/* }}} */
if (!http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
return SUCCESS;
}
- return http_cache_exit_ex(etag, 1, 0);
+ return http_cache_exit_ex((char *)etag, 1, 0);
}
/* if no etag is given and we didn't already start ob_etaghandler -- start it */
/* {{{ proto bool http_cache_last_modified([int timestamp_or_expires]])
*
- * If timestamp_or_exires is greater than 0, it is handled as timestamp
+ * If timestamp_or_expires is greater than 0, it is handled as timestamp
* and will be sent as date of last modification. If it is 0 or omitted,
* the current time will be sent as Last-Modified date. If it's negative,
* it is handled as expiration time in seconds, which means that if the
}
convert_to_string_ex(&zdata);
- http_send_header("Accept-Ranges: bytes");
RETURN_SUCCESS(http_send_data(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata)));
}
/* }}} */
RETURN_FALSE;
}
- http_send_header("Accept-Ranges: bytes");
RETURN_SUCCESS(http_send_file(file));
}
/* }}} */
}
php_stream_from_zval(file, &zstream);
- http_send_header("Accept-Ranges: bytes");
RETURN_SUCCESS(http_send_stream(file));
}
/* }}} */
#include "php_http.h"
#include "php_http_std_defs.h"
#include "php_http_message_api.h"
+#include "php_http_api.h"
#include "php_http_headers_api.h"
#include "phpstr/phpstr.h"
+#define http_message_parse_nested(msg, begin, length) _http_message_parse_nested((msg), (begin), (length) TSRMLS_CC)
+static inline http_message *_http_message_parse_nested(http_message *msg, const char *begin, size_t length TSRMLS_DC)
+{
+ http_message *new;
+
+ while (isspace(*begin)) {
+ ++begin;
+ if (!length--) {
+ return NULL;
+ }
+ }
+
+ if (new = http_message_parse(begin, length)) {
+ new->nested = msg;
+ return new;
+ }
+ return NULL;
+}
+
PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_message_type type)
{
if (!message) {
return message;
}
-PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, char *message, size_t message_length, zend_bool dup TSRMLS_DC)
+PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, const char *message, size_t message_length TSRMLS_DC)
{
char *body = NULL;
size_t header_length = 0;
}
msg = http_message_init(msg);
- msg->len = message_length;
- msg->raw = dup ? estrndup(message, message_length) : message;
if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) {
body += lenof(HTTP_CRLF HTTP_CRLF);
header_length = message_length;
}
- if (SUCCESS != http_parse_headers_cb(message, header_length, &msg->hdrs, 1, http_message_parse_headers_callback, (void **) &msg)) {
+ if (SUCCESS != http_parse_headers_cb((char *)message, header_length, &msg->hdrs, 1, http_message_parse_headers_callback, (void **) &msg)) {
if (free_msg) {
http_message_free(msg);
}
}
if (body) {
- phpstr_from_string_ex(PHPSTR(msg), body, message_length - header_length);
+ zval **c;
+ http_message *nested;
+
+ if (SUCCESS == zend_hash_find(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void **) &c)) {
+ long len = atol(Z_STRVAL_PP(c));
+ phpstr_from_string_ex(PHPSTR(msg), body, len);
+ if (nested = http_message_parse_nested(msg, body + len, message + message_length - body - len)) {
+ return nested;
+ }
+ } else if (
+ SUCCESS == zend_hash_find(&msg->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"), (void **) &c) &&
+ !strcasecmp("chunked", Z_STRVAL_PP(c))) {
+
+ char *decoded, *end;
+ size_t decoded_len;
+
+ if (end = http_chunked_decode(body, message_length - header_length, &decoded, &decoded_len)) {
+ phpstr_from_string_ex(PHPSTR(msg), decoded, decoded_len);
+ efree(decoded);
+ if (nested = http_message_parse_nested(msg, end, message + message_length - end)) {
+ return nested;
+ }
+ }
+ } else {
+ phpstr_from_string_ex(PHPSTR(msg), body, message_length - header_length);
+ }
}
return msg;
ulong idx;
zval **header;
- phpstr_init_ex(&str, msg->len, 1);
- /* set sane alloc size */
- str.size = 4096;
-
- switch (msg->type)
- {
- case HTTP_MSG_REQUEST:
- phpstr_appendf(&str, "%s %s HTTP/%1.1f" HTTP_CRLF,
- msg->info.request.method,
- msg->info.request.URI,
- msg->info.request.http_version);
- break;
-
- case HTTP_MSG_RESPONSE:
- phpstr_appendf(&str, "HTTP/%1.1f %d" HTTP_CRLF,
- msg->info.response.http_version,
- msg->info.response.code);
- break;
- }
+ phpstr_init_ex(&str, 4096, 0);
- FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) {
- if (key) {
- zval **single_header;
-
- switch (Z_TYPE_PP(header))
- {
- case IS_STRING:
- phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(header));
- break;
-
- case IS_ARRAY:
- FOREACH_VAL(*header, single_header) {
- phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header));
- }
- break;
- }
+ do {
+
+ switch (msg->type)
+ {
+ case HTTP_MSG_REQUEST:
+ phpstr_appendf(&str, "%s %s HTTP/%1.1f" HTTP_CRLF,
+ msg->info.request.method,
+ msg->info.request.URI,
+ msg->info.request.http_version);
+ break;
- key = NULL;
+ case HTTP_MSG_RESPONSE:
+ phpstr_appendf(&str, "HTTP/%1.1f %d" HTTP_CRLF,
+ msg->info.response.http_version,
+ msg->info.response.code);
+ break;
}
- }
- phpstr_appends(&str, HTTP_CRLF);
- phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
+ FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) {
+ if (key) {
+ zval **single_header;
+
+ switch (Z_TYPE_PP(header))
+ {
+ case IS_STRING:
+ phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(header));
+ break;
+
+ case IS_ARRAY:
+ FOREACH_VAL(*header, single_header) {
+ phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header));
+ }
+ break;
+ }
+
+ key = NULL;
+ }
+ }
+
+ phpstr_appends(&str, HTTP_CRLF);
+ phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
+ phpstr_appends(&str, HTTP_CRLF);
+
+ } while (msg = msg->nested);
data = phpstr_data(&str, string, length);
if (!string) {
if (message) {
zend_hash_destroy(&message->hdrs);
phpstr_dtor(PHPSTR(message));
- if (message->raw) {
- efree(message->raw);
- message->raw = NULL;
- }
if (message->type == HTTP_MSG_REQUEST) {
if (message->info.request.method) {
efree(message->info.request.method);
if (message) {
if (message->nested) {
http_message_free(message->nested);
+ message->nested = NULL;
}
http_message_dtor(message);
efree(message);
PHP_ME(HttpMessage, setRequestUri, NULL, ZEND_ACC_PUBLIC)
PHP_ME(HttpMessage, getHttpVersion, NULL, ZEND_ACC_PUBLIC)
PHP_ME(HttpMessage, setHttpVersion, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpMessage, getNestedMessage, NULL, ZEND_ACC_PUBLIC)
PHP_ME(HttpMessage, toString, NULL, ZEND_ACC_PUBLIC)
ZEND_MALIAS(HttpMessage, __toString, toString, NULL, ZEND_ACC_PUBLIC)
DCL_PROP(PROTECTED, long, type, HTTP_MSG_NONE);
- DCL_PROP(PROTECTED, string, raw, "");
DCL_PROP(PROTECTED, string, body, "");
DCL_PROP(PROTECTED, string, requestMethod, "");
return EG(uninitialized_zval_ptr);
}
- zval_dtor(return_value);
+ zval_ptr_dtor(&return_value);
#if 0
fprintf(stderr, "Reading property: %s(%d==%d) (%lu)\n", Z_STRVAL_P(member), Z_STRLEN_P(member), strlen(Z_STRVAL_P(member)),
}
break;
- case HTTP_MSG_PROPHASH_RAW:
- if (msg->raw) {
- if (msg->len) {
- RETVAL_STRINGL(msg->raw, msg->len, 1);
- } else {
- RETVAL_STRINGL("", 0, 1);
- }
- } else {
- RETVAL_NULL();
- }
- break;
-
case HTTP_MSG_PROPHASH_BODY:
phpstr_fix(PHPSTR(msg));
RETVAL_PHPSTR(PHPSTR(msg), 0, 1);
break;
case HTTP_MSG_PROPHASH_NESTED_MESSAGE:
- RETVAL_NULL();
+ if (msg->nested) {
+ Z_TYPE_P(return_value) = IS_OBJECT;
+ return_value->value.obj = http_message_object_from_msg(msg->nested);
+ } else {
+ RETVAL_NULL();
+ }
break;
case HTTP_MSG_PROPHASH_REQUEST_METHOD:
}
break;
- case HTTP_MSG_PROPHASH_RAW:
- http_message_dtor(msg);
- http_message_parse_ex(msg, Z_STRVAL_P(value), Z_STRLEN_P(value), 1);
- break;
-
case HTTP_MSG_PROPHASH_BODY:
phpstr_dtor(PHPSTR(msg));
phpstr_from_string_ex(PHPSTR(msg), Z_STRVAL_P(value), Z_STRLEN_P(value));
zend_hash_clean(OBJ_PROP(obj));
ASSOC_PROP(obj, long, "type", msg->type);
- ASSOC_STRINGL(obj, "raw", msg->raw, msg->len)
ASSOC_STRINGL(obj, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg));
MAKE_STD_ZVAL(headers);
zval *zv, *version;
getObject(http_message_object, obj);
- if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv)) {
+ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &zv)) {
return;
}
}
/* }}} */
+/* {{{ proto HttpMessage HttpMessage::getNestedMessage()
+ *
+ * Get nested HTTP Message.
+ */
+PHP_METHOD(HttpMessage, getNestedMessage)
+{
+ zval *nested;
+ getObject(http_message_object, obj);
+
+ NO_ARGS;
+
+ nested = GET_PROP(obj, nestedMessage);
+ if (Z_TYPE_P(nested) == IS_OBJECT) {
+ Z_TYPE_P(return_value) = IS_OBJECT;
+ return_value->is_ref = 1;
+ return_value->value.obj = nested->value.obj;
+ zval_add_ref(&return_value);
+ } else {
+ RETVAL_NULL();
+ }
+}
+/* }}} */
+
/* {{{ proto string HttpMessage::toString()
*
* Get the string representation of the Message.
HTTP_G(etag_started) = 0;
}
+ /* enable partial dl and resume */
+ http_send_header("Accept-Ranges: bytes");
+
zend_hash_init(&ranges, 0, NULL, ZVAL_PTR_DTOR, 0);
range_status = http_get_request_ranges(&ranges, data_size);
return FAILURE;
}
if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) {
- return http_cache_exit(etag, 1, 1);
+ return http_cache_exit_ex(etag, 1, 1);
}
efree(etag);
}
/* send 304 Not Modified if last modified matches */
if (http_match_last_modified("HTTP_IF_MODIFIED_SINCE", HTTP_G(lmod))) {
- return http_cache_exit(http_date(HTTP_G(lmod)), 0, 1);
+ return http_cache_exit_ex(http_date(HTTP_G(lmod)), 0, 1);
}
/* send full entity */
#define http_exit_ex(s, h, f) _http_exit_ex((s), (h), (f) TSRMLS_CC)
extern STATUS _http_exit_ex(int status, char *header, zend_bool free_header TSRMLS_DC);
+#define http_check_method(m) http_check_method_ex((m), HTTP_KNOWN_METHODS)
+#define http_check_method_ex(m, a) _http_check_method_ex((m), (a))
+extern STATUS _http_check_method(const char *method, const char *methods);
+
#define HTTP_GSC(var, name, ret) HTTP_GSP(var, name, return ret)
#define HTTP_GSP(var, name, ret) \
if (!(var = _http_get_server_var_ex(name, strlen(name)+1, 1 TSRMLS_CC))) { \
PHP_HTTP_API zval *_http_get_server_var_ex(const char *key, size_t key_size, zend_bool check TSRMLS_DC);
#define http_chunked_decode(e, el, d, dl) _http_chunked_decode((e), (el), (d), (dl) TSRMLS_CC)
-PHP_HTTP_API STATUS _http_chunked_decode(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC);
+PHP_HTTP_API char *_http_chunked_decode(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC);
#define http_split_response(r, h, b) _http_split_response((r), (h), (b) TSRMLS_CC)
PHP_HTTP_API STATUS _http_split_response(zval *response, zval *headers, zval *body TSRMLS_DC);
} info;
- size_t len;
- char *raw;
-
http_message *nested;
};
#define http_message_init_ex(m, t) _http_message_init_ex((m), (t))
PHP_HTTP_API http_message *_http_message_init_ex(http_message *m, http_message_type t);
-#define http_message_parse(m, l) http_message_parse_ex(NULL, (m), (l), 1)
-#define http_message_parse_ex(h, m, l, d) _http_message_parse_ex((h), (m), (l), (d) TSRMLS_CC)
-PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, char *message, size_t length, zend_bool duplicate TSRMLS_DC);
+#define http_message_parse(m, l) http_message_parse_ex(NULL, (m), (l))
+#define http_message_parse_ex(h, m, l) _http_message_parse_ex((h), (m), (l) TSRMLS_CC)
+PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, const char *message, size_t length TSRMLS_DC);
#define http_message_parse_headers_callback _http_message_parse_headers_callback
PHP_HTTP_API void _http_message_parse_headers_callback(void *message, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC);
PHP_METHOD(HttpMessage, setRequestUri);
PHP_METHOD(HttpMessage, getHttpVersion);
PHP_METHOD(HttpMessage, setHttpVersion);
+PHP_METHOD(HttpMessage, getNestedMessage);
PHP_METHOD(HttpMessage, toString);
PHP_METHOD(HttpMessage, fromString);
#endif
#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+
/* HTTP 1.1 */ \
"GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, CONNECT, " \
/* WebDAV - RFC 2518 */ \
- /* "PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, " */ \
+ "PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, " \
/* WebDAV Versioning - RFC 3253 */ \
- /* "VERSION-CONTROL, REPORT, CHECKOUT, CHECKIN, UNCHECKOUT, " */ \
- /* "MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE-CONTROL, MKACTIVITY, " */ \
+ "VERSION-CONTROL, REPORT, CHECKOUT, CHECKIN, UNCHECKOUT, " \
+ "MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE-CONTROL, MKACTIVITY, " \
/* WebDAV Access Control - RFC 3744 */ \
- /* "ACL, " */ \
+ "ACL, " \
/* END */