- yay, I guess this needs to be done by hand...
[m6w6/ext-http] / http_message_api.c
index 0dec4095e2374c4e353f229e374e3837ead41dfb..dd2cfff2c55035cd949266aa6c37f5f984636940 100644 (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) {
@@ -41,11 +61,11 @@ PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_mes
        return message;
 }
 
-PHP_HTTP_API http_message *_http_message_parse_ex(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;
-       http_message *msg;
+       zend_bool free_msg = msg ? 0 : 1;
 
        if (message_length < HTTP_MSG_MIN_SIZE) {
                return NULL;
@@ -55,9 +75,7 @@ PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t message_
                return NULL;
        }
 
-       msg = http_message_new();
-       msg->len = message_length;
-       msg->raw = dup ? estrndup(message, message_length) : message;
+       msg = http_message_init(msg);
 
        if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) {
                body += lenof(HTTP_CRLF HTTP_CRLF);
@@ -65,13 +83,43 @@ PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t message_
        } else {
                header_length = message_length;
        }
-       
-       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);
+               }
+               return NULL;
+       }
+
        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;
 }
 
@@ -79,22 +127,22 @@ PHP_HTTP_API void _http_message_parse_headers_callback(void **message, char *htt
 {
        http_message *old = (http_message *) *message;
        http_message *new;
-       
+
        if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) {
                new = http_message_new();
-       
+
                new->nested = old;
                *message = new;
                *headers = &new->hdrs;
        } else {
                new = old;
        }
-       
+
        // response
        if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) {
                new->type = HTTP_MSG_RESPONSE;
                new->info.response.http_version = atof(http_line + lenof("HTTP/"));
-               new->info.response.status = atoi(http_line + lenof("HTTP/1.1 "));
+               new->info.response.code = atoi(http_line + lenof("HTTP/1.1 "));
        } else
        // request
        if (!strncmp(http_line + line_length - lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) {
@@ -114,49 +162,52 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_
        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.status);
-               break;
-       }
+       phpstr_init_ex(&str, 4096, 0);
+
+       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;
 
-       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;
+                       case HTTP_MSG_RESPONSE:
+                               phpstr_appendf(&str, "HTTP/%1.1f %d" HTTP_CRLF,
+                                       msg->info.response.http_version,
+                                       msg->info.response.code);
+                       break;
+               }
+
+               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;
                        }
-                       
-                       key = NULL;
                }
-       }
 
-       phpstr_appends(&str, HTTP_CRLF);
-       phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
+               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) {
@@ -171,15 +222,14 @@ PHP_HTTP_API void _http_message_dtor(http_message *message)
        if (message) {
                zend_hash_destroy(&message->hdrs);
                phpstr_dtor(PHPSTR(message));
-               if (message->raw) {
-                       efree(message->raw);
-               }
                if (message->type == HTTP_MSG_REQUEST) {
                        if (message->info.request.method) {
                                efree(message->info.request.method);
+                               message->info.request.method = NULL;
                        }
                        if (message->info.request.URI) {
                                efree(message->info.request.URI);
+                               message->info.request.URI = NULL;
                        }
                }
        }
@@ -190,6 +240,7 @@ PHP_HTTP_API void _http_message_free(http_message *message)
        if (message) {
                if (message->nested) {
                        http_message_free(message->nested);
+                       message->nested = NULL;
                }
                http_message_dtor(message);
                efree(message);