¡Hola! This is yet another patch:
* manager: - Init now requires a hostname, so that each manager will connect to a specific fcgi-server * handler - Added "ServerList" directive in config file, typical use: Extension fcgi { Handler fastcgi { ServerList /tmp/sock ServerList 127.0.0.1:8090 ServerList 127.0.0.1:8070 } } Each entry will be assigned to a manager. Each request will be round-robined through this array of managers. I didn't use Server and Socket since they are string-typed directive. Still not sure if this will be accepted. - I also haven't touch interpreter part since it is string type directive, so only one Interpreter will be allowed, I guess. Should each (local) server requires separate Interpreter directive? - Headers spewed by fcgi-server now consumed and parsed by the handler so it will give the correct Status: and/or Location: value to main server. - SCRIPT_FILENAME is now sent by handler to fcgi-server so fcgi-server can find the correct script name for dynamic fcgi requests (it works with PHP5 now) * some findings and thoughts: - PHP sometimes close the connection on idle time. - manager should be capable to reconnect automatically on disconnections. - the patch couldn't be tested with the latest svn up. The compilation somehow breaks on: /bin/sh ../libtool --tag=CC --mode=link gcc -g -O2 -o cherokee_logrotate cherokee_logrotate.o libcherokee-config.la libcherokee-base.la libcherokee-client.la -ldl gcc -g -O2 -o .libs/cherokee_logrotate cherokee_logrotate.o ./.libs/libcherokee-config.so ./.libs/libcherokee-base.so ./.libs/libcherokee-client.so -ldl -Wl,--rpath -Wl,/opt/ch/lib /opt/ch/lib/libcherokee-server.so.0: undefined reference to `cherokee_buffer_is_empty' I tried to freshly svn co and still the problem exists, also some minor stuff like undefined off_t and the needs to give --enable-trace to ./configure Please check whether I'm on a correct track. Sorry that I'm being slower and slower, getting less leisure (read: hacking) hour per week now *-(
Index: cherokee/fcgi_manager.c =================================================================== --- cherokee/fcgi_manager.c (revision 105) +++ cherokee/fcgi_manager.c (working copy) @@ -22,7 +22,10 @@ * USA */ +#include "connection.h" +#include "connection-protected.h" #include "common-internal.h" +#include "handler_fastcgi.h" #include "fcgi_manager.h" #include "fastcgi.h" @@ -32,12 +35,14 @@ #define DEFAULT_PORT 8002 #define CONN_POLL_INCREMENT 16 +static pthread_mutex_t __global_fastcgi_manager_lock; ret_t -cherokee_fcgi_manager_new (cherokee_fcgi_manager_t **fcgim) +cherokee_fcgi_manager_new (cherokee_fcgi_manager_t **fcgim, char *host) { int i; ret_t ret; + char *port; CHEROKEE_NEW_STRUCT (n,fcgi_manager); /* Init @@ -48,6 +53,8 @@ n->port = DEFAULT_PORT; cherokee_buffer_init (&n->hostname); cherokee_buffer_init (&n->read_buffer); + n->request_id = 0; + n->connected = -1; cherokee_buffer_ensure_size (&n->read_buffer, DEFAULT_READ_SIZE); @@ -55,13 +62,32 @@ n->conn_poll = (cherokee_connection_t **) malloc ( CONN_POLL_INCREMENT * sizeof(cherokee_connection_t *)); + n->remaining_size = 0; for (i=0; i<CONN_POLL_INCREMENT; i++) { n->conn_poll[i] = NULL; } + if (*host == '/') { + cherokee_buffer_add (&n->hostname, host, strlen(host)); + } else { + /* Parse host name + */ + port = strchr(host, ':'); + if (port == NULL) { + cherokee_buffer_add (&n->hostname, host, strlen(host)); + } else { + *port = '\0'; + n->port = atoi(port+1); + cherokee_buffer_add (&n->hostname, host, port - host); + *port = ':'; + } + } + /* Return */ *fcgim = n; + + CHEROKEE_MUTEX_INIT(&__global_fastcgi_manager_lock, NULL); return ret_ok; } @@ -92,36 +118,32 @@ { ret_t ret; - ret = cherokee_socket_set_client (fcgim->socket, AF_INET); - if (ret != ret_ok) return ret; + if (*fcgim->hostname.buf == '/') { + ret = cherokee_socket_set_client (fcgim->socket, AF_UNIX); + if (ret != ret_ok) return ret; + ret = cherokee_socket_gethostbyname (fcgim->socket, &fcgim->hostname); + if (ret != ret_ok) return ret; + } else { + ret = cherokee_socket_set_client (fcgim->socket, AF_INET); + if (ret != ret_ok) return ret; - ret = cherokee_socket_gethostbyname (fcgim->socket, &fcgim->hostname); - if (ret != ret_ok) return ret; + ret = cherokee_socket_gethostbyname (fcgim->socket, &fcgim->hostname); + if (ret != ret_ok) return ret; + SOCKET_SIN_PORT(fcgim->socket) = htons(fcgim->port); + } - SOCKET_SIN_PORT(fcgim->socket) = htons(fcgim->port); + - return cherokee_socket_connect (fcgim->socket); + fcgim->connected = cherokee_socket_connect (fcgim->socket); + return fcgim->connected; } ret_t -cherokee_fcgi_manager_connect_to_srv (cherokee_fcgi_manager_t *fcgim, char *host) +cherokee_fcgi_manager_connect_to_srv (cherokee_fcgi_manager_t *fcgim) { - char *port; - ret_t ret; + ret_t ret; - /* Parse host name - */ - port = strchr(host, ':'); - if (port == NULL) { - cherokee_buffer_add (&fcgim->hostname, host, strlen(host)); - } else { - *port = '\0'; - fcgim->port = atoi(port+1); - cherokee_buffer_add (&fcgim->hostname, host, port - host); - *port = ':'; - } - /* Connect to the server */ ret = connect_to_fastcgi_server (fcgim); @@ -178,7 +200,7 @@ { cuint_t i; cint_t slot = -1; - + /* Look for the first free slot */ for (i=0; i<fcgim->conn_poll_size; i++) { @@ -208,7 +230,7 @@ fcgim->conn_poll[slot] = conn; printf ("registered id=%d\n", slot); - *id = slot; + *id = slot + 1; return ret_ok; } @@ -236,81 +258,234 @@ { ret_t ret; + CHEROKEE_MUTEX_LOCK (&__global_fastcgi_manager_lock); + /*printf ("-----------------------write start--------------------------\n");*/ ret = cherokee_socket_write (fcgim->socket, info, sent); - if (ret != ret_ok) return ret; + /* + cherokee_buffer_print_debug (info, -1); + printf ("-----------------------write end %d-%d-%d--------------------------\n", info->len, *sent, ret);*/ + CHEROKEE_MUTEX_UNLOCK (&__global_fastcgi_manager_lock); + if (ret != ret_ok) { + if (ret == ret_eof) + fcgim->connected = -1; + return ret; + } cherokee_buffer_move_to_begin (info, *sent); return ret_ok; } +static void +set_status (cherokee_fcgi_manager_t *fcgim, cherokee_fcgi_status_t status) +{ + cherokee_handler_fastcgi_t *fcgi; + cherokee_connection_t *conn; + + conn = fcgim->conn_poll [fcgim->request_id - 1]; + if (conn != NULL) { + fcgi = (cherokee_handler_fastcgi_t *) conn->handler; + if (fcgi != NULL) { + fcgi->status = status; + } + } +} +static void +process_buffer (cherokee_fcgi_manager_t *fcgim, void *data, cuint_t data_len) +{ + cherokee_connection_t *conn; + cherokee_handler_fastcgi_t *fcgi; + char *message; + + conn = fcgim->conn_poll [fcgim->request_id - 1]; + if (conn == NULL) + { + return; + } + + fcgi = (cherokee_handler_fastcgi_t *) conn->handler; + switch (fcgim->request_type) + { + case FCGI_STDERR: + message = (char*) strndup (data, data_len + 1); + message [data_len] = 0; + cherokee_logger_write_string (CONN_VSRV(conn)->logger, "%s", message); + free (message); + break; + case FCGI_STDOUT: + cherokee_buffer_add (&fcgi->incoming_buffer, data, data_len); + if (fcgim->remaining_size == 0) + set_status (fcgim, fcgi_data_available); + break; + } +} + static ret_t process_read_buffer (cherokee_fcgi_manager_t *fcgim) { - cuint_t offset; - FCGI_Header *header; - - offset = 0; - while (fcgim->read_buffer.len - offset >= sizeof(FCGI_EndRequestRecord)) - { - cuint_t id; - cuint_t len; + ret_t ret = ret_eagain; + cuint_t len, bytes_to_move, offset = 0; + FCGI_Header *header; + FCGI_EndRequestBody *end_request; + void *start = fcgim->read_buffer.buf; - header = (FCGI_Header *)((&fcgim->read_buffer.buf) + offset); - id = (header->requestIdB0 | (header->requestIdB1 << 8)); - len = (header->contentLengthB0 | (header->contentLengthB1 << 8)) + header->paddingLength; + while (fcgim->read_buffer.len > 0) + { + if (fcgim->remaining_size == 0) { + if (fcgim->read_buffer.len < sizeof(FCGI_Header)) + return ret_eagain; - switch (header->type) { - case FCGI_STDERR: - printf ("strerr\n"); - break; - case FCGI_STDOUT: - printf ("stdout\n"); - break; - case FCGI_END_REQUEST: - printf ("end request\n"); - break; - default: - PRINT_ERROR ("ERROR: Unknown FCGI header type: %d\n", header->type); - } + header = (FCGI_Header *) start; - offset += sizeof(FCGI_EndRequestRecord); - } + if (!(header->type == FCGI_STDERR || + header->type == FCGI_STDOUT || + header->type == FCGI_END_REQUEST)) + { + printf ("rb:%d x:%d rs:%d\n", fcgim->read_buffer.len, fcgim->padding, fcgim->remaining_size); + cherokee_buffer_print_debug (&fcgim->read_buffer, -1); + return ret_error; + } - return ret_ok; + fcgim->request_id = (header->requestIdB0 | (header->requestIdB1 << 8)); + fcgim->request_type = header->type; + len = (header->contentLengthB0 | (header->contentLengthB1 << 8)); + fcgim->padding = header->paddingLength; + fcgim->return_value = 0; + fcgim->status = 0; + + offset = FCGI_HEADER_LEN; + if (len > (fcgim->read_buffer.len - FCGI_HEADER_LEN)) + { + fcgim->remaining_size = len - fcgim->read_buffer.len - FCGI_HEADER_LEN + 16; + len = fcgim->read_buffer.len - FCGI_HEADER_LEN; + bytes_to_move = len; + } else { + fcgim->remaining_size = 0; + bytes_to_move = len; + if ((fcgim->padding + len) > (fcgim->read_buffer.len - FCGI_HEADER_LEN)) { + fcgim->padding = (fcgim->padding + len) - fcgim->read_buffer.len - FCGI_HEADER_LEN; + bytes_to_move += fcgim->padding; + } else { + bytes_to_move += fcgim->padding; + fcgim->padding = 0; + } + } + bytes_to_move += FCGI_HEADER_LEN; + } else { + if (fcgim->remaining_size > fcgim->read_buffer.len) { + fcgim->remaining_size = fcgim->remaining_size - fcgim->read_buffer.len; + len = fcgim->read_buffer.len; + bytes_to_move = len; + } else { + len = fcgim->remaining_size; + bytes_to_move = len; + if (fcgim->padding > 0) { + if ((fcgim->remaining_size + fcgim->padding) > fcgim->read_buffer.len) { + fcgim->padding = fcgim->remaining_size + fcgim->padding - fcgim->read_buffer.len; + } else { + bytes_to_move += fcgim->padding; + fcgim->padding = 0; + } + } + fcgim->remaining_size = 0; + } + } + + switch (fcgim->request_type) { + case FCGI_STDERR: + case FCGI_STDOUT: + if (len > 0) + process_buffer (fcgim, (start + offset), len); + break; + case FCGI_END_REQUEST: + end_request = (FCGI_EndRequestBody *) (start + offset); + fcgim->status = end_request->protocolStatus; + fcgim->return_value = end_request->appStatusB0 | + (end_request->appStatusB0 << 8) | + (end_request->appStatusB0 << 16) | + (end_request->appStatusB0 << 24); + set_status (fcgim, fcgi_data_completed); + break; + default: + PRINT_ERROR ("ERROR: Unknown FCGI header type: %X\n", header->type); + cherokee_buffer_print_debug (&fcgim->read_buffer, -1); + ret = ret_error; + } + + cherokee_buffer_move_to_begin (&fcgim->read_buffer, bytes_to_move); + + if (ret == ret_error) + break; + } + + if (fcgim->read_buffer.len == 0) + cherokee_buffer_mrproper (&fcgim->read_buffer); + + return ret; } - ret_t -cherokee_fcgi_manager_step (cherokee_fcgi_manager_t *fcgim) +fcgi_manager_step (cherokee_fcgi_manager_t *fcgim, cuint_t id) { ret_t ret; size_t size; + cherokee_connection_t *conn; + cherokee_handler_fastcgi_t *fcgi; + + conn = fcgim->conn_poll [id - 1]; + fcgi = (cherokee_handler_fastcgi_t *) conn->handler; - /* Read from the FastCGI application - */ - if (fcgim->read_buffer.len < sizeof(FCGI_Header)) - { - ret = cherokee_socket_read (fcgim->socket, &fcgim->read_buffer, DEFAULT_READ_SIZE, &size); - printf ("cherokee_fcgi_manager_step: _read %d\n", ret); - if (ret != ret_ok) return ret; - } + if (fcgi->status > fcgi_data_available) + return ret_ok; - /* Process the information - */ - if (fcgim->read_buffer.len >= sizeof(FCGI_Header)) - { - ret = process_read_buffer (fcgim); - printf ("cherokee_fcgi_manager_step: process %d\n", ret); - return ret_ok; - } + printf (" [reading start %d %d]\n" , id, fcgi->status); + while (1) + { + /* Read from the FastCGI application + */ + if (fcgim->read_buffer.len < sizeof(FCGI_Header)) + { + /* + printf (" [readin %d %d" , id, fcgi->status);*/ + ret = cherokee_socket_read (fcgim->socket, &fcgim->read_buffer, DEFAULT_READ_SIZE, &size); + /*printf (" readout %d %d %d] ", id, ret, fcgim->remaining_size); + printf ("-----------------------read start--------------------------\n"); + cherokee_buffer_print_debug (&fcgim->read_buffer, -1); + printf ("-----------------------read end %d %d--------------------------\n", size, ret);*/ + if (ret != ret_ok) { + if (ret == ret_eof) + fcgim->connected = -1; + break; + } + } - /* Read - */ - return ret_eagain; + /* Process the information + */ + ret = process_read_buffer (fcgim); + + if (ret == ret_error) + break; + + if (fcgim->remaining_size <= 0) + break; + } + /* + printf (" [reading result %d %d %d]\n" , id, fcgi->status, ret);*/ + + return ret; } +ret_t +cherokee_fcgi_manager_step (cherokee_fcgi_manager_t *fcgim, cuint_t id) +{ + ret_t ret; + CHEROKEE_MUTEX_LOCK (&__global_fastcgi_manager_lock); + ret = fcgi_manager_step (fcgim, id); + CHEROKEE_MUTEX_UNLOCK (&__global_fastcgi_manager_lock); + return ret; +} + ret_t cherokee_fcgi_manager_add_conn (cherokee_fcgi_manager_t *fcgim, cherokee_connection_t *conn) { Index: cherokee/fcgi_manager.h =================================================================== --- cherokee/fcgi_manager.h (revision 105) +++ cherokee/fcgi_manager.h (working copy) @@ -36,9 +36,19 @@ cherokee_socket_t *socket; int port; cherokee_buffer_t hostname; + int connected; cherokee_buffer_t read_buffer; + int request_type; + cuint_t request_id; + cherokee_buffer_t request_buffer; + int return_value; + int status; + + cuint_t padding; + cuint_t remaining_size; + /* Connections */ cherokee_connection_t **conn_poll; @@ -47,16 +57,16 @@ } cherokee_fcgi_manager_t; -ret_t cherokee_fcgi_manager_new (cherokee_fcgi_manager_t **fcgim); +ret_t cherokee_fcgi_manager_new (cherokee_fcgi_manager_t **fcgim, char *host); ret_t cherokee_fcgi_manager_free (cherokee_fcgi_manager_t *fcgim); -ret_t cherokee_fcgi_manager_connect_to_srv (cherokee_fcgi_manager_t *fcgim, char *host); +ret_t cherokee_fcgi_manager_connect_to_srv (cherokee_fcgi_manager_t *fcgim); ret_t cherokee_fcgi_manager_spawn_srv (cherokee_fcgi_manager_t *fcgim, char *command); ret_t cherokee_fcgi_manager_register_conn (cherokee_fcgi_manager_t *fcgim, cherokee_connection_t *conn, cuint_t *id); ret_t cherokee_fcgi_manager_unregister_conn (cherokee_fcgi_manager_t *fcgim, cherokee_connection_t *conn); -ret_t cherokee_fcgi_manager_step (cherokee_fcgi_manager_t *fcgim); +ret_t cherokee_fcgi_manager_step (cherokee_fcgi_manager_t *fcgim, cuint_t); ret_t cherokee_fcgi_manager_send (cherokee_fcgi_manager_t *fcgim, cherokee_buffer_t *info, size_t *sent); ret_t cherokee_fcgi_manager_add_conn (cherokee_fcgi_manager_t *fcgim, cherokee_connection_t *conn); Index: cherokee/macros.h =================================================================== --- cherokee/macros.h (revision 105) +++ cherokee/macros.h (working copy) @@ -187,6 +187,7 @@ /* Tracing facility */ +#define TRACE_ENABLED #ifdef TRACE_ENABLED # define TRACE_ENV "CHEROKEE_TRACE" @@ -241,10 +242,10 @@ # define FMT_OFFSET "%lu" # define CST_OFFSET unsigned long #else -# error Unknown size of off_t +# define FMT_OFFSET "%lu" +# define CST_OFFSET unsigned long #endif - #ifdef O_NOATIME # define CHE_O_READ O_RDONLY | O_BINARY | O_NOATIME #else Index: cherokee/read_config_grammar.y =================================================================== --- cherokee/read_config_grammar.y (revision 105) +++ cherokee/read_config_grammar.y (working copy) @@ -320,7 +320,7 @@ %token T_ICONS T_AUTH T_NAME T_METHOD T_PASSWDFILE T_SSL_CA_LIST_FILE T_FROM T_SOCKET T_LOG_FLUSH_INTERVAL %token T_HEADERFILE T_PANIC_ACTION T_JUST_ABOUT T_LISTEN_QUEUE_SIZE T_SENDFILE T_MINSIZE T_MAXSIZE T_MAX_FDS %token T_SHOW T_CHROOT T_ONLY_SECURE T_MAX_CONNECTION_REUSE T_REWRITE T_POLL_METHOD T_EXTENSION T_IPV6 T_ENV -%token T_REQUEST +%token T_SERVER_LIST T_REQUEST %token <number> T_NUMBER T_PORT T_PORT_TLS %token <string> T_QSTRING T_FULLDIR T_ID T_HTTP_URL T_HTTPS_URL T_HOSTNAME T_IP T_DOMAIN_NAME T_ADDRESS_PORT @@ -1028,6 +1028,27 @@ } } +handler_option : T_SERVER_LIST str_type +{ + cherokee_table_t *properties; + list_t *plist = NULL; + list_t nlist = LIST_HEAD_INIT(nlist); + + properties = current_config_entry->handler_properties; + + if (properties != NULL) { + cherokee_typed_table_get_list (properties, "serverlist", &plist); + } + + if (plist == NULL) { + cherokee_list_add (&nlist, $2); + cherokee_dirs_table_entry_set_prop (current_config_entry, "serverlist", typed_list, &nlist, + (cherokee_typed_free_func_t) cherokee_list_free_item_simple); + } else { + cherokee_list_add_tail (plist, $2); + } +} + handler_option : T_SOCKET T_FULLDIR { dirs_table_set_handler_prop (current_config_entry, "socket", $2); }; Index: cherokee/buffer.c =================================================================== --- cherokee/buffer.c (revision 105) +++ cherokee/buffer.c (working copy) @@ -568,6 +568,7 @@ cherokee_buffer_print_debug (cherokee_buffer_t *buf, int len) { int i, length; + char text[17]; if ((len == -1) || (buf->len <= len)) { length = buf->len; @@ -575,20 +576,24 @@ length = len; } - + text [16] = 0; for (i=0; i < length; i++) { if (i%16 == 0) { printf ("%08x ", i); } printf ("%02x", buf->buf[i] & 0xFF); + if (buf->buf[i] > ' ' && buf->buf[i] < 128) + text [i%16] = (char) buf->buf[i]; + else + text [i%16] = '.'; if ((i+1)%2 == 0) { printf (" "); } if ((i+1)%16 == 0) { - printf ("\n"); + printf ("%s\n", text); } fflush(stdout); Index: cherokee/Makefile.am =================================================================== --- cherokee/Makefile.am (revision 105) +++ cherokee/Makefile.am (working copy) @@ -179,7 +179,7 @@ handler_fastcgi = \ $(common_cgi) \ -fastcgi.h \ +cgi.c cgi.h fastcgi.h \ handler_fastcgi.c \ handler_fastcgi.h \ fcgi_manager.h \ @@ -578,7 +578,6 @@ $(poll_port_src) \ $(poll_select_src) \ $(win32_src) \ -\ cherokee.h \ http.h \ http.c \ @@ -640,7 +639,6 @@ resolv_cache.c \ typed_table.h \ typed_table.c \ -\ mime_grammar.y \ mime_scanner.l Index: cherokee/handler_fastcgi.c =================================================================== --- cherokee/handler_fastcgi.c (revision 105) +++ cherokee/handler_fastcgi.c (working copy) @@ -22,14 +22,15 @@ * USA */ +#include "buffer.h" #include "common-internal.h" #include "handler_fastcgi.h" #include "fastcgi.h" #include "connection.h" #include "connection-protected.h" #include "thread.h" +#include "list_ext.h" - cherokee_module_info_t cherokee_fastcgi_info = { cherokee_handler, /* type */ cherokee_handler_fastcgi_new /* new func */ @@ -38,24 +39,23 @@ /* Global managers */ +static int __global_fastcgi_managers_index; static cherokee_table_t *__global_fastcgi_managers; #ifdef HAVE_PTHREAD static pthread_mutex_t __global_fastcgi_managers_lock; #endif - - static void fcgi_build_header (FCGI_Header *hdr, cuchar_t type, cushort_t request_id, cuint_t content_length, cuchar_t padding) { hdr->version = FCGI_VERSION_1; hdr->type = type; - hdr->requestIdB0 = (cuchar_t) request_id; - hdr->requestIdB1 = (cuchar_t) (request_id >> 8) & 0xff; - hdr->contentLengthB0 = (cuchar_t) (content_length % 256); - hdr->contentLengthB1 = (cuchar_t) (content_length / 256); + hdr->requestIdB0 = (cuchar_t) request_id; + hdr->requestIdB1 = (cuchar_t) (request_id >> 8) & 0xff; + hdr->contentLengthB0 = (cuchar_t) (content_length % 256); + hdr->contentLengthB1 = (cuchar_t) (content_length / 256); hdr->paddingLength = padding; - hdr->reserved = 0; + hdr->reserved = 0; } static void @@ -96,17 +96,15 @@ n->manager_ref = NULL; n->host_ref = NULL; n->interpreter_ref = NULL; + n->server_list = NULL; - cherokee_buffer_init (&n->write_buffer); - cherokee_buffer_init (&n->incoming_buffer); - cherokee_buffer_init (&n->environment); - if (properties) { - cherokee_typed_table_get_str (properties, "server", &n->host_ref); + cherokee_typed_table_get_list (properties, "serverlist", &n->server_list); cherokee_typed_table_get_str (properties, "interpreter", &n->interpreter_ref); } - /* Return + n->max_manager = MAX (__global_fastcgi_managers_index, list_len (n->server_list)); + /* Return */ *hdl = HANDLER(n); return ret_ok; @@ -118,6 +116,7 @@ { cherokee_fcgi_manager_unregister_conn (hdl->manager_ref, HANDLER_CONN(hdl)); + cherokee_buffer_mrproper (&hdl->data); cherokee_buffer_mrproper (&hdl->write_buffer); cherokee_buffer_mrproper (&hdl->incoming_buffer); cherokee_buffer_mrproper (&hdl->environment); @@ -125,34 +124,83 @@ return ret_ok; } +static void +fixup_params (cherokee_buffer_t *buf, cuint_t id) +{ + char *byte, *end, *last_pad; + char padding [8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int length; + int crafted_id [2]; + int pad; + if (buf->len == 0) + return; + end = buf->buf + buf->len; + crafted_id [0] = (cuchar_t) id; + crafted_id [1] = (cuchar_t) (id >> 8) & 0xff; + byte = (char*) buf->buf; + while (byte < end) + { + byte += 2; + if (*byte == (char) 0xFF) + *byte = crafted_id [1]; + byte ++; + if (*byte == (char) 0xFF) + *byte = crafted_id [0]; + byte ++; + length = (*byte << 8); + byte ++; + length |= *byte; + byte ++; + length += *byte; + + last_pad = byte; + byte ++; + byte += (length + 1); + } + + + if ((buf->len % 8) != 0) + { + pad = 8 - (buf->len % 8); + cherokee_buffer_ensure_size (buf, buf->len + pad); + + *last_pad = pad; + cherokee_buffer_add (buf, padding, pad); + } +} + static void add_env_pair (cherokee_buffer_t *buf, char *key, int key_len, char *val, int val_len) { + FCGI_BeginRequestRecord request; int len; - + len = key_len + val_len; - len += key_len > 127 ? 4 : 1; - len += val_len > 127 ? 4 : 1; + len += key_len > 127 ? 4 : 1; + len += val_len > 127 ? 4 : 1; - cherokee_buffer_ensure_size (buf, buf->len + key_len + val_len); + cherokee_buffer_ensure_size (buf, buf->len + key_len + val_len + sizeof(FCGI_Header)); + fcgi_build_header (&request.header, FCGI_PARAMS, 0xFFFF, len, 0); + cherokee_buffer_add (buf, (void *)&request.header, sizeof(FCGI_Header)); + if (key_len <= 127) { buf->buf[buf->len++] = key_len; - } else { + } else { buf->buf[buf->len++] = ((key_len >> 24) & 0xff) | 0x80; buf->buf[buf->len++] = (key_len >> 16) & 0xff; buf->buf[buf->len++] = (key_len >> 8) & 0xff; buf->buf[buf->len++] = (key_len >> 0) & 0xff; - } + } if (val_len <= 127) { buf->buf[buf->len++] = val_len; - } else { + } else { buf->buf[buf->len++] = ((val_len >> 24) & 0xff) | 0x80; buf->buf[buf->len++] = (val_len >> 16) & 0xff; buf->buf[buf->len++] = (val_len >> 8) & 0xff; @@ -163,45 +211,62 @@ cherokee_buffer_add (buf, val, val_len); } +static void +add_more_env (cherokee_handler_fastcgi_t *fcgi, cherokee_buffer_t *buf) +{ + cherokee_connection_t *conn; + cherokee_buffer_t buffer = CHEROKEE_BUF_INIT; + int len; + conn = HANDLER_CONN(fcgi); + cherokee_buffer_add_buffer (&buffer, &conn->local_directory); + + if (conn->request.len > 0) { + if (conn->request.buf [0] == '/') { + cherokee_buffer_add (&buffer, conn->request.buf + 1, conn->request.len - 1); + } else { + cherokee_buffer_add (&buffer, conn->request.buf, conn->request.len); + } + } + + add_env_pair (buf, + "SCRIPT_FILENAME", 15, + buffer.buf, buffer.len); + cherokee_buffer_mrproper (&buffer); +} + static ret_t build_initial_packages (cherokee_handler_fastcgi_t *fcgi) { ret_t ret; - cherokee_buffer_t tmp = CHEROKEE_BUF_INIT; + cherokee_buffer_t tmp = CHEROKEE_BUF_INIT, write_tmp = CHEROKEE_BUF_INIT; cherokee_connection_t *conn; FCGI_BeginRequestRecord request; conn = HANDLER_CONN(fcgi); - /* FCGI_BEGIN_REQUEST */ fcgi_build_header (&request.header, FCGI_BEGIN_REQUEST, fcgi->id, sizeof(request.body), 0); - fcgi_build_request_body (&request.body); + fcgi_build_request_body (&request); cherokee_buffer_add (&fcgi->write_buffer, (void *)&request, sizeof(FCGI_BeginRequestRecord)); - + /* Add enviroment variables - */ - ret = cherokee_cgi_build_basic_env (conn, (cherokee_cgi_set_env_pair_t) add_env_pair, &tmp, &fcgi->write_buffer); - if (unlikely (ret != ret_ok)) return ret; + */ + ret = cherokee_cgi_build_basic_env (conn, (cherokee_cgi_set_env_pair_t) add_env_pair, &tmp, &write_tmp); + if (unlikely (ret != ret_ok)) return ret; + add_more_env (fcgi, &write_tmp); + fixup_params (&write_tmp, fcgi->id); + cherokee_buffer_add_buffer (&fcgi->write_buffer, &write_tmp); cherokee_buffer_mrproper (&tmp); + cherokee_buffer_mrproper (&write_tmp); - fcgi_build_header (&request.header, FCGI_PARAMS, fcgi->id, tmp.size, 0); - cherokee_buffer_add (&fcgi->write_buffer, (void *)&request.header, sizeof(FCGI_Header)); - cherokee_buffer_add_buffer (&fcgi->write_buffer, &tmp); - /* There aren't more parameters */ fcgi_build_header (&request.header, FCGI_PARAMS, fcgi->id, 0, 0); cherokee_buffer_add (&fcgi->write_buffer, (void *)&request.header, sizeof(FCGI_Header)); - /* Stdin - */ - fcgi_build_header (&request.header, FCGI_STDIN, fcgi->id, 0, 0); - cherokee_buffer_add (&fcgi->write_buffer, (void *)&request.header, sizeof(FCGI_Header)); - return ret_ok; } @@ -212,16 +277,41 @@ ret_t ret; size_t sent = 0; cherokee_connection_t *conn = HANDLER_CONN(fcgi); + list_t *list_tmp; + int i = 0; - if (fcgi->host_ref == NULL) { - PRINT_ERROR_S ("ERROR: FastCGI without Host\n"); - return ret_error; - } + cherokee_buffer_init (&fcgi->write_buffer); + cherokee_buffer_init (&fcgi->incoming_buffer); + cherokee_buffer_init (&fcgi->data); + cherokee_buffer_init (&fcgi->environment); + fcgi->status = fcgi_data_unavailable; + fcgi->sending_phase = fcgi_sending_first_data; /* Look for the FCGI managers table */ CHEROKEE_MUTEX_LOCK (&__global_fastcgi_managers_lock); + + if (__global_fastcgi_managers_index > fcgi->max_manager) + __global_fastcgi_managers_index = fcgi->max_manager; + list_for_each (list_tmp, fcgi->server_list) { + char *host; + + if (i < __global_fastcgi_managers_index) { + i ++; + continue; + } else { + fcgi->host_ref = LIST_ITEM_INFO (list_tmp); + + __global_fastcgi_managers_index ++; + + if (__global_fastcgi_managers_index >= fcgi->max_manager) + __global_fastcgi_managers_index = 0; + + break; + } + } + ret = cherokee_table_get (__global_fastcgi_managers, fcgi->host_ref, (void **)&fcgi->manager_ref); if (ret == ret_not_found) { cherokee_fcgi_manager_t *n; @@ -233,7 +323,7 @@ /* Create a new manager object */ - ret = cherokee_fcgi_manager_new (&n); + ret = cherokee_fcgi_manager_new (&n, fcgi->host_ref); if (unlikely (ret != ret_ok)) return ret; /* Assign the object to that path @@ -244,19 +334,23 @@ /* Launch a new FastCGI server and connect to it */ - ret = cherokee_fcgi_manager_spawn_srv (n, fcgi->interpreter_ref); - if (unlikely (ret != ret_ok)) return ret; + + ret = cherokee_fcgi_manager_connect_to_srv (n); + if (unlikely (ret != ret_ok)) { + ret = cherokee_fcgi_manager_spawn_srv (n, fcgi->interpreter_ref); + if (unlikely (ret != ret_ok)) return ret; - ret = cherokee_fcgi_manager_connect_to_srv (n, fcgi->host_ref); - if (unlikely (ret != ret_ok)) return ret_error; + ret = cherokee_fcgi_manager_connect_to_srv (n); + if (unlikely (ret != ret_ok)) return ret_error; + } } CHEROKEE_MUTEX_UNLOCK (&__global_fastcgi_managers_lock); - /* Register this connection in the FastCGI manager */ ret = cherokee_fcgi_manager_register_conn (fcgi->manager_ref, conn, &fcgi->id); + if (unlikely (ret != ret_ok)) return ret; /* Send the first packet @@ -267,34 +361,84 @@ return ret_ok; } +static +void +complete_request (cherokee_handler_fastcgi_t *fcgi) +{ + FCGI_BeginRequestRecord request; + + fcgi_build_header (&request.header, FCGI_STDIN, fcgi->id, 0, 0); + cherokee_buffer_add (&fcgi->write_buffer, (void *)&request.header, sizeof(FCGI_Header)); + fcgi->sending_phase = fcgi_sending_data_finalized; +} + +static ret_t -cherokee_handler_fastcgi_step (cherokee_handler_fastcgi_t *fcgi, cherokee_buffer_t *buffer) +read_fcgi (cherokee_handler_fastcgi_t *fcgi) { - ret_t ret; - size_t done; + ret_t ret = ret_eagain; + size_t size; + cherokee_fcgi_manager_t *fcgim; + cherokee_connection_t *conn; + cherokee_buffer_t post_buffer = CHEROKEE_BUF_INIT; + FCGI_BeginRequestRecord request; - return_if_fail (fcgi->manager_ref != NULL, ret_error); + conn = HANDLER_CONN(fcgi); + fcgim = fcgi->manager_ref; + + if (fcgi->sending_phase == fcgi_sending_first_data_completed) + { + if (! cherokee_post_is_empty (&conn->post)) { + cherokee_post_walk_reset (&conn->post); + fcgi->sending_phase = fcgi_sending_post_data; + } + } - printf ("cherokee_handler_fastcgi_step: begin\n"); - + if (fcgi->sending_phase == fcgi_sending_post_data) + { + ret = cherokee_post_walk_read (&conn->post, &post_buffer, DEFAULT_READ_SIZE); + size = post_buffer.len; + if (size > 0) + { + fcgi_build_header (&request.header, FCGI_STDIN, fcgi->id, size, 0); + cherokee_buffer_add (&fcgi->write_buffer, (void *)&request.header, sizeof(FCGI_Header)); + cherokee_buffer_add_buffer (&fcgi->write_buffer, &post_buffer); + cherokee_buffer_mrproper (&post_buffer); + } + + if (ret == ret_ok) + { + fcgi->sending_phase = fcgi_sending_data_completed; + } + } + + if (fcgi->sending_phase == fcgi_sending_first_data && cherokee_post_is_empty (&conn->post)) + complete_request (fcgi); + + if (fcgi->sending_phase == fcgi_sending_data_completed) + complete_request (fcgi); + /* It has something to send */ - if (! cherokee_buffer_is_empty (&fcgi->write_buffer)) { - ret = cherokee_fcgi_manager_send (fcgi->manager_ref, &fcgi->write_buffer, &done); - printf ("cherokee_handler_fastcgi_step: !empty, send: %d\n", ret); + if (! cherokee_buffer_is_empty (&fcgi->write_buffer)) { + ret = cherokee_fcgi_manager_send (fcgi->manager_ref, &fcgi->write_buffer, &size); + + if (cherokee_buffer_is_empty (&fcgi->write_buffer)) { + cherokee_buffer_mrproper (&fcgi->write_buffer); + if (fcgi->sending_phase == fcgi_sending_first_data) + fcgi->sending_phase = fcgi_sending_first_data_completed; + } switch (ret) { case ret_ok: - if (cherokee_buffer_is_empty (&fcgi->write_buffer)) - return ret_eagain; + break; - return ret_ok; - case ret_eagain: return ret_eagain; case ret_eof: + break; case ret_error: return ret_error; default: @@ -302,22 +446,180 @@ } } + if (fcgi->sending_phase < fcgi_sending_first_data_completed) + { + ret = ret_eagain; + } else { + ret = cherokee_fcgi_manager_step (fcgim, fcgi->id); + if (ret != ret_eof) + { + if (fcgi->status == fcgi_data_available || fcgi->status == fcgi_data_completed) + ret = ret_ok; + else + ret = ret_eagain; + } else { + if (fcgi->sending_phase <= fcgi_sending_first_data_completed) { + + ret = ret_eagain; + } + } + } + + return ret; +} + +ret_t +cherokee_handler_fastcgi_step (cherokee_handler_fastcgi_t *fcgi, cherokee_buffer_t *buffer) +{ + ret_t ret = ret_ok; + + return_if_fail (buffer != NULL, ret_error); + + if (!cherokee_buffer_is_empty (&fcgi->incoming_buffer)) + { + cherokee_buffer_add_buffer (buffer, &fcgi->incoming_buffer); + cherokee_buffer_mrproper (&fcgi->incoming_buffer); + if (fcgi->status != fcgi_data_completed) + { + fcgi->status = fcgi_data_unavailable; + } else { + return ret_ok; + } + } + /* Lets read from the FastCGI server - * As side effect it could update more connections in this call */ - ret = cherokee_fcgi_manager_step (fcgi->manager_ref); - printf ("cherokee_handler_fastcgi_step: manager_step: %d\n", ret); + if (fcgi->status != fcgi_data_completed) { + ret = read_fcgi (fcgi); + if (ret == ret_ok) { + if (fcgi->status > fcgi_data_unavailable) { + cherokee_buffer_add_buffer (buffer, &fcgi->incoming_buffer); + cherokee_buffer_mrproper (&fcgi->incoming_buffer); + } + if (fcgi->status == fcgi_data_completed) + ret = ret_ok; + else { + fcgi->status = fcgi_data_unavailable; + ret = ret_eagain; + } + } - // To continue.. + } else { + ret = ret_ok; + } - return ret_ok; + return ret; } +ret_t +process_header (cherokee_handler_fastcgi_t *fcgi, cherokee_buffer_t *buf) +{ + cherokee_connection_t *conn = HANDLER_CONN(fcgi); + char *tmp, *end; + tmp = buf->buf; + while (1) { + if (tmp == NULL || *tmp == 0) + break; + + end = strstr (tmp, CRLF); + if (end == NULL) + end = tmp + strlen (tmp); + + if (strncmp (tmp, "Status: ", 8) == 0) { + int real_status; + char original_byte; + + original_byte = *end; + *end = 0; + tmp += 8; + real_status = atoi (tmp); + *end = original_byte; + + if (real_status <= 0) { + conn->error_code = http_internal_error; + return ret_error; + } + conn->error_code = real_status; + } + else if (strncmp (tmp, "Location: ", 10) == 0) { + tmp += 10; + cherokee_buffer_add (&conn->redirect, tmp, end - tmp); + } + + tmp = end + (*end == 0 ? 0 : 2); + } + + return ret_ok; +} + + ret_t -cherokee_handler_fastcgi_add_headers (cherokee_handler_fastcgi_t *hdl, cherokee_buffer_t *buffer) +cherokee_handler_fastcgi_add_headers (cherokee_handler_fastcgi_t *fcgi, cherokee_buffer_t *buffer) { - return ret_ok; + ret_t ret; + + int len; + char *content; + int end_len; + + /* Sanity check + */ + return_if_fail (buffer != NULL, ret_error); + + /* Read information from the FCGI + */ + ret = read_fcgi (fcgi); + + switch (ret) { + case ret_eof: + case ret_ok: + break; + + case ret_error: + case ret_eagain: + return ret; + + default: + RET_UNKNOWN(ret); + return ret_error; + } + + if (fcgi->incoming_buffer.buf == NULL) + { + if (ret == ret_eof) + return ret_eof; + else + return ret_ok; + } + + /* Look the end of headers + */ + content = strstr (fcgi->incoming_buffer.buf, CRLF CRLF); + if (content != NULL) { + end_len = 4; + } else { + content = strstr (fcgi->incoming_buffer.buf, "\n\n"); + end_len = 2; + } + + if (content == NULL) { + return (ret == ret_eof) ? ret_eof : ret_eagain; + } + + /* Copy the header + */ + len = content - fcgi->incoming_buffer.buf; + + cherokee_buffer_ensure_size (buffer, len+6); + cherokee_buffer_add (buffer, fcgi->incoming_buffer.buf, len); + cherokee_buffer_add (buffer, CRLF CRLF, 4); + + /* Drop out the headers, we already have a copy + */ + cherokee_buffer_move_to_begin (&fcgi->incoming_buffer, len + end_len); + + return process_header (fcgi, buffer); } @@ -325,7 +627,7 @@ */ void -fastcgi_init (cherokee_module_loader_t *loader) +cherokee_module_fastcgi_init (cherokee_module_loader_t *loader) { PRINT_ERROR_S ("WARNING: The FastCGI is under development, it isn't ready to be used!\n"); @@ -333,4 +635,5 @@ */ cherokee_table_new (&__global_fastcgi_managers); CHEROKEE_MUTEX_INIT(&__global_fastcgi_managers_lock, NULL); + __global_fastcgi_managers_index = 0; } Index: cherokee/handler_fastcgi.h =================================================================== --- cherokee/handler_fastcgi.h (revision 105) +++ cherokee/handler_fastcgi.h (working copy) @@ -56,23 +56,42 @@ // struct sockaddr_un local; } cherokee_fcgi_sockaddr_t; +typedef enum { + fcgi_data_unavailable, + fcgi_data_available, + fcgi_data_completed, +} cherokee_fcgi_status_t; +typedef enum { + fcgi_sending_first_data, + fcgi_sending_first_data_completed, + fcgi_sending_post_data, + fcgi_sending_data_completed, + fcgi_sending_data_finalized +} cherokee_fcgi_sending_phase_t; + + typedef struct { cherokee_handler_t handler; /* FastCGI manager */ - cherokee_fcgi_manager_t *manager_ref; - char *host_ref; - char *interpreter_ref; - cuint_t id; + cherokee_fcgi_manager_t *manager_ref; + char *host_ref; + char *interpreter_ref; + cuint_t id; /* FastCGI protocol stuff */ - cherokee_buffer_t environment; - cherokee_buffer_t write_buffer; - cherokee_buffer_t incoming_buffer; + cherokee_buffer_t environment; + cherokee_buffer_t write_buffer; + cherokee_buffer_t incoming_buffer; + cherokee_buffer_t data; + cuint_t status; + cherokee_fcgi_sending_phase_t sending_phase; + list_t *server_list; + int max_manager; } cherokee_handler_fastcgi_t; #define FCGI(x) ((cherokee_handler_fastcgi_t *)(x)) Index: cherokee/post.c =================================================================== --- cherokee/post.c (revision 105) +++ cherokee/post.c (working copy) @@ -260,19 +260,26 @@ case post_in_memory: cherokee_buffer_add (buf, post->info.buf + post->walk_offset, len); post->walk_offset += len; - return ret_ok; + if (post->walk_offset > post->info.len) + return ret_ok; + + return ret_eagain; + case post_in_tmp_file: cherokee_buffer_ensure_size (buf, buf->len + len + 1); ur = fread (buf->buf + buf->len, 1, len, post->tmp_file_p); if (ur <= 0) { - return (feof(post->tmp_file_p)) ? ret_eof : ret_error; + return (feof(post->tmp_file_p)) ? ret_ok : ret_error; } + + if (ur < len) + return ret_ok; buf->len += ur; buf->buf[buf->len] = '\0'; - return ret_ok; + return ret_eagain; default: SHOULDNT_HAPPEN; Index: cherokee/read_config_scanner.l =================================================================== --- cherokee/read_config_scanner.l (revision 105) +++ cherokee/read_config_scanner.l (working copy) @@ -113,6 +113,7 @@ "MaxConnectionReuse" { return T_MAX_CONNECTION_REUSE; } "IOCache" { return T_IO_CACHE; } "Env" { return T_ENV; } +"ServerList" { return T_SERVER_LIST; } "HeaderFile" { return T_HEADERFILE; } "On" { yylval.number = 1; return T_NUMBER; } "Off" { yylval.number = 0; return T_NUMBER; } Index: cherokee/socket.c =================================================================== --- cherokee/socket.c (revision 105) +++ cherokee/socket.c (working copy) @@ -1050,7 +1050,13 @@ ret_t cherokee_socket_gethostbyname (cherokee_socket_t *socket, cherokee_buffer_t *hostname) { - return cherokee_gethostbyname (hostname->buf, &SOCKET_SIN_ADDR(socket)); + if (SOCKET_AF(socket) == AF_UNIX) { + SOCKET_ADDR_UNIX(socket).sun_family = AF_UNIX; + memset ((char*) SOCKET_SUN_PATH (socket), 0, sizeof (SOCKET_ADDR_UNIX(socket))); + strncpy (SOCKET_SUN_PATH (socket), hostname->buf, hostname->len); + return ret_ok; + } else + return cherokee_gethostbyname (hostname->buf, &SOCKET_SIN_ADDR(socket)); } @@ -1060,7 +1066,10 @@ { int r; - r = connect (SOCKET_FD(socket), (struct sockaddr *) &SOCKET_ADDR(socket), sizeof(cherokee_sockaddr_t)); + if (SOCKET_AF(socket) == AF_UNIX) + r = connect (SOCKET_FD(socket), (struct sockaddr *) &SOCKET_ADDR_UNIX(socket), sizeof(SOCKET_ADDR_UNIX(socket))); + else + r = connect (SOCKET_FD(socket), (struct sockaddr *) &SOCKET_ADDR(socket), sizeof(cherokee_sockaddr_t)); if (r < 0) { int err = SOCK_ERRNO(); Index: cherokee/socket.h =================================================================== --- cherokee/socket.h (revision 105) +++ cherokee/socket.h (working copy) @@ -43,6 +43,7 @@ #ifdef HAVE_SYS_SOCKET_H # include <sys/socket.h> +# include <sys/un.h> #endif #ifdef HAVE_ARPA_INET_H @@ -100,6 +101,7 @@ typedef union { struct sockaddr sa; struct sockaddr_in sa_in; + struct sockaddr_un sa_un; #ifdef HAVE_SOCKADDR_IN6 struct sockaddr_in6 sa_in6; #endif @@ -140,12 +142,14 @@ #define SOCKET_FD(s) (SOCKET(s)->socket) #define SOCKET_AF(s) (SOCKET(s)->client_addr.sa.sa_family) #define SOCKET_ADDR(s) (SOCKET(s)->client_addr) +#define SOCKET_ADDR_UNIX(s) (SOCKET(s)->client_addr.sa_un) #define SOCKET_ADDR_IPv4(s) ((struct sockaddr_in *)&SOCKET(s)->client_addr) #define SOCKET_ADDR_IPv6(s) ((struct sockaddr_in6 *)&SOCKET(s)->client_addr) #define SOCKET_STATUS(s) (SOCKET(s)->status) #define SOCKET_SIN_PORT(s) (SOCKET(s)->client_addr.sa_in.sin_port) #define SOCKET_SIN_ADDR(s) (SOCKET(s)->client_addr.sa_in.sin_addr) +#define SOCKET_SUN_PATH(s) (SOCKET(s)->client_addr.sa_un.sun_path) #define SOCKET_ADDRESS_IPv4(s) (SOCKET_ADDR_IPv4(s)->sin_addr.s_addr) #define SOCKET_ADDRESS_IPv6(s) (SOCKET_ADDR_IPv6(s)->sin6_addr.s6_addr) Index: Makefile.am =================================================================== --- Makefile.am (revision 105) +++ Makefile.am (working copy) @@ -1,6 +1,6 @@ ## Cherokee: Makefile.am -*- makefile -*- -SUBDIRS = m4 contrib www doc icons qa cherokee cget +SUBDIRS = m4 contrib www icons qa cherokee cget bin_SCRIPTS = cherokee-config SUFFIXES = .sample.pre .sample
_______________________________________________ Cherokee mailing list Cherokee@lists.alobbs.com http://www.alobbs.com/cgi-bin/mailman/listinfo/cherokee