1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 8                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2020 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Ruslan Osmanov <osmanov@php.net>                             |
16    +----------------------------------------------------------------------+
17 */
18 #include "../src/common.h"
19 #include "../src/util.h"
20 #include "../src/priv.h"
21 #include "http.h"
22 #include "zend_exceptions.h"
23 
24 /* {{{ Private */
25 
26 /* {{{ _check_http_req_ptr */
27 #define _check_http_req_ptr(http_req)               \
28 {                                                   \
29     if (!http_req->ptr) {                           \
30         php_error_docref(NULL, E_WARNING, \
31                 "Invalid HTTP request object");     \
32         RETURN_FALSE;                               \
33     }                                               \
34 }
35 /* }}} */
36 
37 /* {{{ _check_http_req_type */
38 #define _check_http_req_type(type)                                            \
getContents()39 {                                                                             \
40     if (type & ~(PHP_EVENT_REQ_HEADER_INPUT | PHP_EVENT_REQ_HEADER_OUTPUT)) { \
41         php_error_docref(NULL, E_WARNING,                           \
42                 "Invalid HTTP request type passed: %ld", type);               \
43         RETURN_FALSE;                                                         \
44     }                                                                         \
45 }
46 /* }}} */
47 
48 /* {{{ _get_http_req_headers */
49 static zend_always_inline struct evkeyvalq *_get_http_req_headers(const php_event_http_req_t *http_req, const zend_long type)
50 {
51 	struct evkeyvalq *headers;
52 
53 	if (type == PHP_EVENT_REQ_HEADER_OUTPUT) {
54 		headers = evhttp_request_get_output_headers(http_req->ptr);
55 	} else {
56 		headers = evhttp_request_get_input_headers(http_req->ptr);
57 	}
58 
59 	return headers;
60 }
61 /* }}} */
62 
63 /* {{{ _req_handler */
64 static void _req_handler(struct evhttp_request *req, void *arg)
65 {
66 	php_event_http_req_t *http_req  = (php_event_http_req_t *)arg;
67 	zend_fcall_info       fci;
68 	zval                  argv[2];
69 	zval                  retval;
70 	zval                  zcallable;
71 	zend_string          *func_name;
72 
73 	PHP_EVENT_ASSERT(http_req && http_req->ptr);
74 
75 	/* Protect against accidental destruction of the func name before zend_call_function() finished */
76 	ZVAL_COPY(&zcallable, &http_req->cb.func_name);
77 
78 	if (!zend_is_callable(&zcallable, IS_CALLABLE_STRICT, &func_name)) {
79 		zend_string_release(func_name);
80 		return;
81 	}
82 	zend_string_release(func_name);
83 
84 	/* Call userspace function according to
85 	 * proto void callback(EventHttpRequest req, mixed data); */
86 
87 	/* req == NULL means timeout */
88 	if (req == NULL || Z_ISUNDEF(http_req->self)) {
89 		ZVAL_NULL(&argv[0]);
90 	} else {
91 		ZVAL_COPY(&argv[0], &http_req->self);
92 	}
93 
94 	if (Z_ISUNDEF(http_req->data)) {
95 		ZVAL_NULL(&argv[1]);
96 	} else {
97 		ZVAL_COPY(&argv[1], &http_req->data);
98 	}
99 
100 	fci.size = sizeof(fci);
101 #ifdef HAVE_PHP_ZEND_FCALL_INFO_FUNCTION_TABLE
102 	fci.function_table = EG(function_table);
103 #endif
104 	ZVAL_COPY_VALUE(&fci.function_name, &zcallable);
105 	fci.object = NULL;
106 	fci.retval = &retval;
107 	fci.params = argv;
108 	fci.param_count = 2;
109 	fci.no_separation  = 1;
110 #ifdef HAVE_PHP_ZEND_FCALL_INFO_SYMBOL_TABLE
111 	fci.symbol_table = NULL;
112 #endif
113 
114 	/* Tell Libevent that we will free the request ourselves(evhttp_request_free in the free-storage handler)*/
115 	/*evhttp_request_own(http_req->ptr);*/
116 
117 	if (zend_call_function(&fci, &http_req->cb.fci_cache) == SUCCESS) {
118 		if (!Z_ISUNDEF(retval)) {
119 			zval_ptr_dtor(&retval);
120 		}
121 	} else {
122 		php_error_docref(NULL, E_WARNING, "Failed to invoke http request handler");
123 	}
124 
125 	zval_ptr_dtor(&zcallable);
126 
127 	zval_ptr_dtor(&argv[0]);
128 	zval_ptr_dtor(&argv[1]);
129 }
130 /* }}} */
131 
132 /* }}} */
133 
134 /*{{{ proto int EventHttpRequest::__sleep */
135 PHP_METHOD(EventHttpRequest, __sleep)
136 {
137 	zend_throw_exception_ex(php_event_get_exception(), 0, "EventHttpRequest instances are not serializable");
138 }
139 /*}}}*/
140 
141 /*{{{ proto int EventHttpRequest::__wakeup */
142 PHP_METHOD(EventHttpRequest, __wakeup)
143 {
144 	zend_throw_exception_ex(php_event_get_exception(), 0, "EventHttpRequest instances are not serializable");
145 }
146 /*}}}*/
147 
148 /* {{{ proto EventHttpRequest::__construct(callable callback[, mixed data = NULL]); */
149 PHP_METHOD(EventHttpRequest, __construct)
150 {
151 	zval                  *zself    = getThis();
152 	php_event_http_req_t  *http_req;
153 	zval                  *zcb;
154 	zval                  *zarg     = NULL;
155 	struct evhttp_request *req;
156 
157 
158 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|z!", &zcb, &zarg) == FAILURE) {
159 		return;
160 	}
161 
162 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(zself);
163 
164 	req = evhttp_request_new(_req_handler, (void *)http_req);
165 	PHP_EVENT_ASSERT(req);
166 
167 	/* Tell Libevent that we will free the request ourselves(evhttp_request_free in the free-storage handler)
168 	 * XXX Not sure if it's really needed here though. */
169 	evhttp_request_own(req);
170 	http_req->ptr = req;
171 
172 	ZVAL_COPY(&http_req->self, zself);
173 	if (zarg) {
174 		ZVAL_COPY(&http_req->data, zarg);
175 	} else {
176 		ZVAL_UNDEF(&http_req->data);
177 	}
178 	php_event_copy_callback(&http_req->cb, zcb);
179 }
180 /* }}} */
181 
182 /* {{{ proto void EventHttpRequest::free(void);
183  * Frees the object and removes associated events. */
184 PHP_METHOD(EventHttpRequest, free)
185 {
186 	php_event_http_req_t *http_req;
187 
188 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
189 	PHP_EVENT_ASSERT(http_req);
190 
191 	if (!http_req->ptr || http_req->internal) {
192 		return;
193 	}
194 
195 	if (http_req->ptr) {
196 		/*
197 		 * We're not calling evhttp_request_free(http_req->ptr) because AFAIK
198 		 * Libevent handles the memory of evhttp_request all right.
199 		 *
200 		 * It just so happens that libevent invokes the function itself in
201 		 * evhttp_connection_cb_cleanup() despite the ownership of the request
202 		 * (thus causing a SEGFAULT). See bitbucket issue #3.
203 		 *
204 		 * By marking http_req as `internal` we prevent calling evhttp_request_free()
205 		 * within event_http_req_object_free_storage().
206 		 */
207 		http_req->internal = 1;
208 		/*http_req->ptr = NULL;*/
209 	}
210 
211 	/* Do it once */
212 	if (!Z_ISUNDEF(http_req->self)) {
213 		zval_ptr_dtor(&http_req->self);
214 		ZVAL_UNDEF(&http_req->self);
215 	}
216 }
217 /* }}} */
218 
219 /* {{{ proto int EventHttpRequest::getCommand(void);
220  * Returns the request command, one of EventHttpRequest::CMD_* constants. XXX Make property? */
221 PHP_METHOD(EventHttpRequest, getCommand)
222 {
223 	php_event_http_req_t *http_req;
224 
225 	if (zend_parse_parameters_none() == FAILURE) {
226 		return;
227 	}
228 
229 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
230 
231 	_check_http_req_ptr(http_req);
232 
233 	RETVAL_LONG(evhttp_request_get_command(http_req->ptr));
234 }
235 /* }}} */
236 
237 /* {{{ proto string EventHttpRequest::getHost(void);
238  * Returns the request host. XXX make a property? */
239 PHP_METHOD(EventHttpRequest, getHost)
240 {
241 	php_event_http_req_t *http_req;
242 
243 	if (zend_parse_parameters_none() == FAILURE) {
244 		return;
245 	}
246 
247 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
248 
249 	_check_http_req_ptr(http_req);
250 
251 	RETVAL_STRING(evhttp_request_get_host(http_req->ptr));
252 }
253 /* }}} */
254 
255 /* {{{ proto int EventHttpRequest::getUri(void);
256  * Returns the request URI. XXX make a property? */
257 PHP_METHOD(EventHttpRequest, getUri)
258 {
259 	php_event_http_req_t *http_req;
260 
261 	if (zend_parse_parameters_none() == FAILURE) {
262 		return;
263 	}
264 
265 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
266 
267 	_check_http_req_ptr(http_req);
268 
269 	RETVAL_STRING(evhttp_request_get_uri(http_req->ptr));
270 }
271 /* }}} */
272 
273 /* {{{ proto int EventHttpRequest::getResponseCode(void);
274  * Returns the the response code. XXX make a property? */
275 PHP_METHOD(EventHttpRequest, getResponseCode)
276 {
277 	php_event_http_req_t *http_req;
278 
279 	if (zend_parse_parameters_none() == FAILURE) {
280 		return;
281 	}
282 
283 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
284 
285 	_check_http_req_ptr(http_req);
286 
287 	RETVAL_LONG(evhttp_request_get_response_code(http_req->ptr));
288 }
289 /* }}} */
290 
291 /* {{{ proto array EventHttpRequest::getInputHeaders(void);
292  * Returns associative array of the input headers. */
293 PHP_METHOD(EventHttpRequest, getInputHeaders)
294 {
295 	php_event_http_req_t *http_req;
296 	struct evkeyvalq     *headers;
297 	struct evkeyval      *header;
298 
299 	if (zend_parse_parameters_none() == FAILURE) {
300 		return;
301 	}
302 
303 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
304 
305 	_check_http_req_ptr(http_req);
306 
307 	array_init(return_value);
308 
309 	headers = evhttp_request_get_input_headers(http_req->ptr);
310 	for (header = headers->tqh_first; header;
311 			header = header->next.tqe_next) {
312 		add_assoc_string(return_value, header->key, header->value);
313 	}
314 
315 
316 }
317 /* }}} */
318 
319 /* {{{ proto array EventHttpRequest::getOutputHeaders(void);
320  * Returns associative array of the output headers. */
321 PHP_METHOD(EventHttpRequest, getOutputHeaders)
322 {
323 	php_event_http_req_t *http_req;
324 	struct evkeyvalq     *headers;
325 	struct evkeyval      *header;
326 
327 	if (zend_parse_parameters_none() == FAILURE) {
328 		return;
329 	}
330 
331 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
332 
333 	_check_http_req_ptr(http_req);
334 
335 	array_init(return_value);
336 
337 	headers = evhttp_request_get_output_headers(http_req->ptr);
338 	for (header = headers->tqh_first; header;
339 			header = header->next.tqe_next) {
340 		add_assoc_string(return_value, header->key, header->value);
341 	}
342 }
343 /* }}} */
344 
345 /* {{{ proto EventBuffer EventHttpRequest::getInputBuffer(void);
346  * Returns input buffer. */
347 PHP_METHOD(EventHttpRequest, getInputBuffer)
348 {
349 	php_event_http_req_t *http_req;
350 	php_event_buffer_t   *b;
351 
352 	if (zend_parse_parameters_none() == FAILURE) {
353 		return;
354 	}
355 
356 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
357 
358 	_check_http_req_ptr(http_req);
359 
360 	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_buffer_ce);
361 	b = Z_EVENT_BUFFER_OBJ_P(return_value);
362 	b->buf      = evhttp_request_get_input_buffer(http_req->ptr);
363 	b->internal = 1;
364 }
365 /* }}} */
366 
367 /* {{{ proto EventBuffer EventHttpRequest::getOutputBuffer(void);
368  * Returns output buffer. */
369 PHP_METHOD(EventHttpRequest, getOutputBuffer)
370 {
371 	php_event_http_req_t *http_req;
372 	php_event_buffer_t   *b;
373 
374 	if (zend_parse_parameters_none() == FAILURE) {
375 		return;
376 	}
377 
378 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
379 
380 	_check_http_req_ptr(http_req);
381 
382 	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_buffer_ce);
383 	b = Z_EVENT_BUFFER_OBJ_P(return_value);
384 	b->buf      = evhttp_request_get_output_buffer(http_req->ptr);
385 	b->internal = 1;
386 }
387 /* }}} */
388 
389 #if LIBEVENT_VERSION_NUMBER >= 0x02001100
390 /* {{{ proto EventBufferEvent EventHttpRequest::getBufferEvent(void);
391  * Returns EventBufferEvent object on success, otherwise &null. */
392 PHP_METHOD(EventHttpRequest, getBufferEvent)
393 {
394 	php_event_http_req_t     *http_req;
395 	struct evhttp_connection *conn;
396 	php_event_bevent_t       *bev;
397 
398 	if (zend_parse_parameters_none() == FAILURE) {
399 		return;
400 	}
401 
402 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
403 
404 	_check_http_req_ptr(http_req);
405 
406 	conn = evhttp_request_get_connection(http_req->ptr);
407 	if (conn == NULL) {
408 		RETURN_NULL();
409 	}
410 
411 	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_bevent_ce);
412 	bev = Z_EVENT_BEVENT_OBJ_P(return_value);
413 
414 	bev->bevent = evhttp_connection_get_bufferevent(conn);
415 	ZVAL_COPY(&bev->self, return_value);
416 	ZVAL_UNDEF(&bev->input);
417 	ZVAL_UNDEF(&bev->output);
418 	bev->_internal = 1;
419 }
420 /* }}} */
421 #endif
422 
423 /* {{{ proto EventHttpConnection EventHttpRequest::getConnection(void);
424  * Returns EventHttpConnection object.
425  *
426  * Warning! Libevent API allows http request objects not bound to any http connection.
427  * Therefore we can't unambiguously associate EventHttpRequest with EventHttpConnection.
428  * Thus, we construct EventHttpConnection object on-the-fly. Having no information about
429  * base, dns_base and connection-close callback, we just leave these fields unset.
430  *
431  * If somebody finds some way to return full-value EventHttpConnection object,
432  * please don't hesitate to make a pull request.
433  */
434 PHP_METHOD(EventHttpRequest, getConnection)
435 {
436 	php_event_http_req_t     *http_req;
437 	struct evhttp_connection *conn;
438 	php_event_http_conn_t    *evcon;
439 
440 	if (zend_parse_parameters_none() == FAILURE) {
441 		return;
442 	}
443 
444 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
445 
446 	_check_http_req_ptr(http_req);
447 
448 	conn = evhttp_request_get_connection(http_req->ptr);
449 	if (conn == NULL) {
450 		RETURN_NULL();
451 	}
452 
453 	PHP_EVENT_INIT_CLASS_OBJECT(return_value, php_event_http_conn_ce);
454 	evcon = Z_EVENT_HTTP_CONN_OBJ_P(return_value);
455 
456 	evcon->conn = conn;
457 	evcon->internal = TRUE;
458 	ZVAL_COPY(&evcon->self, return_value);
459 
460 #if 0
461 	ZVAL_UNDEF(&evcon->base);
462 	ZVAL_UNDEF(&evcon->dns_base);
463 	ZVAL_UNDEF(&evcon->data_closecb);
464 	ZVAL_UNDEF(&evcon->cb_close.func_name);
465 #endif
466 
467 #if 0
468 	Z_TRY_ADDREF_P(return_value);
469 #endif
470 }
471 /* }}} */
472 
473 /* {{{ proto void EventHttpRequest::closeConnection(void);
474  */
475 PHP_METHOD(EventHttpRequest, closeConnection)
476 {
477 	php_event_http_req_t     *http_req;
478 	struct evhttp_connection *conn;
479 
480 	if (zend_parse_parameters_none() == FAILURE) {
481 		return;
482 	}
483 
484 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
485 
486 	_check_http_req_ptr(http_req);
487 
488 	conn = evhttp_request_get_connection(http_req->ptr);
489 	if (conn == NULL) {
490 		return;
491 	}
492 	evhttp_connection_free(conn);
493 }
494 /* }}} */
495 
496 /* {{{ proto void EventHttpRequest::sendError(int error[, string reason = NULL]);
497  * Send an HTML error message to the client.
498  */
499 PHP_METHOD(EventHttpRequest, sendError)
500 {
501 	php_event_http_req_t *http_req;
502 	zend_long             error;
503 	char                 *reason     = NULL;
504 	size_t                reason_len;
505 
506 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|s!",
507 				&error, &reason, &reason_len) == FAILURE) {
508 		return;
509 	}
510 
511 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
512 
513 	_check_http_req_ptr(http_req);
514 
515 	evhttp_send_error(http_req->ptr, error, reason);
516 }
517 /* }}} */
518 
519 /* {{{ proto void EventHttpRequest::sendReply(int code, string reason[, EventBuffer buf=&null;]);
520  * Send an HTML reply to client.
521  *
522  * The body of the reply consists of data in <parameter>buf</parameter>. */
523 PHP_METHOD(EventHttpRequest, sendReply)
524 {
525 	php_event_http_req_t *http_req;
526 	zend_long             code;
527 	char                 *reason;
528 	size_t                reason_len;
529 	zval                 *zbuf       = NULL;
530 	php_event_buffer_t   *b;
531 
532 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls|O!",
533 				&code, &reason, &reason_len,
534 				&zbuf, php_event_buffer_ce) == FAILURE) {
535 		return;
536 	}
537 
538 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
539 
540 	_check_http_req_ptr(http_req);
541 
542 	if (zbuf) {
543 		b = Z_EVENT_BUFFER_OBJ_P(zbuf);
544 		PHP_EVENT_ASSERT(b->buf);
545 	}
546 
547 	evhttp_send_reply(http_req->ptr, code, reason,
548 			(zbuf ? b->buf : NULL));
549 }
550 /* }}} */
551 
552 /* {{{ proto void EventHttpRequest::sendReplyChunk(EventBuffer buf);
553  * Send another data chunk as part of an ongoing chunked reply.
554  *
555  * After calling this method <parameter>buf</parameter> will be	empty. */
556 PHP_METHOD(EventHttpRequest, sendReplyChunk)
557 {
558 	php_event_http_req_t *http_req;
559 	zval                 *zbuf;
560 	php_event_buffer_t   *b;
561 
562 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O",
563 				&zbuf, php_event_buffer_ce) == FAILURE) {
564 		return;
565 	}
566 
567 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
568 
569 	_check_http_req_ptr(http_req);
570 
571 	if (zbuf) {
572 		b = Z_EVENT_BUFFER_OBJ_P(zbuf);
573 		PHP_EVENT_ASSERT(b->buf);
574 		evhttp_send_reply_chunk(http_req->ptr, b->buf);
575 	}
576 }
577 /* }}} */
578 
579 /* {{{ proto void EventHttpRequest::sendReplyEnd(void);
580  * Complete a chunked reply, freeing the request as appropriate.
581  */
582 PHP_METHOD(EventHttpRequest, sendReplyEnd)
583 {
584 	php_event_http_req_t *http_req;
585 
586 	if (zend_parse_parameters_none() == FAILURE) {
587 		return;
588 	}
589 
590 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
591 
592 	_check_http_req_ptr(http_req);
593 
594 	evhttp_send_reply_end(http_req->ptr);
595 }
596 /* }}} */
597 
598 /* {{{ proto void EventHttpRequest::sendReplyStart(int code, string reason);
599  * Initiate a reply that uses <literal>Transfer-Encoding</literal>
600  * <literal>chunked</literal>.
601  *
602  * This allows the caller to stream the reply back to the client and is useful
603  * when either not all of the reply data is immediately available or when
604  * sending very large replies.
605  *
606  * The caller needs to supply data chunks with
607  * <method>EventHttpRequest::sendReplyChunk</method> and complete the reply by
608  * calling <method>EventHttpRequest::sendReplyEnd</method>.
609  */
610 PHP_METHOD(EventHttpRequest, sendReplyStart)
611 {
612 	php_event_http_req_t *http_req;
613 	zend_long             code;
614 	char                 *reason;
615 	size_t                reason_len;
616 
617 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls",
618 				&code, &reason, &reason_len) == FAILURE) {
619 		return;
620 	}
621 
622 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
623 
624 	_check_http_req_ptr(http_req);
625 
626 
627 	evhttp_send_reply_start(http_req->ptr, code, reason);
628 }
629 /* }}} */
630 
631 /* {{{ proto void EventHttpRequest::cancel(void);
632  * Cancels a pending HTTP request.
633  *
634  * Cancels an ongoing HTTP request. The callback associated with this request
635  * is not executed and the request object is freed. If the request is currently
636  * being processed, e.g. it is ongoing, the corresponding EventHttpConnection
637  * object is going to get reset.
638  *
639  * A request cannot be canceled if its callback has executed already. A request
640  * may be canceled reentrantly from its chunked callback.
641  */
642 PHP_METHOD(EventHttpRequest, cancel)
643 {
644 	php_event_http_req_t *http_req;
645 
646 	if (zend_parse_parameters_none() == FAILURE) {
647 		return;
648 	}
649 
650 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
651 
652 	_check_http_req_ptr(http_req);
653 
654 	evhttp_cancel_request(http_req->ptr);
655 }
656 /* }}} */
657 
658 /* {{{ proto bool EventHttpRequest::addHeader(string key, string value, int type);
659  * Adds an HTTP header to the headers of the request.
660  * <parameter>type</parameter> is one of <literal>EventHttpRequest::*_HEADER</literal>
661  * constants.
662  */
663 PHP_METHOD(EventHttpRequest, addHeader)
664 {
665 	php_event_http_req_t *http_req;
666 	char                 *key;
667 	char                 *value;
668 	size_t                key_len;
669 	size_t                value_len;
670 	struct evkeyvalq     *headers;
671 	zend_long             type;
672 
673 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl",
674 				&key, &key_len, &value, &value_len, &type) == FAILURE) {
675 		return;
676 	}
677 
678 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
679 	_check_http_req_ptr(http_req);
680 
681 	headers = _get_http_req_headers(http_req, type);
682 	PHP_EVENT_ASSERT(headers);
683 
684 
685 	if (evhttp_add_header(headers, key, value)) {
686 		RETURN_FALSE;
687 	}
688 	RETVAL_TRUE;
689 }
690 /* }}} */
691 
692 /* {{{ proto void EventHttpRequest::clearHeaders(string key, string value);
693  * Removes all output headers from the header list of the request.
694  */
695 PHP_METHOD(EventHttpRequest, clearHeaders)
696 {
697 	php_event_http_req_t *http_req;
698 	struct evkeyvalq     *out_headers;
699 
700 	if (zend_parse_parameters_none() == FAILURE) {
701 		return;
702 	}
703 
704 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
705 	_check_http_req_ptr(http_req);
706 
707 	out_headers = evhttp_request_get_output_headers(http_req->ptr);
708 	PHP_EVENT_ASSERT(out_headers);
709 
710 	evhttp_clear_headers(out_headers);
711 }
712 /* }}} */
713 
714 /* {{{ proto bool EventHttpRequest::removeHeader(string key, int type);
715  * Removes an HTTP header from the headers of the request.
716  * <parameter>type</parameter> is one of <literal>EventHttpRequest::*_HEADER</literal>
717  * constants.
718  */
719 PHP_METHOD(EventHttpRequest, removeHeader)
720 {
721 	php_event_http_req_t *http_req;
722 	char                 *key;
723 	size_t                key_len;
724 	struct evkeyvalq     *headers;
725 	zend_long             type;
726 
727 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl",
728 				&key, &key_len, &type) == FAILURE) {
729 		return;
730 	}
731 
732 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
733 
734 	_check_http_req_ptr(http_req);
735 
736 	headers = _get_http_req_headers(http_req, type);
737 	PHP_EVENT_ASSERT(headers);
738 
739 	if (evhttp_remove_header(headers, key)) {
740 		RETURN_FALSE;
741 	}
742 	RETVAL_TRUE;
743 }
744 /* }}} */
745 
746 /* {{{ proto string EventHttpRequest::findHeader(string key, int type);
747  * Finds the value belonging a header.
748  * <parameter>type</parameter> is one of <literal>EventHttpRequest::*_HEADER</literal>
749  * constants.
750  * Returns &null; if header not found.
751  */
752 PHP_METHOD(EventHttpRequest, findHeader)
753 {
754 	php_event_http_req_t *http_req;
755 	char                 *key;
756 	size_t                key_len;
757 	struct evkeyvalq     *headers;
758 	zend_long             type;
759 	const char           *val;
760 
761 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl",
762 				&key, &key_len, &type) == FAILURE) {
763 		return;
764 	}
765 
766 	_check_http_req_type(type);
767 
768 	http_req = Z_EVENT_HTTP_REQ_OBJ_P(getThis());
769 	_check_http_req_ptr(http_req);
770 
771 	headers = _get_http_req_headers(http_req, type);
772 	PHP_EVENT_ASSERT(headers);
773 
774 	val = evhttp_find_header(headers, key);
775 	if (val == NULL) {
776 		RETURN_NULL();
777 	}
778 
779 	RETVAL_STRING(val);
780 }
781 /* }}} */
782 
783 /*
784  * Local variables:
785  * tab-width: 4
786  * c-basic-offset: 4
787  * End:
788  * vim600: noet sw=4 ts=4 sts=4 fdm=marker
789  * vim<600: noet sw=4 ts=4 sts=4
790  */
791