Commits

Ruslan Osmanov committed ee4a015

Fix: classes/buffer_event.c: possible memory access violation in bufferevent callback
Add: EventHttp methods setMaxBodySize, setMaxHeadersSize, setTimeout, addServerAlias, removeServerAlias
Change: make EventDnsBase parameter optional in EventHttpConnection::__construct
Add: EventHttpConnection::makeRequest
Add: EventHttpRequest, callback and custom data can be now bound to the request for future uses with EventHttpConnection

  • Participants
  • Parent commits d603477

Comments (0)

Files changed (13)

File classes/buffer_event.c

 		} else {
 			ALLOC_INIT_ZVAL(arg_self);
 		}
-		args[0] = &bev->self;
+		args[0] = &arg_self;
 
 		if (arg_data) {
 			Z_ADDREF_P(arg_data);

File classes/http.c

 }
 /* }}} */
 
+/* {{{ proto void EventHttp::setMaxBodySize(int value);
+ */
+PHP_METHOD(EventHttp, setMaxBodySize)
+{
+	zval             *zhttp   = getThis();
+	php_event_http_t *http;
+	long              value;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
+				&value) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP(http, zhttp);
+
+	evhttp_set_max_body_size(http->ptr, value);
+}
+/* }}} */
+
+/* {{{ proto void EventHttp::setMaxHeadersSize(int value);
+ */
+PHP_METHOD(EventHttp, setMaxHeadersSize)
+{
+	zval             *zhttp   = getThis();
+	php_event_http_t *http;
+	long              value;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
+				&value) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP(http, zhttp);
+
+	evhttp_set_max_headers_size(http->ptr, value);
+}
+/* }}} */
+
+/* {{{ proto void EventHttp::setTimeout(int value);
+ * Sets timeout for an HTTP request
+ */
+PHP_METHOD(EventHttp, setTimeout)
+{
+	zval             *zhttp   = getThis();
+	php_event_http_t *http;
+	long              value;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
+				&value) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP(http, zhttp);
+
+	evhttp_set_timeout(http->ptr, value);
+}
+/* }}} */
+
+/* {{{ proto void EventHttp::addServerAlias(string alias);
+ * Adds a server alias to the object.
+ */
+PHP_METHOD(EventHttp, addServerAlias)
+{
+	zval             *zhttp     = getThis();
+	php_event_http_t *http;
+	char             *alias;
+	int               alias_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
+				&alias, &alias_len) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP(http, zhttp);
+
+	evhttp_add_server_alias(http->ptr, alias);
+}
+/* }}} */
+
+/* {{{ proto void EventHttp::removeServerAlias(string alias);
+ * Removes a server alias from the object.
+ */
+PHP_METHOD(EventHttp, removeServerAlias)
+{
+	zval             *zhttp     = getThis();
+	php_event_http_t *http;
+	char             *alias;
+	int               alias_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
+				&alias, &alias_len) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP(http, zhttp);
+
+	evhttp_remove_server_alias(http->ptr, alias);
+}
+/* }}} */
+
+
 /*
  * Local variables:
  * tab-width: 4

File classes/http_connection.c

 #include "src/priv.h"
 
 /* {{{ proto EventHttpConnection EventHttpConnection::__construct(EventBase base, EventDnsBase dns_base, string address, int port);
- * */
+ * If <parameter>dns_base</parameter> is &null;, hostname resolution will block.
+ */
 PHP_METHOD(EventHttpConnection, __construct)
 {
 	zval                     *zbase;
 	php_event_base_t         *b;
-	zval                     *zdns_base;
+	zval                     *zdns_base = NULL;
 	php_event_dns_base_t     *dnsb;
 	char                     *address;
 	int                       address_len;
 	php_event_http_conn_t    *evcon;
 	struct evhttp_connection *conn;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OOsl",
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO!sl",
 				&zbase, php_event_base_ce, &zdns_base, php_event_dns_base_ce,
 				&address, &address_len, &port) == FAILURE) {
 		return;
 	}
 
 	PHP_EVENT_FETCH_BASE(b, zbase);
-	PHP_EVENT_FETCH_DNS_BASE(dnsb, zdns_base);
+
+	if (zdns_base) {
+		PHP_EVENT_FETCH_DNS_BASE(dnsb, zdns_base);
+	}
 
 	PHP_EVENT_FETCH_HTTP_CONN(evcon, getThis());
 
