/*write the in chain to a temp file*/
static ngx_int_t
ngx_http_waf_write_to_file(ngx_temp_file_t *temp_file,ngx_chain_t *in)
{
    ngx_int_t n;
    ngx_chain_t *cl;
    ngx_int_t complete = 0;

    n = ngx_write_chain_to_temp_file(temp_file, in);

    /* TODO: n == 0 or not complete and level event */

    if (n == NGX_ERROR) {
        return NGX_ERROR;
    }

    temp_file->offset += n;

    for(cl=in;cl;cl=cl->next)
    {
        if(cl->buf)
	{
            if(cl->buf->last_buf)
             complete = 1;
            //cl->buf->pos = cl->buf->start;
            //cl->buf->last = cl->buf->start;
            cl->buf->pos = cl->buf->last;
        }
    }


    if(complete)
        return NGX_OK;
    else
        return NGX_AGAIN;
}
static ngx_int_t
ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body)
{
		syslog(LOG_INFO, "[%s:%s:%d]", __FILE__, __func__, __LINE__);
    ssize_t                    n;
    ngx_temp_file_t           *tf;
    ngx_http_request_body_t   *rb;
    ngx_http_core_loc_conf_t  *clcf;

    rb = r->request_body;

    if (rb->temp_file == NULL) {
        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
        if (tf == NULL) {
            return NGX_ERROR;
        }

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        tf->file.fd = NGX_INVALID_FILE;
        tf->file.log = r->connection->log;
        tf->path = clcf->client_body_temp_path;
        tf->pool = r->pool;
        tf->warn = "a client request body is buffered to a temporary file";
        tf->log_level = r->request_body_file_log_level;
        tf->persistent = r->request_body_in_persistent_file;
        tf->clean = r->request_body_in_clean_file;

        if (r->request_body_file_group_access) {
            tf->access = 0660;
        }

        rb->temp_file = tf;

        if (body == NULL) {
            /* empty body with r->request_body_in_file_only */

            if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                     tf->persistent, tf->clean, tf->access)
                != NGX_OK)
            {
                return NGX_ERROR;
            }

            return NGX_OK;
        }
    }

    n = ngx_write_chain_to_temp_file(rb->temp_file, body);

    /* TODO: n == 0 or not complete and level event */

    if (n == NGX_ERROR) {
        return NGX_ERROR;
    }

    rb->temp_file->offset += n;

    return NGX_OK;
}
static ngx_int_t
ngx_http_lua_write_request_body(ngx_http_request_t *r, ngx_chain_t *body)
{
    ssize_t                    n;
    ngx_temp_file_t           *tf;
    ngx_http_request_body_t   *rb;
    ngx_http_core_loc_conf_t  *clcf;

    rb = r->request_body;

    if (rb->temp_file == NULL) {
        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
        if (tf == NULL) {
            return NGX_ERROR;
        }

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        tf->file.fd = NGX_INVALID_FILE;
        tf->file.log = r->connection->log;
        tf->path = clcf->client_body_temp_path;
        tf->pool = r->pool;
        tf->warn = "a client request body is buffered to a temporary file";
        tf->log_level = r->request_body_file_log_level;
        tf->persistent = 1;
        tf->clean = 1;

        if (r->request_body_file_group_access) {
            tf->access = 0660;
        }

        rb->temp_file = tf;
    }

    n = ngx_write_chain_to_temp_file(rb->temp_file, body);

    /* TODO: n == 0 or not complete and level event */

    if (n == NGX_ERROR) {
        return NGX_ERROR;
    }

    rb->temp_file->offset += n;

    return NGX_OK;
}
static ngx_int_t
ngx_http_write_request_body(ngx_http_request_t *r)
{
    ssize_t                    n;
    ngx_chain_t               *cl, *ln;
    ngx_temp_file_t           *tf;
    ngx_http_request_body_t   *rb;
    ngx_http_core_loc_conf_t  *clcf;

    rb = r->request_body;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http write client request body, bufs %p", rb->bufs);

    if (rb->temp_file == NULL) {
        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
        if (tf == NULL) {
            return NGX_ERROR;
        }

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        tf->file.fd = NGX_INVALID_FILE;
        tf->file.log = r->connection->log;
        tf->path = clcf->client_body_temp_path;
        tf->pool = r->pool;
        tf->warn = "a client request body is buffered to a temporary file";
        tf->log_level = r->request_body_file_log_level;
        tf->persistent = r->request_body_in_persistent_file;
        tf->clean = r->request_body_in_clean_file;

        if (r->request_body_file_group_access) {
            tf->access = 0660;
        }

        rb->temp_file = tf;

        if (rb->bufs == NULL) {
            /* empty body with r->request_body_in_file_only */

            if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                     tf->persistent, tf->clean, tf->access)
                    != NGX_OK)
            {
                return NGX_ERROR;
            }

            return NGX_OK;
        }
    }

    if (rb->bufs == NULL) {
        return NGX_OK;
    }

    n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);

    /* TODO: n == 0 or not complete and level event */

    if (n == NGX_ERROR) {
        return NGX_ERROR;
    }

    rb->temp_file->offset += n;

    /* mark all buffers as written */

    for (cl = rb->bufs; cl; /* void */) {

        cl->buf->pos = cl->buf->last;

        ln = cl;
        cl = cl->next;
        ngx_free_chain(r->pool, ln);
    }

    rb->bufs = NULL;

    return NGX_OK;
}
static ngx_int_t
ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)
{
    ssize_t       size, bsize;
    ngx_buf_t    *b;
    ngx_chain_t  *cl, *tl, *next, *out, **ll, **last_free, fl;

    if (p->buf_to_file) {
        fl.buf = p->buf_to_file;
        fl.next = p->in;
        out = &fl;

    } else {
        out = p->in;
    }

    if (!p->cacheable) {

        size = 0;
        cl = out;
        ll = NULL;

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
                       "pipe offset: %O", p->temp_file->offset);

        do {
            bsize = cl->buf->last - cl->buf->pos;

            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0,
                           "pipe buf %p, pos %p, size: %z",
                           cl->buf->start, cl->buf->pos, bsize);

            if ((size + bsize > p->temp_file_write_size)
               || (p->temp_file->offset + size + bsize > p->max_temp_file_size))
            {
                break;
            }

            size += bsize;
            ll = &cl->next;
            cl = cl->next;

        } while (cl);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "size: %z", size);

        if (ll == NULL) {
            return NGX_BUSY;
        }

        if (cl) {
           p->in = cl;
           *ll = NULL;

        } else {
           p->in = NULL;
           p->last_in = &p->in;
        }

    } else {
        p->in = NULL;
        p->last_in = &p->in;
    }

    if (ngx_write_chain_to_temp_file(p->temp_file, out) == NGX_ERROR) {
        return NGX_ABORT;
    }

    for (last_free = &p->free_raw_bufs;
         *last_free != NULL;
         last_free = &(*last_free)->next)
    {
        /* void */
    }

    if (p->buf_to_file) {
        p->temp_file->offset = p->buf_to_file->last - p->buf_to_file->pos;
        p->buf_to_file = NULL;
        out = out->next;
    }

    for (cl = out; cl; cl = next) {
        next = cl->next;
        cl->next = NULL;

        b = cl->buf;
        b->file = &p->temp_file->file;
        b->file_pos = p->temp_file->offset;
        p->temp_file->offset += b->last - b->pos;
        b->file_last = p->temp_file->offset;

        b->in_file = 1;
        b->temp_file = 1;

        if (p->out) {
            *p->last_out = cl;
        } else {
            p->out = cl;
        }
        p->last_out = &cl->next;

        if (b->last_shadow) {

            tl = ngx_alloc_chain_link(p->pool);
            if (tl == NULL) {
                return NGX_ABORT;
            }

            tl->buf = b->shadow;
            tl->next = NULL;

            *last_free = tl;
            last_free = &tl->next;

            b->shadow->pos = b->shadow->start;
            b->shadow->last = b->shadow->start;

            ngx_event_pipe_remove_shadow_links(b->shadow);
        }
    }

    return NGX_OK;
}
static ngx_int_t
ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
{
    u_char            *prev;
    size_t             bsize;
    ngx_int_t          rc;
    ngx_uint_t         flush, flushed, prev_last_shadow;
    ngx_chain_t       *out, **ll, *cl, file;
    ngx_connection_t  *downstream;

    downstream = p->downstream;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
                   "pipe write downstream: %d", downstream->write->ready);

    flushed = 0;

    for ( ;; ) {
        if (p->downstream_error) {
            return ngx_event_pipe_drain_chains(p);
        }

        if (p->upstream_eof || p->upstream_error || p->upstream_done) {

            /* pass the p->out and p->in chains to the output filter */

            for (cl = p->busy; cl; cl = cl->next) {
                cl->buf->recycled = 0;
            }

            if (p->out) {
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
                               "pipe write downstream flush out");

                for (cl = p->out; cl; cl = cl->next) {
                    cl->buf->recycled = 0;
                }

                rc = p->output_filter(p->output_ctx, p->out);

                if (rc == NGX_ERROR) {
                    p->downstream_error = 1;
                    return ngx_event_pipe_drain_chains(p);
                }

                p->out = NULL;
            }

            if (p->in) {
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
                               "pipe write downstream flush in");

                for (cl = p->in; cl; cl = cl->next) {
                    cl->buf->recycled = 0;
                }

                rc = p->output_filter(p->output_ctx, p->in);

                if (rc == NGX_ERROR) {
                    p->downstream_error = 1;
                    return ngx_event_pipe_drain_chains(p);
                }

                p->in = NULL;
            }

            if (p->cacheable && p->buf_to_file) {

                file.buf = p->buf_to_file;
                file.next = NULL;

                if (ngx_write_chain_to_temp_file(p->temp_file, &file)
                    == NGX_ERROR)
                {
                    return NGX_ABORT;
                }
            }

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
                           "pipe write downstream done");

            /* TODO: free unused bufs */

            p->downstream_done = 1;
            break;
        }

        if (downstream->data != p->output_ctx
            || !downstream->write->ready
            || downstream->write->delayed)
        {
            break;
        }

        /* bsize is the size of the busy recycled bufs */

        prev = NULL;
        bsize = 0;

        for (cl = p->busy; cl; cl = cl->next) {

            if (cl->buf->recycled) {
                if (prev == cl->buf->start) {
                    continue;
                }

                bsize += cl->buf->end - cl->buf->start;
                prev = cl->buf->start;
            }
        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
                       "pipe write busy: %uz", bsize);

        out = NULL;

        if (bsize >= (size_t) p->busy_size) {
            flush = 1;
            goto flush;
        }

        flush = 0;
        ll = NULL;
        prev_last_shadow = 1;

        for ( ;; ) {
            if (p->out) {
                cl = p->out;

                if (cl->buf->recycled
                    && bsize + cl->buf->last - cl->buf->pos > p->busy_size)
                {
                    flush = 1;
                    break;
                }

                p->out = p->out->next;

                ngx_event_pipe_free_shadow_raw_buf(&p->free_raw_bufs, cl->buf);

            } else if (!p->cacheable && p->in) {
                cl = p->in;

                ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0,
                               "pipe write buf ls:%d %p %z",
                               cl->buf->last_shadow,
                               cl->buf->pos,
                               cl->buf->last - cl->buf->pos);

                if (cl->buf->recycled
                    && cl->buf->last_shadow
                    && bsize + cl->buf->last - cl->buf->pos > p->busy_size)
                {
                    if (!prev_last_shadow) {
                        p->in = p->in->next;

                        cl->next = NULL;

                        if (out) {
                            *ll = cl;
                        } else {
                            out = cl;
                        }
                    }

                    flush = 1;
                    break;
                }

                prev_last_shadow = cl->buf->last_shadow;

                p->in = p->in->next;

            } else {
                break;
            }

            if (cl->buf->recycled) {
                bsize += cl->buf->last - cl->buf->pos;
            }

            cl->next = NULL;

            if (out) {
                *ll = cl;
            } else {
                out = cl;
            }
            ll = &cl->next;
        }

    flush:

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
                       "pipe write: out:%p, f:%d", out, flush);

        if (out == NULL) {

            if (!flush) {
                break;
            }

            /* a workaround for AIO */
            if (flushed++ > 10) {
                return NGX_BUSY;
            }
        }

        rc = p->output_filter(p->output_ctx, out);

        if (rc == NGX_ERROR) {
            p->downstream_error = 1;
            return ngx_event_pipe_drain_chains(p);
        }

        ngx_chain_update_chains(&p->free, &p->busy, &out, p->tag);

        for (cl = p->free; cl; cl = cl->next) {

            if (cl->buf->temp_file) {
                if (p->cacheable || !p->cyclic_temp_file) {
                    continue;
                }

                /* reset p->temp_offset if all bufs had been sent */

                if (cl->buf->file_last == p->temp_file->offset) {
                    p->temp_file->offset = 0;
                }
            }

            /* TODO: free buf if p->free_bufs && upstream done */

            /* add the free shadow raw buf to p->free_raw_bufs */

            if (cl->buf->last_shadow) {
                if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {
                    return NGX_ABORT;
                }

                cl->buf->last_shadow = 0;
            }

            cl->buf->shadow = NULL;
        }
    }

    return NGX_OK;
}
Exemple #7
0
static ngx_int_t
ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)
{
    ssize_t       size, bsize, n;
    ngx_buf_t    *b;
    ngx_uint_t    prev_last_shadow;
    ngx_chain_t  *cl, *tl, *next, *out, **ll, **last_out, **last_free, fl;

    if (p->buf_to_file) {
        fl.buf = p->buf_to_file;
        fl.next = p->in;
        out = &fl;

    } else {
        out = p->in;
    }

    if (!p->cacheable) {

        size = 0;
        cl = out;
        ll = NULL;
        prev_last_shadow = 1;

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
                       "pipe offset: %O", p->temp_file->offset);

        do {
            bsize = cl->buf->last - cl->buf->pos;

            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, p->log, 0,
                           "pipe buf ls:%d %p, pos %p, size: %z",
                           cl->buf->last_shadow, cl->buf->start,
                           cl->buf->pos, bsize);

            if (prev_last_shadow
                && ((size + bsize > p->temp_file_write_size)
                    || (p->temp_file->offset + size + bsize
                        > p->max_temp_file_size)))
            {
                break;
            }

            prev_last_shadow = cl->buf->last_shadow;

            size += bsize;
            ll = &cl->next;
            cl = cl->next;

        } while (cl);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "size: %z", size);

        if (ll == NULL) {
            return NGX_BUSY;
        }

        if (cl) {
           p->in = cl;
           *ll = NULL;

        } else {
           p->in = NULL;
           p->last_in = &p->in;
        }

    } else {
        p->in = NULL;
        p->last_in = &p->in;
    }

    n = ngx_write_chain_to_temp_file(p->temp_file, out);

    if (n == NGX_ERROR) {
        return NGX_ABORT;
    }

    if (p->buf_to_file) {
        p->temp_file->offset = p->buf_to_file->last - p->buf_to_file->pos;
        n -= p->buf_to_file->last - p->buf_to_file->pos;
        p->buf_to_file = NULL;
        out = out->next;
    }

    if (n > 0) {
        /* update previous buffer or add new buffer */

        if (p->out) {
            for (cl = p->out; cl->next; cl = cl->next) { /* void */ }

            b = cl->buf;

            if (b->file_last == p->temp_file->offset) {
                p->temp_file->offset += n;
                b->file_last = p->temp_file->offset;
                goto free;
            }

            last_out = &cl->next;

        } else {
            last_out = &p->out;
        }

        cl = ngx_chain_get_free_buf(p->pool, &p->free);
        if (cl == NULL) {
            return NGX_ABORT;
        }

        b = cl->buf;

        ngx_memzero(b, sizeof(ngx_buf_t));

        b->tag = p->tag;

        b->file = &p->temp_file->file;
        b->file_pos = p->temp_file->offset;
        p->temp_file->offset += n;
        b->file_last = p->temp_file->offset;

        b->in_file = 1;
        b->temp_file = 1;

        *last_out = cl;
    }

