6 #include "ext/standard/info.h"
9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
26 #define struct_client_from(cli, field) \
27 ((struct client *) (((char *) cli) - offsetof(struct client, ev_##field)))
29 /* https://sie.isc.org/svn/nmsg-httpk/trunk/nmsg-httpk.c */
31 #define SERVER_PORT 8002
43 typedef struct _php_event_callback_t
{ /* {{{ */
46 } php_event_callback_t
;
49 php_event_callback_t
*callback
= NULL
;
51 static int setnonblock(int fd
)
55 flags
= fcntl(fd
, F_GETFL
);
59 if (fcntl(fd
, F_SETFL
, flags
) < 0)
65 static inline void _php_event_callback_free(php_event_callback_t
*this_callback
) /* {{{ */
71 zval_ptr_dtor(&this_callback
->func
);
72 if (this_callback
->arg
) {
73 zval_ptr_dtor(&this_callback
->arg
);
78 static void write_cb(struct ev_loop
*loop
, struct ev_io
*w
, int revents
)
80 struct client
*cli
= struct_client_from(w
, write
);
82 if (revents
& EV_WRITE
) {
83 if (Z_TYPE_P(&cli
->retval
) == IS_STRING
) {
84 write(cli
->fd
, Z_STRVAL_P(&cli
->retval
), Z_STRLEN_P(&cli
->retval
));
89 zval_dtor(&cli
->retval
);
95 ev_io_start(loop
,&cli
->ev_read
);
100 static void read_cb(struct ev_loop
*loop
, struct ev_io
*w
, int revents
)
102 struct client
*cli
= struct_client_from(w
, read
);
103 if ((revents
& EV_READ
) && callback
) {
104 char *rbuff
= emalloc(1024);
107 r
= read(cli
->fd
,rbuff
,1023);
112 MAKE_STD_ZVAL(http_headers
);
113 ZVAL_STRINGL (http_headers
, rbuff
, r
, 0);
115 if (call_user_function(EG(function_table
), NULL
, callback
->func
, &cli
->retval
, 1, &http_headers TSRMLS_CC
) == SUCCESS
) {
116 ev_io_init(&cli
->ev_write
,write_cb
,cli
->fd
,EV_WRITE
);
117 ev_io_start(loop
,&cli
->ev_write
);
118 zval_dtor(http_headers
);
119 FREE_ZVAL(http_headers
);
122 zval_dtor(&cli
->retval
);
123 FREE_ZVAL(http_headers
);
130 static void timeout_cb(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
) {
131 struct client
*cli
= struct_client_from(w
, timeout
);
132 ev_io_stop(EV_A_
&cli
->ev_read
);
133 ev_io_stop(EV_A_
&cli
->ev_write
);
138 static void accept_cb(struct ev_loop
*loop
, struct ev_io
*w
, int revents
)
141 struct client
*client
;
142 struct sockaddr_in client_addr
;
143 socklen_t client_len
= sizeof(client_addr
);
144 client_fd
= accept(w
->fd
, (struct sockaddr
*)&client_addr
, &client_len
);
145 if (client_fd
== -1) {
149 client
= calloc(1,sizeof(*client
));
150 client
->fd
=client_fd
;
151 if (setnonblock(client
->fd
) < 0)
152 err(1, "failed to set client socket to non-blocking");
153 ev_io_init(&client
->ev_read
,read_cb
,client
->fd
,EV_READ
);
156 ev_timer_init(&client
->ev_timeout
, timeout_cb
, timeout
, 0.);
157 ev_timer_start(loop
, &client
->ev_timeout
);
160 ev_io_start(loop
,&client
->ev_read
);
163 /* {{{ mistral_functions[] */
164 zend_function_entry mistral_functions
[] = {
165 PHP_FE(mistral_init
, NULL
)
166 PHP_FE(mistral_register_callback
, NULL
)
167 PHP_FE(mistral_start
, NULL
)
172 /* {{{ mistral_module_entry
174 zend_module_entry mistral_module_entry
= {
175 STANDARD_MODULE_HEADER
,
178 NULL
, /* Replace with NULL if there is nothing to do at php startup */
179 PHP_MSHUTDOWN(mistral
),
180 NULL
, /* Replace with NULL if there is nothing to do at request start */
181 NULL
, /* Replace with NULL if there is nothing to do at request end */
184 STANDARD_MODULE_PROPERTIES
188 #ifdef COMPILE_DL_MISTRAL
189 ZEND_GET_MODULE(mistral
)
192 /* {{{ PHP_MINFO_FUNCTION */
193 PHP_MINFO_FUNCTION(mistral
)
195 php_info_print_table_start();
196 php_info_print_table_header(2, "Mistral support", "enabled");
197 php_info_print_table_row(2, "Version", PHP_MISTRAL_VERSION
);
198 php_info_print_table_end();
202 /* {{{ PHP_MSHUTDOWN_FUNCTION */
203 PHP_MSHUTDOWN_FUNCTION (mistral
)
205 _php_event_callback_free(callback
);
209 PHP_FUNCTION(mistral_init
)
211 int listen_addrfam
= AF_INET6
;
212 int listen_sock
= -1;
218 struct sockaddr_storage sin
;
219 struct sockaddr_in
* sa4
= (struct sockaddr_in
*) &sin
;
220 struct sockaddr_in6
* sa6
= (struct sockaddr_in6
*) &sin
;
222 int reuseaddr_on
= 1;
223 int keepalive_on
= 1;
225 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "sld", &listen_addr
, &listen_addr_len
, &listen_port
, &timeout
) == FAILURE
) {
229 if (listen_port
< 1 || listen_port
> 65535) {
234 listen_sock
= socket(listen_addrfam
, SOCK_STREAM
, 0);
235 if (listen_sock
< 0) {
236 listen_addrfam
= (listen_addrfam
== AF_INET6
? AF_INET
: AF_INET6
);
237 listen_sock
= socket(listen_addrfam
, SOCK_STREAM
, 0);
238 if (listen_sock
< 0) {
239 err(1, "listen failed");
243 if (timeout
<= 0.0) {
246 if (setsockopt(listen_sock
, SOL_SOCKET
, SO_KEEPALIVE
, &keepalive_on
, sizeof(keepalive_on
)) == -1)
247 err(1, "setsockopt failed");
250 if (setsockopt(listen_sock
, SOL_SOCKET
, SO_REUSEADDR
, &reuseaddr_on
, sizeof(reuseaddr_on
)) == -1)
251 err(1, "setsockopt failed");
253 if (listen_addrfam
== AF_INET6
) {
255 setsockopt(listen_sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, &null
, sizeof(null
));
258 memset(&sin
, 0, sizeof(sin
));
260 sin
.ss_family
= listen_addrfam
;
261 switch (sin
.ss_family
) {
263 inet_pton(sin
.ss_family
, listen_addr
, &(sa6
->sin6_addr
));
264 sa6
->sin6_port
= htons(listen_port
);
267 if ( strchr(listen_addr
, ':') )
268 /* Invalid IPv4-string. Use the wildcard address. */
269 listen_addr
= strdup("0.0.0.0");
271 inet_pton(sin
.ss_family
, listen_addr
, &(sa4
->sin_addr
));
272 sa4
->sin_port
= htons(listen_port
);
275 if (bind(listen_sock
, (struct sockaddr
*) &sin
, sizeof(sin
)) < 0)
276 err(1, "bind failed");
278 if (listen(listen_sock
, 5) < 0)
279 err(1, "listen failed");
281 if (setnonblock(listen_sock
) < 0)
282 err(1, "failed to set server socket to non-blocking");
284 ev_io_init(&ev_accept
, accept_cb
, listen_sock
, EV_READ
);
287 /* {{{ proto bool event_set(resource event, resource fd, int events, mixed callback[, mixed arg])
288 * Taken from pecl::libevent event_set
290 PHP_FUNCTION(mistral_register_callback
)
292 zval
*zcallback
, *zarg
= NULL
;
293 php_event_callback_t
*new_callback
, *old_callback
;
296 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "z", &zcallback
) != SUCCESS
) {
300 if (!zend_is_callable(zcallback
, 0, &func_name TSRMLS_CC
)) {
301 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "'%s' is not a valid callback", func_name
);
307 zval_add_ref(&zcallback
);
309 new_callback
= emalloc(sizeof(php_event_callback_t
));
310 new_callback
->func
= zcallback
;
311 new_callback
->arg
= zarg
;
313 old_callback
= callback
;
314 callback
= new_callback
;
317 _php_event_callback_free(old_callback
);
323 PHP_FUNCTION(mistral_start
)
325 struct ev_loop
*loop
= ev_default_loop (0);
327 ev_io_start(loop
, &ev_accept
);