xref: /unit/src/nxt_h1proto.c (revision 741)
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 
11624Sigor@sysoev.ru /*
12624Sigor@sysoev.ru  * nxt_h1p_conn_ prefix is used for connection handlers.
13624Sigor@sysoev.ru  * nxt_h1p_request_ prefix is used for HTTP/1 protocol request methods.
14624Sigor@sysoev.ru  */
15624Sigor@sysoev.ru 
16629Sigor@sysoev.ru static ssize_t nxt_h1p_conn_io_read_handler(nxt_conn_t *c);
17624Sigor@sysoev.ru static void nxt_h1p_conn_proto_init(nxt_task_t *task, void *obj, void *data);
18624Sigor@sysoev.ru static void nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data);
19683Sigor@sysoev.ru static void nxt_h1p_conn_request_header_parse(nxt_task_t *task, void *obj,
20683Sigor@sysoev.ru     void *data);
21620Svbart@nginx.com static nxt_int_t nxt_h1p_header_process(nxt_h1proto_t *h1p,
22620Svbart@nginx.com     nxt_http_request_t *r);
23625Sigor@sysoev.ru static nxt_int_t nxt_h1p_header_buffer_test(nxt_task_t *task,
24625Sigor@sysoev.ru     nxt_h1proto_t *h1p, nxt_conn_t *c, nxt_socket_conf_t *skcf);
25431Sigor@sysoev.ru static nxt_int_t nxt_h1p_connection(void *ctx, nxt_http_field_t *field,
26431Sigor@sysoev.ru     uintptr_t data);
27431Sigor@sysoev.ru static nxt_int_t nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field,
28431Sigor@sysoev.ru     uintptr_t data);
29431Sigor@sysoev.ru static void nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r);
30637Sigor@sysoev.ru static void nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj,
31637Sigor@sysoev.ru     void *data);
32431Sigor@sysoev.ru static void nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r);
33431Sigor@sysoev.ru static void nxt_h1p_request_header_send(nxt_task_t *task,
34431Sigor@sysoev.ru     nxt_http_request_t *r);
35431Sigor@sysoev.ru static void nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r,
36431Sigor@sysoev.ru     nxt_buf_t *out);
37431Sigor@sysoev.ru static nxt_buf_t *nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r,
38431Sigor@sysoev.ru     nxt_buf_t *out);
39740Sigor@sysoev.ru static void nxt_h1p_conn_sent(nxt_task_t *task, void *obj, void *data);
40630Svbart@nginx.com static nxt_off_t nxt_h1p_request_body_bytes_sent(nxt_task_t *task,
41630Svbart@nginx.com     nxt_http_proto_t proto);
42608Sigor@sysoev.ru static void nxt_h1p_request_discard(nxt_task_t *task, nxt_http_request_t *r,
43608Sigor@sysoev.ru     nxt_buf_t *last);
44683Sigor@sysoev.ru static void nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto,
45683Sigor@sysoev.ru     nxt_socket_conf_joint_t *joint);
46431Sigor@sysoev.ru static void nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p,
47431Sigor@sysoev.ru     nxt_conn_t *c);
48431Sigor@sysoev.ru static void nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data);
49*741Sigor@sysoev.ru static void nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data);
50*741Sigor@sysoev.ru static void nxt_h1p_conn_timeout(nxt_task_t *task, void *obj, void *data);
51*741Sigor@sysoev.ru static void nxt_h1p_conn_idle_timeout(nxt_task_t *task, nxt_conn_t *c);
52740Sigor@sysoev.ru static void nxt_h1p_conn_idle_close(nxt_task_t *task, void *obj, void *data);
53740Sigor@sysoev.ru static void nxt_h1p_conn_send_timeout(nxt_task_t *task, void *obj, void *data);
54740Sigor@sysoev.ru static nxt_msec_t nxt_h1p_conn_send_timeout_value(nxt_conn_t *c,
55683Sigor@sysoev.ru     uintptr_t data);
56740Sigor@sysoev.ru static nxt_msec_t nxt_h1p_conn_timeout_value(nxt_conn_t *c, uintptr_t data);
57624Sigor@sysoev.ru static void nxt_h1p_close(nxt_task_t *task, nxt_conn_t *c);
58683Sigor@sysoev.ru static void nxt_h1p_conn_free(nxt_task_t *task, void *obj, void *data);
59624Sigor@sysoev.ru static void nxt_h1p_conn_request_error(nxt_task_t *task, void *obj, void *data);
60624Sigor@sysoev.ru static void nxt_h1p_conn_request_timeout(nxt_task_t *task, void *obj,
61624Sigor@sysoev.ru     void *data);
62626Sigor@sysoev.ru static void nxt_h1p_conn_request_send_timeout(nxt_task_t *task, void *obj,
63626Sigor@sysoev.ru     void *data);
64683Sigor@sysoev.ru static nxt_msec_t nxt_h1p_conn_request_timeout_value(nxt_conn_t *c,
65683Sigor@sysoev.ru     uintptr_t data);
66725Sigor@sysoev.ru nxt_inline void nxt_h1p_request_error(nxt_task_t *task, nxt_h1proto_t *h1p,
67725Sigor@sysoev.ru     nxt_http_request_t *r);
68431Sigor@sysoev.ru 
69431Sigor@sysoev.ru 
70431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_state;
71*741Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_close_state;
72624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_header_parse_state;
73431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_body_state;
74740Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_request_send_state;
75740Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_timeout_send_state;
76624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_keepalive_state;
77683Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_close_state;
78431Sigor@sysoev.ru 
79431Sigor@sysoev.ru 
80431Sigor@sysoev.ru const nxt_http_proto_body_read_t  nxt_http_proto_body_read[3] = {
81431Sigor@sysoev.ru     nxt_h1p_request_body_read,
82431Sigor@sysoev.ru     NULL,
83431Sigor@sysoev.ru     NULL,
84431Sigor@sysoev.ru };
85431Sigor@sysoev.ru 
86431Sigor@sysoev.ru 
87431Sigor@sysoev.ru const nxt_http_proto_local_addr_t  nxt_http_proto_local_addr[3] = {
88431Sigor@sysoev.ru     nxt_h1p_request_local_addr,
89431Sigor@sysoev.ru     NULL,
90431Sigor@sysoev.ru     NULL,
91431Sigor@sysoev.ru };
92431Sigor@sysoev.ru 
93431Sigor@sysoev.ru 
94431Sigor@sysoev.ru const nxt_http_proto_header_send_t  nxt_http_proto_header_send[3] = {
95431Sigor@sysoev.ru     nxt_h1p_request_header_send,
96431Sigor@sysoev.ru     NULL,
97431Sigor@sysoev.ru     NULL,
98431Sigor@sysoev.ru };
99431Sigor@sysoev.ru 
100431Sigor@sysoev.ru 
101431Sigor@sysoev.ru const nxt_http_proto_send_t  nxt_http_proto_send[3] = {
102431Sigor@sysoev.ru     nxt_h1p_request_send,
103431Sigor@sysoev.ru     NULL,
104431Sigor@sysoev.ru     NULL,
105431Sigor@sysoev.ru };
106431Sigor@sysoev.ru 
107431Sigor@sysoev.ru 
108630Svbart@nginx.com const nxt_http_proto_body_bytes_sent_t  nxt_http_proto_body_bytes_sent[3] = {
109630Svbart@nginx.com     nxt_h1p_request_body_bytes_sent,
110630Svbart@nginx.com     NULL,
111630Svbart@nginx.com     NULL,
112630Svbart@nginx.com };
113630Svbart@nginx.com 
114630Svbart@nginx.com 
115608Sigor@sysoev.ru const nxt_http_proto_discard_t  nxt_http_proto_discard[3] = {
116608Sigor@sysoev.ru     nxt_h1p_request_discard,
117608Sigor@sysoev.ru     NULL,
118608Sigor@sysoev.ru     NULL,
119608Sigor@sysoev.ru };
120608Sigor@sysoev.ru 
121608Sigor@sysoev.ru 
122431Sigor@sysoev.ru const nxt_http_proto_close_t  nxt_http_proto_close[3] = {
123431Sigor@sysoev.ru     nxt_h1p_request_close,
124431Sigor@sysoev.ru     NULL,
125431Sigor@sysoev.ru     NULL,
126431Sigor@sysoev.ru };
127431Sigor@sysoev.ru 
128431Sigor@sysoev.ru 
129431Sigor@sysoev.ru static nxt_lvlhsh_t            nxt_h1p_fields_hash;
130431Sigor@sysoev.ru 
131431Sigor@sysoev.ru static nxt_http_field_proc_t   nxt_h1p_fields[] = {
132431Sigor@sysoev.ru     { nxt_string("Connection"),        &nxt_h1p_connection, 0 },
133431Sigor@sysoev.ru     { nxt_string("Transfer-Encoding"), &nxt_h1p_transfer_encoding, 0 },
134431Sigor@sysoev.ru 
135431Sigor@sysoev.ru     { nxt_string("Host"),              &nxt_http_request_host, 0 },
136431Sigor@sysoev.ru     { nxt_string("Cookie"),            &nxt_http_request_field,
137431Sigor@sysoev.ru         offsetof(nxt_http_request_t, cookie) },
138630Svbart@nginx.com     { nxt_string("Referer"),           &nxt_http_request_field,
139630Svbart@nginx.com         offsetof(nxt_http_request_t, referer) },
140630Svbart@nginx.com     { nxt_string("User-Agent"),        &nxt_http_request_field,
141630Svbart@nginx.com         offsetof(nxt_http_request_t, user_agent) },
142431Sigor@sysoev.ru     { nxt_string("Content-Type"),      &nxt_http_request_field,
143431Sigor@sysoev.ru         offsetof(nxt_http_request_t, content_type) },
144431Sigor@sysoev.ru     { nxt_string("Content-Length"),    &nxt_http_request_content_length, 0 },
145431Sigor@sysoev.ru };
146431Sigor@sysoev.ru 
147431Sigor@sysoev.ru 
148431Sigor@sysoev.ru nxt_int_t
149431Sigor@sysoev.ru nxt_h1p_init(nxt_task_t *task, nxt_runtime_t *rt)
150431Sigor@sysoev.ru {
151431Sigor@sysoev.ru     return nxt_http_fields_hash(&nxt_h1p_fields_hash, rt->mem_pool,
152431Sigor@sysoev.ru                                 nxt_h1p_fields, nxt_nitems(nxt_h1p_fields));
153431Sigor@sysoev.ru }
154431Sigor@sysoev.ru 
155431Sigor@sysoev.ru 
156431Sigor@sysoev.ru void
157431Sigor@sysoev.ru nxt_http_conn_init(nxt_task_t *task, void *obj, void *data)
158431Sigor@sysoev.ru {
159431Sigor@sysoev.ru     nxt_conn_t               *c;
160431Sigor@sysoev.ru     nxt_socket_conf_t        *skcf;
161431Sigor@sysoev.ru     nxt_event_engine_t       *engine;
162683Sigor@sysoev.ru     nxt_listen_event_t       *lev;
163431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
164431Sigor@sysoev.ru 
165431Sigor@sysoev.ru     c = obj;
166683Sigor@sysoev.ru     lev = data;
167431Sigor@sysoev.ru 
168431Sigor@sysoev.ru     nxt_debug(task, "http conn init");
169431Sigor@sysoev.ru 
170683Sigor@sysoev.ru     joint = lev->socket.data;
171431Sigor@sysoev.ru     skcf = joint->socket_conf;
172431Sigor@sysoev.ru     c->local = skcf->sockaddr;
173431Sigor@sysoev.ru 
174431Sigor@sysoev.ru     engine = task->thread->engine;
175431Sigor@sysoev.ru     c->read_work_queue = &engine->fast_work_queue;
176431Sigor@sysoev.ru     c->write_work_queue = &engine->fast_work_queue;
177431Sigor@sysoev.ru 
178431Sigor@sysoev.ru     c->read_state = &nxt_h1p_idle_state;
179431Sigor@sysoev.ru 
180637Sigor@sysoev.ru     nxt_conn_read(engine, c);
181431Sigor@sysoev.ru }
182431Sigor@sysoev.ru 
183431Sigor@sysoev.ru 
184431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_state
185431Sigor@sysoev.ru     nxt_aligned(64) =
186431Sigor@sysoev.ru {
187629Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_proto_init,
188624Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_error,
189431Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
190431Sigor@sysoev.ru 
191629Sigor@sysoev.ru     .io_read_handler = nxt_h1p_conn_io_read_handler,
192431Sigor@sysoev.ru 
193*741Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_timeout,
194624Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_timeout_value,
195635Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, idle_timeout),
196636Sigor@sysoev.ru     .timer_autoreset = 1,
197431Sigor@sysoev.ru };
198431Sigor@sysoev.ru 
199431Sigor@sysoev.ru 
200629Sigor@sysoev.ru static ssize_t
201629Sigor@sysoev.ru nxt_h1p_conn_io_read_handler(nxt_conn_t *c)
202629Sigor@sysoev.ru {
203629Sigor@sysoev.ru     size_t                   size;
204629Sigor@sysoev.ru     ssize_t                  n;
205629Sigor@sysoev.ru     nxt_buf_t                *b;
206629Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
207629Sigor@sysoev.ru 
208683Sigor@sysoev.ru     joint = c->listen->socket.data;
209*741Sigor@sysoev.ru 
210*741Sigor@sysoev.ru     if (joint == NULL) {
211*741Sigor@sysoev.ru         /*
212*741Sigor@sysoev.ru          * Listening socket had been closed while
213*741Sigor@sysoev.ru          * connection was in keep-alive state.
214*741Sigor@sysoev.ru          */
215*741Sigor@sysoev.ru         c->read_state = &nxt_h1p_idle_close_state;
216*741Sigor@sysoev.ru         return 0;
217*741Sigor@sysoev.ru     }
218*741Sigor@sysoev.ru 
219629Sigor@sysoev.ru     size = joint->socket_conf->header_buffer_size;
220629Sigor@sysoev.ru 
221629Sigor@sysoev.ru     b = nxt_buf_mem_alloc(c->mem_pool, size, 0);
222629Sigor@sysoev.ru     if (nxt_slow_path(b == NULL)) {
223629Sigor@sysoev.ru         c->socket.error = NXT_ENOMEM;
224629Sigor@sysoev.ru         return NXT_ERROR;
225629Sigor@sysoev.ru     }
226629Sigor@sysoev.ru 
227629Sigor@sysoev.ru     n = c->io->recvbuf(c, b);
228629Sigor@sysoev.ru 
229629Sigor@sysoev.ru     if (n > 0) {
230629Sigor@sysoev.ru         c->read = b;
231629Sigor@sysoev.ru 
232629Sigor@sysoev.ru     } else {
233629Sigor@sysoev.ru         nxt_mp_free(c->mem_pool, b);
234629Sigor@sysoev.ru     }
235629Sigor@sysoev.ru 
236629Sigor@sysoev.ru     return n;
237629Sigor@sysoev.ru }
238629Sigor@sysoev.ru 
239629Sigor@sysoev.ru 
240*741Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_close_state
241*741Sigor@sysoev.ru     nxt_aligned(64) =
242*741Sigor@sysoev.ru {
243*741Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
244*741Sigor@sysoev.ru };
245*741Sigor@sysoev.ru 
246*741Sigor@sysoev.ru 
247431Sigor@sysoev.ru static void
248624Sigor@sysoev.ru nxt_h1p_conn_proto_init(nxt_task_t *task, void *obj, void *data)
249431Sigor@sysoev.ru {
250624Sigor@sysoev.ru     nxt_conn_t     *c;
251624Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
252624Sigor@sysoev.ru 
253624Sigor@sysoev.ru     c = obj;
254624Sigor@sysoev.ru 
255624Sigor@sysoev.ru     nxt_debug(task, "h1p conn proto init");
256624Sigor@sysoev.ru 
257624Sigor@sysoev.ru     h1p = nxt_mp_zget(c->mem_pool, sizeof(nxt_h1proto_t));
258624Sigor@sysoev.ru     if (nxt_slow_path(h1p == NULL)) {
259624Sigor@sysoev.ru         nxt_h1p_close(task, c);
260624Sigor@sysoev.ru         return;
261624Sigor@sysoev.ru     }
262624Sigor@sysoev.ru 
263624Sigor@sysoev.ru     c->socket.data = h1p;
264624Sigor@sysoev.ru     h1p->conn = c;
265624Sigor@sysoev.ru 
266624Sigor@sysoev.ru     nxt_h1p_conn_request_init(task, c, h1p);
267624Sigor@sysoev.ru }
268624Sigor@sysoev.ru 
269624Sigor@sysoev.ru 
270624Sigor@sysoev.ru static void
271624Sigor@sysoev.ru nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data)
272624Sigor@sysoev.ru {
273431Sigor@sysoev.ru     nxt_int_t                ret;
274431Sigor@sysoev.ru     nxt_conn_t               *c;
275431Sigor@sysoev.ru     nxt_h1proto_t            *h1p;
276431Sigor@sysoev.ru     nxt_http_request_t       *r;
277431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
278431Sigor@sysoev.ru 
279431Sigor@sysoev.ru     c = obj;
280431Sigor@sysoev.ru     h1p = data;
281431Sigor@sysoev.ru 
282624Sigor@sysoev.ru     nxt_debug(task, "h1p conn request init");
283431Sigor@sysoev.ru 
284624Sigor@sysoev.ru     r = nxt_http_request_create(task);
285431Sigor@sysoev.ru 
286624Sigor@sysoev.ru     if (nxt_fast_path(r != NULL)) {
287431Sigor@sysoev.ru         h1p->request = r;
288431Sigor@sysoev.ru         r->proto.h1 = h1p;
289624Sigor@sysoev.ru 
290431Sigor@sysoev.ru         r->remote = c->remote;
291431Sigor@sysoev.ru 
292431Sigor@sysoev.ru         ret = nxt_http_parse_request_init(&h1p->parser, r->mem_pool);
293624Sigor@sysoev.ru 
294624Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_OK)) {
295683Sigor@sysoev.ru             joint = c->listen->socket.data;
296683Sigor@sysoev.ru             joint->count++;
297683Sigor@sysoev.ru 
298683Sigor@sysoev.ru             r->conf = joint;
299683Sigor@sysoev.ru             c->local = joint->socket_conf->sockaddr;
300683Sigor@sysoev.ru 
301683Sigor@sysoev.ru             nxt_h1p_conn_request_header_parse(task, c, h1p);
302624Sigor@sysoev.ru             return;
303431Sigor@sysoev.ru         }
304624Sigor@sysoev.ru 
305624Sigor@sysoev.ru         /*
306624Sigor@sysoev.ru          * The request is very incomplete here,
307624Sigor@sysoev.ru          * so "internal server error" useless here.
308624Sigor@sysoev.ru          */
309624Sigor@sysoev.ru         nxt_mp_release(r->mem_pool);
310431Sigor@sysoev.ru     }
311431Sigor@sysoev.ru 
312624Sigor@sysoev.ru     nxt_h1p_close(task, c);
313624Sigor@sysoev.ru }
314624Sigor@sysoev.ru 
315624Sigor@sysoev.ru 
316624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_header_parse_state
317624Sigor@sysoev.ru     nxt_aligned(64) =
318624Sigor@sysoev.ru {
319683Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_request_header_parse,
320624Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_request_error,
321624Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_request_error,
322624Sigor@sysoev.ru 
323624Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_request_timeout,
324683Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_request_timeout_value,
325624Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, header_read_timeout),
326624Sigor@sysoev.ru };
327624Sigor@sysoev.ru 
328624Sigor@sysoev.ru 
329624Sigor@sysoev.ru static void
330683Sigor@sysoev.ru nxt_h1p_conn_request_header_parse(nxt_task_t *task, void *obj, void *data)
331624Sigor@sysoev.ru {
332624Sigor@sysoev.ru     nxt_int_t           ret;
333624Sigor@sysoev.ru     nxt_conn_t          *c;
334624Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
335624Sigor@sysoev.ru     nxt_http_status_t   status;
336624Sigor@sysoev.ru     nxt_http_request_t  *r;
337624Sigor@sysoev.ru 
338624Sigor@sysoev.ru     c = obj;
339624Sigor@sysoev.ru     h1p = data;
340624Sigor@sysoev.ru 
341624Sigor@sysoev.ru     nxt_debug(task, "h1p conn header parse");
342624Sigor@sysoev.ru 
343431Sigor@sysoev.ru     ret = nxt_http_parse_request(&h1p->parser, &c->read->mem);
344431Sigor@sysoev.ru 
345636Sigor@sysoev.ru     ret = nxt_expect(NXT_DONE, ret);
346624Sigor@sysoev.ru 
347636Sigor@sysoev.ru     if (ret != NXT_AGAIN) {
348636Sigor@sysoev.ru         nxt_timer_disable(task->thread->engine, &c->read_timer);
349636Sigor@sysoev.ru     }
350636Sigor@sysoev.ru 
351636Sigor@sysoev.ru     r = h1p->request;
352625Sigor@sysoev.ru 
353625Sigor@sysoev.ru     switch (ret) {
354625Sigor@sysoev.ru 
355625Sigor@sysoev.ru     case NXT_DONE:
356431Sigor@sysoev.ru         /*
357431Sigor@sysoev.ru          * By default the keepalive mode is disabled in HTTP/1.0 and
358431Sigor@sysoev.ru          * enabled in HTTP/1.1.  The mode can be overridden later by
359431Sigor@sysoev.ru          * the "Connection" field processed in nxt_h1p_connection().
360431Sigor@sysoev.ru          */
361481Svbart@nginx.com         h1p->keepalive = (h1p->parser.version.s.minor != '0');
362431Sigor@sysoev.ru 
363620Svbart@nginx.com         ret = nxt_h1p_header_process(h1p, r);
364431Sigor@sysoev.ru 
365431Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_OK)) {
366431Sigor@sysoev.ru             r->state->ready_handler(task, r, NULL);
367431Sigor@sysoev.ru             return;
368431Sigor@sysoev.ru         }
369431Sigor@sysoev.ru 
370480Svbart@nginx.com         /* ret == NXT_ERROR */
371625Sigor@sysoev.ru         status = NXT_HTTP_BAD_REQUEST;
372480Svbart@nginx.com 
373625Sigor@sysoev.ru         goto error;
374431Sigor@sysoev.ru 
375625Sigor@sysoev.ru     case NXT_AGAIN:
376683Sigor@sysoev.ru         status = nxt_h1p_header_buffer_test(task, h1p, c, r->conf->socket_conf);
377431Sigor@sysoev.ru 
378625Sigor@sysoev.ru         if (nxt_fast_path(status == NXT_OK)) {
379625Sigor@sysoev.ru             c->read_state = &nxt_h1p_header_parse_state;
380431Sigor@sysoev.ru 
381625Sigor@sysoev.ru             nxt_conn_read(task->thread->engine, c);
382625Sigor@sysoev.ru             return;
383431Sigor@sysoev.ru         }
384431Sigor@sysoev.ru 
385625Sigor@sysoev.ru         break;
386480Svbart@nginx.com 
387480Svbart@nginx.com     case NXT_HTTP_PARSE_INVALID:
388480Svbart@nginx.com         status = NXT_HTTP_BAD_REQUEST;
389480Svbart@nginx.com         break;
390431Sigor@sysoev.ru 
391482Svbart@nginx.com     case NXT_HTTP_PARSE_UNSUPPORTED_VERSION:
392482Svbart@nginx.com         status = NXT_HTTP_VERSION_NOT_SUPPORTED;
393482Svbart@nginx.com         break;
394482Svbart@nginx.com 
395480Svbart@nginx.com     case NXT_HTTP_PARSE_TOO_LARGE_FIELD:
396480Svbart@nginx.com         status = NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
397480Svbart@nginx.com         break;
398480Svbart@nginx.com 
399480Svbart@nginx.com     default:
400480Svbart@nginx.com     case NXT_ERROR:
401480Svbart@nginx.com         status = NXT_HTTP_INTERNAL_SERVER_ERROR;
402480Svbart@nginx.com         break;
403480Svbart@nginx.com     }
404480Svbart@nginx.com 
405620Svbart@nginx.com     (void) nxt_h1p_header_process(h1p, r);
406625Sigor@sysoev.ru 
407625Sigor@sysoev.ru error:
408625Sigor@sysoev.ru 
409480Svbart@nginx.com     nxt_http_request_error(task, r, status);
410431Sigor@sysoev.ru }
411431Sigor@sysoev.ru 
412431Sigor@sysoev.ru 
413431Sigor@sysoev.ru static nxt_int_t
414620Svbart@nginx.com nxt_h1p_header_process(nxt_h1proto_t *h1p, nxt_http_request_t *r)
415620Svbart@nginx.com {
416620Svbart@nginx.com     r->target.start = h1p->parser.target_start;
417620Svbart@nginx.com     r->target.length = h1p->parser.target_end - h1p->parser.target_start;
418620Svbart@nginx.com 
419620Svbart@nginx.com     if (h1p->parser.version.ui64 != 0) {
420620Svbart@nginx.com         r->version.start = h1p->parser.version.str;
421620Svbart@nginx.com         r->version.length = sizeof(h1p->parser.version.str);
422620Svbart@nginx.com     }
423620Svbart@nginx.com 
424620Svbart@nginx.com     r->method = &h1p->parser.method;
425620Svbart@nginx.com     r->path = &h1p->parser.path;
426620Svbart@nginx.com     r->args = &h1p->parser.args;
427620Svbart@nginx.com 
428620Svbart@nginx.com     r->fields = h1p->parser.fields;
429620Svbart@nginx.com 
430620Svbart@nginx.com     return nxt_http_fields_process(r->fields, &nxt_h1p_fields_hash, r);
431620Svbart@nginx.com }
432620Svbart@nginx.com 
433620Svbart@nginx.com 
434620Svbart@nginx.com static nxt_int_t
435625Sigor@sysoev.ru nxt_h1p_header_buffer_test(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c,
436625Sigor@sysoev.ru     nxt_socket_conf_t *skcf)
437625Sigor@sysoev.ru {
438625Sigor@sysoev.ru     size_t     size, used;
439625Sigor@sysoev.ru     nxt_buf_t  *in, *b;
440625Sigor@sysoev.ru 
441625Sigor@sysoev.ru     in = c->read;
442625Sigor@sysoev.ru 
443625Sigor@sysoev.ru     if (nxt_buf_mem_free_size(&in->mem) == 0) {
444625Sigor@sysoev.ru         size = skcf->large_header_buffer_size;
445625Sigor@sysoev.ru         used = nxt_buf_mem_used_size(&in->mem);
446625Sigor@sysoev.ru 
447625Sigor@sysoev.ru         if (size <= used || h1p->nbuffers >= skcf->large_header_buffers) {
448625Sigor@sysoev.ru             return NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
449625Sigor@sysoev.ru         }
450625Sigor@sysoev.ru 
451625Sigor@sysoev.ru         b = nxt_buf_mem_alloc(c->mem_pool, size, 0);
452625Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
453625Sigor@sysoev.ru             return NXT_HTTP_INTERNAL_SERVER_ERROR;
454625Sigor@sysoev.ru         }
455625Sigor@sysoev.ru 
456625Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.pos, in->mem.pos, used);
457625Sigor@sysoev.ru 
458625Sigor@sysoev.ru         in->next = h1p->buffers;
459625Sigor@sysoev.ru         h1p->buffers = in;
460625Sigor@sysoev.ru         h1p->nbuffers++;
461625Sigor@sysoev.ru 
462625Sigor@sysoev.ru         c->read = b;
463625Sigor@sysoev.ru     }
464625Sigor@sysoev.ru 
465625Sigor@sysoev.ru     return NXT_OK;
466625Sigor@sysoev.ru }
467625Sigor@sysoev.ru 
468625Sigor@sysoev.ru 
469625Sigor@sysoev.ru static nxt_int_t
470431Sigor@sysoev.ru nxt_h1p_connection(void *ctx, nxt_http_field_t *field, uintptr_t data)
471431Sigor@sysoev.ru {
472431Sigor@sysoev.ru     nxt_http_request_t  *r;
473431Sigor@sysoev.ru 
474431Sigor@sysoev.ru     r = ctx;
475431Sigor@sysoev.ru 
476431Sigor@sysoev.ru     if (field->value_length == 5 && nxt_memcmp(field->value, "close", 5) == 0) {
477431Sigor@sysoev.ru         r->proto.h1->keepalive = 0;
478431Sigor@sysoev.ru     }
479431Sigor@sysoev.ru 
480431Sigor@sysoev.ru     return NXT_OK;
481431Sigor@sysoev.ru }
482431Sigor@sysoev.ru 
483431Sigor@sysoev.ru 
484431Sigor@sysoev.ru static nxt_int_t
485431Sigor@sysoev.ru nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field, uintptr_t data)
486431Sigor@sysoev.ru {
487431Sigor@sysoev.ru     nxt_http_te_t       te;
488431Sigor@sysoev.ru     nxt_http_request_t  *r;
489431Sigor@sysoev.ru 
490431Sigor@sysoev.ru     r = ctx;
491431Sigor@sysoev.ru 
492431Sigor@sysoev.ru     if (field->value_length == 7
493431Sigor@sysoev.ru         && nxt_memcmp(field->value, "chunked", 7) == 0)
494431Sigor@sysoev.ru     {
495431Sigor@sysoev.ru         te = NXT_HTTP_TE_CHUNKED;
496431Sigor@sysoev.ru 
497431Sigor@sysoev.ru     } else {
498431Sigor@sysoev.ru         te = NXT_HTTP_TE_UNSUPPORTED;
499431Sigor@sysoev.ru     }
500431Sigor@sysoev.ru 
501431Sigor@sysoev.ru     r->proto.h1->transfer_encoding = te;
502431Sigor@sysoev.ru 
503431Sigor@sysoev.ru     return NXT_OK;
504431Sigor@sysoev.ru }
505431Sigor@sysoev.ru 
506431Sigor@sysoev.ru 
507431Sigor@sysoev.ru static void
508431Sigor@sysoev.ru nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r)
509431Sigor@sysoev.ru {
510527Svbart@nginx.com     size_t             size, body_length;
511459Sigor@sysoev.ru     nxt_buf_t          *in, *b;
512431Sigor@sysoev.ru     nxt_conn_t         *c;
513459Sigor@sysoev.ru     nxt_h1proto_t      *h1p;
514431Sigor@sysoev.ru     nxt_http_status_t  status;
515431Sigor@sysoev.ru 
516459Sigor@sysoev.ru     h1p = r->proto.h1;
517459Sigor@sysoev.ru 
518624Sigor@sysoev.ru     nxt_debug(task, "h1p request body read %O te:%d",
519459Sigor@sysoev.ru               r->content_length_n, h1p->transfer_encoding);
520431Sigor@sysoev.ru 
521459Sigor@sysoev.ru     switch (h1p->transfer_encoding) {
522431Sigor@sysoev.ru 
523431Sigor@sysoev.ru     case NXT_HTTP_TE_CHUNKED:
524431Sigor@sysoev.ru         status = NXT_HTTP_LENGTH_REQUIRED;
525431Sigor@sysoev.ru         goto error;
526431Sigor@sysoev.ru 
527431Sigor@sysoev.ru     case NXT_HTTP_TE_UNSUPPORTED:
528431Sigor@sysoev.ru         status = NXT_HTTP_NOT_IMPLEMENTED;
529431Sigor@sysoev.ru         goto error;
530431Sigor@sysoev.ru 
531431Sigor@sysoev.ru     default:
532431Sigor@sysoev.ru     case NXT_HTTP_TE_NONE:
533431Sigor@sysoev.ru         break;
534431Sigor@sysoev.ru     }
535431Sigor@sysoev.ru 
536431Sigor@sysoev.ru     if (r->content_length_n == -1 || r->content_length_n == 0) {
537431Sigor@sysoev.ru         goto ready;
538431Sigor@sysoev.ru     }
539431Sigor@sysoev.ru 
540683Sigor@sysoev.ru     if (r->content_length_n > (nxt_off_t) r->conf->socket_conf->max_body_size) {
541431Sigor@sysoev.ru         status = NXT_HTTP_PAYLOAD_TOO_LARGE;
542431Sigor@sysoev.ru         goto error;
543431Sigor@sysoev.ru     }
544431Sigor@sysoev.ru 
545527Svbart@nginx.com     body_length = (size_t) r->content_length_n;
546431Sigor@sysoev.ru 
547431Sigor@sysoev.ru     b = r->body;
548431Sigor@sysoev.ru 
549431Sigor@sysoev.ru     if (b == NULL) {
550527Svbart@nginx.com         b = nxt_buf_mem_alloc(r->mem_pool, body_length, 0);
551431Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
552431Sigor@sysoev.ru             status = NXT_HTTP_INTERNAL_SERVER_ERROR;
553431Sigor@sysoev.ru             goto error;
554431Sigor@sysoev.ru         }
555431Sigor@sysoev.ru 
556431Sigor@sysoev.ru         r->body = b;
557431Sigor@sysoev.ru     }
558431Sigor@sysoev.ru 
559459Sigor@sysoev.ru     in = h1p->conn->read;
560431Sigor@sysoev.ru 
561459Sigor@sysoev.ru     size = nxt_buf_mem_used_size(&in->mem);
562431Sigor@sysoev.ru 
563431Sigor@sysoev.ru     if (size != 0) {
564527Svbart@nginx.com         if (size > body_length) {
565527Svbart@nginx.com             size = body_length;
566431Sigor@sysoev.ru         }
567431Sigor@sysoev.ru 
568459Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.free, in->mem.pos, size);
569459Sigor@sysoev.ru         in->mem.pos += size;
570431Sigor@sysoev.ru     }
571431Sigor@sysoev.ru 
572527Svbart@nginx.com     size = nxt_buf_mem_free_size(&b->mem);
573431Sigor@sysoev.ru 
574527Svbart@nginx.com     nxt_debug(task, "h1p body rest: %uz", size);
575431Sigor@sysoev.ru 
576527Svbart@nginx.com     if (size != 0) {
577459Sigor@sysoev.ru         in->next = h1p->buffers;
578459Sigor@sysoev.ru         h1p->buffers = in;
579459Sigor@sysoev.ru 
580459Sigor@sysoev.ru         c = h1p->conn;
581431Sigor@sysoev.ru         c->read = b;
582431Sigor@sysoev.ru         c->read_state = &nxt_h1p_read_body_state;
583431Sigor@sysoev.ru 
584431Sigor@sysoev.ru         nxt_conn_read(task->thread->engine, c);
585431Sigor@sysoev.ru         return;
586431Sigor@sysoev.ru     }
587431Sigor@sysoev.ru 
588431Sigor@sysoev.ru ready:
589431Sigor@sysoev.ru 
590431Sigor@sysoev.ru     nxt_work_queue_add(&task->thread->engine->fast_work_queue,
591431Sigor@sysoev.ru                        r->state->ready_handler, task, r, NULL);
592431Sigor@sysoev.ru 
593431Sigor@sysoev.ru     return;
594431Sigor@sysoev.ru 
595431Sigor@sysoev.ru error:
596431Sigor@sysoev.ru 
597459Sigor@sysoev.ru     h1p->keepalive = 0;
598431Sigor@sysoev.ru 
599431Sigor@sysoev.ru     nxt_http_request_error(task, r, status);
600431Sigor@sysoev.ru }
601431Sigor@sysoev.ru 
602431Sigor@sysoev.ru 
603431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_body_state
604431Sigor@sysoev.ru     nxt_aligned(64) =
605431Sigor@sysoev.ru {
606637Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_request_body_read,
607624Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_request_error,
608624Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_request_error,
609431Sigor@sysoev.ru 
610624Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_request_timeout,
611683Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_request_timeout_value,
612431Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, body_read_timeout),
613431Sigor@sysoev.ru     .timer_autoreset = 1,
614431Sigor@sysoev.ru };
615431Sigor@sysoev.ru 
616431Sigor@sysoev.ru 
617431Sigor@sysoev.ru static void
618637Sigor@sysoev.ru nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj, void *data)
619431Sigor@sysoev.ru {
620431Sigor@sysoev.ru     size_t              size;
621431Sigor@sysoev.ru     nxt_conn_t          *c;
622431Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
623431Sigor@sysoev.ru     nxt_http_request_t  *r;
624637Sigor@sysoev.ru     nxt_event_engine_t  *engine;
625431Sigor@sysoev.ru 
626431Sigor@sysoev.ru     c = obj;
627431Sigor@sysoev.ru     h1p = data;
628431Sigor@sysoev.ru 
629637Sigor@sysoev.ru     nxt_debug(task, "h1p conn request body read");
630431Sigor@sysoev.ru 
631527Svbart@nginx.com     size = nxt_buf_mem_free_size(&c->read->mem);
632431Sigor@sysoev.ru 
633527Svbart@nginx.com     nxt_debug(task, "h1p body rest: %uz", size);
634431Sigor@sysoev.ru 
635637Sigor@sysoev.ru     engine = task->thread->engine;
636637Sigor@sysoev.ru 
637527Svbart@nginx.com     if (size != 0) {
638637Sigor@sysoev.ru         nxt_conn_read(engine, c);
639431Sigor@sysoev.ru 
640431Sigor@sysoev.ru     } else {
641637Sigor@sysoev.ru         c->read = NULL;
642527Svbart@nginx.com         r = h1p->request;
643637Sigor@sysoev.ru 
644637Sigor@sysoev.ru         nxt_work_queue_add(&engine->fast_work_queue, r->state->ready_handler,
645637Sigor@sysoev.ru                            task, r, NULL);
646431Sigor@sysoev.ru     }
647431Sigor@sysoev.ru }
648431Sigor@sysoev.ru 
649431Sigor@sysoev.ru 
650431Sigor@sysoev.ru static void
651431Sigor@sysoev.ru nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r)
652431Sigor@sysoev.ru {
653431Sigor@sysoev.ru     r->local = nxt_conn_local_addr(task, r->proto.h1->conn);
654431Sigor@sysoev.ru }
655431Sigor@sysoev.ru 
656431Sigor@sysoev.ru 
657431Sigor@sysoev.ru #define NXT_HTTP_LAST_SUCCESS                                                 \
658431Sigor@sysoev.ru     (NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1)
659431Sigor@sysoev.ru 
660431Sigor@sysoev.ru static const nxt_str_t  nxt_http_success[] = {
661431Sigor@sysoev.ru     nxt_string("HTTP/1.1 200 OK\r\n"),
662431Sigor@sysoev.ru     nxt_string("HTTP/1.1 201 Created\r\n"),
663431Sigor@sysoev.ru     nxt_string("HTTP/1.1 202 Accepted\r\n"),
664431Sigor@sysoev.ru     nxt_string("HTTP/1.1 203 Non-Authoritative Information\r\n"),
665431Sigor@sysoev.ru     nxt_string("HTTP/1.1 204 No Content\r\n"),
666431Sigor@sysoev.ru     nxt_string("HTTP/1.1 205 Reset Content\r\n"),
667431Sigor@sysoev.ru     nxt_string("HTTP/1.1 206 Partial Content\r\n"),
668431Sigor@sysoev.ru };
669431Sigor@sysoev.ru 
670431Sigor@sysoev.ru 
671431Sigor@sysoev.ru #define NXT_HTTP_LAST_REDIRECTION                                             \
672431Sigor@sysoev.ru     (NXT_HTTP_MULTIPLE_CHOICES + nxt_nitems(nxt_http_redirection) - 1)
673431Sigor@sysoev.ru 
674431Sigor@sysoev.ru static const nxt_str_t  nxt_http_redirection[] = {
675431Sigor@sysoev.ru     nxt_string("HTTP/1.1 300 Multiple Choices\r\n"),
676431Sigor@sysoev.ru     nxt_string("HTTP/1.1 301 Moved Permanently\r\n"),
677431Sigor@sysoev.ru     nxt_string("HTTP/1.1 302 Found\r\n"),
678431Sigor@sysoev.ru     nxt_string("HTTP/1.1 303 See Other\r\n"),
679431Sigor@sysoev.ru     nxt_string("HTTP/1.1 304 Not Modified\r\n"),
680431Sigor@sysoev.ru };
681431Sigor@sysoev.ru 
682431Sigor@sysoev.ru 
683431Sigor@sysoev.ru #define NXT_HTTP_LAST_CLIENT_ERROR                                            \
684431Sigor@sysoev.ru     (NXT_HTTP_BAD_REQUEST + nxt_nitems(nxt_http_client_error) - 1)
685431Sigor@sysoev.ru 
686431Sigor@sysoev.ru static const nxt_str_t  nxt_http_client_error[] = {
687431Sigor@sysoev.ru     nxt_string("HTTP/1.1 400 Bad Request\r\n"),
688431Sigor@sysoev.ru     nxt_string("HTTP/1.1 401 Unauthorized\r\n"),
689431Sigor@sysoev.ru     nxt_string("HTTP/1.1 402 Payment Required\r\n"),
690431Sigor@sysoev.ru     nxt_string("HTTP/1.1 403 Forbidden\r\n"),
691431Sigor@sysoev.ru     nxt_string("HTTP/1.1 404 Not Found\r\n"),
692431Sigor@sysoev.ru     nxt_string("HTTP/1.1 405 Method Not Allowed\r\n"),
693431Sigor@sysoev.ru     nxt_string("HTTP/1.1 406 Not Acceptable\r\n"),
694431Sigor@sysoev.ru     nxt_string("HTTP/1.1 407 Proxy Authentication Required\r\n"),
695431Sigor@sysoev.ru     nxt_string("HTTP/1.1 408 Request Timeout\r\n"),
696431Sigor@sysoev.ru     nxt_string("HTTP/1.1 409 Conflict\r\n"),
697431Sigor@sysoev.ru     nxt_string("HTTP/1.1 410 Gone\r\n"),
698431Sigor@sysoev.ru     nxt_string("HTTP/1.1 411 Length Required\r\n"),
699431Sigor@sysoev.ru     nxt_string("HTTP/1.1 412 Precondition Failed\r\n"),
700431Sigor@sysoev.ru     nxt_string("HTTP/1.1 413 Payload Too Large\r\n"),
701431Sigor@sysoev.ru     nxt_string("HTTP/1.1 414 URI Too Long\r\n"),
702431Sigor@sysoev.ru     nxt_string("HTTP/1.1 415 Unsupported Media Type\r\n"),
703431Sigor@sysoev.ru     nxt_string("HTTP/1.1 416 Range Not Satisfiable\r\n"),
704431Sigor@sysoev.ru     nxt_string("HTTP/1.1 417 Expectation Failed\r\n"),
705431Sigor@sysoev.ru     nxt_string("HTTP/1.1 418\r\n"),
706431Sigor@sysoev.ru     nxt_string("HTTP/1.1 419\r\n"),
707431Sigor@sysoev.ru     nxt_string("HTTP/1.1 420\r\n"),
708431Sigor@sysoev.ru     nxt_string("HTTP/1.1 421\r\n"),
709431Sigor@sysoev.ru     nxt_string("HTTP/1.1 422\r\n"),
710431Sigor@sysoev.ru     nxt_string("HTTP/1.1 423\r\n"),
711431Sigor@sysoev.ru     nxt_string("HTTP/1.1 424\r\n"),
712431Sigor@sysoev.ru     nxt_string("HTTP/1.1 425\r\n"),
713431Sigor@sysoev.ru     nxt_string("HTTP/1.1 426\r\n"),
714431Sigor@sysoev.ru     nxt_string("HTTP/1.1 427\r\n"),
715431Sigor@sysoev.ru     nxt_string("HTTP/1.1 428\r\n"),
716431Sigor@sysoev.ru     nxt_string("HTTP/1.1 429\r\n"),
717431Sigor@sysoev.ru     nxt_string("HTTP/1.1 430\r\n"),
718431Sigor@sysoev.ru     nxt_string("HTTP/1.1 431 Request Header Fields Too Large\r\n"),
719431Sigor@sysoev.ru };
720431Sigor@sysoev.ru 
721431Sigor@sysoev.ru 
722431Sigor@sysoev.ru #define NXT_HTTP_LAST_SERVER_ERROR                                            \
723431Sigor@sysoev.ru     (NXT_HTTP_INTERNAL_SERVER_ERROR + nxt_nitems(nxt_http_server_error) - 1)
724431Sigor@sysoev.ru 
725431Sigor@sysoev.ru static const nxt_str_t  nxt_http_server_error[] = {
726431Sigor@sysoev.ru     nxt_string("HTTP/1.1 500 Internal Server Error\r\n"),
727431Sigor@sysoev.ru     nxt_string("HTTP/1.1 501 Not Implemented\r\n"),
728431Sigor@sysoev.ru     nxt_string("HTTP/1.1 502 Bad Gateway\r\n"),
729431Sigor@sysoev.ru     nxt_string("HTTP/1.1 503 Service Unavailable\r\n"),
730431Sigor@sysoev.ru     nxt_string("HTTP/1.1 504 Gateway Timeout\r\n"),
731482Svbart@nginx.com     nxt_string("HTTP/1.1 505 HTTP Version Not Supported\r\n"),
732431Sigor@sysoev.ru };
733431Sigor@sysoev.ru 
734431Sigor@sysoev.ru 
735703Svbart@nginx.com #define UNKNOWN_STATUS_LENGTH  nxt_length("HTTP/1.1 65536\r\n")
736431Sigor@sysoev.ru 
737431Sigor@sysoev.ru static void
738431Sigor@sysoev.ru nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r)
739431Sigor@sysoev.ru {
740431Sigor@sysoev.ru     u_char              *p;
741431Sigor@sysoev.ru     size_t              size;
742431Sigor@sysoev.ru     nxt_buf_t           *header;
743431Sigor@sysoev.ru     nxt_str_t           unknown_status;
744431Sigor@sysoev.ru     nxt_int_t           conn;
745431Sigor@sysoev.ru     nxt_uint_t          n;
746431Sigor@sysoev.ru     nxt_bool_t          http11;
747431Sigor@sysoev.ru     nxt_conn_t          *c;
748431Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
749431Sigor@sysoev.ru     const nxt_str_t     *status;
750431Sigor@sysoev.ru     nxt_http_field_t    *field;
751431Sigor@sysoev.ru     nxt_event_engine_t  *engine;
752431Sigor@sysoev.ru     u_char              buf[UNKNOWN_STATUS_LENGTH];
753431Sigor@sysoev.ru 
754431Sigor@sysoev.ru     static const char   chunked[] = "Transfer-Encoding: chunked\r\n";
755431Sigor@sysoev.ru 
756431Sigor@sysoev.ru     static const nxt_str_t  connection[2] = {
757431Sigor@sysoev.ru         nxt_string("Connection: close\r\n"),
758431Sigor@sysoev.ru         nxt_string("Connection: keep-alive\r\n"),
759431Sigor@sysoev.ru     };
760431Sigor@sysoev.ru 
761431Sigor@sysoev.ru     nxt_debug(task, "h1p request header send");
762431Sigor@sysoev.ru 
763431Sigor@sysoev.ru     r->header_sent = 1;
764431Sigor@sysoev.ru     h1p = r->proto.h1;
765431Sigor@sysoev.ru     n = r->status;
766431Sigor@sysoev.ru 
767431Sigor@sysoev.ru     if (n >= NXT_HTTP_OK && n <= NXT_HTTP_LAST_SUCCESS) {
768431Sigor@sysoev.ru         status = &nxt_http_success[n - NXT_HTTP_OK];
769431Sigor@sysoev.ru 
770431Sigor@sysoev.ru     } else if (n >= NXT_HTTP_MULTIPLE_CHOICES
771431Sigor@sysoev.ru                && n <= NXT_HTTP_LAST_REDIRECTION)
772431Sigor@sysoev.ru     {
773431Sigor@sysoev.ru         status = &nxt_http_redirection[n - NXT_HTTP_MULTIPLE_CHOICES];
774431Sigor@sysoev.ru 
775431Sigor@sysoev.ru     } else if (n >= NXT_HTTP_BAD_REQUEST && n <= NXT_HTTP_LAST_CLIENT_ERROR) {
776431Sigor@sysoev.ru         status = &nxt_http_client_error[n - NXT_HTTP_BAD_REQUEST];
777431Sigor@sysoev.ru 
778431Sigor@sysoev.ru     } else if (n >= NXT_HTTP_INTERNAL_SERVER_ERROR
779431Sigor@sysoev.ru                && n <= NXT_HTTP_LAST_SERVER_ERROR)
780431Sigor@sysoev.ru     {
781431Sigor@sysoev.ru         status = &nxt_http_server_error[n - NXT_HTTP_INTERNAL_SERVER_ERROR];
782431Sigor@sysoev.ru 
783431Sigor@sysoev.ru     } else {
784431Sigor@sysoev.ru         p = nxt_sprintf(buf, buf + UNKNOWN_STATUS_LENGTH,
785431Sigor@sysoev.ru                         "HTTP/1.1 %03d\r\n", n);
786431Sigor@sysoev.ru 
787431Sigor@sysoev.ru         unknown_status.length = p - buf;
788431Sigor@sysoev.ru         unknown_status.start = buf;
789431Sigor@sysoev.ru         status = &unknown_status;
790431Sigor@sysoev.ru     }
791431Sigor@sysoev.ru 
792450Sigor@sysoev.ru     size = status->length;
793450Sigor@sysoev.ru     /* Trailing CRLF at the end of header. */
794703Svbart@nginx.com     size += nxt_length("\r\n");
795431Sigor@sysoev.ru 
796481Svbart@nginx.com     http11 = (h1p->parser.version.s.minor != '0');
797431Sigor@sysoev.ru 
798431Sigor@sysoev.ru     if (r->resp.content_length == NULL || r->resp.content_length->skip) {
799431Sigor@sysoev.ru         if (http11) {
800431Sigor@sysoev.ru             h1p->chunked = 1;
801703Svbart@nginx.com             size += nxt_length(chunked);
802450Sigor@sysoev.ru             /* Trailing CRLF will be added by the first chunk header. */
803703Svbart@nginx.com             size -= nxt_length("\r\n");
804431Sigor@sysoev.ru 
805431Sigor@sysoev.ru         } else {
806431Sigor@sysoev.ru             h1p->keepalive = 0;
807431Sigor@sysoev.ru         }
808431Sigor@sysoev.ru     }
809431Sigor@sysoev.ru 
810431Sigor@sysoev.ru     conn = -1;
811431Sigor@sysoev.ru 
812431Sigor@sysoev.ru     if (http11 ^ h1p->keepalive) {
813431Sigor@sysoev.ru         conn = h1p->keepalive;
814431Sigor@sysoev.ru         size += connection[conn].length;
815431Sigor@sysoev.ru     }
816431Sigor@sysoev.ru 
817431Sigor@sysoev.ru     nxt_list_each(field, r->resp.fields) {
818431Sigor@sysoev.ru 
819431Sigor@sysoev.ru         if (!field->skip) {
820431Sigor@sysoev.ru             size += field->name_length + field->value_length;
821703Svbart@nginx.com             size += nxt_length(": \r\n");
822431Sigor@sysoev.ru         }
823431Sigor@sysoev.ru 
824431Sigor@sysoev.ru     } nxt_list_loop;
825431Sigor@sysoev.ru 
826608Sigor@sysoev.ru     header = nxt_http_buf_mem(task, r, size);
827431Sigor@sysoev.ru     if (nxt_slow_path(header == NULL)) {
828725Sigor@sysoev.ru         nxt_h1p_request_error(task, h1p, r);
829431Sigor@sysoev.ru         return;
830431Sigor@sysoev.ru     }
831431Sigor@sysoev.ru 
832431Sigor@sysoev.ru     p = header->mem.free;
833431Sigor@sysoev.ru 
834431Sigor@sysoev.ru     p = nxt_cpymem(p, status->start, status->length);
835431Sigor@sysoev.ru 
836431Sigor@sysoev.ru     nxt_list_each(field, r->resp.fields) {
837431Sigor@sysoev.ru 
838431Sigor@sysoev.ru         if (!field->skip) {
839431Sigor@sysoev.ru             p = nxt_cpymem(p, field->name, field->name_length);
840431Sigor@sysoev.ru             *p++ = ':'; *p++ = ' ';
841431Sigor@sysoev.ru             p = nxt_cpymem(p, field->value, field->value_length);
842431Sigor@sysoev.ru             *p++ = '\r'; *p++ = '\n';
843431Sigor@sysoev.ru         }
844431Sigor@sysoev.ru 
845431Sigor@sysoev.ru     } nxt_list_loop;
846431Sigor@sysoev.ru 
847431Sigor@sysoev.ru     if (conn >= 0) {
848431Sigor@sysoev.ru         p = nxt_cpymem(p, connection[conn].start, connection[conn].length);
849431Sigor@sysoev.ru     }
850431Sigor@sysoev.ru 
851431Sigor@sysoev.ru     if (h1p->chunked) {
852703Svbart@nginx.com         p = nxt_cpymem(p, chunked, nxt_length(chunked));
853450Sigor@sysoev.ru         /* Trailing CRLF will be added by the first chunk header. */
854431Sigor@sysoev.ru 
855431Sigor@sysoev.ru     } else {
856431Sigor@sysoev.ru         *p++ = '\r'; *p++ = '\n';
857431Sigor@sysoev.ru     }
858431Sigor@sysoev.ru 
859431Sigor@sysoev.ru     header->mem.free = p;
860431Sigor@sysoev.ru 
861630Svbart@nginx.com     h1p->header_size = nxt_buf_mem_used_size(&header->mem);
862630Svbart@nginx.com 
863431Sigor@sysoev.ru     c = h1p->conn;
864431Sigor@sysoev.ru 
865431Sigor@sysoev.ru     c->write = header;
866740Sigor@sysoev.ru     c->write_state = &nxt_h1p_request_send_state;
867431Sigor@sysoev.ru 
868431Sigor@sysoev.ru     engine = task->thread->engine;
869431Sigor@sysoev.ru 
870431Sigor@sysoev.ru     nxt_work_queue_add(&engine->fast_work_queue, r->state->ready_handler,
871431Sigor@sysoev.ru                        task, r, NULL);
872431Sigor@sysoev.ru 
873431Sigor@sysoev.ru     nxt_conn_write(engine, c);
874431Sigor@sysoev.ru }
875431Sigor@sysoev.ru 
876431Sigor@sysoev.ru 
877740Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_request_send_state
878431Sigor@sysoev.ru     nxt_aligned(64) =
879431Sigor@sysoev.ru {
880740Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_sent,
881624Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_request_error,
882431Sigor@sysoev.ru 
883626Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_request_send_timeout,
884683Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_request_timeout_value,
885431Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, send_timeout),
886431Sigor@sysoev.ru     .timer_autoreset = 1,
887431Sigor@sysoev.ru };
888431Sigor@sysoev.ru 
889431Sigor@sysoev.ru 
890431Sigor@sysoev.ru static void
891431Sigor@sysoev.ru nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
892431Sigor@sysoev.ru {
893725Sigor@sysoev.ru     nxt_conn_t     *c;
894725Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
895431Sigor@sysoev.ru 
896431Sigor@sysoev.ru     nxt_debug(task, "h1p request send");
897431Sigor@sysoev.ru 
898725Sigor@sysoev.ru     h1p = r->proto.h1;
899725Sigor@sysoev.ru     c = h1p->conn;
900431Sigor@sysoev.ru 
901725Sigor@sysoev.ru     if (h1p->chunked) {
902431Sigor@sysoev.ru         out = nxt_h1p_chunk_create(task, r, out);
903431Sigor@sysoev.ru         if (nxt_slow_path(out == NULL)) {
904725Sigor@sysoev.ru             nxt_h1p_request_error(task, h1p, r);
905431Sigor@sysoev.ru             return;
906431Sigor@sysoev.ru         }
907431Sigor@sysoev.ru     }
908431Sigor@sysoev.ru 
909431Sigor@sysoev.ru     if (c->write == NULL) {
910431Sigor@sysoev.ru         c->write = out;
911740Sigor@sysoev.ru         c->write_state = &nxt_h1p_request_send_state;
912431Sigor@sysoev.ru 
913431Sigor@sysoev.ru         nxt_conn_write(task->thread->engine, c);
914431Sigor@sysoev.ru 
915431Sigor@sysoev.ru     } else {
916431Sigor@sysoev.ru         nxt_buf_chain_add(&c->write, out);
917431Sigor@sysoev.ru     }
918431Sigor@sysoev.ru }
919431Sigor@sysoev.ru 
920431Sigor@sysoev.ru 
921431Sigor@sysoev.ru static nxt_buf_t *
922431Sigor@sysoev.ru nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
923431Sigor@sysoev.ru {
924483Sigor@sysoev.ru     nxt_off_t          size;
925431Sigor@sysoev.ru     nxt_buf_t          *b, **prev, *header, *tail;
926431Sigor@sysoev.ru 
927703Svbart@nginx.com     const size_t       chunk_size = 2 * nxt_length("\r\n") + NXT_OFF_T_HEXLEN;
928431Sigor@sysoev.ru     static const char  tail_chunk[] = "\r\n0\r\n\r\n";
929431Sigor@sysoev.ru 
930431Sigor@sysoev.ru     size = 0;
931431Sigor@sysoev.ru     prev = &out;
932431Sigor@sysoev.ru 
933431Sigor@sysoev.ru     for (b = out; b != NULL; b = b->next) {
934431Sigor@sysoev.ru 
935431Sigor@sysoev.ru         if (nxt_buf_is_last(b)) {
936608Sigor@sysoev.ru             tail = nxt_http_buf_mem(task, r, chunk_size);
937431Sigor@sysoev.ru             if (nxt_slow_path(tail == NULL)) {
938431Sigor@sysoev.ru                 return NULL;
939431Sigor@sysoev.ru             }
940431Sigor@sysoev.ru 
941431Sigor@sysoev.ru             *prev = tail;
942431Sigor@sysoev.ru             tail->next = b;
943431Sigor@sysoev.ru             /*
944431Sigor@sysoev.ru              * The tail_chunk size with trailing zero is 8 bytes, so
945431Sigor@sysoev.ru              * memcpy may be inlined with just single 8 byte move operation.
946431Sigor@sysoev.ru              */
947431Sigor@sysoev.ru             nxt_memcpy(tail->mem.free, tail_chunk, sizeof(tail_chunk));
948703Svbart@nginx.com             tail->mem.free += nxt_length(tail_chunk);
949431Sigor@sysoev.ru 
950431Sigor@sysoev.ru             break;
951431Sigor@sysoev.ru         }
952431Sigor@sysoev.ru 
953431Sigor@sysoev.ru         size += nxt_buf_used_size(b);
954431Sigor@sysoev.ru         prev = &b->next;
955431Sigor@sysoev.ru     }
956431Sigor@sysoev.ru 
957431Sigor@sysoev.ru     if (size == 0) {
958431Sigor@sysoev.ru         return out;
959431Sigor@sysoev.ru     }
960431Sigor@sysoev.ru 
961608Sigor@sysoev.ru     header = nxt_http_buf_mem(task, r, chunk_size);
962431Sigor@sysoev.ru     if (nxt_slow_path(header == NULL)) {
963431Sigor@sysoev.ru         return NULL;
964431Sigor@sysoev.ru     }
965431Sigor@sysoev.ru 
966431Sigor@sysoev.ru     header->next = out;
967431Sigor@sysoev.ru     header->mem.free = nxt_sprintf(header->mem.free, header->mem.end,
968431Sigor@sysoev.ru                                    "\r\n%xO\r\n", size);
969431Sigor@sysoev.ru     return header;
970431Sigor@sysoev.ru }
971431Sigor@sysoev.ru 
972431Sigor@sysoev.ru 
973431Sigor@sysoev.ru static void
974740Sigor@sysoev.ru nxt_h1p_conn_sent(nxt_task_t *task, void *obj, void *data)
975431Sigor@sysoev.ru {
976431Sigor@sysoev.ru     nxt_conn_t          *c;
977431Sigor@sysoev.ru     nxt_event_engine_t  *engine;
978431Sigor@sysoev.ru 
979431Sigor@sysoev.ru     c = obj;
980431Sigor@sysoev.ru 
981740Sigor@sysoev.ru     nxt_debug(task, "h1p conn sent");
982431Sigor@sysoev.ru 
983431Sigor@sysoev.ru     engine = task->thread->engine;
984431Sigor@sysoev.ru 
985592Sigor@sysoev.ru     c->write = nxt_sendbuf_completion(task, &engine->fast_work_queue, c->write);
986592Sigor@sysoev.ru 
987431Sigor@sysoev.ru     if (c->write != NULL) {
988431Sigor@sysoev.ru         nxt_conn_write(engine, c);
989431Sigor@sysoev.ru     }
990431Sigor@sysoev.ru }
991431Sigor@sysoev.ru 
992431Sigor@sysoev.ru 
993630Svbart@nginx.com static nxt_off_t
994630Svbart@nginx.com nxt_h1p_request_body_bytes_sent(nxt_task_t *task, nxt_http_proto_t proto)
995630Svbart@nginx.com {
996630Svbart@nginx.com     nxt_off_t      sent;
997630Svbart@nginx.com     nxt_h1proto_t  *h1p;
998630Svbart@nginx.com 
999630Svbart@nginx.com     h1p = proto.h1;
1000630Svbart@nginx.com 
1001630Svbart@nginx.com     sent = h1p->conn->sent - h1p->header_size;
1002630Svbart@nginx.com 
1003630Svbart@nginx.com     return (sent > 0) ? sent : 0;
1004630Svbart@nginx.com }
1005630Svbart@nginx.com 
1006630Svbart@nginx.com 
1007431Sigor@sysoev.ru static void
1008608Sigor@sysoev.ru nxt_h1p_request_discard(nxt_task_t *task, nxt_http_request_t *r,
1009608Sigor@sysoev.ru     nxt_buf_t *last)
1010608Sigor@sysoev.ru {
1011608Sigor@sysoev.ru     nxt_buf_t         *b;
1012608Sigor@sysoev.ru     nxt_conn_t        *c;
1013608Sigor@sysoev.ru     nxt_h1proto_t     *h1p;
1014608Sigor@sysoev.ru     nxt_work_queue_t  *wq;
1015608Sigor@sysoev.ru 
1016608Sigor@sysoev.ru     nxt_debug(task, "h1p request discard");
1017608Sigor@sysoev.ru 
1018608Sigor@sysoev.ru     h1p = r->proto.h1;
1019608Sigor@sysoev.ru     h1p->keepalive = 0;
1020608Sigor@sysoev.ru 
1021608Sigor@sysoev.ru     c = h1p->conn;
1022608Sigor@sysoev.ru     b = c->write;
1023608Sigor@sysoev.ru     c->write = NULL;
1024608Sigor@sysoev.ru 
1025608Sigor@sysoev.ru     wq = &task->thread->engine->fast_work_queue;
1026608Sigor@sysoev.ru 
1027608Sigor@sysoev.ru     nxt_sendbuf_drain(task, wq, b);
1028608Sigor@sysoev.ru     nxt_sendbuf_drain(task, wq, last);
1029608Sigor@sysoev.ru }
1030608Sigor@sysoev.ru 
1031608Sigor@sysoev.ru 
1032608Sigor@sysoev.ru static void
1033683Sigor@sysoev.ru nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto,
1034683Sigor@sysoev.ru     nxt_socket_conf_joint_t *joint)
1035431Sigor@sysoev.ru {
1036431Sigor@sysoev.ru     nxt_conn_t     *c;
1037431Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
1038431Sigor@sysoev.ru 
1039431Sigor@sysoev.ru     nxt_debug(task, "h1p request close");
1040431Sigor@sysoev.ru 
1041431Sigor@sysoev.ru     h1p = proto.h1;
1042431Sigor@sysoev.ru     h1p->request = NULL;
1043431Sigor@sysoev.ru 
1044683Sigor@sysoev.ru     nxt_router_conf_release(task, joint);
1045683Sigor@sysoev.ru 
1046431Sigor@sysoev.ru     c = h1p->conn;
1047431Sigor@sysoev.ru 
1048431Sigor@sysoev.ru     if (h1p->keepalive) {
1049431Sigor@sysoev.ru         nxt_h1p_keepalive(task, h1p, c);
1050431Sigor@sysoev.ru 
1051431Sigor@sysoev.ru     } else {
1052431Sigor@sysoev.ru         nxt_h1p_close(task, c);
1053431Sigor@sysoev.ru     }
1054431Sigor@sysoev.ru }
1055431Sigor@sysoev.ru 
1056431Sigor@sysoev.ru 
1057431Sigor@sysoev.ru static void
1058431Sigor@sysoev.ru nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c)
1059431Sigor@sysoev.ru {
1060431Sigor@sysoev.ru     size_t     size;
1061431Sigor@sysoev.ru     nxt_buf_t  *in, *b, *next;
1062431Sigor@sysoev.ru 
1063431Sigor@sysoev.ru     nxt_debug(task, "h1p keepalive");
1064431Sigor@sysoev.ru 
1065436Sigor@sysoev.ru     if (!c->tcp_nodelay) {
1066436Sigor@sysoev.ru         nxt_conn_tcp_nodelay_on(task, c);
1067436Sigor@sysoev.ru     }
1068436Sigor@sysoev.ru 
1069431Sigor@sysoev.ru     b = h1p->buffers;
1070431Sigor@sysoev.ru 
1071452Sigor@sysoev.ru     nxt_memzero(h1p, offsetof(nxt_h1proto_t, conn));
1072431Sigor@sysoev.ru 
1073630Svbart@nginx.com     c->sent = 0;
1074630Svbart@nginx.com 
1075431Sigor@sysoev.ru     in = c->read;
1076431Sigor@sysoev.ru 
1077459Sigor@sysoev.ru     if (in == NULL) {
1078459Sigor@sysoev.ru         /* A request with large body. */
1079459Sigor@sysoev.ru         in = b;
1080459Sigor@sysoev.ru         c->read = in;
1081459Sigor@sysoev.ru 
1082459Sigor@sysoev.ru         b = in->next;
1083459Sigor@sysoev.ru         in->next = NULL;
1084459Sigor@sysoev.ru     }
1085459Sigor@sysoev.ru 
1086459Sigor@sysoev.ru     while (b != NULL) {
1087459Sigor@sysoev.ru         next = b->next;
1088459Sigor@sysoev.ru         nxt_mp_free(c->mem_pool, b);
1089459Sigor@sysoev.ru         b = next;
1090459Sigor@sysoev.ru     }
1091459Sigor@sysoev.ru 
1092431Sigor@sysoev.ru     size = nxt_buf_mem_used_size(&in->mem);
1093431Sigor@sysoev.ru 
1094431Sigor@sysoev.ru     if (size == 0) {
1095629Sigor@sysoev.ru         nxt_mp_free(c->mem_pool, in);
1096431Sigor@sysoev.ru 
1097629Sigor@sysoev.ru         c->read = NULL;
1098628Sigor@sysoev.ru         c->read_state = &nxt_h1p_keepalive_state;
1099431Sigor@sysoev.ru 
1100628Sigor@sysoev.ru         nxt_conn_read(task->thread->engine, c);
1101431Sigor@sysoev.ru 
1102431Sigor@sysoev.ru     } else {
1103431Sigor@sysoev.ru         nxt_debug(task, "h1p pipelining");
1104431Sigor@sysoev.ru 
1105431Sigor@sysoev.ru         nxt_memmove(in->mem.start, in->mem.pos, size);
1106431Sigor@sysoev.ru 
1107431Sigor@sysoev.ru         in->mem.pos = in->mem.start;
1108431Sigor@sysoev.ru         in->mem.free = in->mem.start + size;
1109431Sigor@sysoev.ru 
1110627Svbart@nginx.com         nxt_h1p_conn_request_init(task, c, c->socket.data);
1111431Sigor@sysoev.ru     }
1112431Sigor@sysoev.ru }
1113431Sigor@sysoev.ru 
1114431Sigor@sysoev.ru 
1115624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_keepalive_state
1116624Sigor@sysoev.ru     nxt_aligned(64) =
1117431Sigor@sysoev.ru {
1118628Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_request_init,
1119624Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_error,
1120624Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
1121624Sigor@sysoev.ru 
1122629Sigor@sysoev.ru     .io_read_handler = nxt_h1p_conn_io_read_handler,
1123629Sigor@sysoev.ru 
1124*741Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_timeout,
1125624Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_timeout_value,
1126624Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, idle_timeout),
1127636Sigor@sysoev.ru     .timer_autoreset = 1,
1128624Sigor@sysoev.ru };
1129624Sigor@sysoev.ru 
1130624Sigor@sysoev.ru 
1131624Sigor@sysoev.ru static void
1132431Sigor@sysoev.ru nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data)
1133431Sigor@sysoev.ru {
1134624Sigor@sysoev.ru     nxt_conn_t  *c;
1135431Sigor@sysoev.ru 
1136431Sigor@sysoev.ru     c = obj;
1137431Sigor@sysoev.ru 
1138431Sigor@sysoev.ru     nxt_debug(task, "h1p conn error");
1139431Sigor@sysoev.ru 
1140624Sigor@sysoev.ru     nxt_h1p_close(task, c);
1141431Sigor@sysoev.ru }
1142431Sigor@sysoev.ru 
1143431Sigor@sysoev.ru 
1144*741Sigor@sysoev.ru static void
1145*741Sigor@sysoev.ru nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data)
1146*741Sigor@sysoev.ru {
1147*741Sigor@sysoev.ru     nxt_conn_t  *c;
1148*741Sigor@sysoev.ru 
1149*741Sigor@sysoev.ru     c = obj;
1150*741Sigor@sysoev.ru 
1151*741Sigor@sysoev.ru     nxt_debug(task, "h1p conn close");
1152*741Sigor@sysoev.ru 
1153*741Sigor@sysoev.ru     nxt_h1p_conn_idle_timeout(task, c);
1154*741Sigor@sysoev.ru }
1155*741Sigor@sysoev.ru 
1156*741Sigor@sysoev.ru 
1157*741Sigor@sysoev.ru static void
1158*741Sigor@sysoev.ru nxt_h1p_conn_timeout(nxt_task_t *task, void *obj, void *data)
1159*741Sigor@sysoev.ru {
1160*741Sigor@sysoev.ru     nxt_conn_t   *c;
1161*741Sigor@sysoev.ru     nxt_timer_t  *timer;
1162*741Sigor@sysoev.ru 
1163*741Sigor@sysoev.ru     timer = obj;
1164*741Sigor@sysoev.ru 
1165*741Sigor@sysoev.ru     nxt_debug(task, "h1p conn idle timeout");
1166*741Sigor@sysoev.ru 
1167*741Sigor@sysoev.ru     c = nxt_read_timer_conn(timer);
1168*741Sigor@sysoev.ru 
1169*741Sigor@sysoev.ru     nxt_h1p_conn_idle_timeout(task, c);
1170*741Sigor@sysoev.ru }
1171*741Sigor@sysoev.ru 
1172*741Sigor@sysoev.ru 
1173740Sigor@sysoev.ru #define NXT_H1P_IDLE_TIMEOUT                                                  \
1174740Sigor@sysoev.ru      "HTTP/1.1 408 Request Timeout\r\n"                                       \
1175740Sigor@sysoev.ru      "Server: " NXT_SERVER "\r\n"                                             \
1176740Sigor@sysoev.ru      "Connection: close\r\n"                                                  \
1177740Sigor@sysoev.ru      "Content-Length: 0\r\n"                                                  \
1178740Sigor@sysoev.ru      "Date: "
1179740Sigor@sysoev.ru 
1180740Sigor@sysoev.ru 
1181431Sigor@sysoev.ru static void
1182*741Sigor@sysoev.ru nxt_h1p_conn_idle_timeout(nxt_task_t *task, nxt_conn_t *c)
1183740Sigor@sysoev.ru {
1184*741Sigor@sysoev.ru     u_char     *p;
1185*741Sigor@sysoev.ru     size_t     size;
1186*741Sigor@sysoev.ru     nxt_buf_t  *out, *last;
1187740Sigor@sysoev.ru 
1188740Sigor@sysoev.ru     size = nxt_length(NXT_H1P_IDLE_TIMEOUT)
1189740Sigor@sysoev.ru            + nxt_http_date_cache.size
1190740Sigor@sysoev.ru            + nxt_length("\r\n\r\n");
1191740Sigor@sysoev.ru 
1192740Sigor@sysoev.ru     out = nxt_buf_mem_alloc(c->mem_pool, size, 0);
1193740Sigor@sysoev.ru     if (nxt_slow_path(out == NULL)) {
1194740Sigor@sysoev.ru         goto fail;
1195740Sigor@sysoev.ru     }
1196740Sigor@sysoev.ru 
1197740Sigor@sysoev.ru     p = nxt_cpymem(out->mem.free, NXT_H1P_IDLE_TIMEOUT,
1198740Sigor@sysoev.ru                    nxt_length(NXT_H1P_IDLE_TIMEOUT));
1199740Sigor@sysoev.ru 
1200740Sigor@sysoev.ru     p = nxt_thread_time_string(task->thread, &nxt_http_date_cache, p);
1201740Sigor@sysoev.ru 
1202740Sigor@sysoev.ru     out->mem.free = nxt_cpymem(p, "\r\n\r\n", 4);
1203740Sigor@sysoev.ru 
1204740Sigor@sysoev.ru     last = nxt_mp_zget(c->mem_pool, NXT_BUF_SYNC_SIZE);
1205740Sigor@sysoev.ru     if (nxt_slow_path(last == NULL)) {
1206740Sigor@sysoev.ru         goto fail;
1207740Sigor@sysoev.ru     }
1208740Sigor@sysoev.ru 
1209740Sigor@sysoev.ru     out->next = last;
1210740Sigor@sysoev.ru     nxt_buf_set_sync(last);
1211740Sigor@sysoev.ru     nxt_buf_set_last(last);
1212740Sigor@sysoev.ru 
1213740Sigor@sysoev.ru     last->completion_handler = nxt_h1p_conn_idle_close;
1214740Sigor@sysoev.ru     last->parent = c;
1215740Sigor@sysoev.ru 
1216740Sigor@sysoev.ru     c->write = out;
1217740Sigor@sysoev.ru     c->write_state = &nxt_h1p_timeout_send_state;
1218740Sigor@sysoev.ru 
1219740Sigor@sysoev.ru     nxt_conn_write(task->thread->engine, c);
1220740Sigor@sysoev.ru     return;
1221740Sigor@sysoev.ru 
1222740Sigor@sysoev.ru fail:
1223740Sigor@sysoev.ru 
1224740Sigor@sysoev.ru     nxt_h1p_close(task, c);
1225740Sigor@sysoev.ru }
1226740Sigor@sysoev.ru 
1227740Sigor@sysoev.ru 
1228740Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_timeout_send_state
1229740Sigor@sysoev.ru     nxt_aligned(64) =
1230740Sigor@sysoev.ru {
1231740Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_sent,
1232740Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
1233740Sigor@sysoev.ru 
1234740Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_send_timeout,
1235740Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_send_timeout_value,
1236740Sigor@sysoev.ru };
1237740Sigor@sysoev.ru 
1238740Sigor@sysoev.ru 
1239740Sigor@sysoev.ru static void
1240740Sigor@sysoev.ru nxt_h1p_conn_idle_close(nxt_task_t *task, void *obj, void *data)
1241740Sigor@sysoev.ru {
1242740Sigor@sysoev.ru     nxt_conn_t  *c;
1243740Sigor@sysoev.ru 
1244740Sigor@sysoev.ru     c = data;
1245740Sigor@sysoev.ru 
1246740Sigor@sysoev.ru     nxt_debug(task, "h1p conn idle close");
1247740Sigor@sysoev.ru 
1248740Sigor@sysoev.ru     nxt_h1p_close(task, c);
1249740Sigor@sysoev.ru }
1250740Sigor@sysoev.ru 
1251740Sigor@sysoev.ru 
1252740Sigor@sysoev.ru static void
1253740Sigor@sysoev.ru nxt_h1p_conn_send_timeout(nxt_task_t *task, void *obj, void *data)
1254431Sigor@sysoev.ru {
1255431Sigor@sysoev.ru     nxt_conn_t   *c;
1256431Sigor@sysoev.ru     nxt_timer_t  *timer;
1257431Sigor@sysoev.ru 
1258431Sigor@sysoev.ru     timer = obj;
1259431Sigor@sysoev.ru 
1260740Sigor@sysoev.ru     nxt_debug(task, "h1p conn send timeout");
1261431Sigor@sysoev.ru 
1262431Sigor@sysoev.ru     c = nxt_read_timer_conn(timer);
1263431Sigor@sysoev.ru 
1264624Sigor@sysoev.ru     nxt_h1p_close(task, c);
1265624Sigor@sysoev.ru }
1266624Sigor@sysoev.ru 
1267624Sigor@sysoev.ru 
1268683Sigor@sysoev.ru static nxt_msec_t
1269740Sigor@sysoev.ru nxt_h1p_conn_send_timeout_value(nxt_conn_t *c, uintptr_t data)
1270740Sigor@sysoev.ru {
1271740Sigor@sysoev.ru     return 10 * 1000;
1272740Sigor@sysoev.ru }
1273740Sigor@sysoev.ru 
1274740Sigor@sysoev.ru 
1275740Sigor@sysoev.ru static nxt_msec_t
1276683Sigor@sysoev.ru nxt_h1p_conn_timeout_value(nxt_conn_t *c, uintptr_t data)
1277683Sigor@sysoev.ru {
1278683Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
1279683Sigor@sysoev.ru 
1280683Sigor@sysoev.ru     joint = c->listen->socket.data;
1281683Sigor@sysoev.ru 
1282683Sigor@sysoev.ru     return nxt_value_at(nxt_msec_t, joint->socket_conf, data);
1283683Sigor@sysoev.ru }
1284683Sigor@sysoev.ru 
1285683Sigor@sysoev.ru 
1286624Sigor@sysoev.ru static void
1287624Sigor@sysoev.ru nxt_h1p_close(nxt_task_t *task, nxt_conn_t *c)
1288624Sigor@sysoev.ru {
1289624Sigor@sysoev.ru     nxt_debug(task, "h1p close");
1290624Sigor@sysoev.ru 
1291624Sigor@sysoev.ru     c->socket.data = NULL;
1292624Sigor@sysoev.ru 
1293683Sigor@sysoev.ru     c->write_state = &nxt_h1p_close_state;
1294624Sigor@sysoev.ru 
1295624Sigor@sysoev.ru     nxt_conn_close(task->thread->engine, c);
1296624Sigor@sysoev.ru }
1297624Sigor@sysoev.ru 
1298624Sigor@sysoev.ru 
1299683Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_close_state
1300683Sigor@sysoev.ru     nxt_aligned(64) =
1301683Sigor@sysoev.ru {
1302683Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_free,
1303683Sigor@sysoev.ru };
1304683Sigor@sysoev.ru 
1305683Sigor@sysoev.ru 
1306683Sigor@sysoev.ru static void
1307683Sigor@sysoev.ru nxt_h1p_conn_free(nxt_task_t *task, void *obj, void *data)
1308683Sigor@sysoev.ru {
1309683Sigor@sysoev.ru     nxt_conn_t          *c;
1310683Sigor@sysoev.ru     nxt_listen_event_t  *lev;
1311683Sigor@sysoev.ru     nxt_event_engine_t  *engine;
1312683Sigor@sysoev.ru 
1313683Sigor@sysoev.ru     c = obj;
1314683Sigor@sysoev.ru 
1315683Sigor@sysoev.ru     nxt_debug(task, "h1p conn free");
1316683Sigor@sysoev.ru 
1317683Sigor@sysoev.ru     nxt_queue_remove(&c->link);
1318683Sigor@sysoev.ru 
1319683Sigor@sysoev.ru     engine = task->thread->engine;
1320683Sigor@sysoev.ru 
1321683Sigor@sysoev.ru     nxt_sockaddr_cache_free(engine, c);
1322683Sigor@sysoev.ru 
1323683Sigor@sysoev.ru     lev = c->listen;
1324683Sigor@sysoev.ru 
1325683Sigor@sysoev.ru     nxt_conn_free(task, c);
1326683Sigor@sysoev.ru 
1327683Sigor@sysoev.ru     nxt_router_listen_event_release(&engine->task, lev, NULL);
1328683Sigor@sysoev.ru }
1329683Sigor@sysoev.ru 
1330683Sigor@sysoev.ru 
1331624Sigor@sysoev.ru static void
1332624Sigor@sysoev.ru nxt_h1p_conn_request_error(nxt_task_t *task, void *obj, void *data)
1333624Sigor@sysoev.ru {
1334624Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
1335624Sigor@sysoev.ru     nxt_http_request_t  *r;
1336624Sigor@sysoev.ru 
1337624Sigor@sysoev.ru     h1p = data;
1338624Sigor@sysoev.ru 
1339624Sigor@sysoev.ru     nxt_debug(task, "h1p conn request error");
1340624Sigor@sysoev.ru 
1341624Sigor@sysoev.ru     r = h1p->request;
1342624Sigor@sysoev.ru 
1343624Sigor@sysoev.ru     if (r->fields == NULL) {
1344624Sigor@sysoev.ru         (void) nxt_h1p_header_process(h1p, r);
1345624Sigor@sysoev.ru     }
1346624Sigor@sysoev.ru 
1347624Sigor@sysoev.ru     if (r->status == 0) {
1348624Sigor@sysoev.ru         r->status = NXT_HTTP_BAD_REQUEST;
1349624Sigor@sysoev.ru     }
1350624Sigor@sysoev.ru 
1351725Sigor@sysoev.ru     nxt_h1p_request_error(task, h1p, r);
1352624Sigor@sysoev.ru }
1353624Sigor@sysoev.ru 
1354624Sigor@sysoev.ru 
1355624Sigor@sysoev.ru static void
1356624Sigor@sysoev.ru nxt_h1p_conn_request_timeout(nxt_task_t *task, void *obj, void *data)
1357624Sigor@sysoev.ru {
1358624Sigor@sysoev.ru     nxt_conn_t          *c;
1359624Sigor@sysoev.ru     nxt_timer_t         *timer;
1360624Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
1361624Sigor@sysoev.ru     nxt_http_request_t  *r;
1362624Sigor@sysoev.ru 
1363624Sigor@sysoev.ru     timer = obj;
1364624Sigor@sysoev.ru 
1365624Sigor@sysoev.ru     nxt_debug(task, "h1p conn request timeout");
1366624Sigor@sysoev.ru 
1367624Sigor@sysoev.ru     c = nxt_read_timer_conn(timer);
1368626Sigor@sysoev.ru     /*
1369626Sigor@sysoev.ru      * Disable SO_LINGER off during socket closing
1370626Sigor@sysoev.ru      * to send "408 Request Timeout" error response.
1371626Sigor@sysoev.ru      */
1372626Sigor@sysoev.ru     c->socket.timedout = 0;
1373626Sigor@sysoev.ru 
1374624Sigor@sysoev.ru     h1p = c->socket.data;
1375725Sigor@sysoev.ru     h1p->keepalive = 0;
1376624Sigor@sysoev.ru     r = h1p->request;
1377624Sigor@sysoev.ru 
1378624Sigor@sysoev.ru     if (r->fields == NULL) {
1379624Sigor@sysoev.ru         (void) nxt_h1p_header_process(h1p, r);
1380624Sigor@sysoev.ru     }
1381624Sigor@sysoev.ru 
1382626Sigor@sysoev.ru     nxt_http_request_error(task, r, NXT_HTTP_REQUEST_TIMEOUT);
1383626Sigor@sysoev.ru }
1384626Sigor@sysoev.ru 
1385624Sigor@sysoev.ru 
1386626Sigor@sysoev.ru static void
1387626Sigor@sysoev.ru nxt_h1p_conn_request_send_timeout(nxt_task_t *task, void *obj, void *data)
1388626Sigor@sysoev.ru {
1389626Sigor@sysoev.ru     nxt_conn_t     *c;
1390626Sigor@sysoev.ru     nxt_timer_t    *timer;
1391626Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
1392626Sigor@sysoev.ru 
1393626Sigor@sysoev.ru     timer = obj;
1394626Sigor@sysoev.ru 
1395626Sigor@sysoev.ru     nxt_debug(task, "h1p conn request send timeout");
1396626Sigor@sysoev.ru 
1397724Sigor@sysoev.ru     c = nxt_write_timer_conn(timer);
1398626Sigor@sysoev.ru     h1p = c->socket.data;
1399626Sigor@sysoev.ru 
1400725Sigor@sysoev.ru     nxt_h1p_request_error(task, h1p, h1p->request);
1401624Sigor@sysoev.ru }
1402624Sigor@sysoev.ru 
1403624Sigor@sysoev.ru 
1404683Sigor@sysoev.ru static nxt_msec_t
1405683Sigor@sysoev.ru nxt_h1p_conn_request_timeout_value(nxt_conn_t *c, uintptr_t data)
1406683Sigor@sysoev.ru {
1407683Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
1408683Sigor@sysoev.ru 
1409683Sigor@sysoev.ru     h1p = c->socket.data;
1410683Sigor@sysoev.ru 
1411683Sigor@sysoev.ru     return nxt_value_at(nxt_msec_t, h1p->request->conf->socket_conf, data);
1412683Sigor@sysoev.ru }
1413683Sigor@sysoev.ru 
1414683Sigor@sysoev.ru 
1415624Sigor@sysoev.ru nxt_inline void
1416725Sigor@sysoev.ru nxt_h1p_request_error(nxt_task_t *task, nxt_h1proto_t *h1p,
1417725Sigor@sysoev.ru     nxt_http_request_t *r)
1418624Sigor@sysoev.ru {
1419725Sigor@sysoev.ru     h1p->keepalive = 0;
1420725Sigor@sysoev.ru 
1421725Sigor@sysoev.ru     r->state->error_handler(task, r, h1p);
1422431Sigor@sysoev.ru }
1423