-	conn = evhttp_connection_base_new(b->base, dnsb->dns_base, address, (unsigned short) port);
+	conn = evhttp_connection_base_new(b->base,
+			(zdns_base ? dnsb->dns_base : NULL),
+			address, (unsigned short) port);
 	if (!conn) {
 		return;
 	}
 	Z_ADDREF_P(zbase);
 
 	evcon->dns_base = zdns_base;
-	Z_ADDREF_P(zdns_base);
+	if (zdns_base) {
+		Z_ADDREF_P(zdns_base);
+	}
 }
 /* }}} */
 
 }
 /* }}} */
 
+/* {{{ proto bool EventHttpConnection::makeRequest(EventHttpRequest req, int type, string uri);
+ * Makes an HTTP request over the specified connection.
+ * <parameter>type</parameter> is one of <literal>EventHttpRequest::CMD_*</literal> constants.
+ */
+PHP_METHOD(EventHttpConnection, makeRequest)
+{
+	zval                  *zevcon   = getThis();
+	php_event_http_conn_t *evcon;
+	zval                  *zreq;
+	php_event_http_req_t  *http_req;
+	long                   type;
+	char                  *uri;
+	int                    uri_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Ols",
+				&zreq, php_event_http_req_ce, &type, &uri, &uri_len) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, zreq);
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Unconfigured HTTP request object passed");
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_CONN(evcon, zevcon);
+
+	if (evhttp_make_request(evcon->conn, http_req->ptr, type, uri)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+
 /*
  * Local variables:
  * tab-width: 4

File classes/http_request.c

 #include "src/priv.h"
 #include "classes/http.h"
 
-/* {{{ EventHttpRequest::__construct */
+/* {{{ Private */
+
+/* {{{ _check_http_req_ptr */
+#define _check_http_req_ptr(http_req)               \
+{                                                   \
+    if (!http_req->ptr) {                           \
+        php_error_docref(NULL TSRMLS_CC, E_WARNING, \
+                "Invalid HTTP request object");     \
+        RETURN_FALSE;                               \
+    }                                               \
+}
+/* }}} */
+
+/* {{{ _check_http_req_type */
+#define _check_http_req_type(type)                                            \
+{                                                                             \
+    if (type & ~(PHP_EVENT_REQ_HEADER_INPUT | PHP_EVENT_REQ_HEADER_OUTPUT)) { \
+        php_error_docref(NULL TSRMLS_CC, E_WARNING,                           \
+                "Invalid HTTP request type passed: %ld", type);               \
+        RETURN_FALSE;                                                         \
+    }                                                                         \
+}
+/* }}} */
+
+/* {{{ _get_http_req_headers */
+static zend_always_inline struct evkeyvalq *_get_http_req_headers(const php_event_http_req_t *http_req, const long type)
+{
+	struct evkeyvalq *headers;
+
+	if (type == PHP_EVENT_REQ_HEADER_OUTPUT) {
+		headers = evhttp_request_get_output_headers(http_req->ptr);
+	} else {
+		headers = evhttp_request_get_input_headers(http_req->ptr);
+	}
+
+	return headers;
+}
+/* }}} */
+
+/* {{{ _req_handler */
+static void _req_handler(struct evhttp_request *req, void *arg)
+{
+	php_event_http_req_t *http_req = (php_event_http_req_t *) arg;
+
+	PHP_EVENT_ASSERT(http_req && http_req->ptr);
+	PHP_EVENT_ASSERT(http_req->fci && http_req->fcc);
+	PHP_EVENT_ASSERT(http_req->self);
+
+	zend_fcall_info       *pfci = http_req->fci;
+	zend_fcall_info_cache *pfcc = http_req->fcc;
+	PHP_EVENT_ASSERT(pfci && pfcc);
+
+	TSRMLS_FETCH_FROM_CTX(http_req->thread_ctx);
+
+	/* Call userspace function according to
+	 * proto void callback(EventHttpRequest req, mixed data); */
+
+	zval  *arg_data = http_req->data;
+	zval  *arg_req;
+	zval **args[2];
+	zval  *retval_ptr;
+
+	arg_req = http_req->self;
+	/* req == NULL means timeout */
+	if (req == NULL || !arg_req) {
+		ALLOC_INIT_ZVAL(arg_req);
+	} else {
+		Z_ADDREF_P(arg_req);
+	}
+	args[0] = &arg_req;
+
+	if (arg_data) {
+		Z_ADDREF_P(arg_data);
+	} else {
+		ALLOC_INIT_ZVAL(arg_data);
+	}
+	args[1] = &arg_data;
+
+	pfci->params		 = args;
+	pfci->retval_ptr_ptr = &retval_ptr;
+	pfci->param_count	 = 2;
+	pfci->no_separation  = 1;
+
+	/* Tell Libevent that we will free the request ourselves(evhttp_request_free in the free-storage handler)*/
+	evhttp_request_own(http_req->ptr);
+
+    if (zend_call_function(pfci, pfcc TSRMLS_CC) == SUCCESS && retval_ptr) {
+        zval_ptr_dtor(&retval_ptr);
+    } else {
+        php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                "An error occurred while invoking the http request callback");
+    }
+
+    zval_ptr_dtor(&arg_req);
+    zval_ptr_dtor(&arg_data);
+}
+/* }}} */
+
+/* }}} */
+
+
+/* {{{ proto EventHttpRequest::__construct(callable callback[, mixed data = NULL]); */
 PHP_METHOD(EventHttpRequest, __construct)
 {
+	zval                  *zself    = getThis();
+	php_event_http_req_t  *http_req;
+	zend_fcall_info        fci      = empty_fcall_info;
+	zend_fcall_info_cache  fcc      = empty_fcall_info_cache;
+	zval                  *zarg     = NULL;
+	struct evhttp_request *req;
+	
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f|z",
+				&fci, &fcc, &zarg) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, zself);
+
+	req = evhttp_request_new(_req_handler, (void *) http_req);
+	PHP_EVENT_ASSERT(req);
+
+	/* Tell Libevent that we will free the request ourselves(evhttp_request_free in the free-storage handler)
+	 * XXX Not sure if it's really needed here though. */
+	evhttp_request_own(req);
+	http_req->ptr = req;
+
+	if (zarg) {
+		Z_ADDREF_P(zarg);
+	}
+	http_req->data = zarg;
+
+	http_req->self = zself;
+	Z_ADDREF_P(zself);
+
+	PHP_EVENT_COPY_FCALL_INFO(http_req->fci, http_req->fcc, &fci, &fcc);
+
+	TSRMLS_SET_CTX(http_req->thread_ctx);
+}
+/* }}} */
+
+/* {{{ proto void EventHttpRequest::free(void);
+ * Frees the object and removes associated events. */
+PHP_METHOD(EventHttpRequest, free)
+{
+	zval                 *zself    = getThis();
 	php_event_http_req_t *http_req;
 
-	if (zend_parse_parameters_none() == FAILURE) {
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, zself);
+
+	if (!http_req->ptr || http_req->internal) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+	evhttp_request_free(http_req->ptr);
 	http_req->ptr = NULL;
+
+	/* Do it once */
+	if (http_req->self) {
+		zval_ptr_dtor(&http_req->self);
+		http_req->self = NULL;
+	}
 }
 /* }}} */
 
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
-		RETURN_FALSE;
-	}
+	_check_http_req_ptr(http_req);
 
 	RETVAL_LONG(evhttp_request_get_command(http_req->ptr));
 }
 /* }}} */
 
