На сегодняшний день достаточно руководств и описаний, как разработать собственный модуль под nginx. Для тех, кто на танке, ссылки можно найти на сайте nginx
Но, как использовать данные POST, к сожалению информации кот наплакал.
Начну с того, что Игорь Сысоев (автор nginx ) очень осторожно смотрит на использование POST данных в модулях. И это в принципе, обоснованно: как правило, в POST данные занимают большой объём по сравнению с объемом заголовком, по этому и механизм обработки у них иной. А обработка лишних данных всегда может косвенно повлиять на производительность. Процессы в nginx должны быть как можно легче, чтоб вокеры как можно быстрее обработали соединение и приступили к обработке следующего.
Рассмотрим цикл обработки:
Как правило, все http модули вешаются на фазу генерации ответа. Исключение, конечно, upstream модули и фильтры. Более подробно у Emiller
В случае с POST все иначе, он может вообще не обрабатываться. Если мы посмотрим структуру запроса ngx_http_request_t * r, которая красной нитью проходит по всем http модулям, то в контент хандлере, значение полей:
r->request_body->buf — текущий буфер и
r->request_body->bufs — цепочка буферов данных POST запроса будут пустыми (NULL). Это потому, что он и не начинался обрабатываться.
Обработка POST запроса осуществляется установкой кэлбека «хандлер тела» в ngx_http_read_client_request_body (функция определена в http_request_body.c).
Должен быть приблизительно такой код:
ngx_http_mymodule_body_handler — это хандлер обработки тела POST запроса. В Хандлере тела, вызывается Хандлер фазы. Код Хандлера тела должен иметь приблизительно такой вид:
В Хандлере фазы осуществляется обработка чанков, содержащих POST данные. Хандлер фазы обрабатывает данные и вновь устанавливает кэллбэк на обработку тела. Код Хандлера фазы должен быть приблизительно такой:
Данные POSt запроса находятся в структуре ngx_http_request_body_t определенной в ngx_http_request.h
указатель на эту структуру определен в структуре запроса ngx_http_request_s:
r->request_body;
Данные текущего окна определены фреймом буфера:
r->request_body->buf->start
r->request_body->buf->end
или при больших размерах POST запроса данные извлекаются из цепочки буферов r->request_body->bufs->buf->start...end и далее извлекается следующий буфер, адрес которого в поле next. см структуру ngx_chain_s:
Но, как использовать данные POST, к сожалению информации кот наплакал.
Начну с того, что Игорь Сысоев (автор nginx ) очень осторожно смотрит на использование POST данных в модулях. И это в принципе, обоснованно: как правило, в POST данные занимают большой объём по сравнению с объемом заголовком, по этому и механизм обработки у них иной. А обработка лишних данных всегда может косвенно повлиять на производительность. Процессы в nginx должны быть как можно легче, чтоб вокеры как можно быстрее обработали соединение и приступили к обработке следующего.
Рассмотрим цикл обработки:
- фаза чтения запроса;
- фаза преобразование URI на уровне сервера;
- поиск конфигурации в которой будет обрабатываться запрос;
- фаза преобразование URI на уровне location;
- фаза обработки результатов преобразование URI запроса;
- подготовительная фаза для проверки доступа;
- фаза проверки доступа;
- фаза обработки результатов проверки доступа;
- фаза обработки try_files;
- фаза генерации ответа;
- фаза записи логов.
Как правило, все http модули вешаются на фазу генерации ответа. Исключение, конечно, upstream модули и фильтры. Более подробно у Emiller
В случае с POST все иначе, он может вообще не обрабатываться. Если мы посмотрим структуру запроса ngx_http_request_t * r, которая красной нитью проходит по всем http модулям, то в контент хандлере, значение полей:
r->request_body->buf — текущий буфер и
r->request_body->bufs — цепочка буферов данных POST запроса будут пустыми (NULL). Это потому, что он и не начинался обрабатываться.
Обработка POST запроса осуществляется установкой кэлбека «хандлер тела» в ngx_http_read_client_request_body (функция определена в http_request_body.c).
Должен быть приблизительно такой код:
rc = ngx_http_read_client_request_body(r, ngx_http_mymodule_body_handler ); // устанавливаем обработчик "хандлер тела"<br/>
// проверяем результат обработки, если не отработало, то возвращает код NGX_AGAIN,<br/>
<br/>
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {<br/>
return rc;<br/>
}<br/>
<br/>
return NGX_DONE;
ngx_http_mymodule_body_handler — это хандлер обработки тела POST запроса. В Хандлере тела, вызывается Хандлер фазы. Код Хандлера тела должен иметь приблизительно такой вид:
static void ngx_http_mymodule_body_handler ( request_body *r ) {<br/>
ngx_int_t rc = NGX_OK;<br/>
<br/>
rc = ngx_http_mymodule_phase_handler ( r ); // вызываем явно обработчик хандлера фазы <br/>
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {<br/>
ngx_http_finalize_request(f,0); // если все удачно, то финализируем запрос, иначе зависним.<br/>
}<br/>
return;<br/>
}<br/>
В Хандлере фазы осуществляется обработка чанков, содержащих POST данные. Хандлер фазы обрабатывает данные и вновь устанавливает кэллбэк на обработку тела. Код Хандлера фазы должен быть приблизительно такой:
<br/>
ngx_int_t ngx_http_mymodule_phase_handler (request_body *r ) {<br/>
<br/>
ngx_int_t rc = NGX_OK;<br/>
if(r->request_body == NULL) { <br/>
// в этом месте POST запрос еще не принимался, необходимо установить хандлер <br/>
rc = ngx_http_read_client_request_body(r, ngx_http_mymodule_body_handler );<br/>
<br/>
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {<br/>
return rc;<br/>
}<br/>
<br/>
return NGX_DONE;<br/>
}<br/>
<br/>
// Всё ещё принимаем тело?<br/>
if(r->request_body->rest) {<br/>
return NGX_DONE;<br/>
}<br/>
return rc;<br/>
} <br/>
Данные POSt запроса находятся в структуре ngx_http_request_body_t определенной в ngx_http_request.h
typedef struct {<br/>
ngx_temp_file_t *temp_file; // имя временного файла (если необходимо)<br/>
ngx_chain_t *bufs; // цепочка вх. буферов<br/>
ngx_buf_t *buf; // текущий буфер<br/>
off_t rest; <br/>
ngx_chain_t *to_write; <br/>
ngx_http_client_body_handler_pt post_handler;<br/>
} ngx_http_request_body_t;<br/>
указатель на эту структуру определен в структуре запроса ngx_http_request_s:
r->request_body;
Данные текущего окна определены фреймом буфера:
r->request_body->buf->start
r->request_body->buf->end
или при больших размерах POST запроса данные извлекаются из цепочки буферов r->request_body->bufs->buf->start...end и далее извлекается следующий буфер, адрес которого в поле next. см структуру ngx_chain_s:
// core/ngx_buf.h
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
size -- размер буфера (расстояние между start и end) должен соответствовать значению Content-Length заголовка определенного в r->headers_in->off_t ;
Для цепочки буферов - это сумма расстояний каждого из буферов.
Отдельное спасибо Валерию Холодкову за консультации.