ngx_int_t ngx_http_modsecurity_rewrite_handler(ngx_http_request_t *r)
{
    ngx_http_modsecurity_ctx_t *ctx = NULL;
    ngx_http_modsecurity_loc_conf_t *cf;

    cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity);
    if (cf == NULL || cf->enable != 1)
    {
        dd("ModSecurity not enabled... returning");
        return NGX_DECLINED;
    }

    if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_POST) {
        dd("ModSecurity is not ready to deal with anything different from " \
            "POST or GET");
        return NGX_DECLINED;
    }

    dd("catching a new _rewrite_ pahase handler");

    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);

    dd("recovering ctx: %p", ctx);

    if (ctx == NULL)
    {
        int ret = 0;

        ngx_connection_t *connection = r->connection;
        /** 
         * FIXME: We may want to use struct sockaddr instead of addr_text.
         *
         */
        ngx_str_t addr_text = connection->addr_text;

        ctx = ngx_http_modsecurity_create_ctx(r);

        dd("ctx was NULL, creating new context: %p", ctx);

        if (ctx == NULL)
        {
            dd("ctx still null; Nothing we can do, returning an error.");

            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        /**
         * FIXME: Check if it is possible to hook on nginx on a earlier phase.
         *
         * At this point we are doing an late connection process. Maybe
         * we have to hook into NGX_HTTP_FIND_CONFIG_PHASE, it seems to be the
         * erliest phase that nginx allow us to attach those kind of hooks.
         *
         */
        int client_port = htons(((struct sockaddr_in *) connection->sockaddr)->sin_port);
        int server_port = htons(((struct sockaddr_in *) connection->listening->sockaddr)->sin_port);
        const char *client_addr = ngx_str_to_char(addr_text, r->pool);
        const char *server_addr = inet_ntoa(((struct sockaddr_in *) connection->sockaddr)->sin_addr);
        ret = msc_process_connection(ctx->modsec_transaction,
            client_addr, client_port,
            server_addr, server_port);
        if (ret != 1){
            dd("Was not able to exract connection information.");
        }
        /**
         *
         * FIXME: Check how we can finalize a request without crash nginx.
         *
         * I don't think nginx is expecting to finalize a request at that
         * point as it seems that it clean the ngx_http_request_t information
         * and try to use it later. 
         *
         */
        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
        if (ret > 0)
        {
            return ret;
        }

        const char *http_version;
        switch (r->http_version) {
            case NGX_HTTP_VERSION_9 :
                http_version = "0.9";
                break;
            case NGX_HTTP_VERSION_10 :
                http_version = "1.0";
                break;
            case NGX_HTTP_VERSION_11 :
                http_version = "1.1";
                break;
            case NGX_HTTP_VERSION_20 :
                http_version = "2.0";
                break;
            default :
                http_version = "1.0";
                break;
        }

        msc_process_uri(ctx->modsec_transaction, ngx_str_to_char(r->unparsed_uri, r->pool),
            ngx_str_to_char(r->method_name, r->pool), http_version
        );
        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
        if (ret > 0)
        {
            return ret;
        }

        /**
         * Since headers are already in place, lets send it to ModSecurity
         * 
         */
        ngx_list_part_t *part = &r->headers_in.headers.part;
        ngx_table_elt_t *data = part->elts;
        ngx_uint_t i = 0;
        for (i = 0 ;; i++) {
            if (i >= part->nelts) {
                if (part->next == NULL) {
                    break;
                }

                part = part->next;
                data = part->elts;
                i = 0;
            }

            /**
             * By using u_char (utf8_t) I believe nginx is hoping to deal
             * with utf8 strings.
             * Casting those into to unsigned char * in order to pass
             * it to ModSecurity, it will handle with those later.
             * 
             */

            msc_add_n_request_header(ctx->modsec_transaction,
                (const unsigned char *) data[i].key.data,
                data[i].key.len,
                (const unsigned char *) data[i].value.data,
                data[i].value.len);
        }

        /**
         * Since ModSecurity already knew about all headers, i guess it is safe
         * to process this information.
         */

        msc_process_request_headers(ctx->modsec_transaction);
        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
        if (ret > 0)
        {
            return ret;
        }
    }


    return NGX_DECLINED;
}
/* NGX_HTTP_PREACCESS_PHASE阶段的处理句柄,处理请求报文体 */
ngx_int_t
ngx_http_modsecurity_pre_access_handler(ngx_http_request_t *r)
{
#if 1
    ngx_http_modsecurity_ctx_t *ctx = NULL;
    ngx_http_modsecurity_loc_conf_t *cf;

    dd("catching a new _preaccess_ phase handler");

    cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity_module);
    if (cf == NULL || cf->enable != 1)
    {
        dd("ModSecurity not enabled... returning");
        return NGX_DECLINED;
    }
    /*
     * FIXME:
     * In order to perform some tests, let's accept everything.
     *
    if (r->method != NGX_HTTP_GET &&
        r->method != NGX_HTTP_POST && r->method != NGX_HTTP_HEAD) {
        dd("ModSecurity is not ready to deal with anything different from " \
            "POST, GET or HEAD");
        return NGX_DECLINED;
    }
    */

    /* 获取安全模块儿的执行环境,在处理请求头时创建,此处不应该为空 */
    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity_module);
    dd("recovering ctx: %p", ctx);
    if (ctx == NULL)
    {
        dd("ctx is null; Nothing we can do, returning an error.");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    /* 等待更多的报文内容,则继续等待;*/
    if (ctx->waiting_more_body == 1)
    {
        dd("waiting for more data before proceed. / count: %d",
            r->main->count);

        return NGX_DONE;
    }

    /* 首次进入,读取报文体 */
    if (ctx->body_requested == 0)
    {
        ngx_int_t rc = NGX_OK;

        ctx->body_requested = 1;                   /* 设置标识,开始处理请求报文体 */

        dd("asking for the request body, if any. Count: %d",
            r->main->count);
        /**
         * TODO: Check if there is any benefit to use request_body_in_single_buf set to 1.
         *
         *       saw some module using this request_body_in_single_buf
         *       but not sure what exactly it does, same for the others options below.
         *
         * r->request_body_in_single_buf = 1;
         */
        r->request_body_in_single_buf = 1;
        r->request_body_in_persistent_file = 1;
        r->request_body_in_clean_file = 1;

        rc = ngx_http_read_client_request_body(r,  /* 设置等待报文的处理函数 */
            ngx_http_modsecurity_request_read);
        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
#if (nginx_version < 1002006) ||                                             \
    (nginx_version >= 1003000 && nginx_version < 1003009)
            r->main->count--;
#endif

            return rc;
        }
        if (rc == NGX_AGAIN)
        {
            dd("nginx is asking us to wait for more data.");

            ctx->waiting_more_body = 1;
            return NGX_DONE;
        }
    }

    /* 报文体接收完毕,处理 */
    if (ctx->waiting_more_body == 0)
    {
        int ret = 0;
        int already_inspected = 0;

        dd("request body is ready to be processed");

        ngx_chain_t *chain = r->request_body->bufs;

        /**
         * TODO: Speed up the analysis by sending chunk while they arrive.
         *
         * Notice that we are waiting for the full request body to
         * start to process it, it may not be necessary. We may send
         * the chunks to ModSecurity while nginx keep calling this
         * function.
         */
        /* 上传文件形式的报文体 */
        if (r->request_body->temp_file != NULL) {
            ngx_str_t file_path = r->request_body->temp_file->file.name;
            const char *file_name = ngx_str_to_char(file_path, r->pool);
            if (file_name == (char*)-1) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
            /*
             * Request body was saved to a file, probably we don't have a
             * copy of it in memory.
             */
            dd("request body inspection: file -- %s", file_name);

            msc_request_body_from_file(ctx->modsec_transaction, file_name);

            already_inspected = 1;
        } else {
            dd("inspection request body in memory.");
        }
        /* 上传缓存形式的报文体 */
        while (chain && !already_inspected)
        {
            u_char *data = chain->buf->start;

            msc_append_request_body(ctx->modsec_transaction, data,
                chain->buf->last - chain->buf->pos);

            if (chain->buf->last_buf) {
                break;
            }
            chain = chain->next;

/* XXX: chains are processed one-by-one, maybe worth to pass all chains and then call intervention() ? */

            /**
             * ModSecurity may perform stream inspection on this buffer,
             * it may ask for a intervention in consequence of that.
             *
             */
            ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
            if (ret > 0) {
                return ret;
            }
        }

        /**
         * At this point, all the request body was sent to ModSecurity
         * and we want to make sure that all the request body inspection
         * happened; consequently we have to check if ModSecurity have
         * returned any kind of intervention.
         */

/* XXX: once more -- is body can be modified ?  content-length need to be adjusted ? */
        /* 报文体完整上传到安全模块儿后,处理报文体 */
        ngx_http_modsecurity_pcre_malloc_init();
        msc_process_request_body(ctx->modsec_transaction);
        ngx_http_modsecurity_pcre_malloc_done();

        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
        if (ret > 0) {
            return ret;
        }
    }

    dd("Nothing to add on the body inspection, reclaiming a NGX_DECLINED");
