]> granicus.if.org Git - php/commitdiff
Merged fastcgi.* reated changes from sapi/cgi
authorDmitry Stogov <dmitry@zend.com>
Wed, 13 May 2015 15:54:41 +0000 (18:54 +0300)
committerDmitry Stogov <dmitry@zend.com>
Wed, 13 May 2015 15:54:41 +0000 (18:54 +0300)
sapi/fpm/fpm/fastcgi.c
sapi/fpm/fpm/fastcgi.h
sapi/fpm/fpm/fpm_main.c
sapi/fpm/fpm/fpm_php.c

index 0e3ec18023f10fd44d9cb404593011c2c18421eb..71eaa78cd486dea6cb84556ac1c31d108cbf2494 100644 (file)
@@ -146,6 +146,226 @@ static sa_t *allowed_clients = NULL;
 
 static sa_t client_sa;
 
+/* hash table */
+
+#define FCGI_HASH_TABLE_SIZE 128
+#define FCGI_HASH_TABLE_MASK (FCGI_HASH_TABLE_SIZE - 1)
+#define FCGI_HASH_SEG_SIZE   4096
+
+typedef struct _fcgi_hash_bucket {
+       unsigned int              hash_value;
+       unsigned int              var_len;
+       char                     *var;
+       unsigned int              val_len;
+       char                     *val;
+       struct _fcgi_hash_bucket *next;
+       struct _fcgi_hash_bucket *list_next;
+} fcgi_hash_bucket;
+
+typedef struct _fcgi_hash_buckets {
+       unsigned int               idx;
+       struct _fcgi_hash_buckets *next;
+       struct _fcgi_hash_bucket   data[FCGI_HASH_TABLE_SIZE];
+} fcgi_hash_buckets;
+
+typedef struct _fcgi_data_seg {
+       char                  *pos;
+       char                  *end;
+       struct _fcgi_data_seg *next;
+       char                   data[1];
+} fcgi_data_seg;
+
+typedef struct _fcgi_hash {
+       fcgi_hash_bucket  *hash_table[FCGI_HASH_TABLE_SIZE];
+       fcgi_hash_bucket  *list;
+       fcgi_hash_buckets *buckets;
+       fcgi_data_seg     *data;
+} fcgi_hash;
+
+static void fcgi_hash_init(fcgi_hash *h)
+{
+       memset(h->hash_table, 0, sizeof(h->hash_table));
+       h->list = NULL;
+       h->buckets = (fcgi_hash_buckets*)malloc(sizeof(fcgi_hash_buckets));
+       h->buckets->idx = 0;
+       h->buckets->next = NULL;
+       h->data = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + FCGI_HASH_SEG_SIZE);
+       h->data->pos = h->data->data;
+       h->data->end = h->data->pos + FCGI_HASH_SEG_SIZE;
+       h->data->next = NULL;
+}
+
+static void fcgi_hash_destroy(fcgi_hash *h)
+{
+       fcgi_hash_buckets *b;
+       fcgi_data_seg *p;
+
+       b = h->buckets;
+       while (b) {
+               fcgi_hash_buckets *q = b;
+               b = b->next;
+               free(q);
+       }
+       p = h->data;
+       while (p) {
+               fcgi_data_seg *q = p;
+               p = p->next;
+               free(q);
+       }
+}
+
+static void fcgi_hash_clean(fcgi_hash *h)
+{
+       memset(h->hash_table, 0, sizeof(h->hash_table));
+       h->list = NULL;
+       /* delete all bucket blocks except the first one */
+       while (h->buckets->next) {
+               fcgi_hash_buckets *q = h->buckets;
+
+               h->buckets = h->buckets->next;
+               free(q);
+       }
+       h->buckets->idx = 0;
+       /* delete all data segments except the first one */
+       while (h->data->next) {
+               fcgi_data_seg *q = h->data;
+
+               h->data = h->data->next;
+               free(q);
+       }
+       h->data->pos = h->data->data;
+}
+
+static inline char* fcgi_hash_strndup(fcgi_hash *h, char *str, unsigned int str_len)
+{
+       char *ret;
+
+       if (UNEXPECTED(h->data->pos + str_len + 1 >= h->data->end)) {
+               unsigned int seg_size = (str_len + 1 > FCGI_HASH_SEG_SIZE) ? str_len + 1 : FCGI_HASH_SEG_SIZE;
+               fcgi_data_seg *p = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + seg_size);
+
+               p->pos = p->data;
+               p->end = p->pos + seg_size;
+               p->next = h->data;
+               h->data = p;
+       }
+       ret = h->data->pos;
+       memcpy(ret, str, str_len);
+       ret[str_len] = 0;
+       h->data->pos += str_len + 1;
+       return ret;
+}
+
+static char* fcgi_hash_set(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, char *val, unsigned int val_len)
+{
+       unsigned int      idx = hash_value & FCGI_HASH_TABLE_MASK;
+       fcgi_hash_bucket *p = h->hash_table[idx];
+
+       while (UNEXPECTED(p != NULL)) {
+               if (UNEXPECTED(p->hash_value == hash_value) &&
+                   p->var_len == var_len &&
+                   memcmp(p->var, var, var_len) == 0) {
+
+                       p->val_len = val_len;
+                       p->val = fcgi_hash_strndup(h, val, val_len);
+                       return p->val;
+               }
+               p = p->next;
+       }
+
+       if (UNEXPECTED(h->buckets->idx >= FCGI_HASH_TABLE_SIZE)) {
+               fcgi_hash_buckets *b = (fcgi_hash_buckets*)malloc(sizeof(fcgi_hash_buckets));
+               b->idx = 0;
+               b->next = h->buckets;
+               h->buckets = b;
+       }
+       p = h->buckets->data + h->buckets->idx;
+       h->buckets->idx++;
+       p->next = h->hash_table[idx];
+       h->hash_table[idx] = p;
+       p->list_next = h->list;
+       h->list = p;
+       p->hash_value = hash_value;
+       p->var_len = var_len;
+       p->var = fcgi_hash_strndup(h, var, var_len);
+       p->val_len = val_len;
+       p->val = fcgi_hash_strndup(h, val, val_len);
+       return p->val;
+}
+
+static void fcgi_hash_del(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len)
+{
+       unsigned int      idx = hash_value & FCGI_HASH_TABLE_MASK;
+       fcgi_hash_bucket **p = &h->hash_table[idx];
+
+       while (*p != NULL) {
+               if ((*p)->hash_value == hash_value &&
+                   (*p)->var_len == var_len &&
+                   memcmp((*p)->var, var, var_len) == 0) {
+
+                   (*p)->val = NULL; /* NULL value means deleted */
+                   (*p)->val_len = 0;
+                       *p = (*p)->next;
+                   return;
+               }
+               p = &(*p)->next;
+       }
+}
+
+static char *fcgi_hash_get(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, unsigned int *val_len)
+{
+       unsigned int      idx = hash_value & FCGI_HASH_TABLE_MASK;
+       fcgi_hash_bucket *p = h->hash_table[idx];
+
+       while (p != NULL) {
+               if (p->hash_value == hash_value &&
+                   p->var_len == var_len &&
+                   memcmp(p->var, var, var_len) == 0) {
+                   *val_len = p->val_len;
+                   return p->val;
+               }
+               p = p->next;
+       }
+       return NULL;
+}
+
+static void fcgi_hash_apply(fcgi_hash *h, fcgi_apply_func func, void *arg)
+{
+       fcgi_hash_bucket *p     = h->list;
+
+       while (p) {
+               if (EXPECTED(p->val != NULL)) {
+                       func(p->var, p->var_len, p->val, p->val_len, arg);
+               }
+               p = p->list_next;
+       }
+}
+
+struct _fcgi_request {
+       int            listen_socket;
+#ifdef _WIN32
+       int            tcp;
+#endif
+       int            fd;
+       int            id;
+       int            keep;
+#ifdef TCP_NODELAY
+       int            nodelay;
+#endif
+       int            closed;
+
+       int            in_len;
+       int            in_pad;
+
+       fcgi_header   *out_hdr;
+       unsigned char *out_pos;
+       unsigned char  out_buf[1024*8];
+       unsigned char  reserved[sizeof(fcgi_end_request_rec)];
+
+       int            has_env;
+       fcgi_hash      env;
+};
+
 #ifdef _WIN32
 
 static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg)
