Commits

Ruslan Osmanov committed 97066d0 Merge

Merge branch 'http'

Comments (0)

Files changed (19)

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);
 #include "src/common.h"
 #include "src/util.h"
 #include "src/priv.h"
+#include "classes/http.h"
+
+/* {{{ Private */
+
+/* {{{ _new_http_cb
+ * Allocate memory for new callback structure for the next HTTP server's URI */
+static zend_always_inline php_event_http_cb_t *_new_http_cb(zval *zarg, const zend_fcall_info *fci, const zend_fcall_info_cache *fcc TSRMLS_CC)
+{
+	php_event_http_cb_t *cb = emalloc(sizeof(php_event_http_cb_t));
+
+	if (zarg) {
+		Z_ADDREF_P(zarg);
+	}
+	cb->data = zarg;
+
+	PHP_EVENT_COPY_FCALL_INFO(cb->fci, cb->fcc, fci, fcc);
+
+	TSRMLS_SET_CTX(cb->thread_ctx);
+
+	cb->next = NULL;
+
+	return cb;
+}
+/* }}} */
+
+/* {{{ _http_callback */
+static void _http_callback(struct evhttp_request *req, void *arg)
+{
+	php_event_http_cb_t *cb = (php_event_http_cb_t *) arg;
+	PHP_EVENT_ASSERT(cb);
+
+	php_event_http_req_t *http_req;
+
+	zend_fcall_info       *pfci = cb->fci;
+	zend_fcall_info_cache *pfcc = cb->fcc;
+	PHP_EVENT_ASSERT(pfci && pfcc);
+
+	TSRMLS_FETCH_FROM_CTX(cb->thread_ctx);
+
+	/* Call userspace function according to
+	 * proto void callback(EventHttpRequest req, mixed data);*/
+
+	zval  *arg_data = cb->data;
+	zval  *arg_req;
+	zval **args[2];
+	zval  *retval_ptr;
+
+	MAKE_STD_ZVAL(arg_req);
+	PHP_EVENT_INIT_CLASS_OBJECT(arg_req, php_event_http_req_ce);
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, arg_req);
+	http_req->ptr      = req;
+	http_req->internal = 1; /* Don't evhttp_request_free(req) */
+	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;
+
+    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);
+}
+/* }}} */
+
+/* {{{ _http_default_callback */
+static void _http_default_callback(struct evhttp_request *req, void *arg)
+{
+	php_event_http_t *http = (php_event_http_t *) arg;
+	PHP_EVENT_ASSERT(http);
+
+	php_event_http_req_t *http_req;
+
+	zend_fcall_info       *pfci = http->fci;
+	zend_fcall_info_cache *pfcc = http->fcc;
+	PHP_EVENT_ASSERT(pfci && pfcc);
+
+	TSRMLS_FETCH_FROM_CTX(http->thread_ctx);
+
+	/* Call userspace function according to
+	 * proto void callback(EventHttpRequest req, mixed data);*/
+
+	zval  *arg_data = http->data;
+	zval  *arg_req;
+	zval **args[2];
+	zval  *retval_ptr;
+
+	MAKE_STD_ZVAL(arg_req);
+	PHP_EVENT_INIT_CLASS_OBJECT(arg_req, php_event_http_req_ce);
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, arg_req);
+	http_req->ptr      = req;
+	http_req->internal = 1; /* Don't evhttp_request_free(req) */
+	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;
+
+    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);
+}
+/* }}} */
+
+/* }}} */
+
+/* {{{  _php_event_free_http_cb */
+void _php_event_free_http_cb(php_event_http_cb_t *cb)
+{
+	if (cb->data) {
+		zval_ptr_dtor(&cb->data);
+		cb->data = NULL;
+	}
+
+	PHP_EVENT_FREE_FCALL_INFO(cb->fci, cb->fcc);
+
+	efree(cb);
+}
+/* }}} */
 
 /* {{{ proto EventHttp EventHttp::__construct(EventBase base);
- *
  * Creates new http server object.
  */
 PHP_METHOD(EventHttp, __construct)
 
 	http_ptr = evhttp_new(b->base);
 	if (!http_ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Failed to allocate space for new HTTP server(evhttp_new)");
 		return;
 	}
 	http->ptr = http_ptr;
 	http->base = zbase;
 	Z_ADDREF_P(zbase);
 