+/* {{{ proto int EventHttpRequest::getHost(void);
+ * Returns the request host. XXX make a property? */
+PHP_METHOD(EventHttpRequest, getHost)
+{
+	php_event_http_req_t *http_req;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	RETVAL_STRING(evhttp_request_get_host(http_req->ptr), 1);
+}
+/* }}} */
+
 /* {{{ proto int EventHttpRequest::getUri(void);
  * Returns the request URI. XXX make a property? */
 PHP_METHOD(EventHttpRequest, getUri)
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
-		RETURN_FALSE;
-	}
+	_check_http_req_ptr(http_req);
 
 	RETVAL_STRING(evhttp_request_get_uri(http_req->ptr), 1);
 }
 /* }}} */
 
+/* {{{ proto int EventHttpRequest::getResponseCode(void);
+ * Returns the the response code. XXX make a property? */
+PHP_METHOD(EventHttpRequest, getResponseCode)
+{
+	php_event_http_req_t *http_req;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	RETVAL_LONG(evhttp_request_get_response_code(http_req->ptr));
+}
+/* }}} */
+
 /* {{{ proto array EventHttpRequest::getInputHeaders(void);
  * Returns associative array of the input headers. */
 PHP_METHOD(EventHttpRequest, getInputHeaders)
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
-		RETURN_FALSE;
-	}
+	_check_http_req_ptr(http_req);
 
 	array_init(return_value);
 
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
-		RETURN_FALSE;
-	}
+	_check_http_req_ptr(http_req);
 
 	array_init(return_value);
 
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
-		RETURN_FALSE;
-	}
+	_check_http_req_ptr(http_req);
 
 	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_buffer_ce);
 	PHP_EVENT_FETCH_BUFFER(b, return_value);
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
-		RETURN_FALSE;
-	}
+	_check_http_req_ptr(http_req);
 
 	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_buffer_ce);
 	PHP_EVENT_FETCH_BUFFER(b, return_value);
 }
 /* }}} */
 
