static ngx_int_t
ngx_http_auth_request_handler(ngx_http_request_t *r)
{
    ngx_table_elt_t               *h, *ho;
    ngx_http_request_t            *sr;
    ngx_http_post_subrequest_t    *ps;
    ngx_http_auth_request_ctx_t   *ctx;
    ngx_http_auth_request_conf_t  *arcf;
    arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module);
    if (arcf->uri.len == 0)
    {
        return NGX_DECLINED;
    }
    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "auth request handler");
    ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);
    if (ctx != NULL)
    {
        if (!ctx->done)
        {
            return NGX_AGAIN;
        }
        /*
         * as soon as we are done - explicitly set variables to make
         * sure they will be available after internal redirects
         */
        if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK)
        {
            return NGX_ERROR;
        }
        /* return appropriate status */
        if (ctx->status == NGX_HTTP_FORBIDDEN)
        {
            return ctx->status;
        }
        if (ctx->status == NGX_HTTP_UNAUTHORIZED)
        {
            sr = ctx->subrequest;
            h = sr->headers_out.www_authenticate;
            if (!h && sr->upstream)
            {
                h = sr->upstream->headers_in.www_authenticate;
            }
            if (h)
            {
                ho = ngx_list_push(&r->headers_out.headers);
                if (ho == NULL)
                {
                    return NGX_ERROR;
                }
                *ho = *h;
                r->headers_out.www_authenticate = ho;
            }
            return ctx->status;
        }
        if (ctx->status >= NGX_HTTP_OK
                && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
        {
            return NGX_OK;
        }
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "auth request unexpected status: %d", ctx->status);
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
    if (ctx == NULL)
    {
        return NGX_ERROR;
    }
    ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (ps == NULL)
    {
        return NGX_ERROR;
    }
    ps->handler = ngx_http_auth_request_done;
    ps->data = ctx;
    if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
                            NGX_HTTP_SUBREQUEST_WAITED)
            != NGX_OK)
    {
        return NGX_ERROR;
    }
    /*
     * allocate fake request body to avoid attempts to read it and to make
     * sure real body file (if already read) won't be closed by upstream
     */
    sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
    if (sr->request_body == NULL)
    {
        return NGX_ERROR;
    }
    sr->header_only = 1;
    ctx->subrequest = sr;
    ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);
    return NGX_AGAIN;
}
static ngx_int_t
ngx_http_auth_request_handler(ngx_http_request_t *r)
{
    ngx_uint_t                    i;
    ngx_int_t                     rc;
    ngx_list_part_t               *part;
    ngx_table_elt_t               *h, *hi;
    ngx_http_request_t            *sr;
    ngx_http_post_subrequest_t    *ps;
    ngx_http_auth_request_ctx_t   *ctx;
    ngx_http_auth_request_conf_t  *arcf;

    arcf = ngx_http_get_module_loc_conf(r, ngx_http_shibboleth_module);

    if (arcf->uri.len == 0) {
        return NGX_DECLINED;
    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "shib request handler");

    ctx = ngx_http_get_module_ctx(r, ngx_http_shibboleth_module);

    if (ctx != NULL) {
        if (!ctx->done) {
            return NGX_AGAIN;
        }

        /*
         * as soon as we are done - explicitly set variables to make
         * sure they will be available after internal redirects
         */

        if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {
            return NGX_ERROR;
        }

        /*
         * Handle the subrequest
         * as per the FastCGI authorizer specification.
         */
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "shib request authorizer handler");
        sr = ctx->subrequest;

        if (ctx->status == NGX_HTTP_OK) {
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "shib request authorizer allows access");

            if (arcf->use_headers) {
                /*
                 * 200 response may include headers prefixed with `Variable-`,
                 * copy these into main request headers
                 */
                part = &sr->headers_out.headers.part;
                h = part->elts;

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

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

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

                    if (h[i].hash == 0) {
                        continue;
                    }

                    if (h[i].key.len >= 9 &&
                        ngx_strncasecmp(h[i].key.data, (u_char *) "Variable-", 9) == 0) {
                        /* copy header into original request */
                        hi = ngx_list_push(&r->headers_in.headers);

                        if (hi == NULL) {
                            return NGX_HTTP_INTERNAL_SERVER_ERROR;
                        }

                        /* Strip the Variable- prefix */
                        hi->key.len = h[i].key.len - 9;
                        hi->key.data = h[i].key.data + 9;
                        hi->hash = ngx_hash_key(hi->key.data, hi->key.len);
                        hi->value = h[i].value;

                        hi->lowcase_key = ngx_pnalloc(r->pool, hi->key.len);
                        if (hi->lowcase_key == NULL) {
                            return NGX_HTTP_INTERNAL_SERVER_ERROR;
                        }
                        ngx_strlow(hi->lowcase_key, hi->key.data, hi->key.len);

                        ngx_log_debug2(
                                NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                                "shib request authorizer copied header: \"%V: %V\"",
                                &hi->key, &hi->value);
                    }
                }

            } else {
                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "shib request authorizer not using headers");
            }


            return NGX_OK;
        }

        /*
         * Unconditionally return subrequest response status, headers
         * and content as per FastCGI spec (section 6.3).
         *
         * The subrequest response body cannot be returned as Nginx does not
         * currently support NGX_HTTP_SUBREQUEST_IN_MEMORY.
         */
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "shib request authorizer returning sub-response");

        /* copy status */
        r->headers_out.status = sr->headers_out.status;

        /* copy headers */
        part = &sr->headers_out.headers.part;
        h = part->elts;

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

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

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

            rc = ngx_http_set_output_header(r, h[i].key, h[i].value);
            if (rc == NGX_ERROR) {
                return NGX_ERROR;
            }

            ngx_log_debug2(
              NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
              "shib request authorizer returning header: \"%V: %V\"",
              &h[i].key, &h[i].value);
        }

        return ctx->status;
    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
    if (ctx == NULL) {
        return NGX_ERROR;
    }

    ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    if (ps == NULL) {
        return NGX_ERROR;
    }

    ps->handler = ngx_http_auth_request_done;
    ps->data = ctx;

    if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
                            NGX_HTTP_SUBREQUEST_WAITED)
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    /*
     * allocate fake request body to avoid attempts to read it and to make
     * sure real body file (if already read) won't be closed by upstream
     */

    sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
    if (sr->request_body == NULL) {
        return NGX_ERROR;
    }

    /* 
     * true FastCGI authorizers should always return the subrequest 
     * response body but the Nginx FastCGI handler does not support
     * NGX_HTTP_SUBREQUEST_IN_MEMORY at present.
     */
    sr->header_only = 1;

    ctx->subrequest = sr;

    ngx_http_set_ctx(r, ctx, ngx_http_shibboleth_module);

    return NGX_AGAIN;
}