-	http->stream_id = -1;
+	http->fci     = NULL;
+	http->fcc     = NULL;
+	http->data    = NULL;
+	http->cb_head = NULL;
 }
 /* }}} */
 
 		RETURN_FALSE;
 	}
 
-#if 0
-	http->stream_id = Z_LVAL_P(ppzfd);
-	zend_list_addref(Z_LVAL_P(ppzfd));
-#endif
-
 	RETVAL_TRUE;
 }
 /* }}} */
  * Can be called multiple times to bind the same http server to multiple different ports. */
 PHP_METHOD(EventHttp, bind)
 {
-	zval              *zhttp = getThis();
-	php_event_http_t  *http;
-	char *address;
-	int address_len;
-	long port;
+	zval             *zhttp       = getThis();
+	php_event_http_t *http;
+	char             *address;
+	int               address_len;
+	long              port;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl",
 				&address, &address_len, &port) == FAILURE) {
 
 	PHP_EVENT_FETCH_HTTP(http, zhttp);
 
+	/* XXX Call evhttp_bind_socket_with_handle instead, and store the bound
+	 * socket in the internal struct for further useful API? */
 	if (evhttp_bind_socket(http->ptr, address, port)) {
 		RETURN_FALSE;
 	}
 }
 /* }}} */
 
+/* {{{ proto bool EventHttp::setCallback(string path, callable cb[, mixed arg = NULL]);
+ * Sets a callback for specified URI.
+ */
+PHP_METHOD(EventHttp, setCallback)
+{
+	zval                  *zhttp    = getThis();
+	php_event_http_t      *http;
+	char                  *path;
+	int                    path_len;
+	zend_fcall_info        fci      = empty_fcall_info;
+	zend_fcall_info_cache  fcc      = empty_fcall_info_cache;
+	zval                  *zarg     = NULL;
+	int                    res;
+	php_event_http_cb_t   *cb;
+	php_event_http_cb_t   *cb_head;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf|z!",
+				&path, &path_len, &fci, &fcc, &zarg) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP(http, zhttp);
+
+	cb = _new_http_cb(zarg, &fci, &fcc TSRMLS_CC);
+	PHP_EVENT_ASSERT(cb);
+
+	res = evhttp_set_cb(http->ptr, path, _http_callback, (void *) cb);
+	if (res == -2) {
+		_php_event_free_http_cb(cb);
+
+		RETURN_FALSE;
+	}
+	if (res == -1) { // the callback existed already
+		_php_event_free_http_cb(cb);
+
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"The callback already exists");
+		RETURN_FALSE;
+	}
+
+	cb_head       = http->cb_head;
+	http->cb_head = cb;
+	cb->next      = cb_head;
+
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto void EventHttp::setDefaultCallback(callable cb[, mixed arg = NULL]);
+ * Sets default callback to handle requests that are not caught by specific callbacks
+ */
+PHP_METHOD(EventHttp, setDefaultCallback)
+{
+	zval                  *zhttp    = getThis();
+	php_event_http_t      *http;
+	zend_fcall_info        fci      = empty_fcall_info;
+	zend_fcall_info_cache  fcc      = empty_fcall_info_cache;
+	zval                  *zarg     = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f|z!",
+				&fci, &fcc, &zarg) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP(http, zhttp);
+
+	if (http->fci) {
+		PHP_EVENT_FREE_FCALL_INFO(http->fci, http->fcc);
+	}
+	PHP_EVENT_COPY_FCALL_INFO(http->fci, http->fcc, &fci, &fcc);
+
+	if (zarg) {
+		Z_ADDREF_P(zarg);
+	}
+	http->data = zarg;
+
+	evhttp_set_gencb(http->ptr, _http_default_callback, (void *) http);
+}
+/* }}} */
+
+/* {{{ proto void EventHttp::setAllowedMethods(int methods);
+ * Sets the what HTTP methods are supported in requests accepted by this
+ * server, and passed to user callbacks.
+ *
+ * If not supported they will generate a <literal>"405 Method not
+ * allowed"</literal> response.
+ *
+ * By default this includes the following methods: GET, POST, HEAD, PUT, DELETE.
+ * See <literal>EventHttpRequest::CMD_*</literal> constants.
+ */
+PHP_METHOD(EventHttp, setAllowedMethods)
+{
+	zval             *zhttp   = getThis();
+	php_event_http_t *http;
+	long              methods;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
+				&methods) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP(http, zhttp);
+
+	evhttp_set_allowed_methods(http->ptr, methods);
+}
+/* }}} */
+
+/* {{{ 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
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2013 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Ruslan Osmanov <osmanov@php.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_EVENT_HTTP_H
+#define PHP_EVENT_HTTP_H
+
+void _php_event_free_http_cb(php_event_http_cb_t *cb);
+
+#endif /* PHP_EVENT_HTTP_H */
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 sts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4 sts=4
+ */

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

