
/*
 * Copyright (C) Yichun Zhang (agentzh)
 */


#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"


#include "ngx_http_srcache_headers.h"


static ngx_int_t ngx_http_srcache_process_content_type(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_srcache_process_content_length(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_srcache_process_last_modified(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t
    ngx_http_srcache_process_multi_header_lines(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_srcache_process_allow_ranges(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_srcache_process_accept_ranges(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_srcache_ignore_header_line(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_srcache_process_location(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);

#if (NGX_HTTP_GZIP)
static ngx_int_t
    ngx_http_srcache_process_content_encoding(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset);
#endif


ngx_http_srcache_header_t  ngx_http_srcache_headers_in[] = {

    { ngx_string("Content-Type"),
                 ngx_http_srcache_process_content_type, 0 },

    { ngx_string("Content-Length"),
                 ngx_http_srcache_process_content_length, 0 },

    { ngx_string("Date"),
                 ngx_http_srcache_process_header_line,
                 offsetof(ngx_http_headers_out_t, date) },

    { ngx_string("Last-Modified"),
                 ngx_http_srcache_process_last_modified, 0 },

    { ngx_string("ETag"),
                 ngx_http_srcache_process_header_line,
                 offsetof(ngx_http_headers_out_t, etag) },

    { ngx_string("Server"),
                 ngx_http_srcache_process_header_line,
                 offsetof(ngx_http_headers_out_t, server) },

    { ngx_string("WWW-Authenticate"),
                 ngx_http_srcache_process_header_line,
                 offsetof(ngx_http_headers_out_t, www_authenticate) },

    { ngx_string("Location"),
                 ngx_http_srcache_process_location,
                 0 },

    { ngx_string("Refresh"),
                 ngx_http_srcache_process_header_line,
                 offsetof(ngx_http_headers_out_t, refresh) },

    { ngx_string("Cache-Control"),
                 ngx_http_srcache_process_multi_header_lines,
                 offsetof(ngx_http_headers_out_t, cache_control) },

    { ngx_string("Expires"),
                 ngx_http_srcache_process_header_line,
                 offsetof(ngx_http_headers_out_t, expires) },

    { ngx_string("X-SRCache-Allow-Ranges"),
                 ngx_http_srcache_process_allow_ranges,
                 offsetof(ngx_http_headers_out_t, accept_ranges) },

    { ngx_string("Accept-Ranges"),
                 ngx_http_srcache_process_accept_ranges,
                 offsetof(ngx_http_headers_out_t, accept_ranges) },

    { ngx_string("Connection"),
                 ngx_http_srcache_ignore_header_line, 0 },

    { ngx_string("Keep-Alive"),
                 ngx_http_srcache_ignore_header_line, 0 },

#if (NGX_HTTP_GZIP)
    { ngx_string("Content-Encoding"),
                 ngx_http_srcache_process_content_encoding,
                 offsetof(ngx_http_headers_out_t, content_encoding) },
#endif

    { ngx_null_string, NULL, 0 }
};


ngx_int_t
ngx_http_srcache_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
    ngx_uint_t offset)
{
    ngx_table_elt_t  *ho, **ph;

    ho = ngx_list_push(&r->headers_out.headers);
    if (ho == NULL) {
        return NGX_ERROR;
    }

    *ho = *h;

    if (offset) {
        ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset);
        *ph = ho;
    }

    return NGX_OK;
}


static ngx_int_t
ngx_http_srcache_process_location(ngx_http_request_t *r, ngx_table_elt_t *h,
    ngx_uint_t offset)
{
    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);
    if (ho == NULL) {
        return NGX_ERROR;
    }

    *ho = *h;

    if (ho->value.data[0] != '/') {
        r->headers_out.location = ho;
    }

    /*
     * we do not set r->headers_out.location here to avoid the handling
     * the local redirects without a host name by ngx_http_header_filter()
     */

    return NGX_OK;
}


static ngx_int_t
ngx_http_srcache_process_content_type(ngx_http_request_t *r, ngx_table_elt_t *h,
    ngx_uint_t offset)
{
    u_char  *p, *last;

    r->headers_out.content_type_len = h->value.len;
    r->headers_out.content_type = h->value;
    r->headers_out.content_type_lowcase = NULL;

    for (p = h->value.data; *p; p++) {

        if (*p != ';') {
            continue;
        }

        last = p;

        while (*++p == ' ') { /* void */ }

        if (*p == '\0') {
            return NGX_OK;
        }

        if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) {
            continue;
        }

        p += 8;

        r->headers_out.content_type_len = last - h->value.data;

        if (*p == '"') {
            p++;
        }

        last = h->value.data + h->value.len;

        if (*(last - 1) == '"') {
            last--;
        }

        r->headers_out.charset.len = last - p;
        r->headers_out.charset.data = p;

        return NGX_OK;
    }

    return NGX_OK;
}


static ngx_int_t
ngx_http_srcache_process_content_length(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset)
{
    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);
    if (ho == NULL) {
        return NGX_ERROR;
    }

    *ho = *h;

    r->headers_out.content_length = ho;
    r->headers_out.content_length_n = ngx_atoof(h->value.data, h->value.len);

    return NGX_OK;
}


static ngx_int_t
ngx_http_srcache_process_last_modified(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset)
{
#if 1
    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);
    if (ho == NULL) {
        return NGX_ERROR;
    }

    *ho = *h;

    r->headers_out.last_modified = ho;
#endif

    r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data,
                                                            h->value.len);

    dd("setting last-modified-time: %d",
       (int) r->headers_out.last_modified_time);

    return NGX_OK;
}


static ngx_int_t
ngx_http_srcache_process_multi_header_lines(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset)
{
#if defined(nginx_version) && nginx_version < 1023000
    ngx_array_t      *pa;
#endif
    ngx_table_elt_t  *ho, **ph;

#if defined(nginx_version) && nginx_version >= 1023000
    ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset);
    while (*ph) {
      ph = &(*ph)->next;
    }
#else
    pa = (ngx_array_t *) ((char *) &r->headers_out + offset);

    if (pa->elts == NULL) {
        if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)
        {
            return NGX_ERROR;
        }
    }

    ph = ngx_array_push(pa);
    if (ph == NULL) {
        return NGX_ERROR;
    }
#endif

    ho = ngx_list_push(&r->headers_out.headers);
    if (ho == NULL) {
        return NGX_ERROR;
    }

    *ho = *h;
    *ph = ho;
#if defined(nginx_version) && nginx_version >= 1023000
    ho->next = NULL;
#endif

    return NGX_OK;
}


static ngx_int_t
ngx_http_srcache_process_accept_ranges(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset)
{
    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);
    if (ho == NULL) {
        return NGX_ERROR;
    }

    *ho = *h;

    r->headers_out.accept_ranges = ho;

    return NGX_OK;
}


static ngx_int_t
ngx_http_srcache_process_allow_ranges(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset)
{
    r->allow_ranges = 1;

    return NGX_OK;
}


static ngx_int_t
ngx_http_srcache_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
    ngx_uint_t offset)
{
    return NGX_OK;
}


#if (NGX_HTTP_GZIP)

static ngx_int_t
ngx_http_srcache_process_content_encoding(ngx_http_request_t *r,
    ngx_table_elt_t *h, ngx_uint_t offset)
{
    ngx_table_elt_t  *ho;

    ho = ngx_list_push(&r->headers_out.headers);
    if (ho == NULL) {
        return NGX_ERROR;
    }

    *ho = *h;

    r->headers_out.content_encoding = ho;

    return NGX_OK;
}

#endif

/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
