Length fix
[mistral.git] / mistral.c
blob6f6c26789cd9f6825878961352853625f4a7351d
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
5 #include "php.h"
6 #include "ext/standard/info.h"
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
12 #include <netdb.h>
13 #include <sys/time.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #include <err.h>
21 #include <stddef.h>
23 #include <ev.h>
25 #include "mistral.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
33 struct client {
34 int fd;
36 ev_io ev_read;
37 ev_io ev_write;
38 ev_timer ev_timeout;
39 ev_timer ev_goodbye;
41 zval retval;
44 ev_io ev_accept;
45 double timeout = 300;
47 typedef struct _php_event_callback_t { /* {{{ */
48 zval *func;
49 zval *arg;
50 } php_event_callback_t;
51 /* }}} */
53 php_event_callback_t *callback = NULL;
55 static int setnonblock(int fd)
57 int flags;
59 flags = fcntl(fd, F_GETFL);
60 if (flags < 0)
61 return flags;
62 flags |= O_NONBLOCK;
63 if (fcntl(fd, F_SETFL, flags) < 0)
64 return -1;
66 return 0;
69 static inline void _php_event_callback_free(php_event_callback_t *this_callback) /* {{{ */
71 if (!this_callback) {
72 return;
75 zval_ptr_dtor(&this_callback->func);
76 if (this_callback->arg) {
77 zval_ptr_dtor(&this_callback->arg);
79 efree(this_callback);
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);
97 close(cli->fd);
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);
106 return;
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);
117 free(test);
118 ev_io_stop(EV_A_ w);
122 zval_dtor(&cli->retval);
124 if (timeout == 0.0) {
125 ev_timer_start(loop, &cli->ev_goodbye);
126 } else {
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);
136 if (rbuff) {
137 struct client *cli = (struct client *) w->data;
138 int r = 0;
139 r = read(cli->fd,rbuff,1023);
141 if (r > 0) {
142 zval *http_headers[1];
143 ev_io_stop(EV_A_ w);
144 rbuff[r] = '\0';
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!! */
154 } else {
155 zval_dtor(http_headers[0]);
156 FREE_ZVAL(http_headers[0]);
157 zval_dtor(&cli->retval);
160 efree(rbuff);
164 struct client *cli = (struct client *) w->data;
165 ev_io_stop(EV_A_ w);
166 ev_timer_start(loop, &cli->ev_goodbye);
169 static void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents)
171 int client_fd;
172 struct client *cli;
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);
176 if (client_fd == -1)
177 return;
179 cli = calloc(1,sizeof(struct client));
180 cli->fd = client_fd;
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)
204 { NULL, NULL, NULL }
206 /* }}} */
208 /* {{{ mistral_module_entry
209 * */
210 zend_module_entry mistral_module_entry = {
211 STANDARD_MODULE_HEADER,
212 "mistral",
213 mistral_functions,
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 */
218 PHP_MINFO(mistral),
219 PHP_MISTRAL_VERSION,
220 STANDARD_MODULE_PROPERTIES
222 /* }}} */
224 #ifdef COMPILE_DL_MISTRAL
225 ZEND_GET_MODULE(mistral)
226 #endif
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();
236 /* }}} */
238 /* {{{ PHP_MSHUTDOWN_FUNCTION */
239 PHP_MSHUTDOWN_FUNCTION (mistral)
241 _php_event_callback_free(callback);
243 /* }}} */
245 PHP_FUNCTION(mistral_init)
247 struct addrinfo *ai = NULL;
248 struct addrinfo hints;
249 struct addrinfo *runp;
250 int listen_sock = -1;
252 char *listen_addr;
253 int listen_addr_len;
254 long listen_port;
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) {
264 RETURN_NULL();
267 if (listen_port < 1 || listen_port > 65535) {
268 RETURN_NULL();
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) {
276 RETURN_NULL();
279 runp = ai;
281 while (runp != NULL && listen_sock < 0) {
282 listen_sock = socket(runp->ai_family, SOCK_STREAM, 0);
283 if (listen_sock < 0)
284 runp = runp->ai_next;
287 if (listen_sock < 0) {
288 err(1, "listen failed");
291 if (timeout <= 0.0) {
292 timeout = 0.0;
293 } else {
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) {
302 int null = 0;
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) {
310 case AF_INET6:
311 inet_pton(sin.ss_family, listen_addr, &(sa6->sin6_addr));
312 sa6->sin6_port = htons(listen_port);
313 break;
314 case AF_INET:
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);
323 if (ai)
324 freeaddrinfo (ai);
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;
345 char *func_name;
347 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcallback) != SUCCESS) {
348 return;
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);
353 efree(func_name);
354 RETURN_FALSE;
356 efree(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;
367 if (old_callback) {
368 _php_event_callback_free(old_callback);
370 RETURN_TRUE;
372 /* }}} */
374 PHP_FUNCTION(mistral_start)
376 struct ev_loop *loop = ev_default_loop (0);
378 ev_io_start(loop, &ev_accept);
379 ev_loop(loop, 0);