ngx_http_subrequest

Valery Kholodkov valery+nginxru at grid.net.ru
Thu Jul 17 23:00:18 MSD 2008


Здравствуйте!

Я пишу модуль для nginx который принимает и разбирает тело POST-запроса 
и передает тело бакэнду в несколько измененном виде.

Я хочу, чтобы бакэндом мог быть не только proxy, но и fastcgi или, по 
возможности, внутренний location nginx. В случае внутреннего location'а 
тело запроса можно отбросить и использовать метод GET.

Я обаружил, что построение модуля согласно пункту 3.2 руководства Эвана 
Миллера (http://emiller.info/nginx-modules-guide.html#proxying) для 
моего случая не подходит, поскольку предполагает реализацию обработки 
всех возможных типов бакэндов. Я боюсь, что таким образом я задублирую 
существующий код, который это уже делает и, таким образом, получу кучу 
ошибок.

Соответственно, я посмотрел ещё раз на функцию ngx_http_subrequest и 
попытался использовать её таким же образом, каким она используется в 
ngx_http_ssi_filter_module.c. Я обнаружил, что она несколько не 
предназначена для работы с запросами, у которых http-метод отличается от 
GET. А именно, хотя тело и отдается бакэнду, http-метод по-умолчанию 
устанавливается в GET. Я вынужден был принудительно изменить метод на 
POST директивой (недокументированной) proxy_method POST.

Хотя я и получил результат (тело отдается бакэнду), но у меня возникла 
проблема с отображением сообщений об ошибках. А именно если генерируется 
Bad Gateway или Gateway Timeout, то в ответе nginx отсутствует заголовок 
(тело передается) и клиент (в моем случае Mozilla) "висит".

Я изучил исходники nginx и обнаружил, что функции ngx_http_subrequest, 
ngx_http_finalize_request, а так же файл ngx_http_upstream.c содержат 
сложную многоуровневую логику, которая напрямую с моей задачей не 
связана, но тем не менее так или иначе в её решение вовлечена. В этом 
коде, а так же в коде существующих модулей я не нашел примеров, которые 
могли хотя бы намекнуть на решение. Поэтому я сдался и решил спросить в 
рассылке.

А именно, я имею вот такой код:

static ngx_int_t
ngx_http_upload_output(ngx_http_request_t *r, void *data, ngx_int_t rc)
{
     if (rc == NGX_ERROR || r->connection->error) {
         return rc;
     }

     r->headers_out.status = r->err_status;

     if (!r->header_sent) {
         if (ngx_http_set_content_type(r) == NGX_ERROR) {
             return NGX_ERROR;
         }

         if (ngx_http_send_header(r) == NGX_ERROR) {
             return NGX_ERROR;
         }
     }

     ngx_http_send_special(r, NGX_HTTP_LAST|NGX_HTTP_FLUSH);

     return rc;
}

static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r) {
     ngx_http_upload_loc_conf_t  *ulcf = ngx_http_get_module_loc_conf(r, 
ngx_http_upload_module);
     ngx_http_upload_ctx_t       *ctx = ngx_http_get_module_ctx(r, 
ngx_http_upload_module);

     ngx_str_t                   args;
     ngx_uint_t                  flags;
     ngx_int_t                   rc;
     ngx_str_t                   *uri;
     ngx_http_post_subrequest_t  *psr;
     ngx_http_request_t          *sr;
     ngx_buf_t                      *b;
     ngx_chain_t                    *cl

     [... Здесь генерируем тело, которое будет отдано бакэнду ...]

     // Изменяем тело запроса
     r->request_body->bufs = ctx->chain;

     // Код обработчика завершения подзапроса см. выше
     psr->handler = ngx_http_upload_output;

     // Здесь посылаем запрос бакэнду
     rc = ngx_http_subrequest(r, uri, &args, &sr, psr, flags);

     if (rc == NGX_ERROR) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
}

ngx_http_upload_body_handler -- это функция, которая вызывается, когда 
завершается разбор тела исходного запроса. Она несколько аналогична 
функции ngx_http_upload_body_handler.

Возникают следующие вопросы:

1) Как сгенерировать заголовок?
2) Верно ли я использую функцию ngx_http_subrequest (может вообще так не 
стоит делать)?
3) Можно ли сделать так, чтобы ngx_http_subrequest копировала http-метод 
из оригинального или родительского запроса при получении некоторого флажка?

nginx 0.6.13

-- 
Regards,
Valery Kholodkov





More information about the nginx-ru mailing list