classes/http_request.c

+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2013 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Ruslan Osmanov <osmanov@php.net>                             |
+   +----------------------------------------------------------------------+
+*/
+#include "src/common.h"
+#include "src/util.h"
+#include "src/priv.h"
+#include "classes/http.h"
+
+/* {{{ 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;
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, zself);
+
+	if (!http_req->ptr || http_req->internal) {
+		return;
+	}
+
+	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;
+	}
+}
+/* }}} */
+
+/* {{{ proto int EventHttpRequest::getCommand(void);
+ * Returns the request command, one of EventHttpRequest::CMD_* constants. XXX Make property? */
+PHP_METHOD(EventHttpRequest, getCommand)
+{
+	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_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_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_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_http_req_t *http_req;
+	struct evkeyvalq     *headers;
+	struct evkeyval      *header;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	array_init(return_value);
+
+	headers = evhttp_request_get_input_headers(http_req->ptr);
+	for (header = headers->tqh_first; header;
+			header = header->next.tqe_next) {
+		add_assoc_string(return_value, header->key, header->value, 1);
+	}
+
+
+}
+/* }}} */
+
+/* {{{ proto array EventHttpRequest::getOutputHeaders(void);
+ * Returns associative array of the output headers. */
+PHP_METHOD(EventHttpRequest, getOutputHeaders)
+{
+	php_event_http_req_t *http_req;
+	struct evkeyvalq     *headers;
+	struct evkeyval      *header;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	array_init(return_value);
+
+	headers = evhttp_request_get_output_headers(http_req->ptr);
+	for (header = headers->tqh_first; header;
+			header = header->next.tqe_next) {
+		add_assoc_string(return_value, header->key, header->value, 1);
+	}
+}
+/* }}} */
+
+/* {{{ proto EventBuffer EventHttpRequest::getInputBuffer(void);
+ * Returns input buffer. */
+PHP_METHOD(EventHttpRequest, getInputBuffer)
+{
+	php_event_http_req_t *http_req;
+	php_event_buffer_t   *b;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_buffer_ce);
+	PHP_EVENT_FETCH_BUFFER(b, return_value);
+	b->buf      = evhttp_request_get_input_buffer(http_req->ptr);
+	b->internal = 1;
+}
+/* }}} */
+
+/* {{{ proto EventBuffer EventHttpRequest::getOutputBuffer(void);
+ * Returns output buffer. */
+PHP_METHOD(EventHttpRequest, getOutputBuffer)
+{
+	php_event_http_req_t *http_req;
+	php_event_buffer_t   *b;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_buffer_ce);
+	PHP_EVENT_FETCH_BUFFER(b, return_value);
+	b->buf      = evhttp_request_get_output_buffer(http_req->ptr);
+	b->internal = 1;
+}
+/* }}} */
+
+/* {{{ 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.
+ *
+ * The body of the reply consists of data in <parameter>buf</parameter>. */
+PHP_METHOD(EventHttpRequest, sendReply)
+{
+	php_event_http_req_t *http_req;
+	long                  code;
+	char                 *reason;
+	int                   reason_len;
+	zval                 *zbuf = NULL;
+	php_event_buffer_t   *b;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls|O!",
+				&code, &reason, &reason_len,
+				&zbuf, php_event_buffer_ce) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	if (zbuf) {
+		PHP_EVENT_FETCH_BUFFER(b, zbuf);
+		PHP_EVENT_ASSERT(b->buf);
+	}
+
+	evhttp_send_reply(http_req->ptr, code, reason,
+			(zbuf ? b->buf : NULL));
+}
+/* }}} */
+
+/* {{{ proto void EventHttpRequest::sendReplyChunk(EventBuffer buf);
+ * Send another data chunk as part of an ongoing chunked reply.
+ *
+ * After calling this method <parameter>buf</parameter> will be	empty. */
+PHP_METHOD(EventHttpRequest, sendReplyChunk)
+{
+	php_event_http_req_t *http_req;
+	zval                 *zbuf;
+	php_event_buffer_t   *b;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O",
+				&zbuf, php_event_buffer_ce) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_check_http_req_ptr(http_req);
+
+	if (zbuf) {
+		PHP_EVENT_FETCH_BUFFER(b, zbuf);
+		PHP_EVENT_ASSERT(b->buf);
+	}
+
+	evhttp_send_reply_chunk(http_req->ptr, b->buf);
+}
+/* }}} */
+
+/* {{{ proto void EventHttpRequest::sendReplyEnd(void);
+ * Complete a chunked reply, freeing the request as appropriate. 
+ */
+PHP_METHOD(EventHttpRequest, sendReplyEnd)
+{
+	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_send_reply_end(http_req->ptr);
+}
+/* }}} */
+
+/* {{{ proto void EventHttpRequest::sendReplyStart(int code, string reason);
+ * Initiate a reply that uses <literal>Transfer-Encoding</literal>
+ * <literal>chunked</literal>.
+ *
+ * This allows the caller to stream the reply back to the client and is useful
+ * when either not all of the reply data is immediately available or when
+ * sending very large replies.
+ *
+ * The caller needs to supply data chunks with
+ * <method>EventHttpRequest::sendReplyChunk</method> and complete the reply by
+ * calling <method>EventHttpRequest::sendReplyEnd</method>.
+ */
+PHP_METHOD(EventHttpRequest, sendReplyStart)
+{
+	php_event_http_req_t *http_req;
+	long                  code;
+	char                 *reason;
+	int                   reason_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls",
+				&code, &reason, &reason_len) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_HTTP_REQ(http_req, getThis());
+
+	_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;
+}
+/* }}} */
+
+/* {{{ 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);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 sts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4 sts=4
+ */
 [  --with-event-core        Include core libevent support])
 
 PHP_ARG_WITH(event-pthreads, for event thread safety support,
-[  --with-event-pthreads    Include libevent's pthreads library and enable thread safety support in event], yes, no)
+[  --with-event-pthreads    Include libevent's pthreads library and enable thread safety support in event], no, no)
 
 PHP_ARG_WITH(event-extra, for event extra functionality support,
 [  --with-event-extra       Include libevent protocol-specific functionality support including HTTP, DNS, and RPC], yes, no)
       classes/dns.c \
       classes/listener.c \
       classes/http.c \
+      classes/http_request.c \
       classes/http_connection.c"
   fi
   dnl }}}

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();
 }
 ?>

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;
+	static $max_requests = 2;
+
+	if (++$counter >= $max_requests)  {
+		echo "Counter reached max requests $max_requests. Exiting\n";
+		exit();
+	}
+
+	echo __METHOD__, " called\n";
+	echo "request:"; var_dump($req);
+	echo "data:"; var_dump($data);
+
+	echo "\n===== DUMP =====\n";
+	echo "Command:", $req->getCommand(), PHP_EOL;
+	echo "URI:", $req->getUri(), PHP_EOL;
+	echo "Input headers:"; var_dump($req->getInputHeaders());
+	echo "Output headers:"; var_dump($req->getOutputHeaders());
+
+	echo "\n >> Sending reply ...";
+	$req->sendReply(200, "OK");
+	echo "OK\n";
+
+	echo "\n >> Reading input buffer ...\n";
+	$buf = $req->getInputBuffer();
+	while ($s = $buf->readLine(EventBuffer::EOL_ANY)) {
+		echo $s, PHP_EOL;
+	}
+	echo "No more data in the buffer\n";
+}
+
+function _http_about($req) {
+	echo __METHOD__, PHP_EOL;
+	echo "URI: ", $req->getUri(), PHP_EOL;
+	echo "\n >> Sending reply ...";
+	$req->sendReply(200, "OK");
+	echo "OK\n";
+}
+
+function _http_default($req, $data) {
+	echo __METHOD__, PHP_EOL;
+	echo "URI: ", $req->getUri(), PHP_EOL;
+	echo "\n >> Sending reply ...";
+	$req->sendReply(200, "OK");
+	echo "OK\n";
+}
+
+$port = 8010;
+if ($argc > 1) {
+	$port = (int) $argv[1];
+}
+if ($port <= 0 || $port > 65535) {
+	exit("Invalid port");
+}
+
+$base = new EventBase();
+$http = new EventHttp($base);
+$http->setAllowedMethods(EventHttpRequest::CMD_GET | EventHttpRequest::CMD_POST);
+
+$http->setCallback("/dump", "_http_dump", array(4, 8));
+$http->setCallback("/about", "_http_about");
+$http->setDefaultCallback("_http_default", "custom data value");
+
+$http->bind("0.0.0.0", 8010);
+$base->loop();
+?>

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;
+?>
     <email>osmanov@php.net</email>
     <active>yes</active>
   </lead>