@@ -181,10 +401,20 @@ static void fcgi_setup_signals(void)
 }
 #endif
 
+int fcgi_in_shutdown(void)
+{
+       return in_shutdown;
+}
+
+void fcgi_terminate(void)
+{
+       in_shutdown = 1;
+}
+
 int fcgi_init(void)
 {
        if (!is_initialized) {
-               zend_hash_init(&fcgi_mgmt_vars, 0, NULL, fcgi_free_mgmt_var_cb, 1);
+               zend_hash_init(&fcgi_mgmt_vars, 8, NULL, fcgi_free_mgmt_var_cb, 1);
                fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS") - 1, "0", sizeof("0")-1);
 
                is_initialized = 1;
@@ -285,9 +515,9 @@ void fcgi_set_allowed_clients(char *ip)
        }
 }
 
-void fcgi_init_request(fcgi_request *req, int listen_socket)
+fcgi_request *fcgi_init_request(int listen_socket)
 {
-       memset(req, 0, sizeof(fcgi_request));
+       fcgi_request *req = (fcgi_request*)calloc(1, sizeof(fcgi_request));
        req->listen_socket = listen_socket;
        req->fd = -1;
        req->id = -1;
@@ -301,6 +531,20 @@ void fcgi_init_request(fcgi_request *req, int listen_socket)
 #ifdef _WIN32
        req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL);
 #endif
+
+#ifdef TCP_NODELAY
+       req->nodelay = 0;
+#endif
+
+       fcgi_hash_init(&req->env);
+
+       return req;
+}
+
+void fcgi_destroy_request(fcgi_request *req)
+{
+       fcgi_hash_destroy(&req->env);
+       free(req);
 }
 
 static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)
@@ -379,120 +623,47 @@ static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int
        return pad;
 }
 
