From b0837d3e73d625ef93859855920a3791ff4bb194 Mon Sep 17 00:00:00 2001 From: Stefan de Konink Date: Thu, 25 Feb 2010 05:20:05 +0100 Subject: [PATCH] This is an attempt to execute the function in a seperate thread. For some reason PHP doesn't like this... --- mistral.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++------------ mistral.h | 3 + 2 files changed, 176 insertions(+), 42 deletions(-) diff --git a/mistral.c b/mistral.c index 6f6c267..b20b9a3 100644 --- a/mistral.c +++ b/mistral.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -38,7 +39,11 @@ struct client { ev_timer ev_timeout; ev_timer ev_goodbye; - zval retval; + struct ev_loop *loop; + char *rbuff; + int rlen; + + int close; }; ev_io ev_accept; @@ -107,21 +112,14 @@ static void write_cb(struct ev_loop *loop, struct ev_io *w, int revents) } if (revents & EV_WRITE) { - if (Z_TYPE_P(&cli->retval) == IS_STRING) { - int length = 256 + Z_STRLEN_P(&cli->retval); - char *test = (char *) malloc(length * sizeof(char)); - 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", - (timeout == 0.0 ? "close" : "keep-alive"), Z_STRLEN_P(&cli->retval), Z_STRVAL_P(&cli->retval)); - write(cli->fd, test, i); - - free(test); - ev_io_stop(EV_A_ w); + if (cli->rbuff) { + write(cli->fd, cli->rbuff, cli->rlen); + free(cli->rbuff); } + ev_io_stop(EV_A_ w); } - zval_dtor(&cli->retval); - - if (timeout == 0.0) { + if (cli->close == 1 || timeout == 0.0) { ev_timer_start(loop, &cli->ev_goodbye); } else { ev_timer_again(loop, &cli->ev_timeout); @@ -129,35 +127,163 @@ static void write_cb(struct ev_loop *loop, struct ev_io *w, int revents) } } +/* Gracefully stolen concept from ebb_pthread */ +void* execute_php(void *_cli) { + int result; + struct client *cli = (struct client *) _cli; + zval retval; + + zval *http_headers[1]; + + cli->rbuff[cli->rlen] = '\0'; + + MAKE_STD_ZVAL(http_headers[0]); + ZVAL_STRINGL (http_headers[0], cli->rbuff, (cli->rlen + 1), 0); + + result = call_user_function(EG(function_table), NULL, callback->func, &retval, 1, http_headers TSRMLS_CC); + free(cli->rbuff); + cli->rbuff = NULL; + cli->rlen = 0; + FREE_ZVAL(http_headers[0]); /* Free's the object itself */ + + + if (result == SUCCESS) { + if (Z_TYPE_P(&retval) == IS_STRING) { + cli->rlen = 256 + Z_STRLEN_P(&retval); + cli->rbuff = (char *) malloc(cli->rlen * sizeof(char)); + cli->rlen = snprintf(cli->rbuff, (cli->rlen - 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", + (timeout == 0.0 ? "close" : "keep-alive"), Z_STRLEN_P(&retval), Z_STRVAL_P(&retval)); + + } else if (Z_TYPE_P(&retval) == IS_ARRAY) { + HashTable *arr_hash = Z_ARRVAL_P(&retval); + HashPosition pointer; + zval **data; + int header_len = 11; /* HTTP/1.1 \r\n */ + int body_len = 0; + int status_len = 0; + char *status_code = NULL; + char *body = NULL; + char *test; + + for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); + zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; + zend_hash_move_forward_ex(arr_hash, &pointer)) { + char *key; + int key_len; + long index; + + if (zend_hash_get_current_key_ex(arr_hash, &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) { + zval temp = **data; + zval_copy_ctor(&temp); + convert_to_string(&temp); + + if (Z_STRLEN(temp) > 0) { + if (php_strcmp("status_code", key, key_len - 1)) { + status_code = strdup(Z_STRVAL(temp)); + status_len += Z_STRLEN(temp); + } else if (php_strcmp("body", key, key_len - 1)) { + body_len += Z_STRLEN(temp); + } else { /* So it is a header */ + header_len += key_len - 1; + header_len += 4; /* : \r\n */ + header_len += Z_STRLEN(temp); + + if (php_strcasecmp("Connection", key, key_len - 1) && php_strcasecmp("close", Z_STRVAL(temp), Z_STRLEN(temp))) { + cli->close = 1; + } + } + } + zval_dtor(&temp); + } + } + + cli->rlen = (header_len + (status_len == 0 ? 8 : status_len + 2) + body_len); + cli->rbuff = (char *) malloc(cli->rlen * sizeof(char)); + test = cli->rbuff; + + if (status_len == 0) { + strncpy(test, "HTTP/1.1 200 OK\r\n", 17); + test += 17; + } else { + strncpy(test, "HTTP/1.1 ", 9); + test += 9; + strncpy(test, status_code, status_len); + test += status_len; + strncpy(test, "\r\n", 2); + test += 2; + free(status_code); + } + + for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); + zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; + zend_hash_move_forward_ex(arr_hash, &pointer)) { + char *key; + int key_len; + long index; + + if (zend_hash_get_current_key_ex(arr_hash, &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) { + zval temp = **data; + zval_copy_ctor(&temp); + convert_to_string(&temp); + + if (Z_STRLEN(temp) > 0) { + if (status_len > 0 && php_strcmp("status_code", key, key_len - 1)) { + /* do nothing */ + } else if (body_len > 0 && php_strcmp("body", key, key_len - 1)) { + body = strdup(Z_STRVAL(temp)); + } else { /* So it is a header */ + strncpy(test, key, key_len - 1); + test += key_len - 1; + strncpy(test, ": ", 2); + test += 2; + strncpy(test, Z_STRVAL(temp), Z_STRLEN(temp)); + test += Z_STRLEN(temp); + strncpy(test, "\r\n", 2); + test += 2; + } + } + } + } + + strncpy(test, "\r\n", 2); + test += 2; + + if (body) { + strncpy(test, body, body_len); + free(body); + test += body_len; + } + } + + ev_io_start(cli->loop,&cli->ev_write); + } + + zval_dtor(&retval); + + if (result != SUCCESS) { + ev_timer_start(cli->loop, &cli->ev_goodbye); + } +} + static void read_cb(struct ev_loop *loop, struct ev_io *w, int revents) { if ((revents & EV_READ) && callback) { - char *rbuff = emalloc(1024); - if (rbuff) { - struct client *cli = (struct client *) w->data; - int r = 0; - r = read(cli->fd,rbuff,1023); - - if (r > 0) { - zval *http_headers[1]; - ev_io_stop(EV_A_ w); - rbuff[r] = '\0'; - - MAKE_STD_ZVAL(http_headers[0]); - ZVAL_STRINGL (http_headers[0], rbuff, r, 0); - - if (call_user_function(EG(function_table), NULL, callback->func, &cli->retval, 1, http_headers TSRMLS_CC) == SUCCESS) { - ev_io_start(loop,&cli->ev_write); - zval_dtor(http_headers[0]); /* Free's the string */ - FREE_ZVAL(http_headers[0]); /* Free's the object itself */ - return; /* NO PROCESSING ANYMORE!! */ - } else { - zval_dtor(http_headers[0]); - FREE_ZVAL(http_headers[0]); - zval_dtor(&cli->retval); - } + struct client *cli = (struct client *) w->data; + cli->rbuff = malloc(1024); + if (cli->rbuff) { + cli->rlen = read(cli->fd,cli->rbuff,1023); + if (cli->rlen > 0) { + pthread_t thread; + ev_io_stop(EV_A_ w); + cli->loop = loop; + pthread_create(&thread, NULL, execute_php, cli); + pthread_detach(thread); + return; + } else { + free(cli->rbuff); + cli->rbuff = NULL; + cli->rlen = 0; } - efree(rbuff); } } @@ -177,6 +303,8 @@ static void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) return; cli = calloc(1,sizeof(struct client)); + cli->rbuff = NULL; + cli->rlen = 0; cli->fd = client_fd; if (setnonblock(cli->fd) < 0) err(1, "failed to set client socket to non-blocking"); @@ -340,8 +468,9 @@ PHP_FUNCTION(mistral_init) */ PHP_FUNCTION(mistral_register_callback) { - zval *zcallback, *zarg = NULL; + zval *zcallback; php_event_callback_t *new_callback, *old_callback; + zval *cb; char *func_name; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcallback) != SUCCESS) { @@ -355,11 +484,13 @@ PHP_FUNCTION(mistral_register_callback) } efree(func_name); - zval_add_ref(&zcallback); + MAKE_STD_ZVAL(cb); + *(cb) = *zcallback; + zval_copy_ctor(cb); new_callback = emalloc(sizeof(php_event_callback_t)); - new_callback->func = zcallback; - new_callback->arg = zarg; + new_callback->func = cb; + new_callback->arg = NULL; old_callback = callback; callback = new_callback; diff --git a/mistral.h b/mistral.h index 6339040..8dff50a 100644 --- a/mistral.h +++ b/mistral.h @@ -17,4 +17,7 @@ PHP_FUNCTION(mistral_init); PHP_FUNCTION(mistral_register_callback); PHP_FUNCTION(mistral_start); +#define php_strcasecmp(string, php_string, php_len) ((strlen(string)) == php_len && strncasecmp(string, php_string, php_len) == 0) +#define php_strcmp(string, php_string, php_len) ((strlen(string)) == php_len && strncmp(string, php_string, php_len) == 0) + #endif /* MISTRAL_H */ -- 2.11.4.GIT