-  <date>2013-03-08</date>
+  <date>2013-03-10</date>
   <!--{{{ Current version -->
   <version>
-    <release>1.3.0</release>
-    <api>1.3.1</api>
+    <release>1.4.0</release>
+    <api>1.4.0</api>
   </version>
   <stability>
     <release>beta</release>
   </stability>
   <license uri="http://www.php.net/license">PHP</license>
   <notes><![CDATA[
-  Fix: possible memory access violations in EventBufferEvent input/output property handlers
-  Change: Event::$timer_pending property removed; generic Event::$pending property added
-  Fix: With OPT_LEAVE_SOCKETS_BLOCKING flag EventListener::__construct turned fd to non-blocking mode
-  Fix: property and class HashTable's were not free'd in MSHUTDOWN
-  Add: Event::$data property
-  Fix: Event::__construct failed with Event::TIMEOUT flag
-  Fix: memory leak in EventBuffer::readLine
-  Add: --with-event-pthreads configure option
-  Fix: EventBase::reInit method's arginfo
+  Add: HTTP server support, EventHttpRequest class
+  Fix: turn off buggy libevent thread lock debugging
   ]]></notes>
   <!--}}}-->
   <!--{{{ Contents -->
         <file role="src" name="event_config.c"/>
         <file role="src" name="event_util.c"/>
         <file role="src" name="http.c"/>
+        <file role="src" name="http.h"/>
         <file role="src" name="http_connection.c"/>
+        <file role="src" name="http_request.c"/>
         <file role="src" name="listener.c"/>
         <file role="src" name="ssl_context.h"/>
         <file role="src" name="ssl_context.c"/>
         </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"/>
         <file role="src" name="02-features.phpt"/>
         <file role="src" name="03-event-del.phpt"/>
         <file role="src" name="04-bevent-socket.phpt"/>
+        <file role="src" name="05-buffer-pos.phpt"/>
+        <file role="src" name="06-timer.phpt"/>
       </dir>
     </dir>
   </contents>
   <extsrcrelease>
     <configureoption default="no" name="enable-event-debug" prompt="Enable internal debugging in event"/>
     <configureoption default="/usr" name="with-event-libevent-dir" prompt="libevent installation prefix"/>
-    <configureoption default="yes" name="with-event-pthreads" prompt="Include libevent's pthreads library and enable thread safety support in event"/>
+    <configureoption default="no" name="with-event-pthreads" prompt="Include libevent's pthreads library and enable thread safety support in event"/>
     <configureoption default="yes" name="with-event-extra" prompt="Include libevent protocol-specific functionality support including HTTP, DNS, and RPC"/>
     <configureoption default="yes" name="with-event-openssl" prompt="Include libevent OpenSSL support"/>
     <configureoption default="no" name="with-openssl-dir" prompt="openssl installation prefix"/>
   </extsrcrelease>
   <!--{{{ changelog-->
   <changelog>
+    <!--{{{ 1.4.0-beta  -->
+    <release>
+      <version>
+        <release>1.4.0</release>
+        <api>1.4.0</api>
+      </version>
+      <stability>
+        <release>beta</release>
+        <api>beta</api>
+      </stability>
+      <license uri="http://www.php.net/license">PHP</license>
+      <notes><![CDATA[
+  Add: HTTP server support, EventHttpRequest class
+  Fix: turn off buggy libevent thread lock debugging
+  ]]></notes>
+    </release>
+    <!--}}}-->
     <!--{{{ 1.3.0-beta-->
     <release>
       <version>
 #include "src/common.h"
 #include "src/util.h"
 #include "src/priv.h"
+#include "classes/http.h"
 
 #if 0
 ZEND_DECLARE_MODULE_GLOBALS(event)
 zend_class_entry *php_event_listener_ce;
 zend_class_entry *php_event_http_conn_ce;
 zend_class_entry *php_event_http_ce;
+zend_class_entry *php_event_http_req_ce;
 #endif
 
 static HashTable classes;
 
 	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);
 static void event_http_object_free_storage(void *ptr TSRMLS_DC)
 {
 	php_event_http_t *http = (php_event_http_t *) ptr;
+	php_event_http_cb_t *cb, *cb_next;
 
 	PHP_EVENT_ASSERT(http);
 
+	PHP_EVENT_FREE_FCALL_INFO(http->fci, http->fcc);
+
+	/* Free attached callbacks */
+	PHP_EVENT_ASSERT(http->cb_head);
+	cb = http->cb_head;
+	while (cb) {
+		cb_next = cb->next;
+		_php_event_free_http_cb(cb);
+		cb = cb_next;
+	}
+
+	if (http->data) {
+		zval_ptr_dtor(&http->data);
+		http->data = NULL;
+	}
+
 	if (http->base) {
 		zval_ptr_dtor(&http->base);
+		http->base = NULL;
 	}
 
 	if (http->ptr) {
 		evhttp_free(http->ptr);
+		http->ptr = NULL;
+	}
+
+	event_generic_object_free_storage(ptr TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ event_http_req_object_free_storage */
+static void event_http_req_object_free_storage(void *ptr TSRMLS_DC)
+{
+	php_event_http_req_t *http_req = (php_event_http_req_t *) ptr;
+
+	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;
 	}
 
 	event_generic_object_free_storage(ptr TSRMLS_CC);
 }
 /* }}} */
 
+/* {{{ event_http_req_object_create
+ * EventHttpRequest object ctor */
+static zend_object_value event_http_req_object_create(zend_class_entry *ce TSRMLS_DC)
+{
+	php_event_abstract_object_t *obj = (php_event_abstract_object_t *) object_new(ce, sizeof(php_event_http_req_t) TSRMLS_CC);
+
+	return register_object(ce, (void *) obj, (zend_objects_store_dtor_t) zend_objects_destroy_object,
+			event_http_req_object_free_storage TSRMLS_CC);
+}
+/* }}} */
+
 #endif /* HAVE_EVENT_EXTRA_LIB */
 
 
 			php_event_http_ce_functions);
 	ce = php_event_http_ce;
 	ce->ce_flags |= ZEND_ACC_FINAL_CLASS;
+
+	PHP_EVENT_REGISTER_CLASS("EventHttpRequest", event_http_req_object_create, php_event_http_req_ce,
+			php_event_http_req_ce_functions);
+	ce = php_event_http_req_ce;
+	ce->ce_flags |= ZEND_ACC_FINAL_CLASS;
+
 #endif /* HAVE_EVENT_EXTRA_LIB */
 
 	PHP_EVENT_REGISTER_CLASS("EventUtil", event_util_object_create, php_event_util_ce,
 # if LIBEVENT_VERSION_NUMBER >= 0x02010100
 	REGISTER_EVENT_CLASS_CONST_LONG(php_event_listener_ce, OPT_DEFERRED_ACCEPT,        LEV_OPT_DEFERRED_ACCEPT);
 # endif
-#endif
+
+	/* EventHttpRequest command types */
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, CMD_GET,     EVHTTP_REQ_GET);
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, CMD_POST,    EVHTTP_REQ_POST);
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, CMD_HEAD,    EVHTTP_REQ_HEAD);
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, CMD_PUT,     EVHTTP_REQ_PUT);
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, CMD_DELETE,  EVHTTP_REQ_DELETE);
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, CMD_OPTIONS, EVHTTP_REQ_OPTIONS);
+	REGISTER_EVENT_CLASS_CONST_LONG(php_event_http_req_ce, CMD_TRACE,   EVHTTP_REQ_TRACE);
+	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);
 
 #ifndef PHP_EVENT_H
 #define PHP_EVENT_H
 
-#define PHP_EVENT_VERSION "1.3.0-beta"
+#define PHP_EVENT_VERSION "1.4.0-beta"
 
 
 extern zend_module_entry event_module_entry;
 # define PHP_EVENT_SOCKETS_SUPPORT
 #endif
 
+
 #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, port)
 ZEND_END_ARG_INFO();
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_set_callback, 0, 0, 2)
+	ZEND_ARG_INFO(0, path)
+	ZEND_ARG_INFO(0, cb)
+	ZEND_ARG_INFO(0, arg)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_set_allowed_methods, 0, 0, 1)
+	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_ARG_INFO(0, buf)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_req_send_reply_chunk, 0, 0, 1)
+	ZEND_ARG_INFO(0, buf)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_http_req_send_reply_start, 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)
 	ZEND_ARG_INFO(0, options)
 /* }}} */
 
 const zend_function_entry php_event_bevent_ce_functions[] = {/* {{{ */
-	PHP_ME(EventBufferEvent, __construct,       arginfo_bufferevent__construct,              ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
-	PHP_ME(EventBufferEvent, free,              arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, connect,           arginfo_bufferevent_connect,                 ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, connectHost,       arginfo_bufferevent_socket_connect_hostname, ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, getDnsErrorString, arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, setCallbacks,      arginfo_bufferevent_set_callbacks,           ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, enable,            arginfo_bufferevent__events,                 ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, disable,           arginfo_bufferevent__events,                 ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, getEnabled,        arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, getInput,          arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, getOutput,         arginfo_event__void,                         ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, setWatermark,      arginfo_bufferevent_setwatermark,            ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, write,             arginfo_bufferevent_write,                   ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, writeBuffer,       arginfo_bufferevent_write_buffer,            ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, read,              arginfo_bufferevent_read,                    ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, readBuffer,        arginfo_bufferevent_write_buffer,            ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, createPair,        arginfo_bufferevent_pair_new,                ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, setPriority,       arginfo_bufferevent_priority_set,            ZEND_ACC_PUBLIC)
-	PHP_ME(EventBufferEvent, setTimeouts,       arginfo_bufferevent_set_timeouts,            ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, __construct,        arginfo_bufferevent__construct,              ZEND_ACC_PUBLIC  | ZEND_ACC_CTOR)
+	PHP_ME(EventBufferEvent, free,               arginfo_event__void,                         ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, connect,            arginfo_bufferevent_connect,                 ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, connectHost,        arginfo_bufferevent_socket_connect_hostname, ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, getDnsErrorString,  arginfo_event__void,                         ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, setCallbacks,       arginfo_bufferevent_set_callbacks,           ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, enable,             arginfo_bufferevent__events,                 ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, disable,            arginfo_bufferevent__events,                 ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, getEnabled,         arginfo_event__void,                         ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, getInput,           arginfo_event__void,                         ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, getOutput,          arginfo_event__void,                         ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, setWatermark,       arginfo_bufferevent_setwatermark,            ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, write,              arginfo_bufferevent_write,                   ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, writeBuffer,        arginfo_bufferevent_write_buffer,            ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, read,               arginfo_bufferevent_read,                    ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, readBuffer,         arginfo_bufferevent_write_buffer,            ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, createPair,         arginfo_bufferevent_pair_new,                ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, setPriority,        arginfo_bufferevent_priority_set,            ZEND_ACC_PUBLIC)
+	PHP_ME(EventBufferEvent, setTimeouts,        arginfo_bufferevent_set_timeouts,            ZEND_ACC_PUBLIC)
 #ifdef HAVE_EVENT_OPENSSL_LIB
 	PHP_ME(EventBufferEvent, sslFilter,         arginfo_bufferevent_ssl_filter,              ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
 	PHP_ME(EventBufferEvent, sslSocket,         arginfo_bufferevent_ssl_socket,              ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
 	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
 };
 
 const zend_function_entry php_event_http_ce_functions[] = {
-	PHP_ME(EventHttp, __construct, arginfo_event_http__construct, ZEND_ACC_PUBLIC  | ZEND_ACC_CTOR)
-	PHP_ME(EventHttp, accept,      arginfo_event_http_accept,     ZEND_ACC_PUBLIC)
-	PHP_ME(EventHttp, bind,        arginfo_event_http_bind,       ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttp, __construct,        arginfo_event_http__construct,          ZEND_ACC_PUBLIC  | ZEND_ACC_CTOR)
+	PHP_ME(EventHttp, accept,             arginfo_event_http_accept,              ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttp, bind,               arginfo_event_http_bind,                ZEND_ACC_PUBLIC)
+	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, bind);
-
+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
 
 extern const zend_function_entry php_event_dns_base_ce_functions[];
 extern const zend_function_entry php_event_http_conn_ce_functions[];
 extern const zend_function_entry php_event_http_ce_functions[];
+extern const zend_function_entry php_event_http_req_ce_functions[];
 extern const zend_function_entry php_event_listener_ce_functions[];
 
 extern zend_class_entry *php_event_dns_base_ce;
 extern zend_class_entry *php_event_listener_ce;
 extern zend_class_entry *php_event_http_conn_ce;
 extern zend_class_entry *php_event_http_ce;
+extern zend_class_entry *php_event_http_req_ce;
 #endif /* HAVE_EVENT_EXTRA_LIB */
 
 #endif /* PHP_EVENT_PRIV_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_COMMON_THREAD_CTX;
 } php_event_listener_t;
 
+typedef struct _php_event_http_cb_t php_event_http_cb_t;
+
+/* Type for an HTTP server callback */
+struct _php_event_http_cb_t {
+	php_event_http_cb_t   *next;   /* Linked list                         */
+	zval                  *data;   /* User custom data passed to callback */
+	zend_fcall_info       *fci;
+	zend_fcall_info_cache *fcc;
+
+	PHP_EVENT_COMMON_THREAD_CTX;
+};
+
+/* Represents EventHttp object */
+typedef struct _php_event_http_t {
+	PHP_EVENT_OBJECT_HEAD;
+
+	struct evhttp         *ptr;
+	zval                  *base;        /* Event base associated with the listener              */
+	zval                  *data;        /* User custom data passed to the gen(default) callback */
+
+	/* General(default) callback for evhttp_gencb() */
+	zend_fcall_info       *fci;
+	zend_fcall_info_cache *fcc;
+
+	/* Linked list of attached callbacks */
+	php_event_http_cb_t   *cb_head;
+
+	PHP_EVENT_COMMON_THREAD_CTX;
+} php_event_http_t;
+
 /* Represents EventHttpConnection object */
 typedef struct _php_event_http_conn_t {
 	PHP_EVENT_OBJECT_HEAD;
 	zval                     *dns_base;   /* Associated EventDnsBase                 */
 } php_event_http_conn_t;
 
-/* Represents EventHttp object */
 typedef struct {
 	PHP_EVENT_OBJECT_HEAD;
 
-	struct evhttp *ptr;
-	zval          *base;        /* Event base associated with the listener       */
-	int            stream_id;   /* Resource ID of socket probably being listened */
-} php_event_http_t;
+	struct evhttp_request *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 }}} */
 
 #define PHP_EVENT_FETCH_HTTP(b, zb) \
 	b = (php_event_http_t *) zend_object_store_get_object(zb TSRMLS_CC)
 
+#define PHP_EVENT_FETCH_HTTP_REQ(b, zb) \
+	b = (php_event_http_req_t *) zend_object_store_get_object(zb TSRMLS_CC)
+
 #define PHP_EVENT_FETCH_BUFFER_POS(p, zp) \
 	p = (php_event_buffer_pos_t *) zend_object_store_get_object(zp TSRMLS_CC)
 

tests/06-timer.phpt

+--TEST--
+Check for timer event basic behaviour 
+--FILE--
+<?php
+$base = new EventBase();
+$e = new Event($base, -1, Event::TIMEOUT, function($fd, $what, $e) {
+	echo "0.4 seconds elapsed";
+	//$e->delTimer();
+});
+$e->data = $e;
+$e->addTimer(0.4);
+$base->loop();
+?>
+--EXPECT--
+0.4 seconds elapsed