+/* {{{ proto void EventHttpRequest::sendError(int error, string reason);
+ * Send an HTML error message to the client.
+ */
+PHP_METHOD(EventHttpRequest, sendError)
+{
+	php_event_http_req_t *http_req;
+	long                  error;
+	char                 *reason;
+	int                   reason_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls",
+				&error, &reason, &reason_len) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	evhttp_send_error(http_req->ptr, error, reason);
+}
+/* }}} */
+
 /* {{{ proto void EventHttpRequest::sendReply(int code, string reason[, EventBuffer buf=&null;]);
  * Send an HTML reply to client.
  *
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
-		RETURN_FALSE;
-	}
+	_check_http_req_ptr(http_req);
 
 	if (zbuf) {
 		PHP_EVENT_FETCH_BUFFER(b, zbuf);
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
-		RETURN_FALSE;
-	}
+	_check_http_req_ptr(http_req);
 
 	if (zbuf) {
 		PHP_EVENT_FETCH_BUFFER(b, zbuf);
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
-		RETURN_FALSE;
-	}
+	_check_http_req_ptr(http_req);
 
 	evhttp_send_reply_end(http_req->ptr);
 }
 
 	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
 
-	if (!http_req->ptr) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING,
-				"Invalid HTTP request object");
+	_check_http_req_ptr(http_req);
+
+
+	evhttp_send_reply_start(http_req->ptr, code, reason);
+}
+/* }}} */
+
+/* {{{ proto void EventHttpRequest::cancel(void);
+ * Cancels a pending HTTP request.
+ *
+ * Cancels an ongoing HTTP request. The callback associated with this request
+ * is not executed and the request object is freed. If the request is currently
+ * being processed, e.g. it is ongoing, the corresponding EventHttpConnection 
+ * object is going to get reset.
+ *
+ * A request cannot be canceled if its callback has executed already. A request
+ * may be canceled reentrantly from its chunked callback.
+ */
+PHP_METHOD(EventHttpRequest, cancel)
+{
+	php_event_http_req_t *http_req;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	evhttp_cancel_request(http_req->ptr);
+}
+/* }}} */
+
+/* {{{ proto bool EventHttpRequest::addHeader(string key, string value, int type);
+ * Adds an HTTP header to the headers of the request.
+ * <parameter>type</parameter> is one of <literal>EventHttpRequest::*_HEADER</literal>
+ * constants.
+ */
+PHP_METHOD(EventHttpRequest, addHeader)
+{
+	php_event_http_req_t *http_req;
+	char                 *key;
+	char                 *value;
+	int                   key_len;
+	int                   value_len;
+	struct evkeyvalq     *headers;
+	long                  type;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl",
+				&key, &key_len, &value, &value_len, &type) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+	_check_http_req_ptr(http_req);
+
+	headers = _get_http_req_headers(http_req, type);
+	PHP_EVENT_ASSERT(headers);
+
+
+	if (evhttp_add_header(headers, key, value)) {
 		RETURN_FALSE;
 	}
+	RETVAL_TRUE;
+}
+/* }}} */
 