-static inline size_t fcgi_get_params_len( int *result, unsigned char *p, unsigned char *end)
-{
-       size_t ret = 0;
-
-       if (p < end) {
-               *result = p[0];
-               if (*result < 128) {
-                       ret = 1;
-               }
-               else if (p + 3 < end) {
-                       *result = ((*result & 0x7f) << 24);
-                       *result |= (p[1] << 16);
-                       *result |= (p[2] << 8);
-                       *result |= p[3];
-                       ret = 4;
-               }
-       }
-       if (*result < 0) {
-               ret = 0;
-       }
-       return ret;
-}
-
-static inline int fcgi_param_get_eff_len( unsigned char *p, unsigned char *end, uint *eff_len)
-{
-       int ret = 1;
-       int zero_found = 0;
-       *eff_len = 0;
-       for (; p != end; ++p) {
-               if (*p == '\0') {
-                       zero_found = 1;
-               }
-               else {
-                       if (zero_found) {
-                               ret = 0;
-                               break;
-                       }
-                       if (*eff_len < ((uint)-1)) {
-                               ++*eff_len;
-                       }
-                       else {
-                               ret = 0;
-                               break;
-                       }
-               }
-       }
-       return ret;
-}
-
 static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end)
 {
-       char buf[128];
-       char *tmp = buf;
-       size_t buf_size = sizeof(buf);
        int name_len = 0;
        int val_len = 0;
-       uint eff_name_len = 0;
-       char *s;
-       int ret = 1;
-       size_t bytes_consumed;
 
        while (p < end) {
-               bytes_consumed = fcgi_get_params_len(&name_len, p, end);
-               if (!bytes_consumed) {
-                       /* Malformated request */
-                       ret = 0;
-                       break;
+               name_len = *p++;
+               if (UNEXPECTED(name_len >= 128)) {
+                       if (UNEXPECTED(p + 3 >= end)) return 0;
+                       name_len = ((name_len & 0x7f) << 24);
+                       name_len |= (*p++ << 16);
+                       name_len |= (*p++ << 8);
+                       name_len |= *p++;
                }
-               p += bytes_consumed;
-               bytes_consumed = fcgi_get_params_len(&val_len, p, end);
-               if (!bytes_consumed) {
-                       /* Malformated request */
-                       ret = 0;
-                       break;
+               if (UNEXPECTED(p >= end)) return 0;
+               val_len = *p++;
+               if (UNEXPECTED(val_len >= 128)) {
+                       if (UNEXPECTED(p + 3 >= end)) return 0;
+                       val_len = ((val_len & 0x7f) << 24);
+                       val_len |= (*p++ << 16);
+                       val_len |= (*p++ << 8);
+                       val_len |= *p++;
                }
-               p += bytes_consumed;
-               if (name_len > (INT_MAX - val_len) || /* would the addition overflow? */
-                   name_len + val_len > end - p) {   /* would we exceed the buffer? */
+               if (UNEXPECTED(name_len > (INT_MAX - val_len)) || /* would the addition overflow? */
+                   UNEXPECTED(name_len + val_len > end - p)) {   /* would we exceed the buffer? */
                        /* Malformated request */
-                       ret = 0;
-                       break;
+                       return 0;
                }
 
                /*
                 * get the effective length of the name in case it's not a valid string
                 * don't do this on the value because it can be binary data
                 */
-               if (!fcgi_param_get_eff_len(p, p+name_len, &eff_name_len)){
+               if (UNEXPECTED(memchr(p, 0, name_len) != NULL)) {
                        /* Malicious request */
-                       ret = 0;
-                       break;
-               }
-               if (eff_name_len >= buf_size-1) {
-                       if (eff_name_len > ((uint)-1)-64) {
-                               ret = 0;
-                               break;
-                       }
-                       buf_size = eff_name_len + 64;
-                       tmp = (tmp == buf ? emalloc(buf_size): erealloc(tmp, buf_size));
-                       if (tmp == NULL) {
-                               ret = 0;
-                               break;
-                       }
+                       return 0;
                }
-               memcpy(tmp, p, eff_name_len);
-               tmp[eff_name_len] = 0;
-               s = estrndup((char*)p + name_len, val_len);
-               zend_hash_str_update_ptr(req->env, tmp, eff_name_len, s);
+               fcgi_hash_set(&req->env, FCGI_HASH_FUNC(p, name_len), (char*)p, name_len, (char*)p + name_len, val_len);
                p += name_len + val_len;
        }
-       if (tmp != buf && tmp != NULL) {
-               efree(tmp);
-       }
-       return ret;
+       return 1;
 }
 
 static void fcgi_free_var(zval *zv)
@@ -511,8 +682,7 @@ static int fcgi_read_request(fcgi_request *req)
        req->in_len = 0;
        req->out_hdr = NULL;
        req->out_pos = req->out_buf;
-       ALLOC_HASHTABLE(req->env);
-       zend_hash_init(req->env, 0, NULL, fcgi_free_var, 0);
+       req->has_env = 1;
 
        if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
            hdr.version < FCGI_VERSION_1) {
@@ -539,25 +709,32 @@ static int fcgi_read_request(fcgi_request *req)
        req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0;
 
        if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) {
-               char *val;
-
                if (safe_read(req, buf, len+padding) != len+padding) {
                        return 0;
                }
 
                req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN);
+#ifdef TCP_NODELAY
+               if (req->keep && req->tcp && !req->nodelay) {
+# ifdef _WIN32
+                       BOOL on = 1;
+# else
+                       int on = 1;
+# endif
+
+                       setsockopt(req->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on));
+                       req->nodelay = 1;
+               }
+#endif
                switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) {
                        case FCGI_RESPONDER:
-                               val = estrdup("RESPONDER");
-                               zend_hash_str_update_ptr(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), val);
+                               fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "RESPONDER", sizeof("RESPONDER")-1);
                                break;
                        case FCGI_AUTHORIZER:
-                               val = estrdup("AUTHORIZER");
-                               zend_hash_str_update_ptr(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), val);
+                               fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "AUTHORIZER", sizeof("AUTHORIZER")-1);
                                break;
                        case FCGI_FILTER:
