Commits

Ruslan Osmanov committed 4a687d1

Dev: adding http server support

Comments (0)

Files changed (10)

 #include "src/util.h"
 #include "src/priv.h"
 
+/* {{{ Private */
+
+/* {{{ _http_callback */
+static void _http_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;
+	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);
+}
+/* }}} */
+
+/* }}} */
+
 /* {{{ proto EventHttp EventHttp::__construct(EventBase base);
  *
  * Creates new http server object.
 	Z_ADDREF_P(zbase);
 
 	http->stream_id = -1;
+	http->fci       = http->fcc      = NULL;
+	http->data      = http->gen_data = NULL;
 }
 /* }}} */
 
 }
 /* }}} */
 
+/* {{{ proto bool EventHttp::setCallback(string path, callable cb[, mixed arg = NULL]);
+ * Set a callback for a 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;
+
+	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);
+
+	res = evhttp_set_cb(http->ptr, path, _http_callback, (void *) http);
+	if (res == -2) {
+		RETURN_FALSE;
+	}
+	if (res == -1) { // the callback existed already
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"The callback already exists");
+		RETURN_FALSE;
+	}
+
+	if (http->data) {
+		zval_ptr_dtor(&http->data);
+	}
+	if (zarg) {
+		Z_ADDREF_P(zarg);
+	}
+	http->data = zarg;
+
+	/* 
+	 * XXX We should set up individual user functions for every path!!!
+	 */
+
+	PHP_EVENT_COPY_FCALL_INFO(http->fci, http->fcc, &fci, &fcc);
+
+	TSRMLS_SET_CTX(http->thread_ctx);
+
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ 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
+ */
+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);
+}
+/* }}} */
+
 /*
  * 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"
+
+/* {{{ 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());
+
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Invalid HTTP request object");
+		RETURN_FALSE;
+	}
+
+	RETVAL_LONG(evhttp_request_get_command(http_req->ptr));
+}
+/* }}} */
+
+/* {{{ proto int EventHttpRequest::getUri(void);
+ * Returns the request URI. XXX make a property? */
+PHP_METHOD(EventHttpRequest, getUri)
+{
+	php_event_http_req_t *http_req;
+	char *uri;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	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;
+	}
+
+	uri = evhttp_request_get_uri(http_req->ptr);
+	RETVAL_STRING(uri, 1);
+	free(uri);
+}
+/* }}} */
+
+/* {{{ 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());
+
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Invalid HTTP request object");
+		RETURN_FALSE;
+	}
+
+	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());
+
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Invalid HTTP request object");
+		RETURN_FALSE;
+	}
+
+	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());
+
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Invalid HTTP request object");
+		RETURN_FALSE;
+	}
+
+	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());
+
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Invalid HTTP request object");
+		RETURN_FALSE;
+	}
+
+	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::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());
+
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Invalid HTTP request object");
+		RETURN_FALSE;
+	}
+
+	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());
+
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Invalid HTTP request object");
+		RETURN_FALSE;
+	}
+
+	if (zbuf) {
+		PHP_EVENT_FETCH_BUFFER(b, zbuf);
+		PHP_EVENT_ASSERT(b->buf);
+	}
+
+	evhttp_send_reply_chunk(http_req->ptr, code, reason, 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());
+
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Invalid HTTP request object");
+		RETURN_FALSE;
+	}
+
+	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, sendReplyEnd)
+{
+	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());
+
+	if (!http_req->ptr) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Invalid HTTP request object");
+		RETURN_FALSE;
+	}
+
+	evhttp_send_reply_start(http_req->ptr, code, reason);
+}
+/* }}} */
+
+/*
+ * 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 }}}
   <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"/>
 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;
 
 	PHP_EVENT_ASSERT(http);
 
+	PHP_EVENT_FREE_FCALL_INFO(http->fci, http->fcc);
+
+	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_create(void *ptr TSRMLS_DC)
+{
+	php_event_http_req_t *http_req = (php_event_http_req_t *) ptr;
+
+	PHP_EVENT_ASSERT(http_req);
+
+	if (http_req->ptr) {
+		evhttp_request_free(http->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);
+
+#endif /* HAVE_EVENT_EXTRA_LIB */
 
 	REGISTER_EVENT_CLASS_CONST_LONG(php_event_util_ce, LIBEVENT_VERSION_NUMBER, LIBEVENT_VERSION_NUMBER);
 
 	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_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_ssl_context__construct, 0, 0, 2)
 	ZEND_ARG_INFO(0, method)
 	ZEND_ARG_INFO(0, options)
 };
 
 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, setAllowedMethods, arginfo_event_http_set_allowed_methods, ZEND_ACC_PUBLIC)
+
+	PHP_FE_END
+};
+
+const zend_function_entry php_event_http_req_ce_functions[] = {
+	PHP_ABSTRACT_ME(EventHttpRequest, __construct, NULL)
+
+	PHP_ME(EventHttpRequest, getCommand,       arginfo_event__void,                     ZEND_ACC_PUBLIC)
+	PHP_ME(EventHttpRequest, getUri,           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, 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_FE_END
 };
 PHP_METHOD(EventHttp, __construct);
 PHP_METHOD(EventHttp, accept);
 PHP_METHOD(EventHttp, bind);
-
+PHP_METHOD(EventHttp, setCallback);
+PHP_METHOD(EventHttp, setAllowedMethods);
+
+PHP_METHOD(EventHttpRequest, getCommand);
+PHP_METHOD(EventHttpRequest, getUri);
+PHP_METHOD(EventHttpRequest, getInputHeaders);
+PHP_METHOD(EventHttpRequest, getOutputHeaders);
+PHP_METHOD(EventHttpRequest, getInputBuffer);
+PHP_METHOD(EventHttpRequest, getOutputBuffer);
+PHP_METHOD(EventHttpRequest, sendReply);
+PHP_METHOD(EventHttpRequest, sendReplyChunk);
+PHP_METHOD(EventHttpRequest, sendReplyEnd);
+PHP_METHOD(EventHttpRequest, sendReplyStart);
 /* 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 */
 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 */
+	struct evhttp         *ptr;
+	zval                  *base;        /* Event base associated with the listener              */
+	zval                  *data;        /* User custom data passed to callback                  */
+	zval                  *gen_data;    /* User custom data passed to the gen(default) callback */
+	int                    stream_id;   /* Resource ID of socket probably being listened        */
+
+	zend_fcall_info       *fci;
+	zend_fcall_info_cache *fcc;
+
+	PHP_EVENT_COMMON_THREAD_CTX;
 } php_event_http_t;
 
+typedef struct {
+	PHP_EVENT_OBJECT_HEAD;
+
+	struct evhttp_request *ptr;
+} php_event_http_req_t;
+
 #endif/* HAVE_EVENT_EXTRA_LIB }}} */
 
 typedef struct _php_event_buffer_pos_t {
 #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)