-	evhttp_send_reply_start(http_req->ptr, code, reason);
+/* {{{ proto void EventHttpRequest::clearHeaders(string key, string value);
+ * Removes all output headers from the header list of the request.
+ */
+PHP_METHOD(EventHttpRequest, clearHeaders)
+{
+	php_event_http_req_t *http_req;
+	struct evkeyvalq     *out_headers;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+	_check_http_req_ptr(http_req);
+
+	out_headers = evhttp_request_get_output_headers(http_req->ptr);
+	PHP_EVENT_ASSERT(out_headers);
+
+	evhttp_clear_headers(out_headers);
+}
+/* }}} */
+
+/* {{{ proto bool EventHttpRequest::removeHeader(string key, int type);
+ * Removes an HTTP header from the headers of the request.
+ * <parameter>type</parameter> is one of <literal>EventHttpRequest::*_HEADER</literal>
+ * constants.
+ */
+PHP_METHOD(EventHttpRequest, removeHeader)
+{
+	php_event_http_req_t *http_req;
+	char                 *key;
+	int                   key_len;
+	struct evkeyvalq     *headers;
+	long                  type;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl",
+				&key, &key_len, &type) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	headers = _get_http_req_headers(http_req, type);
+	PHP_EVENT_ASSERT(headers);
+
+	if (evhttp_remove_header(headers, key)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string EventHttpRequest::findHeader(string key, int type);
+ * Finds the value belonging a header.
+ * <parameter>type</parameter> is one of <literal>EventHttpRequest::*_HEADER</literal>
+ * constants.
+ * Returns &null; if header not found.
+ */
+PHP_METHOD(EventHttpRequest, findHeader)
+{
+	php_event_http_req_t *http_req;
+	char                 *key;
+	int                   key_len;
+	struct evkeyvalq     *headers;
+	long                  type;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl",
+				&key, &key_len, &type) == FAILURE) {
+		return;
+	}
+
+	_check_http_req_type(type);
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+	_check_http_req_ptr(http_req);
+
+	headers = _get_http_req_headers(http_req, type);
+	PHP_EVENT_ASSERT(headers);
+
+	const char *val = evhttp_find_header(headers, key);
+	if (val == NULL) {
+		RETURN_NULL();
+	}
+
+	RETVAL_STRING(val, 1);
 }
 /* }}} */
 

File examples/fibonacci_buffer.php

 <?php