-                               val = estrdup("FILTER");
-                               zend_hash_str_update_ptr(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), val);
+                               fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "FILTER", sizeof("FILTER")-1);
                                break;
                        default:
                                return 0;
@@ -596,8 +773,9 @@ static int fcgi_read_request(fcgi_request *req)
                }
        } else if (hdr.type == FCGI_GET_VALUES) {
                unsigned char *p = buf + sizeof(fcgi_header);
-               zend_string *key;
                zval *value;
+               unsigned int zlen;
+               fcgi_hash_bucket *q;
 
                if (safe_read(req, buf, len+padding) != len+padding) {
                        req->keep = 0;
@@ -609,26 +787,23 @@ static int fcgi_read_request(fcgi_request *req)
                        return 0;
                }
 
-               ZEND_HASH_FOREACH_STR_KEY(req->env, key) {
-                       int zlen;
-                       if (!key) {
-                               continue;
-                       }
-                       value = zend_hash_find(&fcgi_mgmt_vars, key);
-                       if (!value) {
+               q = req->env.list;
+               while (q != NULL) {
+                       if ((value = zend_hash_str_find(&fcgi_mgmt_vars, q->var, q->var_len)) == NULL) {
+                               q = q->list_next;
                                continue;
                        }
-                       zlen = Z_STRLEN_P(value);
-                       if ((p + 4 + 4 + key->len + zlen) >= (buf + sizeof(buf))) {
+                       zlen = (unsigned int)Z_STRLEN_P(value);
+                       if ((p + 4 + 4 + q->var_len + zlen) >= (buf + sizeof(buf))) {
                                break;
                        }
-                       if (key->len < 0x80) {
-                               *p++ = key->len;
+                       if (q->var_len < 0x80) {
+                               *p++ = q->var_len;
                        } else {
-                               *p++ = ((key->len >> 24) & 0xff) | 0x80;
-                               *p++ = (key->len >> 16) & 0xff;
-                               *p++ = (key->len >> 8) & 0xff;
-                               *p++ = key->len & 0xff;
+                               *p++ = ((q->var_len >> 24) & 0xff) | 0x80;
+                               *p++ = (q->var_len >> 16) & 0xff;
+                               *p++ = (q->var_len >> 8) & 0xff;
+                               *p++ = q->var_len & 0xff;
                        }
                        if (zlen < 0x80) {
                                *p++ = zlen;
@@ -638,11 +813,12 @@ static int fcgi_read_request(fcgi_request *req)
                                *p++ = (zlen >> 8) & 0xff;
                                *p++ = zlen & 0xff;
                        }
-                       memcpy(p, key->val, key->len);
-                       p += key->len;
+                       memcpy(p, q->var, q->var_len);
+                       p += q->var_len;
                        memcpy(p, Z_STRVAL_P(value), zlen);
                        p += zlen;
-               } ZEND_HASH_FOREACH_END();
+                       q = q->list_next;
+               }
                len = p - buf - sizeof(fcgi_header);
                len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len);
                if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) {
@@ -712,10 +888,9 @@ int fcgi_read(fcgi_request *req, char *str, int len)
 
 void fcgi_close(fcgi_request *req, int force, int destroy)
 {
-       if (destroy && req->env) {
-               zend_hash_destroy(req->env);
-               FREE_HASHTABLE(req->env);
-               req->env = NULL;
+       if (destroy && req->has_env) {
+               fcgi_hash_clean(&req->env);
+               req->has_env = 0;
        }
 
 #ifdef _WIN32
@@ -750,12 +925,20 @@ void fcgi_close(fcgi_request *req, int force, int destroy)
                        while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
                }
                close(req->fd);
+#endif
+#ifdef TCP_NODELAY
+               req->nodelay = 0;
 #endif
                req->fd = -1;
                fpm_request_finished();
        }
 }
 
+int fcgi_is_closed(fcgi_request *req)
+{
+       return (req->fd < 0);
+}
+
 static int fcgi_is_allowed() {
        int i;
 
@@ -1072,25 +1255,44 @@ int fcgi_finish_request(fcgi_request *req, int force_close)
 
 char* fcgi_getenv(fcgi_request *req, const char* var, int var_len)
 {
-       if (!req) {
-               return NULL;
-       }
+       unsigned int val_len;
+
+       if (!req) return NULL;
+
+       return fcgi_hash_get(&req->env, FCGI_HASH_FUNC(var, var_len), (char*)var, var_len, &val_len);
+}
+
+char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value)
+{
+       unsigned int val_len;
 
-       return zend_hash_str_find_ptr(req->env, var, var_len);
+       return fcgi_hash_get(&req->env, hash_value, (char*)var, var_len, &val_len);
 }
 
 char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val)
 {
-       if (var && req) {
-               if (val == NULL) {
-                       zend_hash_str_del(req->env, var, var_len);
-               } else {
-                       val = estrdup(val);
-                       zend_hash_str_update_ptr(req->env, var, var_len, val);
-                       return val;
-               }
+       if (!req) return NULL;
+       if (val == NULL) {
+               fcgi_hash_del(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len);
+               return NULL;
+       } else {
+               return fcgi_hash_set(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len, val, (unsigned int)strlen(val));
        }
-       return NULL;
+}
+
+char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val)
+{
+       if (val == NULL) {
+               fcgi_hash_del(&req->env, hash_value, var, var_len);
+               return NULL;
+       } else {
+               return fcgi_hash_set(&req->env, hash_value, var, var_len, val, (unsigned int)strlen(val));
+       }
+}
+
+void fcgi_loadenv(fcgi_request *req, fcgi_apply_func func, zval *array)
+{
+       fcgi_hash_apply(&req->env, func, array);
 }
 
 void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len)
