PHP_FUNCTION(http_test)
{
-#define HTTP_MESSAGE_STR \
- "GET / HTTP/1.1\r\n" \
- "Content-Type: foo/bar\r\n" \
- "Robots: Noindex,Nofollow\r\n" \
- "\r\n" \
- "Body Data!\n"
-#define HTTP_MESSAGE_LEN lenof(HTTP_MESSAGE_STR)
- http_message *msg = http_message_parse(HTTP_MESSAGE_STR, HTTP_MESSAGE_LEN);
- char *str;
- size_t len;
-
- http_message_tostring(msg, &str, &len);
-
- RETVAL_STRINGL(str, len, 0);
- http_message_free(msg);
}
/*
/* }}} */
/* {{{ STATUS http_parse_headers(char *, size_t, HashTable *, zend_bool) */
-PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len,
- HashTable *headers, zend_bool prettify TSRMLS_DC)
+PHP_HTTP_API STATUS _http_parse_headers_ex(char *header, size_t header_len,
+ HashTable *headers, zend_bool prettify,
+ http_parse_headers_callback_t func, void **callback_data TSRMLS_DC)
{
- const char *colon = NULL, *line = NULL, *begin = header;
+ char *colon = NULL, *line = NULL, *begin = header;
zval array;
Z_ARRVAL(array) = headers;
return FAILURE;
}
- /* status code */
- if (!strncmp(header, "HTTP/1.", 7)) {
- char *end = strstr(header, HTTP_CRLF);
- size_t len = end - (header + lenof("HTTP/1.x "));
- char *val = estrndup(header + lenof("HTTP/1.x "), len);
-
- add_assoc_stringl(&array, "Status", val, len, 0);
- header = end + 2;
- }
-
line = header;
while (header_len >= (line - begin)) {
case 0:
--value_len; /* we don't have CR so value length is one char less */
case '\n':
- if (colon && ((!(*line - 1)) || ((*line != ' ') && (*line != '\t')))) {
-
- /* skip empty key */
- if (header != colon) {
- zval **previous = NULL;
- char *value = empty_string;
- int keylen = colon - header;
- char *key = estrndup(header, keylen);
-
- if (prettify) {
- key = pretty_key(key, keylen, 1, 1);
+ if ((!(*line - 1)) || ((*line != ' ') && (*line != '\t'))) {
+ /* response/request line */
+ if ( (!strncmp(header, "HTTP/1.", lenof("HTTP/1."))) ||
+ (!strncmp(line - lenof("HTTP/1.x\r") + value_len, "HTTP/1.", lenof("HTTP/1.")))) {
+ if (func) {
+ func(callback_data, header, header - (line + 1), &headers TSRMLS_CC);
+ Z_ARRVAL(array) = headers;
}
+ } else
+
+ /* "header: value" pair */
+ if (colon) {
+
+ /* skip empty key */
+ if (header != colon) {
+ zval **previous = NULL;
+ char *value = empty_string;
+ int keylen = colon - header;
+ char *key = estrndup(header, keylen);
+
+ if (prettify) {
+ key = pretty_key(key, keylen, 1, 1);
+ }
- value_len += line - colon - 1;
-
- /* skip leading ws */
- while (isspace(*(++colon))) --value_len;
- /* skip trailing ws */
- while (isspace(colon[value_len - 1])) --value_len;
+ value_len += line - colon - 1;
- if (value_len > 0) {
- value = estrndup(colon, value_len);
- } else {
- value_len = 0;
- }
+ /* skip leading ws */
+ while (isspace(*(++colon))) --value_len;
+ /* skip trailing ws */
+ while (isspace(colon[value_len - 1])) --value_len;
- /* if we already have got such a header make an array of those */
- if (SUCCESS == zend_hash_find(headers, key, keylen + 1, (void **) &previous)) {
- /* already an array? - just add */
- if (Z_TYPE_PP(previous) == IS_ARRAY) {
- add_next_index_stringl(*previous, value, value_len, 0);
+ if (value_len > 0) {
+ value = estrndup(colon, value_len);
} else {
- /* create the array */
- zval *new_array;
- MAKE_STD_ZVAL(new_array);
- array_init(new_array);
-
- add_next_index_stringl(new_array, Z_STRVAL_PP(previous), Z_STRLEN_PP(previous), 1);
- add_next_index_stringl(new_array, value, value_len, 0);
- add_assoc_zval(&array, key, new_array);
+ value_len = 0;
}
- previous = NULL;
- } else {
- add_assoc_stringl(&array, key, value, value_len, 0);
+ /* if we already have got such a header make an array of those */
+ if (SUCCESS == zend_hash_find(headers, key, keylen + 1, (void **) &previous)) {
+ /* already an array? - just add */
+ if (Z_TYPE_PP(previous) == IS_ARRAY) {
+ add_next_index_stringl(*previous, value, value_len, 0);
+ } else {
+ /* create the array */
+ zval *new_array;
+ MAKE_STD_ZVAL(new_array);
+ array_init(new_array);
+
+ add_next_index_stringl(new_array, Z_STRVAL_PP(previous), Z_STRLEN_PP(previous), 1);
+ add_next_index_stringl(new_array, value, value_len, 0);
+ add_assoc_zval(&array, key, new_array);
+ }
+
+ previous = NULL;
+ } else {
+ add_assoc_stringl(&array, key, value, value_len, 0);
+ }
+ efree(key);
}
- efree(key);
}
-
colon = NULL;
value_len = 0;
header += line - header;
}
/* }}} */
+PHP_HTTP_API void _http_parse_headers_default_callback(void **cb_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC)
+{
+ zval array;
+ Z_ARRVAL(array) = *headers;
+
+ /* response */
+ if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) {
+ add_assoc_stringl(&array, "Response Status", http_line + lenof("HTTP/1.x "), line_length - lenof("HTTP/1.x \r\n"), 0);
+ } else
+ /* request */
+ if (!strncmp(http_line + line_length - lenof("HTTP/1.x\r\n"), "HTTP/1.", lenof("HTTP/1."))) {
+ char *sep = strchr(http_line, ' ');
+
+ add_assoc_stringl(&array, "Request Method", http_line, sep - http_line, 1);
+ add_assoc_stringl(&array, "Request Uri", sep + 1, strstr(sep, "HTTP/1.") - sep + 1 + 1, 1);
+ }
+}
+
/* {{{ */
PHP_HTTP_API STATUS _http_parse_cookie(const char *cookie, HashTable *values TSRMLS_DC)
{
#include "phpstr/phpstr.h"
-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);
- }
- if (message->info.request.URI) {
- efree(message->info.request.URI);
- }
- }
- }
-}
-
-PHP_HTTP_API void _http_message_free(http_message *message)
-{
- if (message) {
- if (message->nested) {
- http_message_free(message->nested);
- }
- http_message_dtor(message);
- efree(message);
- }
-}
-
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(char *message, size_t length, zend_bool dup TSRMLS_DC)
+PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t message_length, zend_bool dup TSRMLS_DC)
{
- char *message_start = message, *body = NULL;
- size_t message_length = length, header_length = 0;
+ char *body = NULL;
+ size_t header_length = 0;
http_message *msg;
- if (length < HTTP_MSG_MIN_SIZE) {
- return NULL;
- }
- if (!message) {
+ if (message_length < HTTP_MSG_MIN_SIZE) {
return NULL;
}
- if (!(message_start = strstr(message, HTTP_CRLF))) {
+ if (!message) {
return NULL;
}
- msg = http_message_init();
+ msg = http_message_new();
+ msg->len = message_length;
+ msg->raw = dup ? estrndup(message, message_length) : message;
- msg->len = length;
- msg->raw = dup ? estrndup(message, length) : message;
-
- // response
- if (!strncmp(message, "HTTP/1.", lenof("HTTP/1."))) {
- msg->type = HTTP_MSG_RESPONSE;
- msg->info.response.http_version = atof(message + lenof("HTTP/"));
- msg->info.response.status = atoi(message + lenof("HTTP/1.1 "));
- } else
- // request
- if (!strncmp(message_start - lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) {
- const char *method_sep_uri = strchr(message, ' ');
-
- msg->type = HTTP_MSG_REQUEST;
- msg->info.request.http_version = atof(message_start - lenof("1.1"));
- msg->info.request.method = estrndup(message, method_sep_uri - message);
- msg->info.request.URI = estrndup(method_sep_uri + 1, message_start - method_sep_uri - 1 - lenof(" HTTP/1.1"));
+ if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) {
+ body += lenof(HTTP_CRLF HTTP_CRLF);
+ header_length = body - message;
} else {
- http_message_free(msg);
- return NULL;
+ header_length = message_length;
}
-
- message_start += lenof(HTTP_CRLF);
- message_length -= message_start - message;
-
- if (body = strstr(message_start, HTTP_CRLF HTTP_CRLF)) {
- body += lenof(HTTP_CRLF HTTP_CRLF);
- header_length = body - message_start;
+
+ http_parse_headers_cb(message, header_length, &msg->hdrs, 1, http_message_parse_headers_callback, (void **) &msg);
+
+ if (body) {
phpstr_from_string_ex(PHPSTR(msg), body, message_length - header_length);
+ }
+
+ return msg;
+}
+
+PHP_HTTP_API void _http_message_parse_headers_callback(void **message, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC)
+{
+ 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 {
- header_length = message_length;
+ 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 "));
+ } else
+ // request
+ if (!strncmp(http_line + line_length - lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) {
+ const char *method_sep_uri = strchr(http_line, ' ');
- if (SUCCESS != http_parse_headers_ex(message_start, header_length, &msg->hdrs, 1)) {
- http_message_free(msg);
- return NULL;
+ new->type = HTTP_MSG_REQUEST;
+ new->info.request.http_version = atof(http_line + line_length - lenof("1.1"));
+ new->info.request.method = estrndup(http_line, method_sep_uri - http_line);
+ new->info.request.URI = estrndup(method_sep_uri + 1, http_line + line_length - method_sep_uri - 1 - lenof(" HTTP/1.1"));
}
- return msg;
}
PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length)
zval **header;
phpstr_init_ex(&str, msg->len, 1);
+ /* set sane alloc size */
str.size = 4096;
switch (msg->type)
FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) {
if (key) {
+ zval **single_header;
+
switch (Z_TYPE_PP(header))
{
case IS_STRING:
break;
case IS_ARRAY:
- {
- zval **single_header;
-
FOREACH_VAL(*header, single_header) {
phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header));
}
- }
break;
}
phpstr_appends(&str, HTTP_CRLF);
phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
- phpstr_fix(&str);
data = phpstr_data(&str, string, length);
if (!string) {
phpstr_dtor(&str);
}
+
+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);
+ }
+ if (message->info.request.URI) {
+ efree(message->info.request.URI);
+ }
+ }
+ }
+}
+
+PHP_HTTP_API void _http_message_free(http_message *message)
+{
+ if (message) {
+ if (message->nested) {
+ http_message_free(message->nested);
+ }
+ http_message_dtor(message);
+ efree(message);
+ }
+}
+
+/*
+ * 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
+ */
+
RANGE_ERR
} http_range_status;
-#define http_parse_headers(h, l, a) _http_parse_headers_ex((h), (l), Z_ARRVAL_P(a), 1 TSRMLS_CC)
-#define http_parse_headers_ex(h, l, ht, p) _http_parse_headers_ex((h), (l), (ht), (p) TSRMLS_CC)
-PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len, HashTable *headers, zend_bool prettify TSRMLS_DC);
+typedef void (*http_parse_headers_callback_t)(void **callback_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC);
+
+#define http_parse_headers_default_callback _http_parse_headers_default_callback
+PHP_HTTP_API void _http_parse_headers_default_callback(void **cb_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC);
+
+#define http_parse_headers(h, l, a) _http_parse_headers_ex((h), (l), Z_ARRVAL_P(a), 1, _http_parse_headers_default_callback, NULL TSRMLS_CC)
+#define http_parse_headers_ex(h, l, ht, p) _http_parse_headers_ex((h), (l), (ht), (p), _http_parse_headers_default_callback, NULL TSRMLS_CC)
+#define http_parse_headers_cb(h, l, ht, p, f, d) _http_parse_headers_ex((h), (l), (ht), (p), (f), (d) TSRMLS_CC)
+PHP_HTTP_API STATUS _http_parse_headers_ex(char *header, size_t header_len, HashTable *headers, zend_bool prettify,
+ http_parse_headers_callback_t func, void *callback_data TSRMLS_DC);
#define http_parse_cookie(c, ht) _http_parse_cookie((c), (ht) TSRMLS_CC)
PHP_HTTP_API STATUS _http_parse_cookie(const char *cookie, HashTable *values TSRMLS_DC);
*/
#include "phpstr/phpstr.h"
+#include "php_http_headers_api.h"
typedef enum {
HTTP_MSG_NONE,
/* required minimum length of an HTTP message "HTTP/1.1 200\r\n" */
#define HTTP_MSG_MIN_SIZE 15
-#define HTTP_MSG_RAW(msg) ( (msg)->dup ? msg->raw.dup : msg->raw.ptr )
-
-#define http_message_init() _http_message_init_ex(NULL, 0)
+#define http_message_new() _http_message_init_ex(NULL, 0)
+#define http_message_init(m) _http_message_init_ex((m), 0)
#define http_message_init_ex(m, t) _http_message_init_ex((m), (t))
-#define http_message_free(m) _http_message_free((m))
-#define http_message_dtor(m) _http_message_dtor((m))
-
-PHP_HTTP_API void _http_message_dtor(http_message *message);
-PHP_HTTP_API void _http_message_free(http_message *message);
+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((m), (l), 1)
#define http_message_parse_ex(m, l, d) _http_message_parse_ex((m), (l), (d) TSRMLS_CC)
PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t length, zend_bool duplicate 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);
+
#define http_message_tostring(m, s, l) _http_message_tostring((m), (s), (l))
PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length);
+#define http_message_dtor(m) _http_message_dtor((m))
+PHP_HTTP_API void _http_message_dtor(http_message *message);
+
+#define http_message_free(m) _http_message_free((m))
+PHP_HTTP_API void _http_message_free(http_message *message);
+/*
+ * 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
+ */