changeset 200:abeaebe0a33c

nginx-0.0.1-2003-11-28-20:41:47 import
author Igor Sysoev <igor@sysoev.ru>
date Fri, 28 Nov 2003 17:41:47 +0000
parents a65b630b3a66
children 267ea1d98683
files src/http/modules/ngx_http_index_handler.c src/http/modules/ngx_http_static_handler.c src/http/ngx_http.h src/http/ngx_http_cache.c src/http/ngx_http_cache.h src/http/ngx_http_parse.c src/http/ngx_http_request.c src/http/ngx_http_request.h
diffstat 8 files changed, 371 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/modules/ngx_http_index_handler.c
+++ b/src/http/modules/ngx_http_index_handler.c
@@ -117,12 +117,12 @@ ngx_log_debug(r->connection->log, "index
 
             redirect.len = cache->data.value.len;
             if (!(redirect.data = ngx_palloc(r->pool, redirect.len + 1))) {
-                ngx_http_cache_unlock(ilcf->cache, cache);
+                ngx_http_cache_unlock(ilcf->cache, cache, r->connection->log);
                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
             }
 
             ngx_memcpy(redirect.data, cache->data.value.data, redirect.len + 1);
-            ngx_http_cache_unlock(ilcf->cache, cache);
+            ngx_http_cache_unlock(ilcf->cache, cache, r->connection->log);
 
             return ngx_http_internal_redirect(r, &redirect, NULL);
         }
