xref: /unit/src/nxt_h1proto.c (revision 482)
1431Sigor@sysoev.ru 
2431Sigor@sysoev.ru /*
3431Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
4431Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
5431Sigor@sysoev.ru  */
6431Sigor@sysoev.ru 
7431Sigor@sysoev.ru #include <nxt_router.h>
8431Sigor@sysoev.ru #include <nxt_http.h>
9431Sigor@sysoev.ru 
10431Sigor@sysoev.ru 
11431Sigor@sysoev.ru static void nxt_h1p_read_header(nxt_task_t *task, void *obj, void *data);
12431Sigor@sysoev.ru static void nxt_h1p_header_parse(nxt_task_t *task, void *obj, void *data);
13431Sigor@sysoev.ru static nxt_int_t nxt_h1p_connection(void *ctx, nxt_http_field_t *field,
14431Sigor@sysoev.ru     uintptr_t data);
15431Sigor@sysoev.ru static nxt_int_t nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field,
16431Sigor@sysoev.ru     uintptr_t data);
17431Sigor@sysoev.ru static void nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r);
18431Sigor@sysoev.ru static void nxt_h1p_body_read(nxt_task_t *task, void *obj, void *data);
19431Sigor@sysoev.ru static void nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r);
20431Sigor@sysoev.ru static void nxt_h1p_request_header_send(nxt_task_t *task,
21431Sigor@sysoev.ru     nxt_http_request_t *r);
22431Sigor@sysoev.ru static void nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r,
23431Sigor@sysoev.ru     nxt_buf_t *out);
24431Sigor@sysoev.ru static nxt_buf_t *nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r,
25431Sigor@sysoev.ru     nxt_buf_t *out);
26431Sigor@sysoev.ru static void nxt_h1p_sent(nxt_task_t *task, void *obj, void *data);
27431Sigor@sysoev.ru static void nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto);
28431Sigor@sysoev.ru static void nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p,
29431Sigor@sysoev.ru     nxt_conn_t *c);
30431Sigor@sysoev.ru static void nxt_h1p_close(nxt_task_t *task, nxt_conn_t *c);
31431Sigor@sysoev.ru static void nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data);
32431Sigor@sysoev.ru static void nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data);
33431Sigor@sysoev.ru static void nxt_h1p_conn_timeout(nxt_task_t *task, void *obj, void *data);
34431Sigor@sysoev.ru static nxt_msec_t nxt_h1p_timeout_value(nxt_conn_t *c, uintptr_t data);
35431Sigor@sysoev.ru 
36431Sigor@sysoev.ru 
37431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_state;
38431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_header_state;
39431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_body_state;
40431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_send_state;
41431Sigor@sysoev.ru 
42431Sigor@sysoev.ru 
43431Sigor@sysoev.ru const nxt_http_proto_body_read_t  nxt_http_proto_body_read[3] = {
44431Sigor@sysoev.ru     nxt_h1p_request_body_read,
45431Sigor@sysoev.ru     NULL,
46431Sigor@sysoev.ru     NULL,
47431Sigor@sysoev.ru };
48431Sigor@sysoev.ru 
49431Sigor@sysoev.ru 
50431Sigor@sysoev.ru const nxt_http_proto_local_addr_t  nxt_http_proto_local_addr[3] = {
51431Sigor@sysoev.ru     nxt_h1p_request_local_addr,
52431Sigor@sysoev.ru     NULL,
53431Sigor@sysoev.ru     NULL,
54431Sigor@sysoev.ru };
55431Sigor@sysoev.ru 
56431Sigor@sysoev.ru 
57431Sigor@sysoev.ru const nxt_http_proto_header_send_t  nxt_http_proto_header_send[3] = {
58431Sigor@sysoev.ru     nxt_h1p_request_header_send,
59431Sigor@sysoev.ru     NULL,
60431Sigor@sysoev.ru     NULL,
61431Sigor@sysoev.ru };
62431Sigor@sysoev.ru 
63431Sigor@sysoev.ru 
64431Sigor@sysoev.ru const nxt_http_proto_send_t  nxt_http_proto_send[3] = {
65431Sigor@sysoev.ru     nxt_h1p_request_send,
66431Sigor@sysoev.ru     NULL,
67431Sigor@sysoev.ru     NULL,
68431Sigor@sysoev.ru };
69431Sigor@sysoev.ru 
70431Sigor@sysoev.ru 
71431Sigor@sysoev.ru const nxt_http_proto_close_t  nxt_http_proto_close[3] = {
72431Sigor@sysoev.ru     nxt_h1p_request_close,
73431Sigor@sysoev.ru     NULL,
74431Sigor@sysoev.ru     NULL,
75431Sigor@sysoev.ru };
76431Sigor@sysoev.ru 
77431Sigor@sysoev.ru 
78431Sigor@sysoev.ru static nxt_lvlhsh_t            nxt_h1p_fields_hash;
79431Sigor@sysoev.ru 
80431Sigor@sysoev.ru static nxt_http_field_proc_t   nxt_h1p_fields[] = {
81431Sigor@sysoev.ru     { nxt_string("Connection"),        &nxt_h1p_connection, 0 },
82431Sigor@sysoev.ru     { nxt_string("Transfer-Encoding"), &nxt_h1p_transfer_encoding, 0 },
83431Sigor@sysoev.ru 
84431Sigor@sysoev.ru     { nxt_string("Host"),              &nxt_http_request_host, 0 },
85431Sigor@sysoev.ru     { nxt_string("Cookie"),            &nxt_http_request_field,
86431Sigor@sysoev.ru         offsetof(nxt_http_request_t, cookie) },
87431Sigor@sysoev.ru     { nxt_string("Content-Type"),      &nxt_http_request_field,
88431Sigor@sysoev.ru         offsetof(nxt_http_request_t, content_type) },
89431Sigor@sysoev.ru     { nxt_string("Content-Length"),    &nxt_http_request_content_length, 0 },
90431Sigor@sysoev.ru };
91431Sigor@sysoev.ru 
92431Sigor@sysoev.ru 
93431Sigor@sysoev.ru nxt_int_t
94431Sigor@sysoev.ru nxt_h1p_init(nxt_task_t *task, nxt_runtime_t *rt)
95431Sigor@sysoev.ru {
96431Sigor@sysoev.ru     return nxt_http_fields_hash(&nxt_h1p_fields_hash, rt->mem_pool,
97431Sigor@sysoev.ru                                 nxt_h1p_fields, nxt_nitems(nxt_h1p_fields));
98431Sigor@sysoev.ru }
99431Sigor@sysoev.ru 
100431Sigor@sysoev.ru 
101431Sigor@sysoev.ru void
102431Sigor@sysoev.ru nxt_http_conn_init(nxt_task_t *task, void *obj, void *data)
103431Sigor@sysoev.ru {
104431Sigor@sysoev.ru     nxt_conn_t               *c;
105431Sigor@sysoev.ru     nxt_socket_conf_t        *skcf;
106431Sigor@sysoev.ru     nxt_event_engine_t       *engine;
107431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
108431Sigor@sysoev.ru 
109431Sigor@sysoev.ru     c = obj;
110431Sigor@sysoev.ru     joint = data;
111431Sigor@sysoev.ru 
112431Sigor@sysoev.ru     nxt_debug(task, "http conn init");
113431Sigor@sysoev.ru 
114431Sigor@sysoev.ru     c->joint = joint;
115431Sigor@sysoev.ru     joint->count++;
116431Sigor@sysoev.ru 
117431Sigor@sysoev.ru     skcf = joint->socket_conf;
118431Sigor@sysoev.ru     c->local = skcf->sockaddr;
119431Sigor@sysoev.ru     c->socket.data = NULL;
120431Sigor@sysoev.ru 
121431Sigor@sysoev.ru     engine = task->thread->engine;
122431Sigor@sysoev.ru     c->read_work_queue = &engine->fast_work_queue;
123431Sigor@sysoev.ru     c->write_work_queue = &engine->fast_work_queue;
124431Sigor@sysoev.ru 
125431Sigor@sysoev.ru     c->read_state = &nxt_h1p_idle_state;
126431Sigor@sysoev.ru 
127431Sigor@sysoev.ru     nxt_conn_wait(c);
128431Sigor@sysoev.ru }
129431Sigor@sysoev.ru 
130431Sigor@sysoev.ru 
131431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_state
132431Sigor@sysoev.ru     nxt_aligned(64) =
133431Sigor@sysoev.ru {
134431Sigor@sysoev.ru     .ready_handler = nxt_h1p_read_header,
135431Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
136431Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
137431Sigor@sysoev.ru 
138431Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_timeout,
139431Sigor@sysoev.ru     .timer_value = nxt_h1p_timeout_value,
140431Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, idle_timeout),
141431Sigor@sysoev.ru };
142431Sigor@sysoev.ru 
143431Sigor@sysoev.ru 
144431Sigor@sysoev.ru static void
145431Sigor@sysoev.ru nxt_h1p_read_header(nxt_task_t *task, void *obj, void *data)
146431Sigor@sysoev.ru {
147431Sigor@sysoev.ru     size_t                   size;
148431Sigor@sysoev.ru     nxt_conn_t               *c;
149431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
150431Sigor@sysoev.ru 
151431Sigor@sysoev.ru     c = obj;
152431Sigor@sysoev.ru 
153431Sigor@sysoev.ru     nxt_debug(task, "h1p read header");
154431Sigor@sysoev.ru 
155431Sigor@sysoev.ru     if (c->read == NULL) {
156431Sigor@sysoev.ru         joint = c->joint;
157431Sigor@sysoev.ru         size = joint->socket_conf->header_buffer_size;
158431Sigor@sysoev.ru 
159431Sigor@sysoev.ru         c->read = nxt_buf_mem_alloc(c->mem_pool, size, 0);
160431Sigor@sysoev.ru         if (nxt_slow_path(c->read == NULL)) {
161431Sigor@sysoev.ru             nxt_h1p_conn_error(task, c, c->socket.data);
162431Sigor@sysoev.ru             return;
163431Sigor@sysoev.ru         }
164431Sigor@sysoev.ru     }
165431Sigor@sysoev.ru 
166431Sigor@sysoev.ru     c->read_state = &nxt_h1p_read_header_state;
167431Sigor@sysoev.ru 
168431Sigor@sysoev.ru     nxt_conn_read(task->thread->engine, c);
169431Sigor@sysoev.ru }
170431Sigor@sysoev.ru 
171431Sigor@sysoev.ru 
172431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_header_state
173431Sigor@sysoev.ru     nxt_aligned(64) =
174431Sigor@sysoev.ru {
175431Sigor@sysoev.ru     .ready_handler = nxt_h1p_header_parse,
176431Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
177431Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
178431Sigor@sysoev.ru 
179431Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_timeout,
180431Sigor@sysoev.ru     .timer_value = nxt_h1p_timeout_value,
181431Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, header_read_timeout),
182431Sigor@sysoev.ru };
183431Sigor@sysoev.ru 
184431Sigor@sysoev.ru 
185431Sigor@sysoev.ru static void
186431Sigor@sysoev.ru nxt_h1p_header_parse(nxt_task_t *task, void *obj, void *data)
187431Sigor@sysoev.ru {
188431Sigor@sysoev.ru     size_t                   size;
189431Sigor@sysoev.ru     nxt_int_t                ret;
190431Sigor@sysoev.ru     nxt_buf_t                *in, *b;
191431Sigor@sysoev.ru     nxt_conn_t               *c;
192431Sigor@sysoev.ru     nxt_h1proto_t            *h1p;
193480Svbart@nginx.com     nxt_http_status_t        status;
194431Sigor@sysoev.ru     nxt_http_request_t       *r;
195431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
196431Sigor@sysoev.ru 
197431Sigor@sysoev.ru     c = obj;
198431Sigor@sysoev.ru     h1p = data;
199431Sigor@sysoev.ru 
200431Sigor@sysoev.ru     nxt_debug(task, "h1p header parse");
201431Sigor@sysoev.ru 
202431Sigor@sysoev.ru     if (h1p == NULL) {
203431Sigor@sysoev.ru         h1p = nxt_mp_zget(c->mem_pool, sizeof(nxt_h1proto_t));
204431Sigor@sysoev.ru         if (nxt_slow_path(h1p == NULL)) {
205431Sigor@sysoev.ru             goto fail;
206431Sigor@sysoev.ru         }
207431Sigor@sysoev.ru 
208431Sigor@sysoev.ru         c->socket.data = h1p;
209431Sigor@sysoev.ru         h1p->conn = c;
210431Sigor@sysoev.ru     }
211431Sigor@sysoev.ru 
212431Sigor@sysoev.ru     r = h1p->request;
213431Sigor@sysoev.ru 
214431Sigor@sysoev.ru     if (r == NULL) {
215431Sigor@sysoev.ru         r = nxt_http_request_create(task);
216431Sigor@sysoev.ru         if (nxt_slow_path(r == NULL)) {
217431Sigor@sysoev.ru             goto fail;
218431Sigor@sysoev.ru         }
219431Sigor@sysoev.ru 
220431Sigor@sysoev.ru         h1p->request = r;
221431Sigor@sysoev.ru         r->proto.h1 = h1p;
222431Sigor@sysoev.ru         joint = c->joint;
223431Sigor@sysoev.ru         r->socket_conf = joint->socket_conf;
224431Sigor@sysoev.ru 
225431Sigor@sysoev.ru         r->remote = c->remote;
226431Sigor@sysoev.ru 
227431Sigor@sysoev.ru         ret = nxt_http_parse_request_init(&h1p->parser, r->mem_pool);
228431Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
229431Sigor@sysoev.ru             /*
230431Sigor@sysoev.ru              * The request is very uncomplete here,
231431Sigor@sysoev.ru              * so "internal server error" useless here.
232431Sigor@sysoev.ru              */
233431Sigor@sysoev.ru             nxt_mp_release(r->mem_pool);
234431Sigor@sysoev.ru             h1p->request = NULL;
235431Sigor@sysoev.ru             goto fail;
236431Sigor@sysoev.ru         }
237431Sigor@sysoev.ru     }
238431Sigor@sysoev.ru 
239431Sigor@sysoev.ru     ret = nxt_http_parse_request(&h1p->parser, &c->read->mem);
240431Sigor@sysoev.ru 
241431Sigor@sysoev.ru     if (nxt_fast_path(ret == NXT_DONE)) {
242431Sigor@sysoev.ru         r->target.start = h1p->parser.target_start;
243431Sigor@sysoev.ru         r->target.length = h1p->parser.target_end - h1p->parser.target_start;
244431Sigor@sysoev.ru 
245431Sigor@sysoev.ru         r->version.start = h1p->parser.version.str;
246431Sigor@sysoev.ru         r->version.length = sizeof(h1p->parser.version.str);
247431Sigor@sysoev.ru 
248431Sigor@sysoev.ru         r->method = &h1p->parser.method;
249431Sigor@sysoev.ru         r->path = &h1p->parser.path;
250431Sigor@sysoev.ru         r->args = &h1p->parser.args;
251431Sigor@sysoev.ru 
252431Sigor@sysoev.ru         /*
253431Sigor@sysoev.ru          * By default the keepalive mode is disabled in HTTP/1.0 and
254431Sigor@sysoev.ru          * enabled in HTTP/1.1.  The mode can be overridden later by
255431Sigor@sysoev.ru          * the "Connection" field processed in nxt_h1p_connection().
256431Sigor@sysoev.ru          */
257481Svbart@nginx.com         h1p->keepalive = (h1p->parser.version.s.minor != '0');
258431Sigor@sysoev.ru 
259431Sigor@sysoev.ru         r->fields = h1p->parser.fields;
260431Sigor@sysoev.ru 
261431Sigor@sysoev.ru         ret = nxt_http_fields_process(r->fields, &nxt_h1p_fields_hash, r);
262431Sigor@sysoev.ru 
263431Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_OK)) {
264431Sigor@sysoev.ru             r->state->ready_handler(task, r, NULL);
265431Sigor@sysoev.ru             return;
266431Sigor@sysoev.ru         }
267431Sigor@sysoev.ru 
268480Svbart@nginx.com         /* ret == NXT_ERROR */
269480Svbart@nginx.com 
270480Svbart@nginx.com         nxt_http_request_error(task, r, NXT_HTTP_BAD_REQUEST);
271480Svbart@nginx.com         return;
272480Svbart@nginx.com     }
273480Svbart@nginx.com 
274480Svbart@nginx.com     if (ret == NXT_AGAIN) {
275431Sigor@sysoev.ru         in = c->read;
276431Sigor@sysoev.ru 
277431Sigor@sysoev.ru         if (nxt_buf_mem_free_size(&in->mem) == 0) {
278431Sigor@sysoev.ru             size = r->socket_conf->large_header_buffer_size;
279431Sigor@sysoev.ru 
280431Sigor@sysoev.ru             if (size <= (size_t) nxt_buf_mem_used_size(&in->mem)
281431Sigor@sysoev.ru                 || h1p->nbuffers >= r->socket_conf->large_header_buffers)
282431Sigor@sysoev.ru             {
283431Sigor@sysoev.ru                 nxt_http_request_error(task, r,
284431Sigor@sysoev.ru                                       NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE);
285431Sigor@sysoev.ru                 return;
286431Sigor@sysoev.ru             }
287431Sigor@sysoev.ru 
288431Sigor@sysoev.ru             b = nxt_buf_mem_alloc(c->mem_pool, size, 0);
289431Sigor@sysoev.ru             if (nxt_slow_path(b == NULL)) {
290431Sigor@sysoev.ru                 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
291431Sigor@sysoev.ru                 return;
292431Sigor@sysoev.ru             }
293431Sigor@sysoev.ru 
294431Sigor@sysoev.ru             h1p->nbuffers++;
295431Sigor@sysoev.ru 
296431Sigor@sysoev.ru             size = nxt_buf_mem_used_size(&in->mem);
297431Sigor@sysoev.ru             b->mem.free = nxt_cpymem(b->mem.pos, in->mem.pos, size);
298431Sigor@sysoev.ru 
299459Sigor@sysoev.ru             in->next = h1p->buffers;
300459Sigor@sysoev.ru             h1p->buffers = in;
301431Sigor@sysoev.ru             c->read = b;
302431Sigor@sysoev.ru         }
303431Sigor@sysoev.ru 
304431Sigor@sysoev.ru         nxt_conn_read(task->thread->engine, c);
305431Sigor@sysoev.ru         return;
306431Sigor@sysoev.ru     }
307431Sigor@sysoev.ru 
308480Svbart@nginx.com     switch (ret) {
309480Svbart@nginx.com 
310480Svbart@nginx.com     case NXT_HTTP_PARSE_INVALID:
311480Svbart@nginx.com         status = NXT_HTTP_BAD_REQUEST;
312480Svbart@nginx.com         break;
313431Sigor@sysoev.ru 
314*482Svbart@nginx.com     case NXT_HTTP_PARSE_UNSUPPORTED_VERSION:
315*482Svbart@nginx.com         status = NXT_HTTP_VERSION_NOT_SUPPORTED;
316*482Svbart@nginx.com         break;
317*482Svbart@nginx.com 
318480Svbart@nginx.com     case NXT_HTTP_PARSE_TOO_LARGE_FIELD:
319480Svbart@nginx.com         status = NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
320480Svbart@nginx.com         break;
321480Svbart@nginx.com 
322480Svbart@nginx.com     default:
323480Svbart@nginx.com     case NXT_ERROR:
324480Svbart@nginx.com         status = NXT_HTTP_INTERNAL_SERVER_ERROR;
325480Svbart@nginx.com         break;
326480Svbart@nginx.com     }
327480Svbart@nginx.com 
328480Svbart@nginx.com     nxt_http_request_error(task, r, status);
329431Sigor@sysoev.ru     return;
330431Sigor@sysoev.ru 
331431Sigor@sysoev.ru fail:
332431Sigor@sysoev.ru 
333431Sigor@sysoev.ru     nxt_h1p_conn_close(task, c, h1p);
334431Sigor@sysoev.ru }
335431Sigor@sysoev.ru 
336431Sigor@sysoev.ru 
337431Sigor@sysoev.ru static nxt_int_t
338431Sigor@sysoev.ru nxt_h1p_connection(void *ctx, nxt_http_field_t *field, uintptr_t data)
339431Sigor@sysoev.ru {
340431Sigor@sysoev.ru     nxt_http_request_t  *r;
341431Sigor@sysoev.ru 
342431Sigor@sysoev.ru     r = ctx;
343431Sigor@sysoev.ru 
344431Sigor@sysoev.ru     if (field->value_length == 5 && nxt_memcmp(field->value, "close", 5) == 0) {
345431Sigor@sysoev.ru         r->proto.h1->keepalive = 0;
346431Sigor@sysoev.ru     }
347431Sigor@sysoev.ru 
348431Sigor@sysoev.ru     return NXT_OK;
349431Sigor@sysoev.ru }
350431Sigor@sysoev.ru 
351431Sigor@sysoev.ru 
352431Sigor@sysoev.ru static nxt_int_t
353431Sigor@sysoev.ru nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field, uintptr_t data)
354431Sigor@sysoev.ru {
355431Sigor@sysoev.ru     nxt_http_te_t       te;
356431Sigor@sysoev.ru     nxt_http_request_t  *r;
357431Sigor@sysoev.ru 
358431Sigor@sysoev.ru     r = ctx;
359431Sigor@sysoev.ru 
360431Sigor@sysoev.ru     if (field->value_length == 7
361431Sigor@sysoev.ru         && nxt_memcmp(field->value, "chunked", 7) == 0)
362431Sigor@sysoev.ru     {
363431Sigor@sysoev.ru         te = NXT_HTTP_TE_CHUNKED;
364431Sigor@sysoev.ru 
365431Sigor@sysoev.ru     } else {
366431Sigor@sysoev.ru         te = NXT_HTTP_TE_UNSUPPORTED;
367431Sigor@sysoev.ru     }
368431Sigor@sysoev.ru 
369431Sigor@sysoev.ru     r->proto.h1->transfer_encoding = te;
370431Sigor@sysoev.ru 
371431Sigor@sysoev.ru     return NXT_OK;
372431Sigor@sysoev.ru }
373431Sigor@sysoev.ru 
374431Sigor@sysoev.ru 
375431Sigor@sysoev.ru static void
376431Sigor@sysoev.ru nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r)
377431Sigor@sysoev.ru {
378431Sigor@sysoev.ru     size_t             size, rest_length;
379459Sigor@sysoev.ru     nxt_buf_t          *in, *b;
380431Sigor@sysoev.ru     nxt_conn_t         *c;
381459Sigor@sysoev.ru     nxt_h1proto_t      *h1p;
382431Sigor@sysoev.ru     nxt_http_status_t  status;
383431Sigor@sysoev.ru 
384459Sigor@sysoev.ru     h1p = r->proto.h1;
385459Sigor@sysoev.ru 
386431Sigor@sysoev.ru     nxt_debug(task, "h1p body read %O te:%d",
387459Sigor@sysoev.ru               r->content_length_n, h1p->transfer_encoding);
388431Sigor@sysoev.ru 
389459Sigor@sysoev.ru     switch (h1p->transfer_encoding) {
390431Sigor@sysoev.ru 
391431Sigor@sysoev.ru     case NXT_HTTP_TE_CHUNKED:
392431Sigor@sysoev.ru         status = NXT_HTTP_LENGTH_REQUIRED;
393431Sigor@sysoev.ru         goto error;
394431Sigor@sysoev.ru 
395431Sigor@sysoev.ru     case NXT_HTTP_TE_UNSUPPORTED:
396431Sigor@sysoev.ru         status = NXT_HTTP_NOT_IMPLEMENTED;
397431Sigor@sysoev.ru         goto error;
398431Sigor@sysoev.ru 
399431Sigor@sysoev.ru     default:
400431Sigor@sysoev.ru     case NXT_HTTP_TE_NONE:
401431Sigor@sysoev.ru         break;
402431Sigor@sysoev.ru     }
403431Sigor@sysoev.ru 
404431Sigor@sysoev.ru     if (r->content_length_n == -1 || r->content_length_n == 0) {
405431Sigor@sysoev.ru         goto ready;
406431Sigor@sysoev.ru     }
407431Sigor@sysoev.ru 
408431Sigor@sysoev.ru     if (r->content_length_n > (nxt_off_t) r->socket_conf->max_body_size) {
409431Sigor@sysoev.ru         status = NXT_HTTP_PAYLOAD_TOO_LARGE;
410431Sigor@sysoev.ru         goto error;
411431Sigor@sysoev.ru     }
412431Sigor@sysoev.ru 
413431Sigor@sysoev.ru     rest_length = (size_t) r->content_length_n;
414431Sigor@sysoev.ru 
415431Sigor@sysoev.ru     b = r->body;
416431Sigor@sysoev.ru 
417431Sigor@sysoev.ru     if (b == NULL) {
418431Sigor@sysoev.ru         b = nxt_buf_mem_alloc(r->mem_pool, rest_length, 0);
419431Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
420431Sigor@sysoev.ru             status = NXT_HTTP_INTERNAL_SERVER_ERROR;
421431Sigor@sysoev.ru             goto error;
422431Sigor@sysoev.ru         }
423431Sigor@sysoev.ru 
424431Sigor@sysoev.ru         r->body = b;
425431Sigor@sysoev.ru     }
426431Sigor@sysoev.ru 
427459Sigor@sysoev.ru     in = h1p->conn->read;
428431Sigor@sysoev.ru 
429459Sigor@sysoev.ru     size = nxt_buf_mem_used_size(&in->mem);
430431Sigor@sysoev.ru 
431431Sigor@sysoev.ru     if (size != 0) {
432431Sigor@sysoev.ru         if (size >= rest_length) {
433431Sigor@sysoev.ru             size = rest_length;
434431Sigor@sysoev.ru             rest_length = 0;
435431Sigor@sysoev.ru 
436431Sigor@sysoev.ru         } else {
437431Sigor@sysoev.ru             rest_length -= size;
438431Sigor@sysoev.ru         }
439431Sigor@sysoev.ru 
440459Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.free, in->mem.pos, size);
441459Sigor@sysoev.ru         in->mem.pos += size;
442431Sigor@sysoev.ru     }
443431Sigor@sysoev.ru 
444431Sigor@sysoev.ru     nxt_debug(task, "h1p body rest: %O", rest_length);
445431Sigor@sysoev.ru 
446431Sigor@sysoev.ru     r->rest_length = rest_length;
447431Sigor@sysoev.ru 
448431Sigor@sysoev.ru     if (rest_length != 0) {
449459Sigor@sysoev.ru         in->next = h1p->buffers;
450459Sigor@sysoev.ru         h1p->buffers = in;
451459Sigor@sysoev.ru 
452459Sigor@sysoev.ru         c = h1p->conn;
453431Sigor@sysoev.ru         c->read = b;
454431Sigor@sysoev.ru         c->read_state = &nxt_h1p_read_body_state;
455431Sigor@sysoev.ru 
456431Sigor@sysoev.ru         nxt_conn_read(task->thread->engine, c);
457431Sigor@sysoev.ru         return;
458431Sigor@sysoev.ru     }
459431Sigor@sysoev.ru 
460431Sigor@sysoev.ru ready:
461431Sigor@sysoev.ru 
462431Sigor@sysoev.ru     nxt_work_queue_add(&task->thread->engine->fast_work_queue,
463431Sigor@sysoev.ru                        r->state->ready_handler, task, r, NULL);
464431Sigor@sysoev.ru 
465431Sigor@sysoev.ru     return;
466431Sigor@sysoev.ru 
467431Sigor@sysoev.ru error:
468431Sigor@sysoev.ru 
469459Sigor@sysoev.ru     h1p->keepalive = 0;
470431Sigor@sysoev.ru 
471431Sigor@sysoev.ru     nxt_http_request_error(task, r, status);
472431Sigor@sysoev.ru }
473431Sigor@sysoev.ru 
474431Sigor@sysoev.ru 
475431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_body_state
476431Sigor@sysoev.ru     nxt_aligned(64) =
477431Sigor@sysoev.ru {
478431Sigor@sysoev.ru     .ready_handler = nxt_h1p_body_read,
479431Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
480431Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
481431Sigor@sysoev.ru 
482431Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_timeout,
483431Sigor@sysoev.ru     .timer_value = nxt_h1p_timeout_value,
484431Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, body_read_timeout),
485431Sigor@sysoev.ru     .timer_autoreset = 1,
486431Sigor@sysoev.ru };
487431Sigor@sysoev.ru 
488431Sigor@sysoev.ru 
489431Sigor@sysoev.ru static void
490431Sigor@sysoev.ru nxt_h1p_body_read(nxt_task_t *task, void *obj, void *data)
491431Sigor@sysoev.ru {
492431Sigor@sysoev.ru     size_t              size;
493431Sigor@sysoev.ru     nxt_conn_t          *c;
494431Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
495431Sigor@sysoev.ru     nxt_http_request_t  *r;
496431Sigor@sysoev.ru 
497431Sigor@sysoev.ru     c = obj;
498431Sigor@sysoev.ru     h1p = data;
499431Sigor@sysoev.ru 
500431Sigor@sysoev.ru     nxt_debug(task, "h1p body read");
501431Sigor@sysoev.ru 
502431Sigor@sysoev.ru     r = h1p->request;
503431Sigor@sysoev.ru     size = nxt_buf_mem_used_size(&c->read->mem);
504431Sigor@sysoev.ru 
505431Sigor@sysoev.ru     r->rest_length -= size;
506431Sigor@sysoev.ru 
507431Sigor@sysoev.ru     nxt_debug(task, "h1p body rest: %O", r->rest_length);
508431Sigor@sysoev.ru 
509431Sigor@sysoev.ru     if (r->rest_length != 0) {
510431Sigor@sysoev.ru         nxt_conn_read(task->thread->engine, c);
511431Sigor@sysoev.ru 
512431Sigor@sysoev.ru     } else {
513459Sigor@sysoev.ru         c->read = NULL;
514431Sigor@sysoev.ru         nxt_work_queue_add(&task->thread->engine->fast_work_queue,
515431Sigor@sysoev.ru                            r->state->ready_handler, task, r, NULL);
516431Sigor@sysoev.ru     }
517431Sigor@sysoev.ru }
518431Sigor@sysoev.ru 
519431Sigor@sysoev.ru 
520431Sigor@sysoev.ru static void
521431Sigor@sysoev.ru nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r)
522431Sigor@sysoev.ru {
523431Sigor@sysoev.ru     r->local = nxt_conn_local_addr(task, r->proto.h1->conn);
524431Sigor@sysoev.ru }
525431Sigor@sysoev.ru 
526431Sigor@sysoev.ru 
527431Sigor@sysoev.ru #define NXT_HTTP_LAST_SUCCESS                                                 \
528431Sigor@sysoev.ru     (NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1)
529431Sigor@sysoev.ru 
530431Sigor@sysoev.ru static const nxt_str_t  nxt_http_success[] = {
531431Sigor@sysoev.ru     nxt_string("HTTP/1.1 200 OK\r\n"),
532431Sigor@sysoev.ru     nxt_string("HTTP/1.1 201 Created\r\n"),
533431Sigor@sysoev.ru     nxt_string("HTTP/1.1 202 Accepted\r\n"),
534431Sigor@sysoev.ru     nxt_string("HTTP/1.1 203 Non-Authoritative Information\r\n"),
535431Sigor@sysoev.ru     nxt_string("HTTP/1.1 204 No Content\r\n"),
536431Sigor@sysoev.ru     nxt_string("HTTP/1.1 205 Reset Content\r\n"),
537431Sigor@sysoev.ru     nxt_string("HTTP/1.1 206 Partial Content\r\n"),
538431Sigor@sysoev.ru };
539431Sigor@sysoev.ru 
540431Sigor@sysoev.ru 
541431Sigor@sysoev.ru #define NXT_HTTP_LAST_REDIRECTION                                             \
542431Sigor@sysoev.ru     (NXT_HTTP_MULTIPLE_CHOICES + nxt_nitems(nxt_http_redirection) - 1)
543431Sigor@sysoev.ru 
544431Sigor@sysoev.ru static const nxt_str_t  nxt_http_redirection[] = {
545431Sigor@sysoev.ru     nxt_string("HTTP/1.1 300 Multiple Choices\r\n"),
546431Sigor@sysoev.ru     nxt_string("HTTP/1.1 301 Moved Permanently\r\n"),
547431Sigor@sysoev.ru     nxt_string("HTTP/1.1 302 Found\r\n"),
548431Sigor@sysoev.ru     nxt_string("HTTP/1.1 303 See Other\r\n"),
549431Sigor@sysoev.ru     nxt_string("HTTP/1.1 304 Not Modified\r\n"),
550431Sigor@sysoev.ru };
551431Sigor@sysoev.ru 
552431Sigor@sysoev.ru 
553431Sigor@sysoev.ru #define NXT_HTTP_LAST_CLIENT_ERROR                                            \
554431Sigor@sysoev.ru     (NXT_HTTP_BAD_REQUEST + nxt_nitems(nxt_http_client_error) - 1)
555431Sigor@sysoev.ru 
556431Sigor@sysoev.ru static const nxt_str_t  nxt_http_client_error[] = {
557431Sigor@sysoev.ru     nxt_string("HTTP/1.1 400 Bad Request\r\n"),
558431Sigor@sysoev.ru     nxt_string("HTTP/1.1 401 Unauthorized\r\n"),
559431Sigor@sysoev.ru     nxt_string("HTTP/1.1 402 Payment Required\r\n"),
560431Sigor@sysoev.ru     nxt_string("HTTP/1.1 403 Forbidden\r\n"),
561431Sigor@sysoev.ru     nxt_string("HTTP/1.1 404 Not Found\r\n"),
562431Sigor@sysoev.ru     nxt_string("HTTP/1.1 405 Method Not Allowed\r\n"),
563431Sigor@sysoev.ru     nxt_string("HTTP/1.1 406 Not Acceptable\r\n"),
564431Sigor@sysoev.ru     nxt_string("HTTP/1.1 407 Proxy Authentication Required\r\n"),
565431Sigor@sysoev.ru     nxt_string("HTTP/1.1 408 Request Timeout\r\n"),
566431Sigor@sysoev.ru     nxt_string("HTTP/1.1 409 Conflict\r\n"),
567431Sigor@sysoev.ru     nxt_string("HTTP/1.1 410 Gone\r\n"),
568431Sigor@sysoev.ru     nxt_string("HTTP/1.1 411 Length Required\r\n"),
569431Sigor@sysoev.ru     nxt_string("HTTP/1.1 412 Precondition Failed\r\n"),
570431Sigor@sysoev.ru     nxt_string("HTTP/1.1 413 Payload Too Large\r\n"),
571431Sigor@sysoev.ru     nxt_string("HTTP/1.1 414 URI Too Long\r\n"),
572431Sigor@sysoev.ru     nxt_string("HTTP/1.1 415 Unsupported Media Type\r\n"),
573431Sigor@sysoev.ru     nxt_string("HTTP/1.1 416 Range Not Satisfiable\r\n"),
574431Sigor@sysoev.ru     nxt_string("HTTP/1.1 417 Expectation Failed\r\n"),
575431Sigor@sysoev.ru     nxt_string("HTTP/1.1 418\r\n"),
576431Sigor@sysoev.ru     nxt_string("HTTP/1.1 419\r\n"),
577431Sigor@sysoev.ru     nxt_string("HTTP/1.1 420\r\n"),
578431Sigor@sysoev.ru     nxt_string("HTTP/1.1 421\r\n"),
579431Sigor@sysoev.ru     nxt_string("HTTP/1.1 422\r\n"),
580431Sigor@sysoev.ru     nxt_string("HTTP/1.1 423\r\n"),
581431Sigor@sysoev.ru     nxt_string("HTTP/1.1 424\r\n"),
582431Sigor@sysoev.ru     nxt_string("HTTP/1.1 425\r\n"),
583431Sigor@sysoev.ru     nxt_string("HTTP/1.1 426\r\n"),
584431Sigor@sysoev.ru     nxt_string("HTTP/1.1 427\r\n"),
585431Sigor@sysoev.ru     nxt_string("HTTP/1.1 428\r\n"),
586431Sigor@sysoev.ru     nxt_string("HTTP/1.1 429\r\n"),
587431Sigor@sysoev.ru     nxt_string("HTTP/1.1 430\r\n"),
588431Sigor@sysoev.ru     nxt_string("HTTP/1.1 431 Request Header Fields Too Large\r\n"),
589431Sigor@sysoev.ru };
590431Sigor@sysoev.ru 
591431Sigor@sysoev.ru 
592431Sigor@sysoev.ru #define NXT_HTTP_LAST_SERVER_ERROR                                            \
593431Sigor@sysoev.ru     (NXT_HTTP_INTERNAL_SERVER_ERROR + nxt_nitems(nxt_http_server_error) - 1)
594431Sigor@sysoev.ru 
595431Sigor@sysoev.ru static const nxt_str_t  nxt_http_server_error[] = {
596431Sigor@sysoev.ru     nxt_string("HTTP/1.1 500 Internal Server Error\r\n"),
597431Sigor@sysoev.ru     nxt_string("HTTP/1.1 501 Not Implemented\r\n"),
598431Sigor@sysoev.ru     nxt_string("HTTP/1.1 502 Bad Gateway\r\n"),
599431Sigor@sysoev.ru     nxt_string("HTTP/1.1 503 Service Unavailable\r\n"),
600431Sigor@sysoev.ru     nxt_string("HTTP/1.1 504 Gateway Timeout\r\n"),
601*482Svbart@nginx.com     nxt_string("HTTP/1.1 505 HTTP Version Not Supported\r\n"),
602431Sigor@sysoev.ru };
603431Sigor@sysoev.ru 
604431Sigor@sysoev.ru 
605431Sigor@sysoev.ru #define UNKNOWN_STATUS_LENGTH  (sizeof("HTTP/1.1 65536\r\n") - 1)
606431Sigor@sysoev.ru 
607431Sigor@sysoev.ru static void
608431Sigor@sysoev.ru nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r)
609431Sigor@sysoev.ru {
610431Sigor@sysoev.ru     u_char              *p;
611431Sigor@sysoev.ru     size_t              size;
612431Sigor@sysoev.ru     nxt_buf_t           *header;
613431Sigor@sysoev.ru     nxt_str_t           unknown_status;
614431Sigor@sysoev.ru     nxt_int_t           conn;
615431Sigor@sysoev.ru     nxt_uint_t          n;
616431Sigor@sysoev.ru     nxt_bool_t          http11;
617431Sigor@sysoev.ru     nxt_conn_t          *c;
618431Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
619431Sigor@sysoev.ru     const nxt_str_t     *status;
620431Sigor@sysoev.ru     nxt_http_field_t    *field;
621431Sigor@sysoev.ru     nxt_event_engine_t  *engine;
622431Sigor@sysoev.ru     u_char              buf[UNKNOWN_STATUS_LENGTH];
623431Sigor@sysoev.ru 
624431Sigor@sysoev.ru     static const char   chunked[] = "Transfer-Encoding: chunked\r\n";
625431Sigor@sysoev.ru 
626431Sigor@sysoev.ru     static const nxt_str_t  connection[2] = {
627431Sigor@sysoev.ru         nxt_string("Connection: close\r\n"),
628431Sigor@sysoev.ru         nxt_string("Connection: keep-alive\r\n"),
629431Sigor@sysoev.ru     };
630431Sigor@sysoev.ru 
631431Sigor@sysoev.ru     nxt_debug(task, "h1p request header send");
632431Sigor@sysoev.ru 
633431Sigor@sysoev.ru     r->header_sent = 1;
634431Sigor@sysoev.ru     h1p = r->proto.h1;
635431Sigor@sysoev.ru     n = r->status;
636431Sigor@sysoev.ru 
637431Sigor@sysoev.ru     if (n >= NXT_HTTP_OK && n <= NXT_HTTP_LAST_SUCCESS) {
638431Sigor@sysoev.ru         status = &nxt_http_success[n - NXT_HTTP_OK];
639431Sigor@sysoev.ru 
640431Sigor@sysoev.ru     } else if (n >= NXT_HTTP_MULTIPLE_CHOICES
641431Sigor@sysoev.ru                && n <= NXT_HTTP_LAST_REDIRECTION)
642431Sigor@sysoev.ru     {
643431Sigor@sysoev.ru         status = &nxt_http_redirection[n - NXT_HTTP_MULTIPLE_CHOICES];
644431Sigor@sysoev.ru 
645431Sigor@sysoev.ru     } else if (n >= NXT_HTTP_BAD_REQUEST && n <= NXT_HTTP_LAST_CLIENT_ERROR) {
646431Sigor@sysoev.ru         status = &nxt_http_client_error[n - NXT_HTTP_BAD_REQUEST];
647431Sigor@sysoev.ru 
648431Sigor@sysoev.ru     } else if (n >= NXT_HTTP_INTERNAL_SERVER_ERROR
649431Sigor@sysoev.ru                && n <= NXT_HTTP_LAST_SERVER_ERROR)
650431Sigor@sysoev.ru     {
651431Sigor@sysoev.ru         status = &nxt_http_server_error[n - NXT_HTTP_INTERNAL_SERVER_ERROR];
652431Sigor@sysoev.ru 
653431Sigor@sysoev.ru     } else {
654431Sigor@sysoev.ru         p = nxt_sprintf(buf, buf + UNKNOWN_STATUS_LENGTH,
655431Sigor@sysoev.ru                         "HTTP/1.1 %03d\r\n", n);
656431Sigor@sysoev.ru 
657431Sigor@sysoev.ru         unknown_status.length = p - buf;
658431Sigor@sysoev.ru         unknown_status.start = buf;
659431Sigor@sysoev.ru         status = &unknown_status;
660431Sigor@sysoev.ru     }
661431Sigor@sysoev.ru 
662450Sigor@sysoev.ru     size = status->length;
663450Sigor@sysoev.ru     /* Trailing CRLF at the end of header. */
664450Sigor@sysoev.ru     size += sizeof("\r\n") - 1;
665431Sigor@sysoev.ru 
666481Svbart@nginx.com     http11 = (h1p->parser.version.s.minor != '0');
667431Sigor@sysoev.ru 
668431Sigor@sysoev.ru     if (r->resp.content_length == NULL || r->resp.content_length->skip) {
669431Sigor@sysoev.ru         if (http11) {
670431Sigor@sysoev.ru             h1p->chunked = 1;
671431Sigor@sysoev.ru             size += sizeof(chunked) - 1;
672450Sigor@sysoev.ru             /* Trailing CRLF will be added by the first chunk header. */
673450Sigor@sysoev.ru             size -= sizeof("\r\n") - 1;
674431Sigor@sysoev.ru 
675431Sigor@sysoev.ru         } else {
676431Sigor@sysoev.ru             h1p->keepalive = 0;
677431Sigor@sysoev.ru         }
678431Sigor@sysoev.ru     }
679431Sigor@sysoev.ru 
680431Sigor@sysoev.ru     conn = -1;
681431Sigor@sysoev.ru 
682431Sigor@sysoev.ru     if (http11 ^ h1p->keepalive) {
683431Sigor@sysoev.ru         conn = h1p->keepalive;
684431Sigor@sysoev.ru         size += connection[conn].length;
685431Sigor@sysoev.ru     }
686431Sigor@sysoev.ru 
687431Sigor@sysoev.ru     nxt_list_each(field, r->resp.fields) {
688431Sigor@sysoev.ru 
689431Sigor@sysoev.ru         if (!field->skip) {
690431Sigor@sysoev.ru             size += field->name_length + field->value_length;
691431Sigor@sysoev.ru             size += sizeof(": \r\n") - 1;
692431Sigor@sysoev.ru         }
693431Sigor@sysoev.ru 
694431Sigor@sysoev.ru     } nxt_list_loop;
695431Sigor@sysoev.ru 
696431Sigor@sysoev.ru     header = nxt_buf_mem_alloc(r->mem_pool, size, 0);
697431Sigor@sysoev.ru     if (nxt_slow_path(header == NULL)) {
698431Sigor@sysoev.ru         /* The internal server error is set just for logging. */
699431Sigor@sysoev.ru         r->status = NXT_HTTP_INTERNAL_SERVER_ERROR;
700431Sigor@sysoev.ru         nxt_h1p_conn_close(task, h1p->conn, h1p);
701431Sigor@sysoev.ru         return;
702431Sigor@sysoev.ru     }
703431Sigor@sysoev.ru 
704431Sigor@sysoev.ru     p = header->mem.free;
705431Sigor@sysoev.ru 
706431Sigor@sysoev.ru     p = nxt_cpymem(p, status->start, status->length);
707431Sigor@sysoev.ru 
708431Sigor@sysoev.ru     nxt_list_each(field, r->resp.fields) {
709431Sigor@sysoev.ru 
710431Sigor@sysoev.ru         if (!field->skip) {
711431Sigor@sysoev.ru             p = nxt_cpymem(p, field->name, field->name_length);
712431Sigor@sysoev.ru             *p++ = ':'; *p++ = ' ';
713431Sigor@sysoev.ru             p = nxt_cpymem(p, field->value, field->value_length);
714431Sigor@sysoev.ru             *p++ = '\r'; *p++ = '\n';
715431Sigor@sysoev.ru         }
716431Sigor@sysoev.ru 
717431Sigor@sysoev.ru     } nxt_list_loop;
718431Sigor@sysoev.ru 
719431Sigor@sysoev.ru     if (conn >= 0) {
720431Sigor@sysoev.ru         p = nxt_cpymem(p, connection[conn].start, connection[conn].length);
721431Sigor@sysoev.ru     }
722431Sigor@sysoev.ru 
723431Sigor@sysoev.ru     if (h1p->chunked) {
724431Sigor@sysoev.ru         p = nxt_cpymem(p, chunked, sizeof(chunked) - 1);
725450Sigor@sysoev.ru         /* Trailing CRLF will be added by the first chunk header. */
726431Sigor@sysoev.ru 
727431Sigor@sysoev.ru     } else {
728431Sigor@sysoev.ru         *p++ = '\r'; *p++ = '\n';
729431Sigor@sysoev.ru     }
730431Sigor@sysoev.ru 
731431Sigor@sysoev.ru     header->mem.free = p;
732431Sigor@sysoev.ru 
733431Sigor@sysoev.ru     c = h1p->conn;
734431Sigor@sysoev.ru 
735431Sigor@sysoev.ru     c->write = header;
736431Sigor@sysoev.ru     c->write_state = &nxt_h1p_send_state;
737431Sigor@sysoev.ru 
738431Sigor@sysoev.ru     engine = task->thread->engine;
739431Sigor@sysoev.ru 
740431Sigor@sysoev.ru     nxt_work_queue_add(&engine->fast_work_queue, r->state->ready_handler,
741431Sigor@sysoev.ru                        task, r, NULL);
742431Sigor@sysoev.ru 
743431Sigor@sysoev.ru     nxt_conn_write(engine, c);
744431Sigor@sysoev.ru }
745431Sigor@sysoev.ru 
746431Sigor@sysoev.ru 
747431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_send_state
748431Sigor@sysoev.ru     nxt_aligned(64) =
749431Sigor@sysoev.ru {
750431Sigor@sysoev.ru     .ready_handler = nxt_h1p_sent,
751431Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
752431Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
753431Sigor@sysoev.ru 
754431Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_timeout,
755431Sigor@sysoev.ru     .timer_value = nxt_h1p_timeout_value,
756431Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, send_timeout),
757431Sigor@sysoev.ru     .timer_autoreset = 1,
758431Sigor@sysoev.ru };
759431Sigor@sysoev.ru 
760431Sigor@sysoev.ru 
761431Sigor@sysoev.ru static void
762431Sigor@sysoev.ru nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
763431Sigor@sysoev.ru {
764431Sigor@sysoev.ru     nxt_conn_t  *c;
765431Sigor@sysoev.ru 
766431Sigor@sysoev.ru     nxt_debug(task, "h1p request send");
767431Sigor@sysoev.ru 
768431Sigor@sysoev.ru     c = r->proto.h1->conn;
769431Sigor@sysoev.ru 
770431Sigor@sysoev.ru     if (r->proto.h1->chunked) {
771431Sigor@sysoev.ru         out = nxt_h1p_chunk_create(task, r, out);
772431Sigor@sysoev.ru         if (nxt_slow_path(out == NULL)) {
773431Sigor@sysoev.ru             nxt_h1p_conn_error(task, c, c->socket.data);
774431Sigor@sysoev.ru             return;
775431Sigor@sysoev.ru         }
776431Sigor@sysoev.ru     }
777431Sigor@sysoev.ru 
778431Sigor@sysoev.ru     if (c->write == NULL) {
779431Sigor@sysoev.ru         c->write = out;
780431Sigor@sysoev.ru         c->write_state = &nxt_h1p_send_state;
781431Sigor@sysoev.ru 
782431Sigor@sysoev.ru         nxt_conn_write(task->thread->engine, c);
783431Sigor@sysoev.ru 
784431Sigor@sysoev.ru     } else {
785431Sigor@sysoev.ru         nxt_buf_chain_add(&c->write, out);
786431Sigor@sysoev.ru     }
787431Sigor@sysoev.ru }
788431Sigor@sysoev.ru 
789431Sigor@sysoev.ru 
790431Sigor@sysoev.ru static nxt_buf_t *
791431Sigor@sysoev.ru nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
792431Sigor@sysoev.ru {
793431Sigor@sysoev.ru     size_t             size;
794431Sigor@sysoev.ru     nxt_buf_t          *b, **prev, *header, *tail;
795431Sigor@sysoev.ru 
796431Sigor@sysoev.ru     const size_t       chunk_size = 2 * (sizeof("\r\n") - 1) + NXT_OFF_T_HEXLEN;
797431Sigor@sysoev.ru     static const char  tail_chunk[] = "\r\n0\r\n\r\n";
798431Sigor@sysoev.ru 
799431Sigor@sysoev.ru     size = 0;
800431Sigor@sysoev.ru     prev = &out;
801431Sigor@sysoev.ru 
802431Sigor@sysoev.ru     for (b = out; b != NULL; b = b->next) {
803431Sigor@sysoev.ru 
804431Sigor@sysoev.ru         if (nxt_buf_is_last(b)) {
805431Sigor@sysoev.ru             tail = nxt_buf_mem_alloc(r->mem_pool, chunk_size, 0);
806431Sigor@sysoev.ru             if (nxt_slow_path(tail == NULL)) {
807431Sigor@sysoev.ru                 return NULL;
808431Sigor@sysoev.ru             }
809431Sigor@sysoev.ru 
810431Sigor@sysoev.ru             *prev = tail;
811431Sigor@sysoev.ru             tail->next = b;
812431Sigor@sysoev.ru             /*
813431Sigor@sysoev.ru              * The tail_chunk size with trailing zero is 8 bytes, so
814431Sigor@sysoev.ru              * memcpy may be inlined with just single 8 byte move operation.
815431Sigor@sysoev.ru              */
816431Sigor@sysoev.ru             nxt_memcpy(tail->mem.free, tail_chunk, sizeof(tail_chunk));
817431Sigor@sysoev.ru             tail->mem.free += sizeof(tail_chunk) - 1;
818431Sigor@sysoev.ru 
819431Sigor@sysoev.ru             break;
820431Sigor@sysoev.ru         }
821431Sigor@sysoev.ru 
822431Sigor@sysoev.ru         size += nxt_buf_used_size(b);
823431Sigor@sysoev.ru         prev = &b->next;
824431Sigor@sysoev.ru     }
825431Sigor@sysoev.ru 
826431Sigor@sysoev.ru     if (size == 0) {
827431Sigor@sysoev.ru         return out;
828431Sigor@sysoev.ru     }
829431Sigor@sysoev.ru 
830431Sigor@sysoev.ru     header = nxt_buf_mem_alloc(r->mem_pool, chunk_size, 0);
831431Sigor@sysoev.ru     if (nxt_slow_path(header == NULL)) {
832431Sigor@sysoev.ru         return NULL;
833431Sigor@sysoev.ru     }
834431Sigor@sysoev.ru 
835431Sigor@sysoev.ru     header->next = out;
836431Sigor@sysoev.ru     header->mem.free = nxt_sprintf(header->mem.free, header->mem.end,
837431Sigor@sysoev.ru                                    "\r\n%xO\r\n", size);
838431Sigor@sysoev.ru     return header;
839431Sigor@sysoev.ru }
840431Sigor@sysoev.ru 
841431Sigor@sysoev.ru 
842431Sigor@sysoev.ru static void
843431Sigor@sysoev.ru nxt_h1p_sent(nxt_task_t *task, void *obj, void *data)
844431Sigor@sysoev.ru {
845431Sigor@sysoev.ru     nxt_conn_t          *c;
846431Sigor@sysoev.ru     nxt_event_engine_t  *engine;
847431Sigor@sysoev.ru 
848431Sigor@sysoev.ru     c = obj;
849431Sigor@sysoev.ru 
850431Sigor@sysoev.ru     nxt_debug(task, "h1p sent");
851431Sigor@sysoev.ru 
852431Sigor@sysoev.ru     engine = task->thread->engine;
853431Sigor@sysoev.ru 
854431Sigor@sysoev.ru     c->write = nxt_sendbuf_completion0(task, &engine->fast_work_queue,
855431Sigor@sysoev.ru                                        c->write);
856431Sigor@sysoev.ru     if (c->write != NULL) {
857431Sigor@sysoev.ru         nxt_conn_write(engine, c);
858431Sigor@sysoev.ru     }
859431Sigor@sysoev.ru }
860431Sigor@sysoev.ru 
861431Sigor@sysoev.ru 
862431Sigor@sysoev.ru static void
863431Sigor@sysoev.ru nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto)
864431Sigor@sysoev.ru {
865431Sigor@sysoev.ru     nxt_conn_t     *c;
866431Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
867431Sigor@sysoev.ru 
868431Sigor@sysoev.ru     nxt_debug(task, "h1p request close");
869431Sigor@sysoev.ru 
870431Sigor@sysoev.ru     h1p = proto.h1;
871431Sigor@sysoev.ru     h1p->request = NULL;
872431Sigor@sysoev.ru 
873431Sigor@sysoev.ru     c = h1p->conn;
874431Sigor@sysoev.ru 
875431Sigor@sysoev.ru     if (h1p->keepalive) {
876431Sigor@sysoev.ru         nxt_h1p_keepalive(task, h1p, c);
877431Sigor@sysoev.ru 
878431Sigor@sysoev.ru     } else {
879431Sigor@sysoev.ru         nxt_h1p_close(task, c);
880431Sigor@sysoev.ru     }
881431Sigor@sysoev.ru }
882431Sigor@sysoev.ru 
883431Sigor@sysoev.ru 
884431Sigor@sysoev.ru static void
885431Sigor@sysoev.ru nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c)
886431Sigor@sysoev.ru {
887431Sigor@sysoev.ru     size_t     size;
888431Sigor@sysoev.ru     nxt_buf_t  *in, *b, *next;
889431Sigor@sysoev.ru 
890431Sigor@sysoev.ru     nxt_debug(task, "h1p keepalive");
891431Sigor@sysoev.ru 
892436Sigor@sysoev.ru     if (!c->tcp_nodelay) {
893436Sigor@sysoev.ru         nxt_conn_tcp_nodelay_on(task, c);
894436Sigor@sysoev.ru     }
895436Sigor@sysoev.ru 
896431Sigor@sysoev.ru     b = h1p->buffers;
897431Sigor@sysoev.ru 
898452Sigor@sysoev.ru     nxt_memzero(h1p, offsetof(nxt_h1proto_t, conn));
899431Sigor@sysoev.ru 
900431Sigor@sysoev.ru     in = c->read;
901431Sigor@sysoev.ru 
902459Sigor@sysoev.ru     if (in == NULL) {
903459Sigor@sysoev.ru         /* A request with large body. */
904459Sigor@sysoev.ru         in = b;
905459Sigor@sysoev.ru         c->read = in;
906459Sigor@sysoev.ru 
907459Sigor@sysoev.ru         b = in->next;
908459Sigor@sysoev.ru         in->next = NULL;
909459Sigor@sysoev.ru     }
910459Sigor@sysoev.ru 
911459Sigor@sysoev.ru     while (b != NULL) {
912459Sigor@sysoev.ru         next = b->next;
913459Sigor@sysoev.ru         nxt_mp_free(c->mem_pool, b);
914459Sigor@sysoev.ru         b = next;
915459Sigor@sysoev.ru     }
916459Sigor@sysoev.ru 
917431Sigor@sysoev.ru     size = nxt_buf_mem_used_size(&in->mem);
918431Sigor@sysoev.ru 
919431Sigor@sysoev.ru     if (size == 0) {
920431Sigor@sysoev.ru         in->mem.pos = in->mem.start;
921431Sigor@sysoev.ru         in->mem.free = in->mem.start;
922431Sigor@sysoev.ru 
923431Sigor@sysoev.ru         if (c->socket.read_ready) {
924431Sigor@sysoev.ru             c->read_state = &nxt_h1p_read_header_state;
925431Sigor@sysoev.ru             nxt_conn_read(task->thread->engine, c);
926431Sigor@sysoev.ru 
927431Sigor@sysoev.ru         } else {
928431Sigor@sysoev.ru             c->read_state = &nxt_h1p_idle_state;
929431Sigor@sysoev.ru             nxt_conn_wait(c);
930431Sigor@sysoev.ru         }
931431Sigor@sysoev.ru 
932431Sigor@sysoev.ru     } else {
933431Sigor@sysoev.ru         nxt_debug(task, "h1p pipelining");
934431Sigor@sysoev.ru 
935431Sigor@sysoev.ru         nxt_memmove(in->mem.start, in->mem.pos, size);
936431Sigor@sysoev.ru 
937431Sigor@sysoev.ru         in->mem.pos = in->mem.start;
938431Sigor@sysoev.ru         in->mem.free = in->mem.start + size;
939431Sigor@sysoev.ru 
940431Sigor@sysoev.ru         nxt_h1p_header_parse(task, c, c->socket.data);
941431Sigor@sysoev.ru     }
942431Sigor@sysoev.ru }
943431Sigor@sysoev.ru 
944431Sigor@sysoev.ru 
945431Sigor@sysoev.ru static void
946431Sigor@sysoev.ru nxt_h1p_close(nxt_task_t *task, nxt_conn_t *c)
947431Sigor@sysoev.ru {
948431Sigor@sysoev.ru     nxt_debug(task, "h1p close");
949431Sigor@sysoev.ru 
950431Sigor@sysoev.ru     c->socket.data = NULL;
951431Sigor@sysoev.ru 
952431Sigor@sysoev.ru     if (c->socket.fd != -1) {
953431Sigor@sysoev.ru         c->write_state = &nxt_router_conn_close_state;
954431Sigor@sysoev.ru 
955431Sigor@sysoev.ru         nxt_conn_close(task->thread->engine, c);
956431Sigor@sysoev.ru     }
957431Sigor@sysoev.ru }
958431Sigor@sysoev.ru 
959431Sigor@sysoev.ru 
960431Sigor@sysoev.ru static void
961431Sigor@sysoev.ru nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data)
962431Sigor@sysoev.ru {
963431Sigor@sysoev.ru     nxt_conn_t          *c;
964431Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
965431Sigor@sysoev.ru     nxt_http_request_t  *r;
966431Sigor@sysoev.ru 
967431Sigor@sysoev.ru     c = obj;
968431Sigor@sysoev.ru     h1p = data;
969431Sigor@sysoev.ru 
970431Sigor@sysoev.ru     nxt_debug(task, "h1p conn close");
971431Sigor@sysoev.ru 
972431Sigor@sysoev.ru     if (h1p != NULL) {
973431Sigor@sysoev.ru         r = h1p->request;
974431Sigor@sysoev.ru 
975431Sigor@sysoev.ru         if (r != NULL) {
976431Sigor@sysoev.ru             r->state->error_handler(task, r, r->proto.h1);
977431Sigor@sysoev.ru             return;
978431Sigor@sysoev.ru         }
979431Sigor@sysoev.ru     }
980431Sigor@sysoev.ru 
981431Sigor@sysoev.ru     nxt_h1p_close(task, c);
982431Sigor@sysoev.ru }
983431Sigor@sysoev.ru 
984431Sigor@sysoev.ru 
985431Sigor@sysoev.ru static void
986431Sigor@sysoev.ru nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data)
987431Sigor@sysoev.ru {
988431Sigor@sysoev.ru     nxt_conn_t     *c;
989431Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
990431Sigor@sysoev.ru 
991431Sigor@sysoev.ru     c = obj;
992431Sigor@sysoev.ru     h1p = data;
993431Sigor@sysoev.ru 
994431Sigor@sysoev.ru     nxt_debug(task, "h1p conn error");
995431Sigor@sysoev.ru 
996431Sigor@sysoev.ru     nxt_h1p_conn_close(task, c, h1p);
997431Sigor@sysoev.ru }
998431Sigor@sysoev.ru 
999431Sigor@sysoev.ru 
1000431Sigor@sysoev.ru static void
1001431Sigor@sysoev.ru nxt_h1p_conn_timeout(nxt_task_t *task, void *obj, void *data)
1002431Sigor@sysoev.ru {
1003431Sigor@sysoev.ru     nxt_conn_t   *c;
1004431Sigor@sysoev.ru     nxt_timer_t  *timer;
1005431Sigor@sysoev.ru 
1006431Sigor@sysoev.ru     timer = obj;
1007431Sigor@sysoev.ru 
1008431Sigor@sysoev.ru     nxt_debug(task, "h1p conn timeout");
1009431Sigor@sysoev.ru 
1010431Sigor@sysoev.ru     c = nxt_read_timer_conn(timer);
1011431Sigor@sysoev.ru 
1012431Sigor@sysoev.ru     nxt_h1p_conn_close(task, c, c->socket.data);
1013431Sigor@sysoev.ru }
1014431Sigor@sysoev.ru 
1015431Sigor@sysoev.ru 
1016431Sigor@sysoev.ru static nxt_msec_t
1017431Sigor@sysoev.ru nxt_h1p_timeout_value(nxt_conn_t *c, uintptr_t data)
1018431Sigor@sysoev.ru {
1019431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
1020431Sigor@sysoev.ru 
1021431Sigor@sysoev.ru     joint = c->joint;
1022431Sigor@sysoev.ru 
1023431Sigor@sysoev.ru     return nxt_value_at(nxt_msec_t, joint->socket_conf, data);
1024431Sigor@sysoev.ru }
1025