index fb7385afcc5a7f17a46606afd3ac468b9f76cc96..31419dfdd0bef16517c41840818e5abf9b7e92d7 100644 (file)
 
 #define FCGI_KEEP_CONN  1
 
+/* this is near the perfect hash function for most useful FastCGI variables
+ * which combines efficiency and minimal hash collisions
+ */
+
+#define FCGI_HASH_FUNC(var, var_len) \
+       (UNEXPECTED(var_len < 3) ? var_len : \
+               (((unsigned int)var[3]) << 2) + \
+               (((unsigned int)var[var_len-2]) << 4) + \
+               (((unsigned int)var[var_len-1]) << 2) + \
+               var_len)
+
+#define FCGI_GETENV(request, name) \
+       fcgi_quick_getenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1))
+
+#define FCGI_PUTENV(request, name, value) \
+       fcgi_quick_putenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1), value)
+
 typedef enum _fcgi_role {
        FCGI_RESPONDER  = 1,
        FCGI_AUTHORIZER = 2,
@@ -91,39 +108,28 @@ typedef struct _fcgi_end_request_rec {
 
 /* FastCGI client API */
 
-typedef struct _fcgi_request {
-       int            listen_socket;
-#ifdef _WIN32
-       int            tcp;
-#endif
-       int            fd;
-       int            id;
-       int            keep;
-       int            closed;
-
-       int            in_len;
-       int            in_pad;
-
-       fcgi_header   *out_hdr;
-       unsigned char *out_pos;
-       unsigned char  out_buf[1024*8];
-       unsigned char  reserved[sizeof(fcgi_end_request_rec)];
+typedef void (*fcgi_apply_func)(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg);
 
-       HashTable     *env;
-} fcgi_request;
+typedef struct _fcgi_request fcgi_request;
 
 int fcgi_init(void);
 void fcgi_shutdown(void);
-void fcgi_init_request(fcgi_request *req, int listen_socket);
+int fcgi_in_shutdown(void);
+void fcgi_terminate(void);
+fcgi_request* fcgi_init_request(int listen_socket);
+void fcgi_destroy_request(fcgi_request *req);
 int fcgi_accept_request(fcgi_request *req);
 int fcgi_finish_request(fcgi_request *req, int force_close);
 
-void fcgi_set_in_shutdown(int);
 void fcgi_set_allowed_clients(char *);
 void fcgi_close(fcgi_request *req, int force, int destroy);
+int fcgi_is_closed(fcgi_request *req);
 
 char* fcgi_getenv(fcgi_request *req, const char* var, int var_len);
 char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val);
+char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value);
+char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val);
+void  fcgi_loadenv(fcgi_request *req, fcgi_apply_func load_func, zval *array);
 
 int fcgi_read(fcgi_request *req, char *str, int len);
 