free:

    for (last_free = &p->free_raw_bufs;
         *last_free != NULL;
         last_free = &(*last_free)->next)
    {
        /* void */
    }

    for (cl = out; cl; cl = next) {
        next = cl->next;

        cl->next = p->free;
        p->free = cl;

        b = cl->buf;

        if (b->last_shadow) {

            tl = ngx_alloc_chain_link(p->pool);
            if (tl == NULL) {
                return NGX_ABORT;
            }

            tl->buf = b->shadow;
            tl->next = NULL;

            *last_free = tl;
            last_free = &tl->next;

            b->shadow->pos = b->shadow->start;
            b->shadow->last = b->shadow->start;

            ngx_event_pipe_remove_shadow_links(b->shadow);
        }
    }

    return NGX_OK;
}
/* mostly an exact clone of the ngx_http_write_request_body
 * function in ngx_http_request_body.c of nginx 0.8.20.
 * copyrighted by Igor Sysoev. */
static ngx_int_t
ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body,
        int chain_count)
{
    ssize_t                    n;
    ngx_temp_file_t           *tf;
    ngx_http_request_body_t   *rb;
    ngx_http_core_loc_conf_t  *clcf;
    int                       i;
    ngx_chain_t               *cl, *saved_next;

    rb = r->request_body;

    if (rb->temp_file == NULL) {
        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
        if (tf == NULL) {
            return NGX_ERROR;
        }

        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        tf->file.fd = NGX_INVALID_FILE;
        tf->file.log = r->connection->log;
        tf->path = clcf->client_body_temp_path;
        tf->pool = r->pool;

        if (rb->buf && rb->buf->last == rb->buf->end) {
            tf->warn = "a client request body is buffered to a temporary file "
                "(exceeding client_body_buffer_size)";
        } else {
            tf->warn = "a client request body is buffered to a temporary file "
                "(exceeding chunkin_max_chunks_per_buf)";
        }

        tf->log_level = r->request_body_file_log_level;
        tf->persistent = r->request_body_in_persistent_file;
        tf->clean = r->request_body_in_clean_file;

        if (r->request_body_file_group_access) {
            tf->access = 0660;
        }

        rb->temp_file = tf;
    }

    if (body == NULL) {
        return NGX_OK;
    }

    /* we need this hack to work around a bug in
     * ngx_write_chain_to_temp_file when the chain
     * link is longer than 1024, we'll receive
     * random alert like this:
     *   [alert] 13493#0: *1 pread() read only 1024
     *   of 3603 from
     *   "/opt/nginx/client_body_temp/0000000001" */
    if (chain_count > MAX_CHUNKS_PER_DISK_WRITE) {
        i = 0;
        for (cl = body; cl; cl = cl->next) {
            if (i >= MAX_CHUNKS_PER_DISK_WRITE) {
                /* dd("wrote %d links first...", i+1); */

                saved_next = cl->next;
                cl->next = NULL;
                n = ngx_write_chain_to_temp_file(rb->temp_file, body);
                /* TODO: n == 0 or not complete and level event */

                if (n == NGX_ERROR) {
                    return NGX_ERROR;
                }

                rb->temp_file->offset += n;

                cl->next = saved_next;
                body = cl->next;
                i = 0;
            } else {
                i++;
            }
        }
    }

    if (body) {
        n = ngx_write_chain_to_temp_file(rb->temp_file, body);
        /* TODO: n == 0 or not complete and level event */

        if (n == NGX_ERROR) {
            return NGX_ERROR;
        }

        rb->temp_file->offset += n;
    }

    return NGX_OK;
}
static ngx_int_t
ngx_http_filter_cache_header_filter(ngx_http_request_t *r)
{
    ngx_http_filter_cache_ctx_t *ctx = NULL;
    ngx_http_filter_cache_conf_t *conf = NULL;
    time_t  now, valid;
    ngx_temp_file_t *tf;
    ngx_chain_t   out;
    ssize_t offset;
    ngx_list_part_t *part;
    ngx_table_elt_t *h;
    ngx_uint_t i;
    u_char *p;
    size_t len;
    ngx_pool_cleanup_t *cln = NULL;

    ngx_http_filter_cache_meta_t meta;

    if(r != r->main) {
        /*just skip as we got headers in main*/
        return ngx_http_next_header_filter(r);
    }

    conf = ngx_http_get_module_loc_conf(r, ngx_http_filter_cache_module);

    switch (ngx_http_test_predicates(r, conf->upstream.no_cache)) {
    case NGX_ERROR:
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, __FILE__" ngx_http_test_predicates returned an error for no_cache");
        return NGX_ERROR;
    case NGX_DECLINED:
        goto nocache;
    default: /* NGX_OK */
        break;
    }

    ctx = r->filter_cache;

    if(!ctx || (FILTER_DONOTCACHE == ctx->cacheable)) {
        goto nocache;
    }
    /* ngx_http_filter_cache_create(r); */

    if (ctx->cache && ctx->cache->file.fd != NGX_INVALID_FILE) {
        ngx_pool_run_cleanup_file(r->pool, ctx->cache->file.fd);
        ctx->cache->file.fd = NGX_INVALID_FILE;
    }

    ctx->cache->valid_sec = 0;

    now = ngx_time();

    valid = 0;
    valid = ngx_http_filter_cache_valid(conf->upstream.cache_valid,
                                      r->headers_out.status);
    if (valid) {
        ctx->cache->valid_sec = now + valid;
    } else {
        goto nocache;
    }

    tf =  ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));

    if (tf == NULL) {
        return NGX_ERROR;
    }

    tf->file.fd = NGX_INVALID_FILE;
    tf->file.log = r->connection->log;
    tf->path = conf->upstream.temp_path;
    tf->pool = r->pool;
    tf->persistent = 1;

    if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                             tf->persistent, tf->clean, tf->access)
        != NGX_OK) {
        return NGX_ERROR;
    }
    ctx->tf = tf;

    cln = ngx_pool_cleanup_add(r->pool, 0);
    if (cln == NULL) {
        return NGX_ERROR;
    }
    cln->handler = filter_cache_cleanup;
    cln->data = ctx;

    ctx->buffer.pos = ctx->buffer.start = ngx_palloc(r->pool, conf->upstream.buffer_size);
    ctx->buffer.end = ctx->buffer.start + conf->upstream.buffer_size;
    ctx->buffer.temporary = 1;
    ctx->buffer.memory = 1;
    ctx->buffer.last_buf = 1;

    ctx->buffer.pos += ctx->cache->header_start;

    ctx->cache->last_modified = r->headers_out.last_modified_time;
    ctx->cache->date = now;

    /* Headers */

    /* fill in the metadata*/
    meta.status = r->headers_out.status;