-/* TODO: Maybe use bufferevent pairs to complete example? */
-
 function write_callback_fibonacci($bev, $c) {
 	/* Here's a callback that adds some Fibonacci numbers to the
 	   	output buffer of $bev.  It stops once we have added 1k of
 	   	data; once this data is drained, we'll add more. */
 
-	echo __FUNCTION__, PHP_EOL;
-
-	$tmp = evbuffer_new();
-	while (evbuffer_get_length($tmp) < 1024) {
+	$tmp = new EventBuffer();
+	while ($tmp->length < 1024) {
 		$next = $c[0] + $c[1];
 		$c[0] = $c[1];
 		$c[1] = $next;
 
-		evbuffer_add($tmp, $next);
+		$tmp->add($next);
 	}
 
-	/* Now we add the whole contents of tmp to bev. */
-	bufferevent_write_buffer($bev, $tmp);
+	// Now we add the whole contents of tmp to bev
+	$bev->writeBuffer($tmp);
 
-	/* We don't need tmp any longer. */
-	evbuffer_free($tmp);
+	// We don't need tmp any longer
+	$tmp->free();
 }
 ?>

File examples/http.php

 <?php
+/*
+ * Simple HTTP server.
+ *
+ * To test it:
+ * 1) Run it on a port of your choice, e.g.:
+ * $ php examples/http.php 8010
+ * 2) In another terminal connect to some address on this port
+ * and make GET or POST request(others are turned off here), e.g.:
+ * $ nc -t 127.0.0.1 8010
+ * POST /about HTTP/1.0
+ * Content-Type: text/plain
+ * Content-Length: 4
+ * Connection: close
+ * (press Enter)
+ *
+ * It will output
+ * a=12
+ * HTTP/1.0 200 OK
+ * Content-Type: text/html; charset=ISO-8859-1
+ * Connection: close
+ *
+ * $ nc -t 127.0.0.1 8010
+ * GET /dump HTTP/1.0
+ * Content-Type: text/plain
+ * Content-Encoding: UTF-8
+ * Connection: close
+ * (press Enter)
+ *
+ * It will output:
+ * HTTP/1.0 200 OK
+ * Content-Type: text/html; charset=ISO-8859-1
+ * Connection: close
+ * (press Enter)
+ *
+ * $ nc -t 127.0.0.1 8010
+ * GET /unknown HTTP/1.0
+ * Connection: close
+ *
+ * It will output:
+ * HTTP/1.0 200 OK
+ * Content-Type: text/html; charset=ISO-8859-1
+ * Connection: close
+ *
+ * 3) See what the server outputs on the previous terminal window.
+ */
 
 function _http_dump($req, $data) {
 	static $counter      = 0;

File examples/http_request.php

+<?php
+
+function _request_handler($req, $base) {
+	echo __FUNCTION__, PHP_EOL;
+
+	if (is_null($req)) {
+		echo "Timed out\n";
+	} else {
+		$response_code = $req->getResponseCode();
+
+		if ($response_code == 0) {
+			echo "Connection refused\n";
+		} elseif ($response_code != 200) {
+			echo "Unexpected response: $response_code\n";
+		} else {
+			echo "Success: $response_code\n";
+			$buf = $req->getInputBuffer();
+			echo "Body:\n";
+			while ($s = $buf->readLine(EventBuffer::EOL_ANY)) {
+				echo $s, PHP_EOL;
+			}
+		}
+	}
+	
+	$base->exit(NULL);
+}
+
+
+$address = "127.0.0.1";
+$port = 80;
+
+$base = new EventBase();
+$conn = new EventHttpConnection($base, NULL, $address, $port);
+$conn->setTimeout(5);
+$req = new EventHttpRequest("_request_handler", $base);
+
+$req->addHeader("Host", $address, EventHttpRequest::OUTPUT_HEADER);
+$req->addHeader("Content-Length", "0", EventHttpRequest::OUTPUT_HEADER);
+$conn->makeRequest($req, EventHttpRequest::CMD_GET, "/index.cphp");
+
+$base->loop();
+$base;
+?>
         </dir>
         <file role="doc" name="eio.php"/>
         <file role="doc" name="fibonacci_buffer.php"/>
+        <file role="doc" name="http.php"/>
         <file role="doc" name="httpv0client.php"/>
         <file role="doc" name="httpv0client2.php"/>
         <file role="doc" name="listener.php"/>
 
 	if (evcon->base) {
 		zval_ptr_dtor(&evcon->base);
+		evcon->base = NULL;
 	}
 
 	if (evcon->dns_base) {
 		zval_ptr_dtor(&evcon->dns_base);
+		evcon->dns_base = NULL;
 	}
 
 	if (evcon->conn) {
 		evhttp_connection_free(evcon->conn);
+		evcon->conn = NULL;
 	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 
 	PHP_EVENT_ASSERT(http_req);
 
+	PHP_EVENT_FREE_FCALL_INFO(http_req->fci, http_req->fcc);
+
+	if (http_req->self) {
+		zval_ptr_dtor(&http_req->self);
+		http_req->self = NULL;
+	}
+	if (http_req->data) {
+		zval_ptr_dtor(&http_req->data);
+		http_req->data = NULL;
+	}
+
 	if (!http_req->internal && http_req->ptr) {
 		evhttp_request_free(http_req->ptr);
 		http_req->ptr = NULL;
 	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, CMD_CONNECT, EVHTTP_REQ_CONNECT);
 	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, CMD_PATCH,   EVHTTP_REQ_PATCH);
 
+	/* EventHttpRequest header types */
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, INPUT_HEADER,  PHP_EVENT_REQ_HEADER_INPUT);
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, OUTPUT_HEADER, PHP_EVENT_REQ_HEADER_OUTPUT);
+
 #endif /* HAVE_EVENT_EXTRA_LIB */
 
 	REGISTER_EVENT_CLASS_CONST_LONG(php_event_util_ce, LIBEVENT_VERSION_NUMBER, LIBEVENT_VERSION_NUMBER);

File src/common.h

 #endif
 
 
-#include <event2/keyvalq_struct.h>
 #include <event2/event.h>
 #include <event2/bufferevent.h>
 #include <event2/buffer.h>
 #endif
 
 #ifdef HAVE_EVENT_EXTRA_LIB