index ecfc9be0246778018068165964cfc01774ef1aca..efd1f7155a0ca92b48fd9b9365d5dde2264b529e 100644 (file)
@@ -460,8 +460,7 @@ static size_t sapi_cgi_read_post(char *buffer, size_t count_bytes)
        while (read_bytes < count_bytes) {
                fcgi_request *request = (fcgi_request*) SG(server_context);
                if (request_body_fd == -1) {
-                       char *request_body_filename = sapi_cgibin_getenv((char *) "REQUEST_BODY_FILE",
-                                       sizeof("REQUEST_BODY_FILE") - 1);
+                       char *request_body_filename = FCGI_GETENV(request, "REQUEST_BODY_FILE");
 
                        if (request_body_filename && *request_body_filename) {
                                request_body_fd = open(request_body_filename, O_RDONLY);
@@ -515,7 +514,20 @@ static char *_sapi_cgibin_putenv(char *name, char *value)
 
 static char *sapi_cgi_read_cookies(void)
 {
-       return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE") - 1);
+       fcgi_request *request = (fcgi_request*) SG(server_context);
+
+       return FCGI_GETENV(request, "HTTP_COOKIE");
+}
+
+static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg)
+{
+       zval *array_ptr = (zval*)arg;
+       int filter_arg = (Z_ARR_P(array_ptr) == Z_ARR(PG(http_globals)[TRACK_VARS_ENV]))?PARSE_ENV:PARSE_SERVER;
+       size_t new_val_len;
+
+       if (sapi_module.input_filter(filter_arg, var, &val, strlen(val), &new_val_len)) {
+               php_register_variable_safe(var, val, new_val_len, array_ptr);
+       }
 }
 
 void cgi_php_import_environment_variables(zval *array_ptr)
@@ -545,20 +557,12 @@ void cgi_php_import_environment_variables(zval *array_ptr)
        php_php_import_environment_variables(array_ptr);
 
        request = (fcgi_request*) SG(server_context);
-       filter_arg = Z_ARR_P(array_ptr) == Z_ARR(PG(http_globals)[TRACK_VARS_ENV])
-               ? PARSE_ENV : PARSE_SERVER;
-
-       ZEND_HASH_FOREACH_STR_KEY_PTR(request->env, var, val) {
-               size_t new_val_len;
-
-               if (var && sapi_module.input_filter(filter_arg, var->val, &val, strlen(val), &new_val_len)) {
-                       php_register_variable_safe(var->val, val, new_val_len, array_ptr);
-               }
-       } ZEND_HASH_FOREACH_END();
+       fcgi_loadenv(request, cgi_php_load_env_var, array_ptr);
 }
 
 static void sapi_cgi_register_variables(zval *track_vars_array)
 {
+       fcgi_request *request = (fcgi_request*) SG(server_context);
        size_t php_self_len;
        char *php_self;
 
@@ -570,7 +574,7 @@ static void sapi_cgi_register_variables(zval *track_vars_array)
        if (CGIG(fix_pathinfo)) {
                char *script_name = SG(request_info).request_uri;
                unsigned int script_name_len = script_name ? strlen(script_name) : 0;
-               char *path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO") - 1);
+               char *path_info = FCGI_GETENV(request, "PATH_INFO");
                unsigned int path_info_len = path_info ? strlen(path_info) : 0;
 
                php_self_len = script_name_len + path_info_len;
@@ -711,6 +715,7 @@ static void php_cgi_ini_activate_user_config(char *path, int path_len, const cha
 
 static int sapi_cgi_activate(void)
 {
+       fcgi_request *request = (fcgi_request*) SG(server_context);
        char *path, *doc_root, *server_name;
        uint path_len, doc_root_len, server_name_len;
 
@@ -721,7 +726,7 @@ static int sapi_cgi_activate(void)
 
        if (php_ini_has_per_host_config()) {
                /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
-               server_name = sapi_cgibin_getenv("SERVER_NAME", sizeof("SERVER_NAME") - 1);
+               server_name = FCGI_GETENV(request, "SERVER_NAME");
                /* SERVER_NAME should also be defined at this stage..but better check it anyway */
                if (server_name) {
                        server_name_len = strlen(server_name);
@@ -755,7 +760,7 @@ static int sapi_cgi_activate(void)
 
                /* Load and activate user ini files in path starting from DOCUMENT_ROOT */
                if (PG(user_ini_filename) && *PG(user_ini_filename)) {
-                       doc_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1);
+                       doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT");
                        /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
                        if (doc_root) {
                                doc_root_len = strlen(doc_root);
@@ -984,8 +989,9 @@ static int is_valid_path(const char *path)
  */
 static void init_request_info(void)
 {
-       char *env_script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME") - 1);
-       char *env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED", sizeof("PATH_TRANSLATED") - 1);
+       fcgi_request *request = (fcgi_request*) SG(server_context);
+       char *env_script_filename = FCGI_GETENV(request, "SCRIPT_FILENAME");
+       char *env_path_translated = FCGI_GETENV(request, "PATH_TRANSLATED");
        char *script_path_translated = env_script_filename;
        char *ini;
        int apache_was_here = 0;
@@ -1013,25 +1019,25 @@ static void init_request_info(void)
         * of the script will be retreived later via argc/argv */
        if (script_path_translated) {
                const char *auth;
-               char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH", sizeof("CONTENT_LENGTH") - 1);
-               char *content_type = sapi_cgibin_getenv("CONTENT_TYPE", sizeof("CONTENT_TYPE") - 1);
-               char *env_path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO") - 1);
-               char *env_script_name = sapi_cgibin_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME") - 1);
+               char *content_length = FCGI_GETENV(request, "CONTENT_LENGTH");
+               char *content_type = FCGI_GETENV(request, "CONTENT_TYPE");
+               char *env_path_info = FCGI_GETENV(request, "PATH_INFO");
+               char *env_script_name = FCGI_GETENV(request, "SCRIPT_NAME");
 
                /* Hack for buggy IIS that sets incorrect PATH_INFO */
-               char *env_server_software = sapi_cgibin_getenv("SERVER_SOFTWARE", sizeof("SERVER_SOFTWARE") - 1);
+               char *env_server_software = FCGI_GETENV(request, "SERVER_SOFTWARE");
                if (env_server_software &&
                        env_script_name &&
                        env_path_info &&
                        strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS") - 1) == 0 &&
                        strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0
                ) {
-                       env_path_info = _sapi_cgibin_putenv("ORIG_PATH_INFO", env_path_info);
+                       env_path_info = FCGI_PUTENV(request, "ORIG_PATH_INFO", env_path_info);
                        env_path_info += strlen(env_script_name);
                        if (*env_path_info == 0) {
                                env_path_info = NULL;
                        }
-                       env_path_info = _sapi_cgibin_putenv("PATH_INFO", env_path_info);
+                       env_path_info = FCGI_PUTENV(request, "PATH_INFO", env_path_info);
                }
 
 #define APACHE_PROXY_FCGI_PREFIX "proxy:fcgi://"
@@ -1066,8 +1072,8 @@ static void init_request_info(void)
                if (CGIG(fix_pathinfo)) {
                        struct stat st;
                        char *real_path = NULL;
-                       char *env_redirect_url = sapi_cgibin_getenv("REDIRECT_URL", sizeof("REDIRECT_URL") - 1);
-                       char *env_document_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1);
+                       char *env_redirect_url = FCGI_GETENV(request, "REDIRECT_URL");
+                       char *env_document_root = FCGI_GETENV(request, "DOCUMENT_ROOT");
                        char *orig_path_translated = env_path_translated;
                        char *orig_path_info = env_path_info;
                        char *orig_script_name = env_script_name;
@@ -1075,7 +1081,7 @@ static void init_request_info(void)
                        int script_path_translated_len;
 
                        if (!env_document_root && PG(doc_root)) {
-                               env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT", PG(doc_root));
+                               env_document_root = FCGI_PUTENV(request, "DOCUMENT_ROOT", PG(doc_root));
                                /* fix docroot */
                                TRANSLATE_SLASHES(env_document_root);
                        }
@@ -1156,15 +1162,15 @@ static void init_request_info(void)
                                                                if (orig_path_info) {
                                                                        char old;
 
-                                                                       _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info);
+                                                                       FCGI_PUTENV(request, "ORIG_PATH_INFO", orig_path_info);
                                                                        old = path_info[0];
                                                                        path_info[0] = 0;
                                                                        if (!orig_script_name ||
                                                                                strcmp(orig_script_name, env_path_info) != 0) {
                                                                                if (orig_script_name) {
-                                                                                       _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name);
+                                                                                       FCGI_PUTENV(request, "ORIG_SCRIPT_NAME", orig_script_name);
                                                                                }
-                                                                               SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_path_info);
+                                                                               SG(request_info).request_uri = FCGI_PUTENV(request, "SCRIPT_NAME", env_path_info);
                                                                        } else {
                                                                                SG(request_info).request_uri = orig_script_name;
                                                                        }
@@ -1176,19 +1182,19 @@ static void init_request_info(void)
                                                                         */
                                                                        int snlen = strlen(env_script_name);
                                                                        if (snlen>slen && !strcmp(env_script_name+snlen-slen, path_info)) {
-                                                                               _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name);
+                                                                               FCGI_PUTENV(request, "ORIG_SCRIPT_NAME", orig_script_name);
                                                                                env_script_name[snlen-slen] = 0;
-                                                                               SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name);
+                                                                               SG(request_info).request_uri = FCGI_PUTENV(request, "SCRIPT_NAME", env_script_name);
                                                                        }
                                                                }
-                                                               env_path_info = _sapi_cgibin_putenv("PATH_INFO", path_info);
+                                                               env_path_info = FCGI_PUTENV(request, "PATH_INFO", path_info);
                                                        }
                                                        if (!orig_script_filename ||
                                                                strcmp(orig_script_filename, pt) != 0) {
                                                                if (orig_script_filename) {
-                                                                       _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename);
+                                                                       FCGI_PUTENV(request, "ORIG_SCRIPT_FILENAME", orig_script_filename);
                                                                }
-                                                               script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", pt);
+                                                               script_path_translated = FCGI_PUTENV(request, "SCRIPT_FILENAME", pt);
                                                        }
                                                        TRANSLATE_SLASHES(pt);
 
@@ -1218,9 +1224,9 @@ static void init_request_info(void)
                                                                }
                                                                path_translated[path_translated_len] = '\0';
                                                                if (orig_path_translated) {
-                                                                       _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated);
+                                                                       FCGI_PUTENV(request, "ORIG_PATH_TRANSLATED", orig_path_translated);
                                                                }
-                                                               env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated);
+                                                               env_path_translated = FCGI_PUTENV(request, "PATH_TRANSLATED", path_translated);
                                                                efree(path_translated);
                                                        } else if (     env_script_name &&
                                                                                strstr(pt, env_script_name)
@@ -1237,9 +1243,9 @@ static void init_request_info(void)
                                                                }
                                                                path_translated[path_translated_len] = '\0';
                                                                if (orig_path_translated) {
-                                                                       _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated);
+                                                                       FCGI_PUTENV(request, "ORIG_PATH_TRANSLATED", orig_path_translated);
                                                                }