#if (NGX_HTTP_GZIP)
    meta.gzip_vary = r->gzip_vary; /* Note: there is still some wierdness to how gzip_vary works...*/
#endif

    meta.last_modified_time = r->headers_out.last_modified_time;

    ngx_memcpy((void *)(ctx->buffer.pos), (void *)(&meta), sizeof(ngx_http_filter_cache_meta_t) );
    ctx->buffer.pos += sizeof(ngx_http_filter_cache_meta_t);

    /* Headers taht aren't in teh table for some reason */

    /*Do we need to try to set it if it's not set???*/
    /* Content Type */
    if ( r->headers_out.content_type.data ) {
        p = memchr((void *)r->headers_out.content_type.data, ';', r->headers_out.content_type.len );
        if ( p ) {
            len = p - r->headers_out.content_type.data;
            ngx_cpystrn( ctx->buffer.pos, r->headers_out.content_type.data, len + 1);
            ctx->buffer.pos += len + 1;
        }
        else {
            ngx_cpystrn( ctx->buffer.pos, r->headers_out.content_type.data, r->headers_out.content_type.len + 1 );
            ctx->buffer.pos += r->headers_out.content_type.len + 1;
        }
    }
    else {
        *ctx->buffer.pos = (u_char)'\0';
        ctx->buffer.pos++;
    }

    /* Charset */
    if ( r->headers_out.charset.data ) {
        ngx_cpystrn( ctx->buffer.pos, r->headers_out.charset.data, r->headers_out.charset.len + 1 );
        ctx->buffer.pos += r->headers_out.charset.len + 1;
    }
    else {
        *ctx->buffer.pos = (u_char)'\0';
        ctx->buffer.pos++;
    }

    /* Content Encoding */
    if ( r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) {
        ngx_cpystrn( ctx->buffer.pos, r->headers_out.content_encoding->value.data, r->headers_out.content_encoding->value.len + 1 );
        ctx->buffer.pos += r->headers_out.content_encoding->value.len + 1;
    }
    else {
        *ctx->buffer.pos = (u_char)'\0';
        ctx->buffer.pos++;
    }

    /* Last-Modified */
    if(r->headers_out.last_modified_time && r->headers_out.last_modified && r->headers_out.last_modified->value.len) {
        ngx_cpystrn( ctx->buffer.pos, r->headers_out.last_modified->value.data, r->headers_out.last_modified->value.len + 1 );
        ctx->buffer.pos += r->headers_out.last_modified->value.len + 1;
    } else {
        *ctx->buffer.pos = (u_char)'\0';
        ctx->buffer.pos++;
    }


    /* XXX: is last-modified special???*/
    /* Everything From the Table */
    part = &r->headers_out.headers.part;
    h = part->elts;
    for (i=0; /* void */; i++) {
        if ( i >= part->nelts || !part->nelts ) {
            if ( part->next == NULL ) {
                ctx->cacheable = FILTER_CACHEABLE;
                break;
            }
            part = part->next;
            h = part->elts;
            i = 0;
        }

        /*need to be really sure this header is "valid"*/
        /* if(h[i].key.len && h[i].value.len && h[i].hash && h[i].lowcase_key) {*/

            /* if(!h[i].lowcase_key) { */
            /*     if((h[i].lowcase_key = ngx_pnalloc(r->pool, h->key.len +1)) == NULL) { */
            /*         continue; */
            /*     } */
            /*     ngx_strlow(h[i].lowcase_key, h[i].key.data, h[i].key.len); */
            /* } */

            /* if(!h[i].hash) { */
            /*     h[i].hash = ngx_hash_key_lc(h[i].key.data, h[i].key.len); */
            /* } */

            /* if (ngx_hash_find(&conf->upstream.hide_headers_hash, h[i].hash, */
            /*                   h[i].lowcase_key, h[i].key.len)) */
            /* { */
            /*     continue; */
            /* } */

        if(h[i].key.len && h[i].value.len) {
            if(find_string_in_array(&(h[i].key), conf->upstream.hide_headers)){
                continue;
            }
            if ( (ngx_uint_t)(h[i].key.len + h[i].value.len + 4) > (ngx_uint_t)(ctx->buffer.last - ctx->buffer.pos) ) {
                ctx->cacheable = FILTER_DONOTCACHE;
                break;
            }

            ngx_cpystrn( ctx->buffer.pos, h[i].key.data, h[i].key.len + 1 );
            ctx->buffer.pos += h[i].key.len + 1;

            ngx_cpystrn( ctx->buffer.pos, h[i].value.data, h[i].value.len + 1 );
            ctx->buffer.pos += h[i].value.len + 1;
        }

    }

    if(FILTER_CACHEABLE != ctx->cacheable) {
        goto nocache;
    }

    ctx->buffer.last = ctx->buffer.pos;

    ctx->cache->body_start = (u_short) (ctx->buffer.pos - ctx->buffer.start);
    ngx_http_filter_cache_set_header(r, ctx->buffer.start);
    ctx->cache->date = now;

    /*write to temp file*/
    ctx->buffer.pos =  ctx->buffer.start;
    out.buf = &ctx->buffer;
    out.next = NULL;
    offset = ngx_write_chain_to_temp_file(tf, &out);
    tf->offset += offset;


    r->main_filter_need_in_memory = 1;

    return ngx_http_next_header_filter(r);