#endif
    return NGX_DECLINED;
}
ngx_int_t ngx_http_modsecurity_header_filter(ngx_http_request_t *r)
{
    ngx_http_modsecurity_ctx_t *ctx;
    ngx_list_part_t *part = &r->headers_out.headers.part;
    ngx_table_elt_t *data = part->elts;
    ngx_uint_t i = 0;
    int ret = 0;

    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);

    dd("header filter, recovering ctx: %p", ctx);

    if (ctx == NULL)
    {
        dd("something really bad happened or ModSecurity is disabled. going to the next filter.");
        return ngx_http_next_header_filter(r);;
    }

    if (ctx && ctx->processed)
    {
        /**
         *FIXME: verify if this request is already processed.
         *
         */
        dd("Already processed... going to the next header...");
        return ngx_http_next_header_filter(r);
    }

    ctx->processed = 1;

    for (i = 0 ;; i++)
    {
        if (i >= part->nelts)
        {
            if (part->next == NULL)
            {
                break;
            }

            part = part->next;
            data = part->elts;
            i = 0;
        }
        /**
         * Doing this ugly cast here, explanation on the request_header
         *
         */

        msc_add_n_response_header(ctx->modsec_transaction,
                                  (const unsigned char *) data[i].key.data,
                                  data[i].key.len,
                                  (const unsigned char *) data[i].value.data,
                                  data[i].value.len);
    }

    msc_process_response_headers(ctx->modsec_transaction);
    ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
    if (ret > 0)
    {
        return ret;
    }

    /**
     * Proxies will not like this... but it is necessary to unset
     * the content length in order to manipulate the content of
     * response body in ModSecurity.
     *
     * This header may arrive at the client before ModSecurity had
     * a change to make any modification. That is why it is necessary
     * to set this to -1 here.
     *
     * We need to have some kind of flag the decide if ModSecurity
     * will make a modification or not. If not, keep the content and
     * make the proxy servers happy.
     *
     */
    /**
     * The line below is commented to make the spdy test to work
     *
     */
    //r->headers_out.content_length_n = -1;

    return ngx_http_next_header_filter(r);
}
ngx_int_t
ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    int buffer_fully_loadead = 0;
    ngx_chain_t *chain = in;
    ngx_http_modsecurity_ctx_t *ctx = NULL;