-                                                               env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated);
+                                                               env_path_translated = FCGI_PUTENV(request, "PATH_TRANSLATED", path_translated);
                                                                efree(path_translated);
                                                        }
                                                        break;
@@ -1255,18 +1261,18 @@ static void init_request_info(void)
                                         * have failed anyway... we output 'no input file' now.
                                         */
                                        if (orig_script_filename) {
-                                               _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename);
+                                               FCGI_PUTENV(request, "ORIG_SCRIPT_FILENAME", orig_script_filename);
                                        }
-                                       script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", NULL);
+                                       script_path_translated = FCGI_PUTENV(request, "SCRIPT_FILENAME", NULL);
                                        SG(sapi_headers).http_response_code = 404;
                                }
                                if (!SG(request_info).request_uri) {
                                        if (!orig_script_name ||
                                                strcmp(orig_script_name, env_script_name) != 0) {
                                                if (orig_script_name) {
-                                                       _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name);
+                                                       FCGI_PUTENV(request, "ORIG_SCRIPT_NAME", orig_script_name);
                                                }
-                                               SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name);
+                                               SG(request_info).request_uri = FCGI_PUTENV(request, "SCRIPT_NAME", env_script_name);
                                        } else {
                                                SG(request_info).request_uri = orig_script_name;
                                        }
@@ -1280,27 +1286,27 @@ static void init_request_info(void)
                                        (script_path_translated != orig_script_filename &&
                                        strcmp(script_path_translated, orig_script_filename) != 0)) {
                                        if (orig_script_filename) {
-                                               _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename);
+                                               FCGI_PUTENV(request, "ORIG_SCRIPT_FILENAME", orig_script_filename);
                                        }
