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