HTTP
Michael Wallner, Sara Golemon, Daniel Stenberg (libcurl)
+
static inline void _http_check_allowed_methods(char *methods, int length TSRMLS_DC)
{
if (length && SG(request_info).request_method) {
- if (SUCCESS != http_check_method(SG(request_info).request_method, methods)) {
+ if (SUCCESS != http_check_method_ex(SG(request_info).request_method, methods)) {
char *header = emalloc(length + sizeof("Allow: "));
sprintf(header, "Allow: %s", methods);
http_exit(405, header);
/* {{{ char *http_chunked_decode(char *, size_t, char **, size_t *) */
-PHP_HTTP_API char *_http_chunked_decode(const char *encoded, size_t encoded_len,
+PHP_HTTP_API const char *_http_chunked_decode(const char *encoded, size_t encoded_len,
char **decoded, size_t *decoded_len TSRMLS_DC)
{
const char *e_ptr;
hex_len[i++] = *e_ptr++;
}
- /* reached the end */
- if (!strcmp(hex_len, "0")) {
- break;
- }
-
- /* new line */
- if (strncmp(e_ptr, HTTP_CRLF, 2)) {
- 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 NULL;
- }
-
/* hex to long */
{
char *error = NULL;
}
}
+ /* reached the end */
+ if (!chunk_len) {
+ break;
+ }
+
+ /* new line */
+ if (strncmp(e_ptr, HTTP_CRLF, 2)) {
+ http_error_ex(E_WARNING, HTTP_E_PARSE,
+ "Invalid character (expected 0x0D 0x0A; got: 0x%x 0x%x)", *e_ptr, *(e_ptr + 1));
+ efree(*decoded);
+ return NULL;
+ }
+
memcpy(d_ptr, e_ptr += 2, chunk_len);
d_ptr += chunk_len;
e_ptr += chunk_len + 2;
memcpy(*body, real_body, *body_len);
}
- return http_parse_headers_ex(header, real_body ? response_len - *body_len : response_len, headers, 1);
+ return http_parse_headers_ex(header, headers, 1);
}
/* }}} */
/* {{{ static size_t http_curl_callback(char *, size_t, size_t, void *) */
static size_t http_curl_callback(char *buf, size_t len, size_t n, void *s)
{
- return phpstr_append(PHPSTR(s), buf, len *= n);
+ return s ? phpstr_append(PHPSTR(s), buf, len * n) : len * n;
}
/* }}} */
/* compress, empty string enables deflate and gzip */
if (zoption = http_curl_getopt(options, "compress", IS_BOOL)) {
if (Z_LVAL_P(zoption)) {
- HTTP_CURL_OPT(ENCODING, "");
+ HTTP_CURL_OPT(ENCODING, http_curl_copystr(""));
}
}
#define HTTP_CURL_OPT_STRING_EX(keyname, optname) \
if (!strcasecmp(key, #keyname)) { \
convert_to_string_ex(param); \
- HTTP_CURL_OPT(optname, Z_STRVAL_PP(param)); \
+ HTTP_CURL_OPT(optname, http_curl_copystr(Z_STRVAL_PP(param))); \
key = NULL; \
continue; \
}
RETURN_FALSE;
}
- if (SUCCESS == http_chunked_decode(encoded, encoded_len, &decoded, &decoded_len)) {
+ if (NULL != http_chunked_decode(encoded, encoded_len, &decoded, &decoded_len)) {
RETURN_STRINGL(decoded, decoded_len, 0);
} else {
RETURN_FALSE;
* <?php
* array(
* 0 => array(
- * 'Status' => '200 Ok',
+ * 'Response Status' => '200 Ok',
* 'Content-Type' => 'text/plain',
* 'Content-Language' => 'en-US'
* ),
*/
PHP_FUNCTION(http_parse_headers)
{
- char *header, *rnrn;
+ char *header;
int header_len;
if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &header, &header_len)) {
}
array_init(return_value);
-
- if (rnrn = strstr(header, HTTP_CRLF HTTP_CRLF)) {
- header_len = rnrn - header + 2;
- }
- if (SUCCESS != http_parse_headers(header, header_len, return_value)) {
+ if (SUCCESS != http_parse_headers(header, return_value)) {
http_error(E_WARNING, HTTP_E_PARSE, "Could not parse HTTP headers");
zval_dtor(return_value);
RETURN_FALSE;
PHP_FUNCTION(http_test)
{
+ RETURN_NULL();
}
/*
}
/* }}} */
-/* {{{ STATUS http_parse_headers(char *, size_t, HashTable *, zend_bool) */
-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)
+/* {{{ STATUS http_parse_headers(char *, HashTable *, zend_bool) */
+PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *headers, zend_bool prettify, http_parse_headers_callback_t func, void **callback_data TSRMLS_DC)
{
- char *colon = NULL, *line = NULL, *begin = header;
+ const char *colon = NULL, *line = NULL, *begin = header, *crlfcrlf = NULL;
+ size_t header_len;
zval array;
Z_ARRVAL(array) = headers;
+ if (crlfcrlf = strstr(header, HTTP_CRLF HTTP_CRLF)) {
+ header_len = crlfcrlf - header;
+ } else {
+ header_len = strlen(header);
+ }
+
+
if (header_len < 2 || !strchr(header, ':')) {
http_error(E_WARNING, HTTP_E_PARSE, "Cannot parse too short or malformed HTTP headers");
return FAILURE;
case '\n':
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 ( (!strncmp(header, "HTTP/1.", lenof("HTTP/1."))) ||
+ (!strncmp(line - lenof("HTTP/1.x" HTTP_CRLF) + value_len, "HTTP/1.", lenof("HTTP/1.")))) {
if (func) {
- func(callback_data, header, line - header + value_len, &headers TSRMLS_CC);
+ func(header, &headers, callback_data TSRMLS_CC);
Z_ARRVAL(array) = headers;
}
} else
efree(key);
}
}
+ /* stop at CRLF CRLF */
+ if (!strncmp(HTTP_CRLF, line + 1, lenof(HTTP_CRLF))) {
+ return SUCCESS;
+ }
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)
+PHP_HTTP_API void _http_parse_headers_default_callback(const char *http_line, HashTable **headers, void **cb_data TSRMLS_DC)
{
zval array;
+ char *crlf = NULL;
+ size_t line_length;
Z_ARRVAL(array) = *headers;
+ if (crlf = strstr(http_line, HTTP_CRLF)) {
+ line_length = crlf - http_line;
+ } else {
+ line_length = strlen(http_line);
+ }
+
/* 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"), 1);
+ char *status = estrndup(http_line + lenof("HTTP/1.x "), line_length - lenof("HTTP/1.x "));
+ add_assoc_stringl(&array, "Response Status", status, line_length - lenof("HTTP/1.x "), 0);
} else
/* request */
- if (!strncmp(http_line + line_length - lenof("HTTP/1.x\r\n"), "HTTP/1.", lenof("HTTP/1."))) {
+ if (!strncmp(http_line + line_length - lenof("HTTP/1.x"), "HTTP/1.", lenof("HTTP/1."))) {
char *sep = strchr(http_line, ' ');
+ char *url = estrndup(sep + 1, strstr(sep, "HTTP/1.") - sep + 1 + 1);
+ char *met = estrndup(http_line, sep - 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);
+ add_assoc_stringl(&array, "Request Method", met, sep - http_line, 0);
+ add_assoc_stringl(&array, "Request Uri", url, strstr(sep, "HTTP/1.") - sep + 1 + 1, 0);
}
}
#include "php.h"
#include "php_http.h"
#include "php_http_std_defs.h"
-#include "php_http_message_api.h"
#include "php_http_api.h"
+#include "php_http_message_api.h"
#include "php_http_headers_api.h"
+#include "php_http_send_api.h"
+#include "php_http_curl_api.h"
+#include "php_http_url_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)
+#define http_message_headers_cb _http_message_headers_cb
+static void _http_message_headers_cb(const char *http_line, HashTable **headers, void **message TSRMLS_DC)
{
- http_message *new;
+ size_t line_length;
+ char *crlf = NULL;
+ http_message *new, *old = (http_message *) *message;
- while (isspace(*begin)) {
- ++begin;
- if (!length--) {
- return NULL;
- }
+ if (crlf = strstr(http_line, HTTP_CRLF)) {
+ line_length = crlf - http_line;
+ } else {
+ line_length = strlen(http_line);
}
- if (new = http_message_parse(begin, length)) {
- new->nested = msg;
- return new;
+ if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) {
+ new = http_message_new();
+
+ new->parent = old;
+ *message = new;
+ *headers = &new->hdrs;
+ } else {
+ new = old;
+ }
+
+ while (isspace(http_line[line_length-1])) --line_length;
+
+ // 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.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."))) {
+ const char *method_sep_uri = strchr(http_line, ' ');
+ 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"));
+ }
+}
+
+#define http_message_init_type _http_message_init_type
+static inline void _http_message_init_type(http_message *message, http_message_type type)
+{
+ switch (message->type = type)
+ {
+ case HTTP_MSG_RESPONSE:
+ message->info.response.http_version = .0;
+ message->info.response.code = 0;
+ break;
+
+ case HTTP_MSG_REQUEST:
+ message->info.request.http_version = .0;
+ message->info.request.method = NULL;
+ message->info.request.URI = NULL;
+ break;
+
+ case HTTP_MSG_NONE:
+ default:
+ break;
+ }
+}
+
+#define http_message_header(m, h) _http_message_header_ex((m), (h), sizeof(h))
+#define http_message_header_ex _http_message_header_ex
+static inline zval *_http_message_header_ex(http_message *msg, char *key_str, size_t key_len)
+{
+ zval **header;
+ if (SUCCESS == zend_hash_find(&msg->hdrs, key_str, key_len, (void **) &header)) {
+ return *header;
}
return NULL;
}
message = ecalloc(1, sizeof(http_message));
}
- message->type = type;
- message->nested = NULL;
+ http_message_init_type(message, type);
+ message->parent = NULL;
phpstr_init(&message->body);
zend_hash_init(&message->hdrs, 0, NULL, ZVAL_PTR_DTOR, 0);
return message;
}
+
+PHP_HTTP_API void _http_message_set_type(http_message *message, http_message_type type)
+{
+ /* just act if different */
+ if (type != message->type) {
+
+ /* free request info */
+ 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);
+ }
+ }
+
+ /* init */
+ http_message_init_type(message, type);
+ }
+}
+
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;
zend_bool free_msg = msg ? 0 : 1;
if (message_length < HTTP_MSG_MIN_SIZE) {
msg = http_message_init(msg);
- if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) {
- body += lenof(HTTP_CRLF HTTP_CRLF);
- header_length = body - message;
- } else {
- header_length = message_length;
- }
-
- if (SUCCESS != http_parse_headers_cb((char *)message, header_length, &msg->hdrs, 1, http_message_parse_headers_callback, (void **) &msg)) {
+ if (SUCCESS != http_parse_headers_cb(message, &msg->hdrs, 1, http_message_headers_cb, (void **) &msg)) {
if (free_msg) {
http_message_free(msg);
}
return NULL;
}
- if (body) {
- zval **c;
- http_message *nested;
+ /* header parsing stops at CRLF CRLF */
+ if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) {
+ zval *c;
+ const char *continue_at = NULL;
+
+ body += lenof(HTTP_CRLF HTTP_CRLF);
- if (SUCCESS == zend_hash_find(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void **) &c)) {
- long len = atol(Z_STRVAL_PP(c));
+ /* message has content-length header */
+ if (c = http_message_header(msg, "Content-Length")) {
+ long len = atol(Z_STRVAL_P(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;
+ continue_at = body + len;
+ } else
+
+ /* message has chunked transfer encoding */
+ if (c = http_message_header(msg, "Transfer-Encoding")) {
+ if (!strcasecmp("chunked", Z_STRVAL_P(c))) {
+ char *decoded;
+ size_t decoded_len;
+
+ if (continue_at = http_chunked_decode(body, message + message_length - body, &decoded, &decoded_len)) {
+ phpstr_from_string_ex(PHPSTR(msg), decoded, decoded_len);
+ }
+ }
+ } else
+
+ /* message has content-range header */
+ if (c = http_message_header(msg, "Content-Range")) {
+ ulong start = 0, end = 0;
+
+ sscanf(Z_STRVAL_P(c), "bytes=%lu-%lu", &start, &end);
+ if (end > start) {
+ phpstr_from_string_ex(PHPSTR(msg), body, (size_t) (end - start));
+ continue_at = body + (end - start);
}
- } 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
+
+ /* no headers that indicate content length */
+ if (1) {
+ phpstr_from_string_ex(PHPSTR(msg), body, message + message_length - body);
+ }
+
+ /* check for following messages */
+ if (continue_at) {
+ while (isspace(*continue_at)) ++continue_at;
+ if (continue_at < (message + message_length)) {
+ http_message *next = NULL, *most = NULL;
+
+ /* set current message to parent of most parent following messages and return deepest */
+ if (most = next = http_message_parse(continue_at, message + message_length - continue_at)) {
+ while (most->parent) most = most->parent;
+ most->parent = msg;
+ msg = next;
}
}
- } else {
- 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)
+PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length)
{
- http_message *old = (http_message *) *message;
- http_message *new;
+ phpstr str;
+ char *key, *data;
+ ulong idx;
+ zval **header;
- if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) {
- new = http_message_new();
+ phpstr_init_ex(&str, 4096, 0);
- new->nested = old;
- *message = new;
- *headers = &new->hdrs;
- } else {
- new = old;
+ 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;
+
+ case HTTP_MSG_NONE:
+ default:
+ break;
}
- // 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.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."))) {
- const char *method_sep_uri = strchr(http_line, ' ');
+ 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;
+ }
- 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"));
+ key = NULL;
+ }
+ }
+
+ phpstr_appends(&str, HTTP_CRLF);
+ phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
+ phpstr_appends(&str, HTTP_CRLF);
+
+ data = phpstr_data(&str, string, length);
+ if (!string) {
+ efree(data);
}
+
+ phpstr_dtor(&str);
}
-PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length)
+PHP_HTTP_API void _http_message_serialize(http_message *message, char **string, size_t *length)
{
+ char *buf;
+ size_t len;
phpstr str;
- char *key, *data;
- ulong idx;
- zval **header;
- phpstr_init_ex(&str, 4096, 0);
+ phpstr_init(&str);
do {
+ http_message_tostring(message, &buf, &len);
+ phpstr_append(&str, buf, len);
+ efree(buf);
+ } while (message = message->parent);
+
+ buf = phpstr_data(&str, string, length);
+ if (!string) {
+ efree(buf);
+ }
+
+ phpstr_dtor(&str);
+}
+
+PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC)
+{
+ STATUS rs = FAILURE;
- switch (msg->type)
+ switch (message->type)
+ {
+ case HTTP_MSG_RESPONSE:
{
- 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;
+ char *key;
+ ulong idx;
+ zval **val;
+
+ FOREACH_HASH_KEYVAL(&message->hdrs, key, idx, val) {
+ if (key) {
+ char *header;
+ spprintf(&header, 0, "%s: %s", key, Z_STRVAL_PP(val));
+ http_send_header(header);
+ efree(header);
+ key = NULL;
+ }
+ }
+ rs = SUCCESS == http_send_status(message->info.response.code) &&
+ SUCCESS == http_send_data(PHPSTR_VAL(message), PHPSTR_LEN(message)) ?
+ SUCCESS : FAILURE;
}
+ 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_REQUEST:
+ {
+#ifdef HTTP_HAVE_CURL
+ char *uri = NULL;
+ zval **zhost, options, headers;
+
+ array_init(&options);
+ array_init(&headers);
+ zend_hash_copy(Z_ARRVAL(headers), &message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+ add_assoc_zval(&options, "headers", &headers);
+
+ /* check host header */
+ if (SUCCESS == zend_hash_find(&message->hdrs, "Host", sizeof("Host"), (void **) &zhost)) {
+ char *colon = NULL, *host = NULL;
+ size_t host_len = 0;
+ int port = 0;
+
+ /* check for port */
+ if (colon = strchr(Z_STRVAL_PP(zhost), ':')) {
+ port = atoi(colon + 1);
+ host = estrndup(Z_STRVAL_PP(zhost), host_len = (Z_STRVAL_PP(zhost) - colon - 1));
+ } else {
+ host = estrndup(Z_STRVAL_PP(zhost), host_len = Z_STRLEN_PP(zhost));
}
-
- key = NULL;
+ uri = http_absolute_uri_ex(
+ message->info.request.URI, strlen(message->info.request.URI),
+ NULL, 0, host, host_len, port);
+ efree(host);
+ } else {
+ uri = http_absolute_uri(message->info.request.URI);
}
- }
- phpstr_appends(&str, HTTP_CRLF);
- phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
- phpstr_appends(&str, HTTP_CRLF);
+ if (!strcasecmp("POST", message->info.request.method)) {
+ rs = http_post_data(uri, PHPSTR_VAL(message), PHPSTR_LEN(message), Z_ARRVAL(options), NULL, NULL);
+ } else
+ if (!strcasecmp("GET", message->info.request.method)) {
+ rs = http_get(uri, Z_ARRVAL(options), NULL, NULL);
+ } else
+ if (!strcasecmp("HEAD", message->info.request.method)) {
+ rs = http_head(uri, Z_ARRVAL(options), NULL, NULL);
+ } else {
+ http_error_ex(E_WARNING, HTTP_E_MSG,
+ "Cannot send HttpMessage. Request method %s not supported",
+ message->info.request.method);
+ }
- } while (msg = msg->nested);
+ efree(uri);
+#else
+ http_error(E_WARNING, HTTP_E_MSG, "HTTP requests not supported - ext/http was not linked against libcurl.");
+#endif
+ }
+ break;
- data = phpstr_data(&str, string, length);
- if (!string) {
- efree(data);
+ case HTTP_MSG_NONE:
+ default:
+ http_error(E_WARNING, HTTP_E_MSG, "HttpMessage is neither of type HTTP_MSG_REQUEST nor HTTP_MSG_RESPONSE");
+ break;
}
- phpstr_dtor(&str);
+ return rs;
}
PHP_HTTP_API void _http_message_dtor(http_message *message)
if (message) {
zend_hash_destroy(&message->hdrs);
phpstr_dtor(PHPSTR(message));
- if (message->type == HTTP_MSG_REQUEST) {
+ if (HTTP_MSG_TYPE(REQUEST, message)) {
if (message->info.request.method) {
efree(message->info.request.method);
message->info.request.method = NULL;
PHP_HTTP_API void _http_message_free(http_message *message)
{
if (message) {
- if (message->nested) {
- http_message_free(message->nested);
- message->nested = NULL;
+ if (message->parent) {
+ http_message_free(message->parent);
+ message->parent = NULL;
}
http_message_dtor(message);
efree(message);
zend_class_entry *http_message_object_ce;
zend_function_entry http_message_object_fe[] = {
+ PHP_ME(HttpMessage, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(HttpMessage, getBody, NULL, ZEND_ACC_PUBLIC)
PHP_ME(HttpMessage, getHeaders, NULL, ZEND_ACC_PUBLIC)
PHP_ME(HttpMessage, setHeaders, NULL, ZEND_ACC_PUBLIC)
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, getParentMessage, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(HttpMessage, send, NULL, ZEND_ACC_PUBLIC)
PHP_ME(HttpMessage, toString, NULL, ZEND_ACC_PUBLIC)
-
+
ZEND_MALIAS(HttpMessage, __toString, toString, NULL, ZEND_ACC_PUBLIC)
PHP_ME(HttpMessage, fromString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
o = ecalloc(1, sizeof(http_message_object));
o->zo.ce = ce;
o->message = NULL;
+ o->parent.handle = 0;
+ o->parent.handlers = NULL;
if (msg) {
o->message = msg;
- if (msg->nested) {
- o->nested = http_message_object_from_msg(msg->nested);
+ if (msg->parent) {
+ o->parent = http_message_object_from_msg(msg->parent);
}
}
DCL_PROP(PROTECTED, long, responseCode, 0);
DCL_PROP_N(PROTECTED, httpVersion);
DCL_PROP_N(PROTECTED, headers);
-
- DCL_PROP_N(PUBLIC, nestedMessage);
+ DCL_PROP_N(PROTECTED, parentMessage);
}
static void _http_message_object_free(zend_object *object TSRMLS_DC)
return EG(uninitialized_zval_ptr);
}
- zval_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)),
- zend_get_hash_value(Z_STRVAL_P(member), strlen(Z_STRVAL_P(member)) + 1)
- );
+#if 1
+ fprintf(stderr, "Reading property: %s(%d==%d)\n", Z_STRVAL_P(member), Z_STRLEN_P(member), strlen(Z_STRVAL_P(member)));
#endif
- switch (zend_get_hash_value(Z_STRVAL_P(member), strlen(Z_STRVAL_P(member)) + 1))
+ switch (zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1))
{
case HTTP_MSG_PROPHASH_TYPE:
RETVAL_LONG(msg->type);
case HTTP_MSG_PROPHASH_HTTP_VERSION:
switch (msg->type)
{
- case HTTP_MSG_NONE:
- RETVAL_NULL();
- break;
-
case HTTP_MSG_REQUEST:
RETVAL_DOUBLE(msg->info.request.http_version);
break;
case HTTP_MSG_RESPONSE:
RETVAL_DOUBLE(msg->info.response.http_version);
break;
+
+ case HTTP_MSG_NONE:
+ default:
+ RETVAL_NULL();
+ break;
}
break;
zend_hash_copy(Z_ARRVAL_P(return_value), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
break;
- case HTTP_MSG_PROPHASH_NESTED_MESSAGE:
- if (msg->nested) {
+ case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
+ if (msg->parent) {
+ RETVAL_OBJVAL(obj->parent);
Z_TYPE_P(return_value) = IS_OBJECT;
- return_value->value.obj = obj->nested;
+ return_value->value.obj = obj->parent;
zend_objects_store_add_ref(return_value TSRMLS_CC);
} else {
RETVAL_NULL();
break;
case HTTP_MSG_PROPHASH_REQUEST_METHOD:
- if (msg->type == HTTP_MSG_REQUEST && msg->info.request.method) {
+ if (HTTP_MSG_TYPE(REQUEST, msg) && msg->info.request.method) {
RETVAL_STRING(msg->info.request.method, 1);
} else {
RETVAL_NULL();
break;
case HTTP_MSG_PROPHASH_REQUEST_URI:
- if (msg->type == HTTP_MSG_REQUEST && msg->info.request.URI) {
+ if (HTTP_MSG_TYPE(REQUEST, msg) && msg->info.request.URI) {
RETVAL_STRING(msg->info.request.URI, 1);
} else {
RETVAL_NULL();
break;
case HTTP_MSG_PROPHASH_RESPONSE_CODE:
- if (msg->type == HTTP_MSG_RESPONSE) {
+ if (HTTP_MSG_TYPE(RESPONSE, msg)) {
RETVAL_LONG(msg->info.response.code);
} else {
RETVAL_NULL();
zend_error(E_WARNING, "Cannot access protected property %s::$%s", obj->zo.ce->name, Z_STRVAL_P(member));
}
-#if 0
- fprintf(stderr, "Writing property: %s(%d==%d) (%lu)\n", Z_STRVAL_P(member), Z_STRLEN_P(member), strlen(Z_STRVAL_P(member)),
- zend_get_hash_value(Z_STRVAL_P(member), strlen(Z_STRVAL_P(member)) + 1)
- );
+#if 1
+ fprintf(stderr, "Writing property: %s(%d==%d)\n", Z_STRVAL_P(member), Z_STRLEN_P(member), strlen(Z_STRVAL_P(member)));
#endif
- switch (zend_get_hash_value(Z_STRVAL_P(member), strlen(Z_STRVAL_P(member)) + 1))
+ switch (zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1))
{
case HTTP_MSG_PROPHASH_TYPE:
+ convert_to_long_ex(&value);
if (Z_LVAL_P(value) != msg->type) {
- if (msg->type == HTTP_MSG_REQUEST) {
+ if (HTTP_MSG_TYPE(REQUEST, msg)) {
if (msg->info.request.method) {
efree(msg->info.request.method);
}
}
}
msg->type = Z_LVAL_P(value);
- if (msg->type == HTTP_MSG_REQUEST) {
+ if (HTTP_MSG_TYPE(REQUEST, msg)) {
msg->info.request.method = NULL;
msg->info.request.URI = NULL;
}
break;
case HTTP_MSG_PROPHASH_HTTP_VERSION:
+ convert_to_long_ex(&value);
switch (msg->type)
{
case HTTP_MSG_REQUEST:
break;
case HTTP_MSG_PROPHASH_BODY:
+ convert_to_string_ex(&value);
phpstr_dtor(PHPSTR(msg));
phpstr_from_string_ex(PHPSTR(msg), Z_STRVAL_P(value), Z_STRLEN_P(value));
break;
case HTTP_MSG_PROPHASH_HEADERS:
+ convert_to_array_ex(&value);
zend_hash_clean(&msg->hdrs);
zend_hash_copy(&msg->hdrs, Z_ARRVAL_P(value), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
break;
- case HTTP_MSG_PROPHASH_NESTED_MESSAGE:
- if (msg->nested) {
+ case HTTP_MSG_PROPHASH_PARENT_MESSAGE:
+ if (msg->parent) {
zval tmp;
- tmp.value.obj = obj->nested;
+ tmp.value.obj = obj->parent;
zend_objects_store_del_ref(&tmp TSRMLS_CC);
}
zend_objects_store_add_ref(value TSRMLS_CC);
- obj->nested = value->value.obj;
+ obj->parent = value->value.obj;
break;
case HTTP_MSG_PROPHASH_REQUEST_METHOD:
- if (msg->type == HTTP_MSG_REQUEST) {
+ convert_to_string_ex(&value);
+ if (HTTP_MSG_TYPE(REQUEST, msg)) {
if (msg->info.request.method) {
efree(msg->info.request.method);
}
break;
case HTTP_MSG_PROPHASH_REQUEST_URI:
- if (msg->type == HTTP_MSG_REQUEST) {
+ convert_to_string_ex(&value);
+ if (HTTP_MSG_TYPE(REQUEST, msg)) {
if (msg->info.request.URI) {
efree(msg->info.request.URI);
}
break;
case HTTP_MSG_PROPHASH_RESPONSE_CODE:
- if (msg->type == HTTP_MSG_RESPONSE) {
+ convert_to_long_ex(&value);
+ if (HTTP_MSG_TYPE(RESPONSE, msg)) {
msg->info.response.code = Z_LVAL_P(value);
}
break;
}
/* }}} */
+/* {{{ proto void HttpMessage::__construct([string message])
+ *
+ * Instantiate a new HttpMessage object.
+ */
+PHP_METHOD(HttpMessage, __construct)
+{
+ char *message = NULL;
+ int length = 0;
+ getObject(http_message_object, obj);
+
+ SET_EH_THROW_HTTP();
+ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) {
+ if (obj->message = http_message_parse(message, length)) {
+ if (obj->message->parent) {
+ obj->parent = http_message_object_from_msg(obj->message->parent);
+ }
+ }
+ } else if (!obj->message) {
+ obj->message = http_message_new();
+ }
+ SET_EH_NORMAL();
+}
+/* }}} */
+
/* {{{ proto string HttpMessage::getBody()
*
* Get the body of the parsed Message.
if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) {
return;
}
- if (type != obj->message->type) {
- if (obj->message->type == HTTP_MSG_REQUEST) {
- if (obj->message->info.request.method) {
- efree(obj->message->info.request.method);
- }
- if (obj->message->info.request.URI) {
- efree(obj->message->info.request.URI);
- }
- }
- obj->message->type = type;
- if (obj->message->type == HTTP_MSG_REQUEST) {
- obj->message->info.request.method = NULL;
- obj->message->info.request.URI = NULL;
- }
- }
+ http_message_set_type(obj->message, type);
}
/* }}} */
NO_ARGS;
- if (obj->message->type != HTTP_MSG_RESPONSE) {
+ if (!HTTP_MSG_TYPE(RESPONSE, obj->message)) {
http_error(E_NOTICE, HTTP_E_MSG, "HttpMessage is not of type HTTP_MSG_RESPONSE");
RETURN_NULL();
}
PHP_METHOD(HttpMessage, getHttpVersion)
{
char ver[4] = {0};
- float *version;
+ float version;
getObject(http_message_object, obj);
NO_ARGS;
switch (obj->message->type)
{
case HTTP_MSG_RESPONSE:
- version = &obj->message->info.response.http_version;
+ version = obj->message->info.response.http_version;
break;
+
case HTTP_MSG_REQUEST:
- version = &obj->message->info.request.http_version;
+ version = obj->message->info.request.http_version;
break;
+
case HTTP_MSG_NONE:
default:
RETURN_NULL();
}
- sprintf(ver, "1.1f", version);
+ sprintf(ver, "%1.1f", version);
RETURN_STRINGL(ver, 3, 1);
}
/* }}} */
}
/* }}} */
-/* {{{ proto HttpMessage HttpMessage::getNestedMessage()
+/* {{{ proto HttpMessage HttpMessage::getParentMessage()
*
- * Get nested Message.
+ * Get parent Message.
*/
-PHP_METHOD(HttpMessage, getNestedMessage)
+PHP_METHOD(HttpMessage, getParentMessage)
{
getObject(http_message_object, obj);
NO_ARGS;
- if (obj->message->nested) {
- Z_TYPE_P(return_value) = IS_OBJECT;
- return_value->value.obj = obj->nested;
- return_value->is_ref = 1;
- zend_objects_store_add_ref(return_value TSRMLS_CC);
+ if (obj->message->parent) {
+ RETVAL_OBJVAL(obj->parent);
} else {
RETVAL_NULL();
}
}
/* }}} */
-/* {{{ proto string HttpMessage::toString()
+/* {{{ proto bool HttpMessage::send()
+ *
+ * Send the Message according to its type as Response or Request.
+ */
+PHP_METHOD(HttpMessage, send)
+{
+ getObject(http_message_object, obj);
+
+ NO_ARGS;
+
+ RETURN_SUCCESS(http_message_send(obj->message));
+}
+/* }}} */
+
+/* {{{ proto string HttpMessage::toString([bool include_parent = true])
*
* Get the string representation of the Message.
*/
{
char *string;
size_t length;
+ zend_bool include_parent = 1;
getObject(http_message_object, obj);
- NO_ARGS;
+ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) {
+ RETURN_FALSE;
+ }
- http_message_tostring(obj->message, &string, &length);
+ if (include_parent) {
+ http_message_serialize(obj->message, &string, &length);
+ } else {
+ http_message_tostring(obj->message, &string, &length);
+ }
RETURN_STRINGL(string, length, 0);
}
/* }}} */
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 char *_http_chunked_decode(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC);
+PHP_HTTP_API const 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);
RANGE_ERR
} http_range_status;
-typedef void (*http_parse_headers_callback_t)(void **callback_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC);
+typedef void (*http_parse_headers_callback_t)(const char *http_line, HashTable **headers, void **callback_data 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);
+PHP_HTTP_API void _http_parse_headers_default_callback(const char *http_line, HashTable **headers, void **cb_data 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_headers(h, a) _http_parse_headers_ex((h), Z_ARRVAL_P(a), 1, _http_parse_headers_default_callback, NULL TSRMLS_CC)
+#define http_parse_headers_ex(h, ht, p) _http_parse_headers_ex((h), (ht), (p), _http_parse_headers_default_callback, NULL TSRMLS_CC)
+#define http_parse_headers_cb(h, ht, p, f, d) _http_parse_headers_ex((h), (ht), (p), (f), (d) TSRMLS_CC)
+PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, 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);
#ifndef PHP_HTTP_MESSAGE_API_H
#define PHP_HTTP_MESSAGE_API_H
-/*
-DUMP:
-HttpMessage
- ->toResponseString();
- ->toRequestString();
- ->__toString(); ->__sleep(); ->serialize();
- ->fromString(); __wakeup($message); ->unserialize();
- ->setStatusCode();
- ->setHeader(); ->addHeader()...
-*/
-
#include "phpstr/phpstr.h"
-#include "php_http_headers_api.h"
typedef enum {
HTTP_MSG_NONE,
} info;
- http_message *nested;
+ http_message *parent;
};
/* required minimum length of an HTTP message "HTTP/1.1 200\r\n" */
#define HTTP_MSG_MIN_SIZE 15
+/* shorthand for type checks */
+#define HTTP_MSG_TYPE(TYPE, msg) ((msg) && ((msg)->type == HTTP_MSG_ ##TYPE))
+
#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))
PHP_HTTP_API http_message *_http_message_init_ex(http_message *m, http_message_type t);
+#define http_message_set_type(m, t) _http_message_set_type((m), (t))
+PHP_HTTP_API void _http_message_set_type(http_message *m, http_message_type t);
+
#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);
-
#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_serialize(m, s, l) _http_message_serialize((m), (s), (l))
+PHP_HTTP_API void _http_message_serialize(http_message *message, char **string, size_t *length);
+
+#define http_message_send(m) _http_message_send((m) TSRMLS_CC)
+PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC);
+
#define http_message_dtor(m) _http_message_dtor((m))
PHP_HTTP_API void _http_message_dtor(http_message *message);
typedef struct {
zend_object zo;
http_message *message;
- zend_object_value nested;
+ zend_object_value parent;
} http_message_object;
extern zend_class_entry *http_message_object_ce;
#define HTTP_MSG_PROPHASH_BODY 254474387LU
#define HTTP_MSG_PROPHASH_HEADERS 3199929089LU
#define HTTP_MSG_PROPHASH_NESTED_MESSAGE 3652857165LU
+#define HTTP_MSG_PROPHASH_PARENT_MESSAGE 0LU
#define HTTP_MSG_PROPHASH_REQUEST_METHOD 1669022159LU
#define HTTP_MSG_PROPHASH_REQUEST_URI 3208695486LU
#define HTTP_MSG_PROPHASH_RESPONSE_STATUS 3857097400LU
#define HTTP_MSG_PROPHASH_RESPONSE_CODE 1305615119LU
+#define HTTP_MSG_CHECK_OBJ(obj, dofail) \
+ if (!(obj)->message) { \
+ http_error(E_WARNING, HTTP_E_MSG, "HttpMessage is empty"); \
+ dofail; \
+ }
+#define HTTP_MSG_CHECK_STD() HTTP_MSG_CHECK_OBJ(obj, RETURN_FALSE)
+
+#define HTTP_MSG_INIT_OBJ(obj) \
+ if (!(obj)->message) { \
+ (obj)->message = http_message_new(); \
+ }
+#define HTTP_MSG_INIT_STD() HTTP_MSG_INIT_OBJ(obj)
+
+PHP_METHOD(HttpMessage, __construct);
PHP_METHOD(HttpMessage, getBody);
PHP_METHOD(HttpMessage, getHeaders);
PHP_METHOD(HttpMessage, setHeaders);
PHP_METHOD(HttpMessage, setRequestUri);
PHP_METHOD(HttpMessage, getHttpVersion);
PHP_METHOD(HttpMessage, setHttpVersion);
-PHP_METHOD(HttpMessage, getNestedMessage);
+PHP_METHOD(HttpMessage, getParentMessage);
+PHP_METHOD(HttpMessage, send);
PHP_METHOD(HttpMessage, toString);
PHP_METHOD(HttpMessage, fromString);
#define lenof(S) (sizeof(S) - 1)
/* return bool (v == SUCCESS) */
+#define RETVAL_SUCCESS(v) RETVAL_BOOL(SUCCESS == (v))
#define RETURN_SUCCESS(v) RETURN_BOOL(SUCCESS == (v))
+/* return object(values) */
+#define RETVAL_OBJECT(o) \
+ return_value->is_ref = 1; \
+ return_value->type = IS_OBJECT; \
+ return_value->value.obj = (o)->value.obj; \
+ zval_add_ref(&return_value)
+#define RETURN_OBJECT(o) \
+ RETVAL_OBJECT(o); \
+ return
+#define RETVAL_OBJVAL(ov) \
+ return_value->is_ref = 1; \
+ return_value->type = IS_OBJECT; \
+ return_value->value.obj = (ov); \
+ zend_objects_store_add_ref(return_value TSRMLS_CC)
+#define RETURN_OBJVAL(ov) \
+ RETVAL_OBJVAL(ov); \
+ return
/* function accepts no args */
#define NO_ARGS \