6 #include "ext/standard/info.h"
9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
28 * We did use some ideas from:
29 * https://sie.isc.org/svn/nmsg-httpk/trunk/nmsg-httpk.c
30 * http://github.com/ry/libebb/blob/master/ebb.c
47 typedef struct _php_event_callback_t
{ /* {{{ */
50 } php_event_callback_t
;
53 php_event_callback_t
*callback
= NULL
;
55 static int setnonblock(int fd
)
59 flags
= fcntl(fd
, F_GETFL
);
63 if (fcntl(fd
, F_SETFL
, flags
) < 0)
69 static inline void _php_event_callback_free(php_event_callback_t
*this_callback
) /* {{{ */
75 zval_ptr_dtor(&this_callback
->func
);
76 if (this_callback
->arg
) {
77 zval_ptr_dtor(&this_callback
->arg
);
82 static void timeout_cb(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
) {
83 struct client
*cli
= (struct client
*) w
->data
;
84 assert(w
== &cli
->ev_timeout
);
86 ev_timer_start(loop
, &cli
->ev_goodbye
);
89 static void goodbye_cb(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
) {
90 struct client
*cli
= (struct client
*) w
->data
;
91 assert(w
== &cli
->ev_goodbye
);
93 ev_io_stop(loop
, &cli
->ev_read
);
94 ev_io_stop(loop
, &cli
->ev_write
);
95 ev_timer_stop(loop
, &cli
->ev_timeout
);
100 static void write_cb(struct ev_loop
*loop
, struct ev_io
*w
, int revents
)
102 struct client
*cli
= (struct client
*) w
->data
;
104 if(EV_ERROR
& revents
) {
105 ev_timer_start(loop
, &cli
->ev_goodbye
);
109 if (revents
& EV_WRITE
) {
110 if (Z_TYPE_P(&cli
->retval
) == IS_STRING
) {
111 int length
= 256 + Z_STRLEN_P(&cli
->retval
);
112 char *test
= (char *) malloc(length
* sizeof(char));
113 int i
= snprintf(test
, (length
- 1), "HTTP/1.1 200 OK\r\nConnection: %s\r\nContent-Type: text/plain\r\nServer: mistral/0.1\r\nContent-Length: %u\r\n\r\n%s",
114 (timeout
== 0.0 ? "close" : "keep-alive"), Z_STRLEN_P(&cli
->retval
), Z_STRVAL_P(&cli
->retval
));
115 write(cli
->fd
, test
, i
);
122 zval_dtor(&cli
->retval
);
124 if (timeout
== 0.0) {
125 ev_timer_start(loop
, &cli
->ev_goodbye
);
127 ev_timer_again(loop
, &cli
->ev_timeout
);
128 ev_io_start(loop
, &cli
->ev_read
);
132 static void read_cb(struct ev_loop
*loop
, struct ev_io
*w
, int revents
)
134 if ((revents
& EV_READ
) && callback
) {
135 char *rbuff
= emalloc(1024);
137 struct client
*cli
= (struct client
*) w
->data
;
139 r
= read(cli
->fd
,rbuff
,1023);
142 zval
*http_headers
[1];
146 MAKE_STD_ZVAL(http_headers
[0]);
147 ZVAL_STRINGL (http_headers
[0], rbuff
, r
, 0);
149 if (call_user_function(EG(function_table
), NULL
, callback
->func
, &cli
->retval
, 1, http_headers TSRMLS_CC
) == SUCCESS
) {
150 ev_io_start(loop
,&cli
->ev_write
);
151 zval_dtor(http_headers
[0]); /* Free's the string */
152 FREE_ZVAL(http_headers
[0]); /* Free's the object itself */
153 return; /* NO PROCESSING ANYMORE!! */
155 zval_dtor(http_headers
[0]);
156 FREE_ZVAL(http_headers
[0]);
157 zval_dtor(&cli
->retval
);
164 struct client
*cli
= (struct client
*) w
->data
;
166 ev_timer_start(loop
, &cli
->ev_goodbye
);
169 static void accept_cb(struct ev_loop
*loop
, struct ev_io
*w
, int revents
)
173 struct sockaddr_in client_addr
;
174 socklen_t client_len
= sizeof(client_addr
);
175 client_fd
= accept(w
->fd
, (struct sockaddr
*)&client_addr
, &client_len
);
179 cli
= calloc(1,sizeof(struct client
));
181 if (setnonblock(cli
->fd
) < 0)
182 err(1, "failed to set client socket to non-blocking");
184 ev_io_init(&cli
->ev_read
, read_cb
, cli
->fd
, EV_READ
);
185 cli
->ev_read
.data
= cli
;
187 ev_io_init(&cli
->ev_write
, write_cb
, cli
->fd
, EV_WRITE
);
188 cli
->ev_write
.data
= cli
;
190 ev_timer_init(&cli
->ev_goodbye
, goodbye_cb
, 0., 0.);
191 cli
->ev_goodbye
.data
= cli
;
193 ev_timer_init(&cli
->ev_timeout
, timeout_cb
, 0., (timeout
== 0.0 ? 30.0 : timeout
));
194 cli
->ev_timeout
.data
= cli
;
196 ev_io_start(loop
,&cli
->ev_read
);
199 /* {{{ mistral_functions[] */
200 zend_function_entry mistral_functions
[] = {
201 PHP_FE(mistral_init
, NULL
)
202 PHP_FE(mistral_register_callback
, NULL
)
203 PHP_FE(mistral_start
, NULL
)
208 /* {{{ mistral_module_entry
210 zend_module_entry mistral_module_entry
= {
211 STANDARD_MODULE_HEADER
,
214 NULL
, /* Replace with NULL if there is nothing to do at php startup */
215 PHP_MSHUTDOWN(mistral
),
216 NULL
, /* Replace with NULL if there is nothing to do at request start */
217 NULL
, /* Replace with NULL if there is nothing to do at request end */
220 STANDARD_MODULE_PROPERTIES
224 #ifdef COMPILE_DL_MISTRAL
225 ZEND_GET_MODULE(mistral
)
228 /* {{{ PHP_MINFO_FUNCTION */
229 PHP_MINFO_FUNCTION(mistral
)
231 php_info_print_table_start();
232 php_info_print_table_header(2, "Mistral support", "enabled");
233 php_info_print_table_row(2, "Version", PHP_MISTRAL_VERSION
);
234 php_info_print_table_end();
238 /* {{{ PHP_MSHUTDOWN_FUNCTION */
239 PHP_MSHUTDOWN_FUNCTION (mistral
)
241 _php_event_callback_free(callback
);
245 PHP_FUNCTION(mistral_init
)
247 struct addrinfo
*ai
= NULL
;
248 struct addrinfo hints
;
249 struct addrinfo
*runp
;
250 int listen_sock
= -1;
256 struct sockaddr_storage sin
;
257 struct sockaddr_in
* sa4
= (struct sockaddr_in
*) &sin
;
258 struct sockaddr_in6
* sa6
= (struct sockaddr_in6
*) &sin
;
260 int reuseaddr_on
= 1;
261 int keepalive_on
= 1;
263 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "sld", &listen_addr
, &listen_addr_len
, &listen_port
, &timeout
) == FAILURE
) {
267 if (listen_port
< 1 || listen_port
> 65535) {
271 bzero(&hints
, sizeof (hints
));
272 hints
.ai_flags
= AI_ADDRCONFIG
|| AI_NUMERICSERV
;
273 hints
.ai_socktype
= SOCK_STREAM
;
275 if (getaddrinfo (listen_addr
, "", &hints
, &ai
) != 0) {
281 while (runp
!= NULL
&& listen_sock
< 0) {
282 listen_sock
= socket(runp
->ai_family
, SOCK_STREAM
, 0);
284 runp
= runp
->ai_next
;
287 if (listen_sock
< 0) {
288 err(1, "listen failed");
291 if (timeout
<= 0.0) {
294 if (setsockopt(listen_sock
, SOL_SOCKET
, SO_KEEPALIVE
, &keepalive_on
, sizeof(keepalive_on
)) == -1)
295 err(1, "setsockopt failed");
298 if (setsockopt(listen_sock
, SOL_SOCKET
, SO_REUSEADDR
, &reuseaddr_on
, sizeof(reuseaddr_on
)) == -1)
299 err(1, "setsockopt failed");
301 if (runp
->ai_family
== AF_INET6
) {
303 setsockopt(listen_sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, &null
, sizeof(null
));
306 memset(&sin
, 0, sizeof(sin
));
308 sin
.ss_family
= runp
->ai_family
;
309 switch (sin
.ss_family
) {
311 inet_pton(sin
.ss_family
, listen_addr
, &(sa6
->sin6_addr
));
312 sa6
->sin6_port
= htons(listen_port
);
315 if ( strchr(listen_addr
, ':') )
316 /* Invalid IPv4-string. Use the wildcard address. */
317 listen_addr
= strdup("0.0.0.0");
319 inet_pton(sin
.ss_family
, listen_addr
, &(sa4
->sin_addr
));
320 sa4
->sin_port
= htons(listen_port
);
326 if (bind(listen_sock
, (struct sockaddr
*) &sin
, sizeof(sin
)) < 0)
327 err(1, "bind failed");
329 if (listen(listen_sock
, 5) < 0)
330 err(1, "listen failed");
332 if (setnonblock(listen_sock
) < 0)
333 err(1, "failed to set server socket to non-blocking");
335 ev_io_init(&ev_accept
, accept_cb
, listen_sock
, EV_READ
);
338 /* {{{ proto bool event_set(resource event, resource fd, int events, mixed callback[, mixed arg])
339 * Taken from pecl::libevent event_set
341 PHP_FUNCTION(mistral_register_callback
)
343 zval
*zcallback
, *zarg
= NULL
;
344 php_event_callback_t
*new_callback
, *old_callback
;
347 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "z", &zcallback
) != SUCCESS
) {
351 if (!zend_is_callable(zcallback
, 0, &func_name TSRMLS_CC
)) {
352 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "'%s' is not a valid callback", func_name
);
358 zval_add_ref(&zcallback
);
360 new_callback
= emalloc(sizeof(php_event_callback_t
));
361 new_callback
->func
= zcallback
;
362 new_callback
->arg
= zarg
;
364 old_callback
= callback
;
365 callback
= new_callback
;
368 _php_event_callback_free(old_callback
);
374 PHP_FUNCTION(mistral_start
)
376 struct ev_loop
*loop
= ev_default_loop (0);
378 ev_io_start(loop
, &ev_accept
);