#ifdef MODSECURITY_SANITY_CHECKS
    ngx_list_part_t *part = &r->headers_out.headers.part;
    ngx_table_elt_t *data = part->elts;
    ngx_uint_t i = 0;
#endif

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

    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);

    dd("body filter, recovering ctx: %p", ctx);

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

#ifdef MODSECURITY_SANITY_CHECKS
#if 0
    dd("dumping stored ctx headers");
    for (i = 0; i < ctx->sanity_headers_out->nelts; i++)
    {
	ngx_http_modsecurity_header_t *vals = ctx->sanity_headers_out->elts;
	ngx_str_t *s2 = &vals[i].name, *s3 = &vals[i].value;
	dd(" dump[%d]: name = '%.*s', value = '%.*s'", (int)i,
		(int)s2->len, (char*)s2->data,
		(int)s3->len, (char*)s3->data);
    }
#endif
    /*
     * Identify if there is a header that was not inspected by ModSecurity.
     */
    int worth_to_fail = 0;

    for (i = 0; ; i++)
    {
        int found = 0;
        ngx_uint_t j = 0;
        ngx_table_elt_t *s1;
        ngx_http_modsecurity_header_t *vals;

        if (i >= part->nelts)
        {
            if (part->next == NULL) {
                break;
            }

            part = part->next;
            data = part->elts;
            i = 0;
        }

        vals = ctx->sanity_headers_out->elts;
        s1 = &data[i];

        /*
         * Headers that were inspected by ModSecurity.
         */
        while (j < ctx->sanity_headers_out->nelts)
        {
            ngx_str_t *s2 = &vals[j].name;
            ngx_str_t *s3 = &vals[j].value;

            if (s1->key.len == s2->len && ngx_strncmp(s1->key.data, s2->data, s1->key.len) == 0)
            {
                if (s1->value.len == s3->len && ngx_strncmp(s1->value.data, s3->data, s1->value.len) == 0)
                {
                    found = 1;
                    break;
                }
            }
            j++;
        }
        if (!found) {
            dd("header: `%.*s' with value: `%.*s' was not inspected by ModSecurity",
                (int) s1->key.len,
                (const char *) s1->key.data,
                (int) s1->value.len,
                (const char *) s1->value.data);
	    worth_to_fail++;
        }
    }

    if (worth_to_fail)
    {
        dd("%d header(s) were not inspected by ModSecurity, so exiting", worth_to_fail);
        return ngx_http_filter_finalize_request(r,
            &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
    }
#endif

    for (; chain != NULL; chain = chain->next)
    {
/* XXX: chain->buf->last_buf || chain->buf->last_in_chain */
        if (chain->buf->last_buf) {
            buffer_fully_loadead = 1;
        }
    }

    if (buffer_fully_loadead == 1)
    {
        int ret;

        for (chain = in; chain != NULL; chain = chain->next)
        {
            u_char *data = chain->buf->start;

            msc_append_response_body(ctx->modsec_transaction, data, chain->buf->end - data);
            ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
            if (ret > 0) {
                return ngx_http_filter_finalize_request(r,
                    &ngx_http_modsecurity, ret);
            }
        }

        msc_process_response_body(ctx->modsec_transaction);
/* XXX: I don't get how body from modsec being transferred to nginx's buffer.  If so - after adjusting of nginx's
   XXX: body we can proceed to adjust body size (content-length).  see xslt_body_filter() for example */
        ret = ngx_http_modsecurity_process_intervention(ctx->modsec_transaction, r);
        if (ret > 0) {
            return ret;
        }
	else if (ret < 0) {
            return ngx_http_filter_finalize_request(r,
                &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR);
        }
    }
    else
    {
        dd("buffer was not fully loaded! ctx: %p", ctx);
    }

/* XXX: xflt_filter() -- return NGX_OK here */
    return ngx_http_next_body_filter(r, in);
}