+# include <event2/keyvalq_struct.h>
 # include <event2/listener.h>
 # include <event2/dns.h>
 # include <event2/http.h>
 	ZEND_ARG_INFO(0, methods)
 ZEND_END_ARG_INFO();
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_set_value, 0, 0, 1)
+	ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_add_alias, 0, 0, 1)
+	ZEND_ARG_INFO(0, alias)
+ZEND_END_ARG_INFO();
+
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_req_send_error, 0, 0, 2)
+	ZEND_ARG_INFO(0, error)
+	ZEND_ARG_INFO(0, reason)
+ZEND_END_ARG_INFO();
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_req_send_reply, 0, 0, 2)
 	ZEND_ARG_INFO(0, code)
 	ZEND_ARG_INFO(0, reason)
 ZEND_END_ARG_INFO();
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_con_make_request, 0, 0, 3)
+	ZEND_ARG_INFO(0, req)
+	ZEND_ARG_INFO(0, type)
+	ZEND_ARG_INFO(0, uri)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_req_add_header, 0, 0, 3)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, value)
+	ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_req_remove_header, 0, 0, 2)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO();
+
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_event_ssl_context__construct, 0, 0, 2)
 	ZEND_ARG_INFO(0, method)
 	PHP_ME(EventHttpConnection, setMaxHeadersSize, arginfo_event_evhttp_connection_set_max_size,      ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpConnection, setMaxBodySize,    arginfo_event_evhttp_connection_set_max_size,      ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpConnection, setRetries,        arginfo_event_evhttp_connection_set_retries,       ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpConnection, makeRequest,       arginfo_event_http_con_make_request,               ZEND_ACC_PUBLIC)
 
 	PHP_FE_END
 };
 	PHP_ME(EventHttp, setCallback,        arginfo_event_http_set_callback,        ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttp, setDefaultCallback, arginfo_event_http_set_callback,        ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttp, setAllowedMethods,  arginfo_event_http_set_allowed_methods, ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttp, setMaxBodySize,     arginfo_event_http_set_value,           ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttp, setMaxHeadersSize,  arginfo_event_http_set_value,           ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttp, setTimeout,         arginfo_event_http_set_value,           ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttp, addServerAlias,     arginfo_event_http_add_alias,           ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttp, removeServerAlias,  arginfo_event_http_add_alias,           ZEND_ACC_PUBLIC)
 
 	PHP_FE_END
 };
 const zend_function_entry php_event_http_req_ce_functions[] = {
 	PHP_ME(EventHttpRequest, __construct, arginfo_event__void, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
 
+	PHP_ME(EventHttpRequest, free,             arginfo_event__void,                     ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, getCommand,       arginfo_event__void,                     ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpRequest, getHost,          arginfo_event__void,                     ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, getUri,           arginfo_event__void,                     ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpRequest, getResponseCode,  arginfo_event__void,                     ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, getInputHeaders,  arginfo_event__void,                     ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, getOutputHeaders, arginfo_event__void,                     ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, getInputBuffer,   arginfo_event__void,                     ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, getOutputBuffer,  arginfo_event__void,                     ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpRequest, sendError,        arginfo_event_http_req_send_error,       ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, sendReply,        arginfo_event_http_req_send_reply,       ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, sendReplyChunk,   arginfo_event_http_req_send_reply_chunk, ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, sendReplyEnd,     arginfo_event__void,                     ZEND_ACC_PUBLIC)
 	PHP_ME(EventHttpRequest, sendReplyStart,   arginfo_event_http_req_send_reply_start, ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpRequest, cancel,           arginfo_event__void,                     ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpRequest, addHeader,        arginfo_event_http_req_add_header,       ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpRequest, clearHeaders,     arginfo_event__void,                     ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpRequest, removeHeader,     arginfo_event_http_req_remove_header,    ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpRequest, findHeader,       arginfo_event_http_req_remove_header,    ZEND_ACC_PUBLIC)
 
 	PHP_FE_END
 };
 PHP_METHOD(EventHttpConnection, setMaxHeadersSize);
 PHP_METHOD(EventHttpConnection, setMaxBodySize);
 PHP_METHOD(EventHttpConnection, setRetries);
+PHP_METHOD(EventHttpConnection, makeRequest);
 
 PHP_METHOD(EventHttp, __construct);
 PHP_METHOD(EventHttp, accept);
 PHP_METHOD(EventHttp, setCallback);
 PHP_METHOD(EventHttp, setDefaultCallback);
 PHP_METHOD(EventHttp, setAllowedMethods);
+PHP_METHOD(EventHttp, setMaxBodySize);
+PHP_METHOD(EventHttp, setMaxHeadersSize);
+PHP_METHOD(EventHttp, setTimeout);
+PHP_METHOD(EventHttp, addServerAlias);
+PHP_METHOD(EventHttp, removeServerAlias);
 
 PHP_METHOD(EventHttpRequest, __construct);
+PHP_METHOD(EventHttpRequest, free);
 PHP_METHOD(EventHttpRequest, getCommand);
+PHP_METHOD(EventHttpRequest, getHost);
 PHP_METHOD(EventHttpRequest, getUri);
+PHP_METHOD(EventHttpRequest, getResponseCode);
 PHP_METHOD(EventHttpRequest, getInputHeaders);
 PHP_METHOD(EventHttpRequest, getOutputHeaders);
 PHP_METHOD(EventHttpRequest, getInputBuffer);
 PHP_METHOD(EventHttpRequest, getOutputBuffer);
+PHP_METHOD(EventHttpRequest, sendError);
 PHP_METHOD(EventHttpRequest, sendReply);
 PHP_METHOD(EventHttpRequest, sendReplyChunk);
 PHP_METHOD(EventHttpRequest, sendReplyEnd);
 PHP_METHOD(EventHttpRequest, sendReplyStart);
+PHP_METHOD(EventHttpRequest, cancel);
+PHP_METHOD(EventHttpRequest, addHeader);
+PHP_METHOD(EventHttpRequest, clearHeaders);
+PHP_METHOD(EventHttpRequest, removeHeader);
+PHP_METHOD(EventHttpRequest, findHeader);
 /* Extra API END }}} */
 #endif
 

File src/structs.h

 	PHP_EVENT_OBJECT_HEAD;
 } php_event_abstract_object_t;
 