@@ -230,7 +230,8 @@ ngx_log_error(NGX_LOG_DEBUG, r->connecti
                 {
                     cache->accessed = ngx_cached_time;
                     cache->updated = ngx_cached_time;
-                    ngx_http_cache_unlock(ilcf->cache, cache);
+                    ngx_http_cache_unlock(ilcf->cache, cache,
+                                          r->connection->log);
 
                     return ngx_http_internal_redirect(r, &redirect, NULL);
 
@@ -270,7 +271,7 @@ ngx_log_debug(r->connection->log, "index
         }
 
         if (cache) {
-            ngx_http_cache_unlock(ilcf->cache, cache);
+            ngx_http_cache_unlock(ilcf->cache, cache, r->connection->log);
         }
 
         return ngx_http_internal_redirect(r, &redirect, NULL);
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -41,9 +41,10 @@ ngx_module_t  ngx_http_static_module = {
 
 ngx_int_t ngx_http_static_translate_handler(ngx_http_request_t *r)
 {
-    ngx_int_t                  rc, level;
+    char                      *location, *last, *path;
     uint32_t                   crc;
-    char                      *location, *last;
+    ngx_int_t                  rc, level;
+    ngx_str_t                  name;
     ngx_err_t                  err;
     ngx_http_cache_t          *cache;
     ngx_http_cache_conf_t     *ccf;
@@ -55,62 +56,67 @@ ngx_int_t ngx_http_static_translate_hand
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
+
     if (r->uri.data[r->uri.len - 1] == '/') {
-        if (r->path.data == NULL) {
-            ngx_test_null(r->path.data,
-                          ngx_palloc(r->pool,
-                                     clcf->doc_root.len + r->uri.len),
-                          NGX_HTTP_INTERNAL_SERVER_ERROR);
 
-            ngx_cpystrn(ngx_cpymem(r->path.data, clcf->doc_root.data,
-                                   clcf->doc_root.len),
-                        r->uri.data, r->uri.len + 1);
+        /* there is no index handler */
 
-        } else {
-            r->path.data[r->path.len] = '\0';
+        if (!(path = ngx_palloc(r->pool, clcf->doc_root.len + r->uri.len))) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
+        ngx_cpystrn(ngx_cpymem(path, clcf->doc_root.data, clcf->doc_root.len),
+                    r->uri.data, r->uri.len + 1);
+
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "directory index of \"%s\" is forbidden", r->path.data);
+                      "directory index of \"%s\" is forbidden", path);
 
         return NGX_HTTP_FORBIDDEN;
     }
 
-    /* "+ 2" is for trailing '/' in possible redirect and '\0' */
-    ngx_test_null(r->file.name.data,
+
+    /* "+ 2" is for a trailing '/' in a possible redirect and '\0' */
+    ngx_test_null(name.data,
                   ngx_palloc(r->pool, clcf->doc_root.len + r->uri.len + 2),
                   NGX_HTTP_INTERNAL_SERVER_ERROR);
 
-    location = ngx_cpymem(r->file.name.data, clcf->doc_root.data,
-                          clcf->doc_root.len),
-
+    location = ngx_cpymem(name.data, clcf->doc_root.data, clcf->doc_root.len);
     last = ngx_cpystrn(location, r->uri.data, r->uri.len + 1);
 
-ngx_log_debug(r->connection->log, "HTTP filename: '%s'" _ r->file.name.data);
+    ngx_log_debug(r->connection->log, "HTTP filename: '%s'" _ data);
+
 
-    ccf = ngx_http_get_module_loc_conf(r, ngx_http_cache_module);
+    if (r->cache == NULL) {
+
+        /* look up an open files cache */
 
-    if (ccf->open_files) {
-        cache = ngx_http_cache_get(ccf->open_files, &r->file.name, &crc);
+        ccf = ngx_http_get_module_loc_conf(r, ngx_http_cache_module);
 
-ngx_log_debug(r->connection->log, "cache get: %x" _ cache);
+        if (ccf->open_files) {
+            cache = ngx_http_cache_get(ccf->open_files, &name, &crc);
+
+            ngx_log_debug(r->connection->log, "cache get: %x" _ cache);
 
-        if (cache
-            && ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT)
-                || ccf->open_files->update >= ngx_cached_time - cache->updated))
-        {
-            cache->refs++;
-            r->file.fd = cache->fd;
-            r->file.name = cache->key;
-            r->content_handler = ngx_http_static_handler;
+            if (cache
+                && ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT)
+                    || ccf->open_files->update
+                                          >= ngx_cached_time - cache->updated))
+            {
+                r->cache = cache;
+                r->content_handler = ngx_http_static_handler;
 
-            return NGX_OK;
+                return NGX_OK;
+            }
+
+        } else {
+            cache = NULL;
         }
 
     } else {
-        cache = NULL;
+        cache = r->cache;
     }
 
+
 #if (WIN9X)
 
     if (ngx_win32_version < NGX_WIN_NT) {
@@ -121,10 +127,10 @@ ngx_log_debug(r->connection->log, "cache
          * so we need to check its type before the opening
          */
 
-        if (ngx_file_info(r->file.name.data, &r->file.info) == NGX_FILE_ERROR) {
+        if (ngx_file_info(name.data, &r->file.info) == NGX_FILE_ERROR) {
             err = ngx_errno;
             ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
-                          ngx_file_info_n " \"%s\" failed", r->file.name.data);
+                          ngx_file_info_n " \"%s\" failed", name.data);
 
             if (err == NGX_ENOENT || err == NGX_ENOTDIR) {
                 return NGX_HTTP_NOT_FOUND;
@@ -138,7 +144,7 @@ ngx_log_debug(r->connection->log, "cache
         }
 
         if (ngx_is_dir(&r->file.info)) {
-ngx_log_debug(r->connection->log, "HTTP DIR: '%s'" _ r->file.name.data);
+            ngx_log_debug(r->connection->log, "HTTP DIR: '%s'" _ name.data);
 
             if (!(r->headers_out.location =
                    ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
@@ -159,6 +165,7 @@ ngx_log_debug(r->connection->log, "HTTP 
 
 #endif
 
+
     if (r->file.fd == NGX_INVALID_FILE) {
         r->file.fd = ngx_open_file(r->file.name.data,
                                    NGX_FILE_RDONLY, NGX_FILE_OPEN);
@@ -282,6 +289,7 @@ static int ngx_http_static_handler(ngx_h
     ngx_hunk_t                *h;
     ngx_chain_t                out;
     ngx_http_type_t           *type;
+    ngx_http_cleanup_t        *cleanup;
     ngx_http_log_ctx_t        *ctx;
     ngx_http_core_loc_conf_t  *clcf;
 
@@ -294,6 +302,10 @@ static int ngx_http_static_handler(ngx_h
     ctx = r->connection->log->data;
     ctx->action = "sending response to client";
 
+    if (!(cleanup = ngx_push_array(&r->cleanup))) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     if (r->file.fd == NGX_INVALID_FILE) {
         r->file.fd = ngx_open_file(r->file.name.data,
                                    NGX_FILE_RDONLY, NGX_FILE_OPEN);
@@ -316,6 +328,10 @@ static int ngx_http_static_handler(ngx_h
         }
     }
 
+    cleanup->data.file.fd = r->file.fd;
+    cleanup->data.file.name = r->file.name.data;
+    cleanup->cache = 0;
+
     if (!r->file.info_valid) {
         if (ngx_fd_info(r->file.fd, &r->file.info) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
@@ -379,7 +395,6 @@ static int ngx_http_static_handler(ngx_h
     ngx_test_null(h->file, ngx_pcalloc(r->pool, sizeof(ngx_file_t)),
                   NGX_HTTP_INTERNAL_SERVER_ERROR);
 
-
     rc = ngx_http_send_header(r);
 
     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -48,8 +48,11 @@ int ngx_http_init(ngx_pool_t *pool, ngx_
 /**/
 
 void ngx_http_init_connection(ngx_connection_t *c);
+
 int ngx_http_parse_request_line(ngx_http_request_t *r);
+int ngx_http_parse_complex_uri(ngx_http_request_t *r);
 int ngx_http_parse_header_line(ngx_http_request_t *r, ngx_hunk_t *h);
+
 int ngx_http_find_server_conf(ngx_http_request_t *r);
 void ngx_http_handler(ngx_http_request_t *r);
 void ngx_http_finalize_request(ngx_http_request_t *r, int error);
--- a/src/http/ngx_http_cache.c
+++ b/src/http/ngx_http_cache.c
@@ -176,6 +176,29 @@ ngx_http_cache_t *ngx_http_cache_alloc(n
 }
 
 
+void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash,
+                           ngx_http_cache_t *cache, ngx_log_t *log)
+{
+    ngx_mutex_lock(&hash->mutex);
+
+    cache->refs--;
+
+    if (cache->refs == 0 && cache->deleted) {
+ngx_log_debug(log, "CLOSE FILE: %d" _ cache->fd);
+        if (cache->fd != NGX_INVALID_FILE) {
+            if (ngx_close_file(cache->fd) == NGX_FILE_ERROR) {
+                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                              ngx_close_file_n " \"%s\" failed",
+                              cache->key.data);
+            }
+        }
+        cache->key.data = NULL;
+    }
+
+    ngx_mutex_unlock(&hash->mutex);
+}
+
+
 int ngx_http_cache_open_file(ngx_http_cache_ctx_t *ctx, ngx_file_uniq_t uniq)
 {
     ssize_t                   n;
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -74,12 +74,6 @@ typedef struct {
 } ngx_http_cache_conf_t;
 
 
-#define ngx_http_cache_unlock(ch, ce)                                        \
-            ngx_mutex_lock(&ch->mutex);                                      \
-            ce->refs--;                                                      \
-            ngx_mutex_unlock(&ch->mutex);
-
-
 
 #define NGX_HTTP_CACHE_STALE     1
 #define NGX_HTTP_CACHE_AGED      2
@@ -96,6 +90,9 @@ ngx_http_cache_t *ngx_http_cache_get(ngx
 ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *cache,
                                        ngx_str_t *key, uint32_t crc,
                                        ngx_log_t *log);
+void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash,
+                           ngx_http_cache_t *cache, ngx_log_t *log);
+
 
 int ngx_garbage_collector_http_cache_handler(ngx_gc_t *gc, ngx_str_t *name,
                                              ngx_dir_t *dir);
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -5,8 +5,7 @@
 
 int ngx_http_parse_request_line(ngx_http_request_t *r)
 {
-    char   ch;
-    char  *p;
+    char   ch, *p;
     enum {
         sw_start = 0,
         sw_G,
@@ -198,9 +197,7 @@ int ngx_http_parse_request_line(ngx_http
                 state = sw_uri;
                 break;
             case '/':
-#if (WIN32)
                 r->complex_uri = 1;
-#endif
                 break;
             case '?':
                 r->args_start = p;
@@ -421,11 +418,11 @@ int ngx_http_parse_request_line(ngx_http
     }
 }
 
+
 int ngx_http_parse_header_line(ngx_http_request_t *r, ngx_hunk_t *h)
 {
-    char   c, ch;
-    char  *p;
-    enum  {
+    char   c, ch, *p;
+    enum {
         sw_start = 0,
         sw_name,
         sw_space_before_value,
@@ -622,3 +619,197 @@ int ngx_http_parse_header_line(ngx_http_
         return NGX_AGAIN;
     }
 }
+
+
+int ngx_http_parse_complex_uri(ngx_http_request_t *r)
+{
+    char  c, ch, decoded, *p, *u;
+    enum {
+        sw_usual = 0,
+        sw_slash,
+        sw_dot,
+        sw_dot_dot,
+#if (WIN32)
+        sw_dot_dot_dot,
+#endif
+        sw_quoted,
+        sw_quoted_second
+    } state, quoted_state;
+
+    decoded = '\0';
+    quoted_state = sw_usual;
+
+    state = sw_usual;
+    p = r->uri_start;
+    u = r->uri.data;
+
+    ch = *p++;
+
+    while (p < r->uri_start + r->uri.len + 1) {
+
+ngx_log_debug(r->connection->log, "S: %d UN: '%x:%c', URI: '%c'" _
+              state _ ch _ ch _ *u);
+
+        switch (state) {
+        case sw_usual:
+            switch(ch) {
+            case '/':
+                state = sw_slash;
+                *u++ = ch;
+                break;
+            case '%':
+                quoted_state = state;
+                state = sw_quoted;
+                break;
+            default:
+                *u++ = ch;
+                break;
+            }
+            ch = *p++;
+            break;
+
+        case sw_slash:
+            switch(ch) {
+            case '/':
+                break;
+            case '.':
+                state = sw_dot;
+                *u++ = ch;
+                break;
+            case '%':
+                quoted_state = state;
+                state = sw_quoted;
+                break;
+            default:
+                state = sw_usual;
+                *u++ = ch;
+                break;
+            }
+            ch = *p++;
+            break;
+
+        case sw_dot:
+            switch(ch) {
+            case '/':
+                state = sw_slash;
+                u--;
+                break;
+            case '.':
+                state = sw_dot_dot;
+                *u++ = ch;
+                break;
+            case '%':
+                quoted_state = state;
+                state = sw_quoted;
+                break;
+            default:
+                state = sw_usual;
+                *u++ = ch;
+                break;
+            }
+            ch = *p++;
+            break;
+
+        case sw_dot_dot:
+            switch(ch) {
+            case '/':
+                state = sw_slash;
+                u -= 4;
+                if (u < r->uri.data) {
+                    return NGX_HTTP_PARSE_INVALID_REQUEST;
+                }
+                while (*(u - 1) != '/') {
+                    u--;
+                }
+                break;
+            case '%':
+                quoted_state = state;
+                state = sw_quoted;
+                break;
+#if (WIN32)
+            case '.':
+                state = sw_dot_dot_dot;
+                *u++ = ch;
+                break;
+#endif
+            default:
+                state = sw_usual;
+                *u++ = ch;
+                break;
+            }
+            ch = *p++;
+            break;
+
+#if (WIN32)
+        case sw_dot_dot_dot:
+            switch(ch) {
+            case '/':
+                state = sw_slash;
+                u -= 5;
+                if (u < r->uri.data) {
+                    return NGX_HTTP_PARSE_INVALID_REQUEST;
+                }
+                while (*u != '/') {
+                    u--;
+                }
+                if (u < r->uri.data) {
+                    return NGX_HTTP_PARSE_INVALID_REQUEST;
+                }
+                while (*(u - 1) != '/') {
+                    u--;
+                }
+                break;
+            case '%':
+                quoted_state = state;
+                state = sw_quoted;
+                break;
+            default:
+                state = sw_usual;
+                *u++ = ch;
+                break;
+            }
+            ch = *p++;
+            break;
+#endif
+
+        case sw_quoted:
+            if (ch >= '0' && ch <= '9') {
+                decoded = ch - '0';
+                state = sw_quoted_second;
+                ch = *p++;
+                break;
+            }
+
+            c = ch | 0x20;
+            if (c >= 'a' && c <= 'f') {
+                decoded = c - 'a' + 10;
+                state = sw_quoted_second;
+                ch = *p++;
+                break;
+            }
+
+            return NGX_HTTP_PARSE_INVALID_REQUEST;
+
+        case sw_quoted_second:
+            if (ch >= '0' && ch <= '9') {
+                ch = (decoded << 4) + ch - '0';
+                state = quoted_state;
+                break;
+            }
+
+            c = ch | 0x20;
+            if (c >= 'a' && c <= 'f') {
+                ch = (decoded << 4) + c - 'a' + 10;
+                state = quoted_state;
+                break;
+            }
+
+            return NGX_HTTP_PARSE_INVALID_REQUEST;
+        }
+    }
+
+    r->uri.len = u - r->uri.data;
+    r->uri.data[r->uri.len] = '\0';
+
+    return NGX_OK;
+}
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -225,6 +225,21 @@ ngx_log_debug(rev->log, "IN: %08x" _ in_
         return;
     }
 
+    r->cleanup.elts = ngx_palloc(r->pool, 5 * sizeof(ngx_http_cleanup_t));
+    if (r->cleanup.elts == NULL) {
+        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        ngx_http_close_connection(c);
+        return;
+    }
+    /*
+     * set by ngx_pcalloc():
+     *
+     * r->cleanup.nelts = 0;
+     */
+    r->cleanup.nalloc = 5;
+    r->cleanup.size = sizeof(ngx_http_cleanup_t);
+    r->cleanup.pool = r->pool;
+
     /* TODO: ngx_init_table */
     if (!(r->headers_out.headers = ngx_create_table(r->pool, 20))) {
         ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -289,7 +304,7 @@ static void ngx_http_process_request_lin
         /* the request line has been parsed successfully */
 
         /* TODO: we need to handle such URIs */
-        if (r->complex_uri || r->unusual_uri) {
+        if (r->unusual_uri) {
             r->request_line.len = r->request_end - r->request_start;
             r->request_line.data = r->request_start;
             r->request_line.data[r->request_line.len] = '\0';
@@ -313,6 +328,20 @@ static void ngx_http_process_request_lin
         }
 
 
+        /* copy unparsed URI */
+
+        r->unparsed_uri.len = r->uri_end - r->uri_start;
+        r->unparsed_uri.data = ngx_palloc(r->pool, r->unparsed_uri.len + 1);
+        if (r->unparsed_uri.data == NULL) {
+            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            ngx_http_close_connection(c);
+            return;
+        }
+
+        ngx_cpystrn(r->unparsed_uri.data, r->uri_start,
+                    r->unparsed_uri.len + 1);
+
+
         /* copy URI */
 
         if (r->args_start) {
@@ -327,22 +356,13 @@ static void ngx_http_process_request_lin
             return;
         }
 
-        ngx_cpystrn(r->uri.data, r->uri_start, r->uri.len + 1);
-
-
-        /* copy unparsed URI */
+        if (r->complex_uri) {
+            rc = ngx_http_parse_complex_uri(r);
 
-        r->unparsed_uri.len = r->uri_end - r->uri_start;
-        r->unparsed_uri.data = ngx_palloc(r->pool, r->unparsed_uri.len + 1);
-        if (r->unparsed_uri.data == NULL) {
-            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-            ngx_http_close_connection(c);
-            return;
+        } else {
+            ngx_cpystrn(r->uri.data, r->uri_start, r->uri.len + 1);
         }
 
-        ngx_cpystrn(r->unparsed_uri.data, r->uri_start,
-                    r->unparsed_uri.len + 1);
-
 
         r->request_line.len = r->request_end - r->request_start;
 
@@ -369,6 +389,16 @@ static void ngx_http_process_request_lin
         }
 
 
+        if (rc != NGX_OK) {
+            /*
+             * we check ngx_http_parse_complex_uri() result here to log
+             * the request line
+             */
+            ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST);
+            return;
+        }
+
+
         if (r->uri_ext) {
 
             /* copy URI extention */
@@ -1356,7 +1386,9 @@ int ngx_http_send_last(ngx_http_request_
 
 void ngx_http_close_request(ngx_http_request_t *r, int error)
 {
+    ngx_int_t            i;
     ngx_http_log_ctx_t  *ctx;
+    ngx_http_cleanup_t  *cleanup;
 
     ngx_log_debug(r->connection->log, "close http request");
 
@@ -1372,6 +1404,22 @@ void ngx_http_close_request(ngx_http_req
 
     ngx_http_log_handler(r);
 
+    cleanup = r->cleanup.elts;
+    for (i = 0; i < r->cleanup.nelts; i++) {
+        if (cleanup[i].cache) {
+            ngx_http_cache_unlock(cleanup[i].data.cache.hash,
+                                  cleanup[i].data.cache.cache,
+                                  r->connection->log);
+            continue;
+        }
+
+        if (ngx_close_file(cleanup[i].data.file.fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed",
+                          cleanup[i].data.file.name);
+        }
+    }
+
     if (r->file.fd != NGX_INVALID_FILE) {
         if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR) {
             ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -144,17 +144,34 @@ typedef struct {
 } ngx_http_headers_out_t;
 
 
+typedef struct {
+    union {
+        struct {
+            ngx_fd_t                 fd;
+            char                    *name;
+        } file;
+
+        struct {
+            ngx_http_cache_hash_t   *hash;
+            ngx_http_cache_t        *cache;
+        } cache;
+    } data;
+
+    unsigned                         cache:1;
+} ngx_http_cleanup_t;
+
+
 typedef int (*ngx_http_handler_pt)(ngx_http_request_t *r);
 
 struct ngx_http_request_s {
-    ngx_connection_t    *connection;
+    ngx_connection_t         *connection;
 
-    void               **ctx;
-    void               **main_conf;
-    void               **srv_conf;
-    void               **loc_conf;
+    void                    **ctx;
+    void                    **main_conf;
+    void                    **srv_conf;
+    void                    **loc_conf;
 
-    ngx_http_cache_t    *cache;
+    ngx_http_cache_t         *cache;
 
     ngx_file_t           file;
 
@@ -199,6 +216,8 @@ struct ngx_http_request_s {
     void               (*request_body_handler) (void *data); 
     void                *data;
 
+    ngx_array_t          cleanup;
+
     char                *discarded_buffer;
     void               **err_ctx;
     int                  err_status;