ngx_http_lua_handle_subreq_responses(ngx_http_request_t *r,
    ngx_http_lua_ctx_t *ctx)
{
    ngx_uint_t                   i, count;
    ngx_uint_t                   index;
    lua_State                   *co;
    ngx_str_t                   *body_str;
    ngx_table_elt_t             *header;
    ngx_list_part_t             *part;
    ngx_http_headers_out_t      *sr_headers;
    ngx_http_lua_co_ctx_t       *coctx;

    u_char                  buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1];

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "lua handle subrequest responses");

    coctx = ctx->cur_co_ctx;
    co = coctx->co;

    for (index = 0; index < coctx->nsubreqs; index++) {
        dd("summary: reqs %d, subquery %d, pending %d, req %.*s",
           (int) coctx->nsubreqs,
           (int) index,
           (int) coctx->pending_subreqs,
           (int) r->uri.len, r->uri.data);

        /*  {{{ construct ret value */
        lua_createtable(co, 0 /* narr */, 4 /* nrec */);

        /*  copy captured status */
        lua_pushinteger(co, coctx->sr_statuses[index]);
        lua_setfield(co, -2, "status");

        dd("captured subrequest flags: %d", (int) coctx->sr_flags[index]);

        /* set truncated flag if truncation happens */
        if (coctx->sr_flags[index] & NGX_HTTP_LUA_SUBREQ_TRUNCATED) {
            lua_pushboolean(co, 1);
            lua_setfield(co, -2, "truncated");

        } else {
            lua_pushboolean(co, 0);
            lua_setfield(co, -2, "truncated");
        }

        /*  copy captured body */

        body_str = &coctx->sr_bodies[index];

        lua_pushlstring(co, (char *) body_str->data, body_str->len);
        lua_setfield(co, -2, "body");

        if (body_str->data) {
            dd("free body buffer ASAP");
            ngx_pfree(r->pool, body_str->data);
        }

        /* copy captured headers */

        sr_headers = coctx->sr_headers[index];

        part = &sr_headers->headers.part;
        count = part->nelts;
        while (part->next) {
            part = part->next;
            count += part->nelts;
        }

        lua_createtable(co, 0, count + 5); /* res.header */

        dd("saving subrequest response headers");

        part = &sr_headers->headers.part;
        header = part->elts;

        for (i = 0; /* void */; i++) {

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

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

            dd("checking sr header %.*s", (int) header[i].key.len,
               header[i].key.data);

#if 1
            if (header[i].hash == 0) {
                continue;
            }
#endif

            header[i].hash = 0;

            dd("pushing sr header %.*s", (int) header[i].key.len,
               header[i].key.data);

            lua_pushlstring(co, (char *) header[i].key.data,
                            header[i].key.len); /* header key */
            lua_pushvalue(co, -1); /* stack: table key key */

            /* check if header already exists */
            lua_rawget(co, -3); /* stack: table key value */

            if (lua_isnil(co, -1)) {
                lua_pop(co, 1); /* stack: table key */

                lua_pushlstring(co, (char *) header[i].value.data,
                                header[i].value.len);
                    /* stack: table key value */

                lua_rawset(co, -3); /* stack: table */

            } else {

                if (!lua_istable(co, -1)) { /* already inserted one value */
                    lua_createtable(co, 4, 0);
                        /* stack: table key value table */

                    lua_insert(co, -2); /* stack: table key table value */
                    lua_rawseti(co, -2, 1); /* stack: table key table */

                    lua_pushlstring(co, (char *) header[i].value.data,
                                    header[i].value.len);
                        /* stack: table key table value */

                    lua_rawseti(co, -2, lua_objlen(co, -2) + 1);
                        /* stack: table key table */

                    lua_rawset(co, -3); /* stack: table */

                } else {
                    lua_pushlstring(co, (char *) header[i].value.data,
                                    header[i].value.len);
                        /* stack: table key table value */

                    lua_rawseti(co, -2, lua_objlen(co, -2) + 1);
                        /* stack: table key table */

                    lua_pop(co, 2); /* stack: table */
                }
            }
        }

        if (sr_headers->content_type.len) {
            lua_pushliteral(co, "Content-Type"); /* header key */
            lua_pushlstring(co, (char *) sr_headers->content_type.data,
                            sr_headers->content_type.len); /* head key value */
            lua_rawset(co, -3); /* head */
        }

        if (sr_headers->content_length == NULL
            && sr_headers->content_length_n >= 0)
        {
            lua_pushliteral(co, "Content-Length"); /* header key */

            lua_pushnumber(co, (lua_Number) sr_headers->content_length_n);
                /* head key value */

            lua_rawset(co, -3); /* head */
        }

        /* to work-around an issue in ngx_http_static_module
         * (github issue #41) */
        if (sr_headers->location && sr_headers->location->value.len) {
            lua_pushliteral(co, "Location"); /* header key */
            lua_pushlstring(co, (char *) sr_headers->location->value.data,
                            sr_headers->location->value.len);
            /* head key value */
            lua_rawset(co, -3); /* head */
        }

        if (sr_headers->last_modified_time != -1) {
            if (sr_headers->status != NGX_HTTP_OK
                && sr_headers->status != NGX_HTTP_PARTIAL_CONTENT
                && sr_headers->status != NGX_HTTP_NOT_MODIFIED
                && sr_headers->status != NGX_HTTP_NO_CONTENT)
            {
                sr_headers->last_modified_time = -1;
                sr_headers->last_modified = NULL;
            }
        }

        if (sr_headers->last_modified == NULL
            && sr_headers->last_modified_time != -1)
        {
            (void) ngx_http_time(buf, sr_headers->last_modified_time);

            lua_pushliteral(co, "Last-Modified"); /* header key */
            lua_pushlstring(co, (char *) buf, sizeof(buf)); /* head key value */
            lua_rawset(co, -3); /* head */
        }

        lua_setfield(co, -2, "header");

        /*  }}} */
    }
}
ngx_http_lua_handle_subreq_responses(ngx_http_request_t *r,
    ngx_http_lua_ctx_t *ctx)
{
    ngx_uint_t                   i, count;
    ngx_uint_t                   index;
    lua_State                   *co;
    ngx_str_t                   *body_str;
    ngx_table_elt_t             *header;
    ngx_list_part_t             *part;
    ngx_http_headers_out_t      *sr_headers;
    ngx_http_lua_co_ctx_t       *coctx;

    u_char                  buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1];

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "lua handle subrequest responses");

    coctx = ctx->cur_co_ctx;
    co = coctx->co;

    for (index = 0; index < coctx->nsubreqs; index++) {
        dd("summary: reqs %d, subquery %d, pending %d, req %.*s",
           (int) coctx->nsubreqs,
           (int) index,
           (int) coctx->pending_subreqs,
           (int) r->uri.len, r->uri.data);

         
        lua_createtable(co, 0  , 4  );

         
        lua_pushinteger(co, coctx->sr_statuses[index]);
        lua_setfield(co, -2, "status");

        dd("captured subrequest flags: %d", (int) coctx->sr_flags[index]);

         
        if (coctx->sr_flags[index] & NGX_HTTP_LUA_SUBREQ_TRUNCATED) {
            lua_pushboolean(co, 1);
            lua_setfield(co, -2, "truncated");

        } else {
            lua_pushboolean(co, 0);
            lua_setfield(co, -2, "truncated");
        }

         

        body_str = &coctx->sr_bodies[index];

        lua_pushlstring(co, (char *) body_str->data, body_str->len);
        lua_setfield(co, -2, "body");

        if (body_str->data) {
            dd("free body buffer ASAP");
            ngx_pfree(r->pool, body_str->data);
        }

         

        sr_headers = coctx->sr_headers[index];

        part = &sr_headers->headers.part;
        count = part->nelts;
        while (part->next) {
            part = part->next;
            count += part->nelts;
        }

        lua_createtable(co, 0, count + 5);  

        dd("saving subrequest response headers");

        part = &sr_headers->headers.part;
        header = part->elts;

        for (i = 0;  ; i++) {

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

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

            dd("checking sr header %.*s", (int) header[i].key.len,
               header[i].key.data);

#if 1
            if (header[i].hash == 0) {
                continue;
            }
#endif

            header[i].hash = 0;

            dd("pushing sr header %.*s", (int) header[i].key.len,
               header[i].key.data);

            lua_pushlstring(co, (char *) header[i].key.data,
                            header[i].key.len);  
            lua_pushvalue(co, -1);  

             
            lua_rawget(co, -3);  

            if (lua_isnil(co, -1)) {
                lua_pop(co, 1);  

                lua_pushlstring(co, (char *) header[i].value.data,
                                header[i].value.len);
                     

                lua_rawset(co, -3);  

            } else {

                if (!lua_istable(co, -1)) {  
                    lua_createtable(co, 4, 0);
                         

                    lua_insert(co, -2);  
                    lua_rawseti(co, -2, 1);  

                    lua_pushlstring(co, (char *) header[i].value.data,
                                    header[i].value.len);
                         

                    lua_rawseti(co, -2, lua_objlen(co, -2) + 1);
                         

                    lua_rawset(co, -3);  

                } else {
                    lua_pushlstring(co, (char *) header[i].value.data,
                                    header[i].value.len);
                         

                    lua_rawseti(co, -2, lua_objlen(co, -2) + 1);
                         

                    lua_pop(co, 2);  
                }
            }
        }

        if (sr_headers->content_type.len) {
            lua_pushliteral(co, "Content-Type");  
            lua_pushlstring(co, (char *) sr_headers->content_type.data,
                            sr_headers->content_type.len);  
            lua_rawset(co, -3);  
        }

        if (sr_headers->content_length == NULL
            && sr_headers->content_length_n >= 0)
        {
            lua_pushliteral(co, "Content-Length");  

            lua_pushnumber(co, (lua_Number) sr_headers->content_length_n);
                 

            lua_rawset(co, -3);  
        }

         
        if (sr_headers->location && sr_headers->location->value.len) {
            lua_pushliteral(co, "Location");  
            lua_pushlstring(co, (char *) sr_headers->location->value.data,
                            sr_headers->location->value.len);
             
            lua_rawset(co, -3);  
        }

        if (sr_headers->last_modified_time != -1) {
            if (sr_headers->status != NGX_HTTP_OK
                && sr_headers->status != NGX_HTTP_PARTIAL_CONTENT
                && sr_headers->status != NGX_HTTP_NOT_MODIFIED
                && sr_headers->status != NGX_HTTP_NO_CONTENT)
            {
                sr_headers->last_modified_time = -1;
                sr_headers->last_modified = NULL;
            }
        }

        if (sr_headers->last_modified == NULL
            && sr_headers->last_modified_time != -1)
        {
            (void) ngx_http_time(buf, sr_headers->last_modified_time);

            lua_pushliteral(co, "Last-Modified");  
            lua_pushlstring(co, (char *) buf, sizeof(buf));  
            lua_rawset(co, -3);  
        }

        lua_setfield(co, -2, "header");

         
    }
}