+/* Represents EventBase object */
+typedef struct _php_event_base_t {
+	PHP_EVENT_OBJECT_HEAD;
+
+	struct event_base *base;
+	zend_bool          internal;   /* Whether is an internal pointer, e.g. obtained with evconnlistener_get_base() */
+} php_event_base_t;
+
 /* Represents Event object */
 typedef struct _php_event_t {
 	PHP_EVENT_OBJECT_HEAD;
 
-	struct event          *event;       /* Pointer returned by event_new                        */
+	struct event          *event;       /* Pointer returned by event_new                  */
 	int                    stream_id;   /* Resource ID of the file descriptor. -1 if none */
-	zval                  *data;        /* User custom data                                     */
+	zval                  *data;        /* User custom data                               */
 	/* fci and fcc represent userspace callback */
 	zend_fcall_info       *fci;
 	zend_fcall_info_cache *fcc;
 	PHP_EVENT_COMMON_THREAD_CTX;
 } php_event_t;
 
-/* Represents EventBase object */
-typedef struct _php_event_base_t {
-	PHP_EVENT_OBJECT_HEAD;
-
-	struct event_base *base;
-	zend_bool          internal;   /* Whether is an internal pointer, e.g. obtained with evconnlistener_get_base() */
-} php_event_base_t;
-
 /* Represents EventConfig object */
 typedef struct _php_event_config_t {
 	PHP_EVENT_OBJECT_HEAD;
 
 #ifdef HAVE_EVENT_EXTRA_LIB/* {{{ */
 
+enum {
+	PHP_EVENT_REQ_HEADER_INPUT  = 1,
+	PHP_EVENT_REQ_HEADER_OUTPUT = 2,
+};
+
 /* Represents EventDnsBase object */
 typedef struct _php_event_dns_base_t {
 	PHP_EVENT_OBJECT_HEAD;
 	PHP_EVENT_OBJECT_HEAD;
 
 	struct evhttp_request *ptr;
-   	/* Whether is artificially created object that must not free 'ptr' */
+	/* Whether is artificially created object that must not free 'ptr' */
 	zend_bool              internal;
+	zval                  *self;
+	/* User custom data passed to the gen(default) callback */
+	zval                  *data;
+	/* General(default) callback for evhttp_gencb() */
+	zend_fcall_info       *fci;
+	zend_fcall_info_cache *fcc;
+
+	PHP_EVENT_COMMON_THREAD_CTX;
 } php_event_http_req_t;
 
 #endif/* HAVE_EVENT_EXTRA_LIB }}} */