nocache:

    if(ctx) {
        ctx->cacheable = FILTER_DONOTCACHE;
        /* if(ctx->cache) { */
            /* ngx_http_filter_cache_free(ctx->cache, ctx->tf); */
        /* } */

    }
    return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_filter_cache_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_filter_cache_ctx_t *ctx = NULL;
    ssize_t offset = 0;
    ngx_chain_t *chain_link = NULL;
    int done = 0;

    /* ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, */
    /*                "ngx_http_filter_cache_body_filter start"); */

    if (in == NULL) {
        return ngx_http_next_body_filter(r, in);
    }

    ctx = r->main->filter_cache;

    if (!ctx || (FILTER_CACHEABLE != ctx->cacheable)) {
        /* ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, */
        /*                "ngx_http_filter_cache_body_filter not cacheable"); */
        return ngx_http_next_body_filter(r, in);
    }

    if( ctx->tf->file.fd == NGX_INVALID_FILE ) {
        /* ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, */
        /*                "ngx_http_filter_cache_body_filter invalid temp file"); */
        return ngx_http_next_body_filter(r, in);
    } else {
        ngx_chain_t *cl = NULL;
        ngx_chain_t *head = NULL;
        ngx_buf_t *b = NULL;

        for ( chain_link = in; chain_link != NULL; chain_link = chain_link->next ) {
            b = chain_link->buf;

            if(b->pos && b->last) {
                if(!cl) {
                    head = cl = ngx_alloc_chain_link(r->pool);
                } else {
                    cl->next = ngx_alloc_chain_link(r->pool);
                    cl = cl->next;
                }
                 if (cl == NULL) {
                        return NGX_ERROR;
                 }
                 cl->buf = b;
                 cl->next = NULL;
            }
        }

        if (head) {
            offset = ngx_write_chain_to_temp_file(ctx->tf, head);
            ctx->tf->offset += offset;
        }
    }

    r->connection->buffered |= NGX_HTTP_FILTERCACHE_BUFFERED;

    for ( chain_link = in; chain_link != NULL; chain_link = chain_link->next ) {
        /* last_in_chain is used for sub requests?  also maybe need to find out about ->sync??*/
        /* if (chain_link->buf->last_buf || chain_link->buf->last_in_chain) { */
        if (chain_link->buf->last_buf) {
            done = 1;
        }
    }

    if(done) {
        ngx_http_filter_cache_update(r, ctx->tf);
        ctx->cacheable = FILTER_CACHEDONE;
        r->connection->buffered &= ~NGX_HTTP_FILTERCACHE_BUFFERED;
    }
    return ngx_http_next_body_filter(r, in);
}