-                                       script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", script_path_translated);
+                                       script_path_translated = FCGI_PUTENV(request, "SCRIPT_FILENAME", script_path_translated);
                                }
                                if (!apache_was_here && env_redirect_url) {
                                        /* if we used PATH_TRANSLATED to work around Apache mod_fastcgi (but not mod_proxy_fcgi,
                                         * hence !apache_was_here) weirdness, strip info accordingly */
                                        if (orig_path_info) {
-                                               _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info);
-                                               _sapi_cgibin_putenv("PATH_INFO", NULL);
+                                               FCGI_PUTENV(request, "ORIG_PATH_INFO", orig_path_info);
+                                               FCGI_PUTENV(request, "PATH_INFO", NULL);
                                        }
                                        if (orig_path_translated) {
-                                               _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated);
-                                               _sapi_cgibin_putenv("PATH_TRANSLATED", NULL);
+                                               FCGI_PUTENV(request, "ORIG_PATH_TRANSLATED", orig_path_translated);
+                                               FCGI_PUTENV(request, "PATH_TRANSLATED", NULL);
                                        }
                                }
                                if (env_script_name != orig_script_name) {
                                        if (orig_script_name) {
-                                               _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name);
+                                               FCGI_PUTENV(request, "ORIG_SCRIPT_NAME", orig_script_name);
                                        }
-                                       SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name);
+                                       SG(request_info).request_uri = FCGI_PUTENV(request, "SCRIPT_NAME", env_script_name);
                                } else {
                                        SG(request_info).request_uri = env_script_name;
                                }
@@ -1322,19 +1328,19 @@ static void init_request_info(void)
                        SG(request_info).path_translated = estrdup(script_path_translated);
                }
 
-               SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD", sizeof("REQUEST_METHOD") - 1);
+               SG(request_info).request_method = FCGI_GETENV(request, "REQUEST_METHOD");
                /* FIXME - Work out proto_num here */
-               SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING", sizeof("QUERY_STRING") - 1);
+               SG(request_info).query_string = FCGI_GETENV(request, "QUERY_STRING");
                SG(request_info).content_type = (content_type ? content_type : "" );
                SG(request_info).content_length = (content_length ? atol(content_length) : 0);
 
                /* The CGI RFC allows servers to pass on unvalidated Authorization data */
-               auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION", sizeof("HTTP_AUTHORIZATION") - 1);
+               auth = FCGI_GETENV(request, "HTTP_AUTHORIZATION");
                php_handle_auth_data(auth);
        }
 
        /* INI stuff */
-       ini = sapi_cgibin_getenv("PHP_VALUE", sizeof("PHP_VALUE") - 1);
+       ini = FCGI_GETENV(request, "PHP_VALUE");
        if (ini) {
                int mode = ZEND_INI_USER;
                char *tmp;
@@ -1343,7 +1349,7 @@ static void init_request_info(void)
                efree(tmp);
        }
 
-       ini = sapi_cgibin_getenv("PHP_ADMIN_VALUE", sizeof("PHP_ADMIN_VALUE") - 1);
+       ini = FCGI_GETENV(request, "PHP_ADMIN_VALUE");
        if (ini) {
                int mode = ZEND_INI_SYSTEM;
                char *tmp;
@@ -1463,7 +1469,7 @@ PHP_FUNCTION(fastcgi_finish_request) /* {{{ */
 {
        fcgi_request *request = (fcgi_request*) SG(server_context);
 
-       if (request->fd >= 0) {
+       if (!fcgi_is_closed(request)) {
 
                php_output_end_all();
                php_header();
@@ -1517,7 +1523,7 @@ int main(int argc, char *argv[])
        int max_requests = 500;
        int requests = 0;
        int fcgi_fd = 0;
-       fcgi_request request;
+       fcgi_request *request;
        char *fpm_config = NULL;
        char *fpm_prefix = NULL;
        char *fpm_pid = NULL;
@@ -1813,12 +1819,12 @@ consult the installation file that came with this distribution, or visit \n\
        php_import_environment_variables = cgi_php_import_environment_variables;
 
        /* library is already initialized, now init our request */
-       fcgi_init_request(&request, fcgi_fd);
+       request = fcgi_init_request(fcgi_fd);
 
        zend_first_try {
-               while (fcgi_accept_request(&request) >= 0) {
+               while (fcgi_accept_request(request) >= 0) {
                        request_body_fd = -1;
-                       SG(server_context) = (void *) &request;
+                       SG(server_context) = (void *) request;
                        init_request_info();
                        char *primary_script = NULL;
 
@@ -1827,7 +1833,7 @@ consult the installation file that came with this distribution, or visit \n\
                        /* request startup only after we've done all we can to
                         *            get path_translated */
                        if (php_request_startup() == FAILURE) {
-                               fcgi_finish_request(&request, 1);
+                               fcgi_finish_request(request, 1);
                                SG(server_context) = NULL;
                                php_module_shutdown();
                                return FPM_EXIT_SOFTWARE;
@@ -1920,11 +1926,12 @@ fastcgi_request_done:
 
                        requests++;
                        if (max_requests && (requests == max_requests)) {
-                               fcgi_finish_request(&request, 1);
+                               fcgi_finish_request(request, 1);
                                break;
                        }
                        /* end of fastcgi loop */
                }
+               fcgi_destroy_request(request);
                fcgi_shutdown();
 
                if (cgi_sapi_module.php_ini_path_override) {
index 7e703889b22e210d42aea78b811be51541e59c4e..9ddc45e5b935d32cbaf37162f0055ecb8d4af90a 100644 (file)
@@ -201,7 +201,7 @@ static void fpm_php_cleanup(int which, void *arg) /* {{{ */
 
 void fpm_php_soft_quit() /* {{{ */
 {
-       fcgi_set_in_shutdown(1);
+       fcgi_terminate();
 }
 /* }}} */