Но, как использовать данные 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 ;
Для цепочки буферов - это сумма расстояний каждого из буферов.
Отдельное спасибо Валерию Холодкову за консультации.
Комментарии (7)
Tonik
31.12.2009 01:24Спасибо! Информации по nginx, что не говорите, все же мало. Поэтому каждый пост на эту тему — очень полезен. Продолжайте в том же духе!
akalend Автор
31.12.2009 01:24по конфигурации и использованию — информации вполне достаточно, что касается inSide, то тем кто знаеет и мог бы это написать, как всегда нет времени, да и их по пальцам можно пересчитать. Вот, сам с трудом нашел несколько часов чтоб изложить то, над чем бился несколько дней.
Tonik
31.12.2009 01:24Да, я именно про внутрение устройство. На прошлом хайлоаде был интересный доклад, но опять же это для тех кто уже в теме. А вот что бы сесть и с нуля написать модуль — сложнее. Но думаю со временем будут и такие доки.
akalend Автор
31.12.2009 01:24ровно два года назад я прочитал Емиллера (ссылка в статье, можно найти русский перевод) и в зимние каникулы сделал свой первый модуль labyrinter.ru/clicker (конечно у него есть недостатки...), модифицированная версия работает в одном из проектов. Теперь мной реализовано их штук пять, так что поверь, ничего сложного нет и информации для реализации модуля средней сложности вполне достаточно.
sunnybear
код нужно оформить нормально
akalend Автор
еще бы знать как
sunnybear
чтобы он был читабельным. Хотя бы отступами (и от пояснительного текста тоже). Можно и расцветкой.