xref: /unit/src/nxt_h1proto.c (revision 1131)
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>
9*1131Smax.romanov@nginx.com #include <nxt_h1proto.h>
10*1131Smax.romanov@nginx.com #include <nxt_websocket.h>
11*1131Smax.romanov@nginx.com #include <nxt_websocket_header.h>
121113Sigor@sysoev.ru 
131113Sigor@sysoev.ru 
14624Sigor@sysoev.ru /*
15771Sigor@sysoev.ru  * nxt_http_conn_ and nxt_h1p_conn_ prefixes are used for connection handlers.
16771Sigor@sysoev.ru  * nxt_h1p_idle_ prefix is used for idle connection handlers.
17624Sigor@sysoev.ru  * nxt_h1p_request_ prefix is used for HTTP/1 protocol request methods.
18624Sigor@sysoev.ru  */
19624Sigor@sysoev.ru 
20771Sigor@sysoev.ru #if (NXT_TLS)
21771Sigor@sysoev.ru static ssize_t nxt_http_idle_io_read_handler(nxt_conn_t *c);
22771Sigor@sysoev.ru static void nxt_http_conn_test(nxt_task_t *task, void *obj, void *data);
23771Sigor@sysoev.ru #endif
24771Sigor@sysoev.ru static ssize_t nxt_h1p_idle_io_read_handler(nxt_conn_t *c);
25624Sigor@sysoev.ru static void nxt_h1p_conn_proto_init(nxt_task_t *task, void *obj, void *data);
26624Sigor@sysoev.ru static void nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data);
27683Sigor@sysoev.ru static void nxt_h1p_conn_request_header_parse(nxt_task_t *task, void *obj,
28683Sigor@sysoev.ru     void *data);
29*1131Smax.romanov@nginx.com static nxt_int_t nxt_h1p_header_process(nxt_task_t *task, nxt_h1proto_t *h1p,
30620Svbart@nginx.com     nxt_http_request_t *r);
31625Sigor@sysoev.ru static nxt_int_t nxt_h1p_header_buffer_test(nxt_task_t *task,
32625Sigor@sysoev.ru     nxt_h1proto_t *h1p, nxt_conn_t *c, nxt_socket_conf_t *skcf);
33431Sigor@sysoev.ru static nxt_int_t nxt_h1p_connection(void *ctx, nxt_http_field_t *field,
34431Sigor@sysoev.ru     uintptr_t data);
35*1131Smax.romanov@nginx.com static nxt_int_t nxt_h1p_upgrade(void *ctx, nxt_http_field_t *field,
36*1131Smax.romanov@nginx.com     uintptr_t data);
37*1131Smax.romanov@nginx.com static nxt_int_t nxt_h1p_websocket_key(void *ctx, nxt_http_field_t *field,
38*1131Smax.romanov@nginx.com     uintptr_t data);
39*1131Smax.romanov@nginx.com static nxt_int_t nxt_h1p_websocket_version(void *ctx, nxt_http_field_t *field,
40*1131Smax.romanov@nginx.com     uintptr_t data);
41431Sigor@sysoev.ru static nxt_int_t nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field,
42431Sigor@sysoev.ru     uintptr_t data);
43431Sigor@sysoev.ru static void nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r);
44637Sigor@sysoev.ru static void nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj,
45637Sigor@sysoev.ru     void *data);
46431Sigor@sysoev.ru static void nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r);
47431Sigor@sysoev.ru static void nxt_h1p_request_header_send(nxt_task_t *task,
48431Sigor@sysoev.ru     nxt_http_request_t *r);
49431Sigor@sysoev.ru static void nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r,
50431Sigor@sysoev.ru     nxt_buf_t *out);
51431Sigor@sysoev.ru static nxt_buf_t *nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r,
52431Sigor@sysoev.ru     nxt_buf_t *out);
53630Svbart@nginx.com static nxt_off_t nxt_h1p_request_body_bytes_sent(nxt_task_t *task,
54630Svbart@nginx.com     nxt_http_proto_t proto);
55608Sigor@sysoev.ru static void nxt_h1p_request_discard(nxt_task_t *task, nxt_http_request_t *r,
56608Sigor@sysoev.ru     nxt_buf_t *last);
57624Sigor@sysoev.ru static void nxt_h1p_conn_request_error(nxt_task_t *task, void *obj, void *data);
58624Sigor@sysoev.ru static void nxt_h1p_conn_request_timeout(nxt_task_t *task, void *obj,
59624Sigor@sysoev.ru     void *data);
60626Sigor@sysoev.ru static void nxt_h1p_conn_request_send_timeout(nxt_task_t *task, void *obj,
61626Sigor@sysoev.ru     void *data);
62725Sigor@sysoev.ru nxt_inline void nxt_h1p_request_error(nxt_task_t *task, nxt_h1proto_t *h1p,
63725Sigor@sysoev.ru     nxt_http_request_t *r);
64771Sigor@sysoev.ru static void nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto,
65771Sigor@sysoev.ru     nxt_socket_conf_joint_t *joint);
66771Sigor@sysoev.ru static void nxt_h1p_conn_sent(nxt_task_t *task, void *obj, void *data);
67771Sigor@sysoev.ru static void nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data);
68771Sigor@sysoev.ru static void nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data);
69771Sigor@sysoev.ru static nxt_msec_t nxt_h1p_conn_timer_value(nxt_conn_t *c, uintptr_t data);
70771Sigor@sysoev.ru static void nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p,
71771Sigor@sysoev.ru     nxt_conn_t *c);
72771Sigor@sysoev.ru static void nxt_h1p_idle_close(nxt_task_t *task, void *obj, void *data);
73771Sigor@sysoev.ru static void nxt_h1p_idle_timeout(nxt_task_t *task, void *obj, void *data);
74771Sigor@sysoev.ru static void nxt_h1p_idle_response(nxt_task_t *task, nxt_conn_t *c);
75771Sigor@sysoev.ru static void nxt_h1p_idle_response_sent(nxt_task_t *task, void *obj, void *data);
76771Sigor@sysoev.ru static void nxt_h1p_idle_response_timeout(nxt_task_t *task, void *obj,
77771Sigor@sysoev.ru     void *data);
78771Sigor@sysoev.ru static nxt_msec_t nxt_h1p_idle_response_timer_value(nxt_conn_t *c,
79771Sigor@sysoev.ru     uintptr_t data);
80771Sigor@sysoev.ru static void nxt_h1p_shutdown(nxt_task_t *task, nxt_conn_t *c);
81*1131Smax.romanov@nginx.com static void nxt_h1p_shutdown_(nxt_task_t *task, nxt_conn_t *c);
82*1131Smax.romanov@nginx.com static void nxt_h1p_conn_ws_shutdown(nxt_task_t *task, void *obj, void *data);
83771Sigor@sysoev.ru static void nxt_h1p_conn_closing(nxt_task_t *task, void *obj, void *data);
84771Sigor@sysoev.ru static void nxt_h1p_conn_free(nxt_task_t *task, void *obj, void *data);
85431Sigor@sysoev.ru 
86771Sigor@sysoev.ru #if (NXT_TLS)
87771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_http_idle_state;
88771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_shutdown_state;
89771Sigor@sysoev.ru #endif
90431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_state;
91624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_header_parse_state;
92431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_body_state;
93740Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_request_send_state;
94771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_timeout_response_state;
95624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_keepalive_state;
96683Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_close_state;
97431Sigor@sysoev.ru 
98431Sigor@sysoev.ru 
991112Sigor@sysoev.ru const nxt_http_proto_table_t  nxt_http_proto[3] = {
1001112Sigor@sysoev.ru     /* NXT_HTTP_PROTO_H1 */
1011112Sigor@sysoev.ru     {
1021112Sigor@sysoev.ru         .body_read        = nxt_h1p_request_body_read,
1031112Sigor@sysoev.ru         .local_addr       = nxt_h1p_request_local_addr,
1041112Sigor@sysoev.ru         .header_send      = nxt_h1p_request_header_send,
1051112Sigor@sysoev.ru         .send             = nxt_h1p_request_send,
1061112Sigor@sysoev.ru         .body_bytes_sent  = nxt_h1p_request_body_bytes_sent,
1071112Sigor@sysoev.ru         .discard          = nxt_h1p_request_discard,
1081112Sigor@sysoev.ru         .close            = nxt_h1p_request_close,
109*1131Smax.romanov@nginx.com         .ws_frame_start   = nxt_h1p_websocket_frame_start,
1101112Sigor@sysoev.ru     },
1111112Sigor@sysoev.ru     /* NXT_HTTP_PROTO_H2      */
1121112Sigor@sysoev.ru     /* NXT_HTTP_PROTO_DEVNULL */
113431Sigor@sysoev.ru };
114431Sigor@sysoev.ru 
115431Sigor@sysoev.ru 
116431Sigor@sysoev.ru static nxt_lvlhsh_t            nxt_h1p_fields_hash;
117431Sigor@sysoev.ru 
118431Sigor@sysoev.ru static nxt_http_field_proc_t   nxt_h1p_fields[] = {
119431Sigor@sysoev.ru     { nxt_string("Connection"),        &nxt_h1p_connection, 0 },
120*1131Smax.romanov@nginx.com     { nxt_string("Upgrade"),           &nxt_h1p_upgrade, 0 },
121*1131Smax.romanov@nginx.com     { nxt_string("Sec-WebSocket-Key"), &nxt_h1p_websocket_key, 0 },
122*1131Smax.romanov@nginx.com     { nxt_string("Sec-WebSocket-Version"),
123*1131Smax.romanov@nginx.com                                        &nxt_h1p_websocket_version, 0 },
124431Sigor@sysoev.ru     { nxt_string("Transfer-Encoding"), &nxt_h1p_transfer_encoding, 0 },
125431Sigor@sysoev.ru 
126431Sigor@sysoev.ru     { nxt_string("Host"),              &nxt_http_request_host, 0 },
127431Sigor@sysoev.ru     { nxt_string("Cookie"),            &nxt_http_request_field,
128431Sigor@sysoev.ru         offsetof(nxt_http_request_t, cookie) },
129630Svbart@nginx.com     { nxt_string("Referer"),           &nxt_http_request_field,
130630Svbart@nginx.com         offsetof(nxt_http_request_t, referer) },
131630Svbart@nginx.com     { nxt_string("User-Agent"),        &nxt_http_request_field,
132630Svbart@nginx.com         offsetof(nxt_http_request_t, user_agent) },
133431Sigor@sysoev.ru     { nxt_string("Content-Type"),      &nxt_http_request_field,
134431Sigor@sysoev.ru         offsetof(nxt_http_request_t, content_type) },
135431Sigor@sysoev.ru     { nxt_string("Content-Length"),    &nxt_http_request_content_length, 0 },
136431Sigor@sysoev.ru };
137431Sigor@sysoev.ru 
138431Sigor@sysoev.ru 
139431Sigor@sysoev.ru nxt_int_t
140431Sigor@sysoev.ru nxt_h1p_init(nxt_task_t *task, nxt_runtime_t *rt)
141431Sigor@sysoev.ru {
142431Sigor@sysoev.ru     return nxt_http_fields_hash(&nxt_h1p_fields_hash, rt->mem_pool,
143431Sigor@sysoev.ru                                 nxt_h1p_fields, nxt_nitems(nxt_h1p_fields));
144431Sigor@sysoev.ru }
145431Sigor@sysoev.ru 
146431Sigor@sysoev.ru 
147431Sigor@sysoev.ru void
148431Sigor@sysoev.ru nxt_http_conn_init(nxt_task_t *task, void *obj, void *data)
149431Sigor@sysoev.ru {
150431Sigor@sysoev.ru     nxt_conn_t               *c;
151431Sigor@sysoev.ru     nxt_socket_conf_t        *skcf;
152431Sigor@sysoev.ru     nxt_event_engine_t       *engine;
153683Sigor@sysoev.ru     nxt_listen_event_t       *lev;
154431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
155431Sigor@sysoev.ru 
156431Sigor@sysoev.ru     c = obj;
157683Sigor@sysoev.ru     lev = data;
158431Sigor@sysoev.ru 
159431Sigor@sysoev.ru     nxt_debug(task, "http conn init");
160431Sigor@sysoev.ru 
161683Sigor@sysoev.ru     joint = lev->socket.data;
162431Sigor@sysoev.ru     skcf = joint->socket_conf;
163431Sigor@sysoev.ru     c->local = skcf->sockaddr;
164431Sigor@sysoev.ru 
165431Sigor@sysoev.ru     engine = task->thread->engine;
166431Sigor@sysoev.ru     c->read_work_queue = &engine->fast_work_queue;
167431Sigor@sysoev.ru     c->write_work_queue = &engine->fast_work_queue;
168431Sigor@sysoev.ru 
169431Sigor@sysoev.ru     c->read_state = &nxt_h1p_idle_state;
170431Sigor@sysoev.ru 
171771Sigor@sysoev.ru #if (NXT_TLS)
172771Sigor@sysoev.ru     if (skcf->tls != NULL) {
173771Sigor@sysoev.ru         c->read_state = &nxt_http_idle_state;
174771Sigor@sysoev.ru     }
175771Sigor@sysoev.ru #endif
176771Sigor@sysoev.ru 
177637Sigor@sysoev.ru     nxt_conn_read(engine, c);
178431Sigor@sysoev.ru }
179431Sigor@sysoev.ru 
180431Sigor@sysoev.ru 
181771Sigor@sysoev.ru #if (NXT_TLS)
182771Sigor@sysoev.ru 
183771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_http_idle_state
184431Sigor@sysoev.ru     nxt_aligned(64) =
185431Sigor@sysoev.ru {
186771Sigor@sysoev.ru     .ready_handler = nxt_http_conn_test,
187771Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
188431Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
189431Sigor@sysoev.ru 
190771Sigor@sysoev.ru     .io_read_handler = nxt_http_idle_io_read_handler,
191431Sigor@sysoev.ru 
192771Sigor@sysoev.ru     .timer_handler = nxt_h1p_idle_timeout,
193771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_timer_value,
194635Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, idle_timeout),
195431Sigor@sysoev.ru };
196431Sigor@sysoev.ru 
197431Sigor@sysoev.ru 
198629Sigor@sysoev.ru static ssize_t
199771Sigor@sysoev.ru nxt_http_idle_io_read_handler(nxt_conn_t *c)
200629Sigor@sysoev.ru {
201629Sigor@sysoev.ru     size_t                   size;
202629Sigor@sysoev.ru     ssize_t                  n;
203629Sigor@sysoev.ru     nxt_buf_t                *b;
204629Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
205629Sigor@sysoev.ru 
206683Sigor@sysoev.ru     joint = c->listen->socket.data;
207741Sigor@sysoev.ru 
208771Sigor@sysoev.ru     if (nxt_slow_path(joint == NULL)) {
209741Sigor@sysoev.ru         /*
210741Sigor@sysoev.ru          * Listening socket had been closed while
211741Sigor@sysoev.ru          * connection was in keep-alive state.
212741Sigor@sysoev.ru          */
213741Sigor@sysoev.ru         c->read_state = &nxt_h1p_idle_close_state;
214741Sigor@sysoev.ru         return 0;
215741Sigor@sysoev.ru     }
216741Sigor@sysoev.ru 
217629Sigor@sysoev.ru     size = joint->socket_conf->header_buffer_size;
218629Sigor@sysoev.ru 
219629Sigor@sysoev.ru     b = nxt_buf_mem_alloc(c->mem_pool, size, 0);
220629Sigor@sysoev.ru     if (nxt_slow_path(b == NULL)) {
221629Sigor@sysoev.ru         c->socket.error = NXT_ENOMEM;
222629Sigor@sysoev.ru         return NXT_ERROR;
223629Sigor@sysoev.ru     }
224629Sigor@sysoev.ru 
225771Sigor@sysoev.ru     /*
226771Sigor@sysoev.ru      * 1 byte is enough to distinguish between SSLv3/TLS and plain HTTP.
227771Sigor@sysoev.ru      * 11 bytes are enough to log supported SSLv3/TLS version.
228771Sigor@sysoev.ru      * 16 bytes are just for more optimized kernel copy-out operation.
229771Sigor@sysoev.ru      */
230771Sigor@sysoev.ru     n = c->io->recv(c, b->mem.pos, 16, MSG_PEEK);
231629Sigor@sysoev.ru 
232629Sigor@sysoev.ru     if (n > 0) {
233629Sigor@sysoev.ru         c->read = b;
234629Sigor@sysoev.ru 
235629Sigor@sysoev.ru     } else {
236771Sigor@sysoev.ru         c->read = NULL;
237629Sigor@sysoev.ru         nxt_mp_free(c->mem_pool, b);
238629Sigor@sysoev.ru     }
239629Sigor@sysoev.ru 
240629Sigor@sysoev.ru     return n;
241629Sigor@sysoev.ru }
242629Sigor@sysoev.ru 
243629Sigor@sysoev.ru 
244771Sigor@sysoev.ru static void
245771Sigor@sysoev.ru nxt_http_conn_test(nxt_task_t *task, void *obj, void *data)
246771Sigor@sysoev.ru {
247771Sigor@sysoev.ru     u_char                   *p;
248771Sigor@sysoev.ru     nxt_buf_t                *b;
249771Sigor@sysoev.ru     nxt_conn_t               *c;
250771Sigor@sysoev.ru     nxt_tls_conf_t           *tls;
251771Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
252771Sigor@sysoev.ru 
253771Sigor@sysoev.ru     c = obj;
254771Sigor@sysoev.ru 
255771Sigor@sysoev.ru     nxt_debug(task, "h1p conn https test");
256771Sigor@sysoev.ru 
257771Sigor@sysoev.ru     b = c->read;
258771Sigor@sysoev.ru     p = b->mem.pos;
259771Sigor@sysoev.ru 
260771Sigor@sysoev.ru     c->read_state = &nxt_h1p_idle_state;
261771Sigor@sysoev.ru 
262771Sigor@sysoev.ru     if (p[0] != 0x16) {
263771Sigor@sysoev.ru         b->mem.free = b->mem.pos;
264771Sigor@sysoev.ru 
265771Sigor@sysoev.ru         nxt_conn_read(task->thread->engine, c);
266771Sigor@sysoev.ru         return;
267771Sigor@sysoev.ru     }
268771Sigor@sysoev.ru 
269771Sigor@sysoev.ru     /* SSLv3/TLS ClientHello message. */
270771Sigor@sysoev.ru 
271771Sigor@sysoev.ru #if (NXT_DEBUG)
272771Sigor@sysoev.ru     if (nxt_buf_mem_used_size(&b->mem) >= 11) {
273771Sigor@sysoev.ru         u_char      major, minor;
274771Sigor@sysoev.ru         const char  *protocol;
275771Sigor@sysoev.ru 
276771Sigor@sysoev.ru         major = p[9];
277771Sigor@sysoev.ru         minor = p[10];
278771Sigor@sysoev.ru 
279771Sigor@sysoev.ru         if (major == 3) {
280771Sigor@sysoev.ru             if (minor == 0) {
281771Sigor@sysoev.ru                 protocol = "SSLv";
282771Sigor@sysoev.ru 
283771Sigor@sysoev.ru             } else {
284771Sigor@sysoev.ru                 protocol = "TLSv";
285771Sigor@sysoev.ru                 major -= 2;
286771Sigor@sysoev.ru                 minor -= 1;
287771Sigor@sysoev.ru             }
288771Sigor@sysoev.ru 
289771Sigor@sysoev.ru             nxt_debug(task, "SSL/TLS: %s%ud.%ud", protocol, major, minor);
290771Sigor@sysoev.ru         }
291771Sigor@sysoev.ru     }
292771Sigor@sysoev.ru #endif
293771Sigor@sysoev.ru 
294771Sigor@sysoev.ru     c->read = NULL;
295771Sigor@sysoev.ru     nxt_mp_free(c->mem_pool, b);
296771Sigor@sysoev.ru 
297771Sigor@sysoev.ru     joint = c->listen->socket.data;
298771Sigor@sysoev.ru 
299771Sigor@sysoev.ru     if (nxt_slow_path(joint == NULL)) {
300771Sigor@sysoev.ru         /*
301771Sigor@sysoev.ru          * Listening socket had been closed while
302771Sigor@sysoev.ru          * connection was in keep-alive state.
303771Sigor@sysoev.ru          */
304771Sigor@sysoev.ru         nxt_h1p_shutdown(task, c);
305771Sigor@sysoev.ru         return;
306771Sigor@sysoev.ru     }
307771Sigor@sysoev.ru 
308771Sigor@sysoev.ru     tls = joint->socket_conf->tls;
309771Sigor@sysoev.ru 
310771Sigor@sysoev.ru     tls->conn_init(task, tls, c);
311771Sigor@sysoev.ru }
312771Sigor@sysoev.ru 
313771Sigor@sysoev.ru #endif
314771Sigor@sysoev.ru 
315771Sigor@sysoev.ru 
316771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_state
317741Sigor@sysoev.ru     nxt_aligned(64) =
318741Sigor@sysoev.ru {
319771Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_proto_init,
320741Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
321771Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
322771Sigor@sysoev.ru 
323771Sigor@sysoev.ru     .io_read_handler = nxt_h1p_idle_io_read_handler,
324771Sigor@sysoev.ru 
325771Sigor@sysoev.ru     .timer_handler = nxt_h1p_idle_timeout,
326771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_timer_value,
327771Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, idle_timeout),
328771Sigor@sysoev.ru     .timer_autoreset = 1,
329741Sigor@sysoev.ru };
330741Sigor@sysoev.ru 
331741Sigor@sysoev.ru 
332771Sigor@sysoev.ru static ssize_t
333771Sigor@sysoev.ru nxt_h1p_idle_io_read_handler(nxt_conn_t *c)
334771Sigor@sysoev.ru {
335771Sigor@sysoev.ru     size_t                   size;
336771Sigor@sysoev.ru     ssize_t                  n;
337771Sigor@sysoev.ru     nxt_buf_t                *b;
338771Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
339771Sigor@sysoev.ru 
340771Sigor@sysoev.ru     joint = c->listen->socket.data;
341771Sigor@sysoev.ru 
342771Sigor@sysoev.ru     if (nxt_slow_path(joint == NULL)) {
343771Sigor@sysoev.ru         /*
344771Sigor@sysoev.ru          * Listening socket had been closed while
345771Sigor@sysoev.ru          * connection was in keep-alive state.
346771Sigor@sysoev.ru          */
347771Sigor@sysoev.ru         c->read_state = &nxt_h1p_idle_close_state;
348771Sigor@sysoev.ru         return 0;
349771Sigor@sysoev.ru     }
350771Sigor@sysoev.ru 
351771Sigor@sysoev.ru     b = c->read;
352771Sigor@sysoev.ru 
353771Sigor@sysoev.ru     if (b == NULL) {
354771Sigor@sysoev.ru         size = joint->socket_conf->header_buffer_size;
355771Sigor@sysoev.ru 
356771Sigor@sysoev.ru         b = nxt_buf_mem_alloc(c->mem_pool, size, 0);
357771Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
358771Sigor@sysoev.ru             c->socket.error = NXT_ENOMEM;
359771Sigor@sysoev.ru             return NXT_ERROR;
360771Sigor@sysoev.ru         }
361771Sigor@sysoev.ru     }
362771Sigor@sysoev.ru 
363771Sigor@sysoev.ru     n = c->io->recvbuf(c, b);
364771Sigor@sysoev.ru 
365771Sigor@sysoev.ru     if (n > 0) {
366771Sigor@sysoev.ru         c->read = b;
367771Sigor@sysoev.ru 
368771Sigor@sysoev.ru     } else {
369771Sigor@sysoev.ru         c->read = NULL;
370771Sigor@sysoev.ru         nxt_mp_free(c->mem_pool, b);
371771Sigor@sysoev.ru     }
372771Sigor@sysoev.ru 
373771Sigor@sysoev.ru     return n;
374771Sigor@sysoev.ru }
375771Sigor@sysoev.ru 
376771Sigor@sysoev.ru 
377431Sigor@sysoev.ru static void
378624Sigor@sysoev.ru nxt_h1p_conn_proto_init(nxt_task_t *task, void *obj, void *data)
379431Sigor@sysoev.ru {
380624Sigor@sysoev.ru     nxt_conn_t     *c;
381624Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
382624Sigor@sysoev.ru 
383624Sigor@sysoev.ru     c = obj;
384624Sigor@sysoev.ru 
385624Sigor@sysoev.ru     nxt_debug(task, "h1p conn proto init");
386624Sigor@sysoev.ru 
387624Sigor@sysoev.ru     h1p = nxt_mp_zget(c->mem_pool, sizeof(nxt_h1proto_t));
388624Sigor@sysoev.ru     if (nxt_slow_path(h1p == NULL)) {
389771Sigor@sysoev.ru         nxt_h1p_shutdown(task, c);
390624Sigor@sysoev.ru         return;
391624Sigor@sysoev.ru     }
392624Sigor@sysoev.ru 
393624Sigor@sysoev.ru     c->socket.data = h1p;
394624Sigor@sysoev.ru     h1p->conn = c;
395624Sigor@sysoev.ru 
396624Sigor@sysoev.ru     nxt_h1p_conn_request_init(task, c, h1p);
397624Sigor@sysoev.ru }
398624Sigor@sysoev.ru 
399624Sigor@sysoev.ru 
400624Sigor@sysoev.ru static void
401624Sigor@sysoev.ru nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data)
402624Sigor@sysoev.ru {
403431Sigor@sysoev.ru     nxt_int_t                ret;
404431Sigor@sysoev.ru     nxt_conn_t               *c;
405431Sigor@sysoev.ru     nxt_h1proto_t            *h1p;
406431Sigor@sysoev.ru     nxt_http_request_t       *r;
407431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
408431Sigor@sysoev.ru 
409431Sigor@sysoev.ru     c = obj;
410431Sigor@sysoev.ru     h1p = data;
411431Sigor@sysoev.ru 
412624Sigor@sysoev.ru     nxt_debug(task, "h1p conn request init");
413431Sigor@sysoev.ru 
414624Sigor@sysoev.ru     r = nxt_http_request_create(task);
415431Sigor@sysoev.ru 
416624Sigor@sysoev.ru     if (nxt_fast_path(r != NULL)) {
417431Sigor@sysoev.ru         h1p->request = r;
418431Sigor@sysoev.ru         r->proto.h1 = h1p;
419624Sigor@sysoev.ru 
4201112Sigor@sysoev.ru         /* r->protocol = NXT_HTTP_PROTO_H1 is done by zeroing. */
421431Sigor@sysoev.ru         r->remote = c->remote;
422431Sigor@sysoev.ru 
4231110Saxel.duch@nginx.com #if (NXT_TLS)
4241110Saxel.duch@nginx.com         r->tls = c->u.tls;
4251110Saxel.duch@nginx.com #endif
4261110Saxel.duch@nginx.com 
427431Sigor@sysoev.ru         ret = nxt_http_parse_request_init(&h1p->parser, r->mem_pool);
428624Sigor@sysoev.ru 
429624Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_OK)) {
430683Sigor@sysoev.ru             joint = c->listen->socket.data;
431683Sigor@sysoev.ru             joint->count++;
432683Sigor@sysoev.ru 
433683Sigor@sysoev.ru             r->conf = joint;
434683Sigor@sysoev.ru             c->local = joint->socket_conf->sockaddr;
435683Sigor@sysoev.ru 
436683Sigor@sysoev.ru             nxt_h1p_conn_request_header_parse(task, c, h1p);
437624Sigor@sysoev.ru             return;
438431Sigor@sysoev.ru         }
439624Sigor@sysoev.ru 
440624Sigor@sysoev.ru         /*
441624Sigor@sysoev.ru          * The request is very incomplete here,
442624Sigor@sysoev.ru          * so "internal server error" useless here.
443624Sigor@sysoev.ru          */
444624Sigor@sysoev.ru         nxt_mp_release(r->mem_pool);
445431Sigor@sysoev.ru     }
446431Sigor@sysoev.ru 
447771Sigor@sysoev.ru     nxt_h1p_shutdown(task, c);
448624Sigor@sysoev.ru }
449624Sigor@sysoev.ru 
450624Sigor@sysoev.ru 
451624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_header_parse_state
452624Sigor@sysoev.ru     nxt_aligned(64) =
453624Sigor@sysoev.ru {
454683Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_request_header_parse,
455624Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_request_error,
456624Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_request_error,
457624Sigor@sysoev.ru 
458624Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_request_timeout,
459771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_request_timer_value,
460624Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, header_read_timeout),
461624Sigor@sysoev.ru };
462624Sigor@sysoev.ru 
463624Sigor@sysoev.ru 
464624Sigor@sysoev.ru static void
465683Sigor@sysoev.ru nxt_h1p_conn_request_header_parse(nxt_task_t *task, void *obj, void *data)
466624Sigor@sysoev.ru {
467624Sigor@sysoev.ru     nxt_int_t           ret;
468624Sigor@sysoev.ru     nxt_conn_t          *c;
469624Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
470624Sigor@sysoev.ru     nxt_http_status_t   status;
471624Sigor@sysoev.ru     nxt_http_request_t  *r;
472624Sigor@sysoev.ru 
473624Sigor@sysoev.ru     c = obj;
474624Sigor@sysoev.ru     h1p = data;
475624Sigor@sysoev.ru 
476624Sigor@sysoev.ru     nxt_debug(task, "h1p conn header parse");
477624Sigor@sysoev.ru 
478431Sigor@sysoev.ru     ret = nxt_http_parse_request(&h1p->parser, &c->read->mem);
479431Sigor@sysoev.ru 
480636Sigor@sysoev.ru     ret = nxt_expect(NXT_DONE, ret);
481624Sigor@sysoev.ru 
482636Sigor@sysoev.ru     if (ret != NXT_AGAIN) {
483636Sigor@sysoev.ru         nxt_timer_disable(task->thread->engine, &c->read_timer);
484636Sigor@sysoev.ru     }
485636Sigor@sysoev.ru 
486636Sigor@sysoev.ru     r = h1p->request;
487625Sigor@sysoev.ru 
488625Sigor@sysoev.ru     switch (ret) {
489625Sigor@sysoev.ru 
490625Sigor@sysoev.ru     case NXT_DONE:
491431Sigor@sysoev.ru         /*
492431Sigor@sysoev.ru          * By default the keepalive mode is disabled in HTTP/1.0 and
493431Sigor@sysoev.ru          * enabled in HTTP/1.1.  The mode can be overridden later by
494431Sigor@sysoev.ru          * the "Connection" field processed in nxt_h1p_connection().
495431Sigor@sysoev.ru          */
496481Svbart@nginx.com         h1p->keepalive = (h1p->parser.version.s.minor != '0');
497431Sigor@sysoev.ru 
498*1131Smax.romanov@nginx.com         ret = nxt_h1p_header_process(task, h1p, r);
499431Sigor@sysoev.ru 
500431Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_OK)) {
501772Sigor@sysoev.ru 
502772Sigor@sysoev.ru #if (NXT_TLS)
503772Sigor@sysoev.ru             if (c->u.tls == NULL && r->conf->socket_conf->tls != NULL) {
504772Sigor@sysoev.ru                 status = NXT_HTTP_TO_HTTPS;
505772Sigor@sysoev.ru                 goto error;
506772Sigor@sysoev.ru             }
507772Sigor@sysoev.ru #endif
508772Sigor@sysoev.ru 
509431Sigor@sysoev.ru             r->state->ready_handler(task, r, NULL);
510431Sigor@sysoev.ru             return;
511431Sigor@sysoev.ru         }
512431Sigor@sysoev.ru 
513945Svbart@nginx.com         status = ret;
514625Sigor@sysoev.ru         goto error;
515431Sigor@sysoev.ru 
516625Sigor@sysoev.ru     case NXT_AGAIN:
517683Sigor@sysoev.ru         status = nxt_h1p_header_buffer_test(task, h1p, c, r->conf->socket_conf);
518431Sigor@sysoev.ru 
519625Sigor@sysoev.ru         if (nxt_fast_path(status == NXT_OK)) {
520625Sigor@sysoev.ru             c->read_state = &nxt_h1p_header_parse_state;
521431Sigor@sysoev.ru 
522625Sigor@sysoev.ru             nxt_conn_read(task->thread->engine, c);
523625Sigor@sysoev.ru             return;
524431Sigor@sysoev.ru         }
525431Sigor@sysoev.ru 
526625Sigor@sysoev.ru         break;
527480Svbart@nginx.com 
528480Svbart@nginx.com     case NXT_HTTP_PARSE_INVALID:
529480Svbart@nginx.com         status = NXT_HTTP_BAD_REQUEST;
530480Svbart@nginx.com         break;
531431Sigor@sysoev.ru 
532482Svbart@nginx.com     case NXT_HTTP_PARSE_UNSUPPORTED_VERSION:
533482Svbart@nginx.com         status = NXT_HTTP_VERSION_NOT_SUPPORTED;
534482Svbart@nginx.com         break;
535482Svbart@nginx.com 
536480Svbart@nginx.com     case NXT_HTTP_PARSE_TOO_LARGE_FIELD:
537480Svbart@nginx.com         status = NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
538480Svbart@nginx.com         break;
539480Svbart@nginx.com 
540480Svbart@nginx.com     default:
541480Svbart@nginx.com     case NXT_ERROR:
542480Svbart@nginx.com         status = NXT_HTTP_INTERNAL_SERVER_ERROR;
543480Svbart@nginx.com         break;
544480Svbart@nginx.com     }
545480Svbart@nginx.com 
546*1131Smax.romanov@nginx.com     (void) nxt_h1p_header_process(task, h1p, r);
547625Sigor@sysoev.ru 
548625Sigor@sysoev.ru error:
549625Sigor@sysoev.ru 
550960Sigor@sysoev.ru     h1p->keepalive = 0;
551960Sigor@sysoev.ru 
552480Svbart@nginx.com     nxt_http_request_error(task, r, status);
553431Sigor@sysoev.ru }
554431Sigor@sysoev.ru 
555431Sigor@sysoev.ru 
556431Sigor@sysoev.ru static nxt_int_t
557*1131Smax.romanov@nginx.com nxt_h1p_header_process(nxt_task_t *task, nxt_h1proto_t *h1p,
558*1131Smax.romanov@nginx.com     nxt_http_request_t *r)
559620Svbart@nginx.com {
560*1131Smax.romanov@nginx.com     u_char     *m;
561*1131Smax.romanov@nginx.com     nxt_int_t  ret;
562*1131Smax.romanov@nginx.com 
563620Svbart@nginx.com     r->target.start = h1p->parser.target_start;
564620Svbart@nginx.com     r->target.length = h1p->parser.target_end - h1p->parser.target_start;
565620Svbart@nginx.com 
566620Svbart@nginx.com     if (h1p->parser.version.ui64 != 0) {
567620Svbart@nginx.com         r->version.start = h1p->parser.version.str;
568620Svbart@nginx.com         r->version.length = sizeof(h1p->parser.version.str);
569620Svbart@nginx.com     }
570620Svbart@nginx.com 
571620Svbart@nginx.com     r->method = &h1p->parser.method;
572620Svbart@nginx.com     r->path = &h1p->parser.path;
573620Svbart@nginx.com     r->args = &h1p->parser.args;
574620Svbart@nginx.com 
575620Svbart@nginx.com     r->fields = h1p->parser.fields;
576620Svbart@nginx.com 
577*1131Smax.romanov@nginx.com     ret = nxt_http_fields_process(r->fields, &nxt_h1p_fields_hash, r);
578*1131Smax.romanov@nginx.com     if (nxt_slow_path(ret != NXT_OK)) {
579*1131Smax.romanov@nginx.com         return ret;
580*1131Smax.romanov@nginx.com     }
581*1131Smax.romanov@nginx.com 
582*1131Smax.romanov@nginx.com     if (h1p->connection_upgrade && h1p->upgrade_websocket) {
583*1131Smax.romanov@nginx.com         m = h1p->parser.method.start;
584*1131Smax.romanov@nginx.com 
585*1131Smax.romanov@nginx.com         if (nxt_slow_path(h1p->parser.method.length != 3
586*1131Smax.romanov@nginx.com                           || m[0] != 'G'
587*1131Smax.romanov@nginx.com                           || m[1] != 'E'
588*1131Smax.romanov@nginx.com                           || m[2] != 'T'))
589*1131Smax.romanov@nginx.com         {
590*1131Smax.romanov@nginx.com             nxt_log(task, NXT_LOG_INFO, "h1p upgrade: bad method");
591*1131Smax.romanov@nginx.com 
592*1131Smax.romanov@nginx.com             return NXT_HTTP_BAD_REQUEST;
593*1131Smax.romanov@nginx.com         }
594*1131Smax.romanov@nginx.com 
595*1131Smax.romanov@nginx.com         if (nxt_slow_path(h1p->parser.version.s.minor != '1')) {
596*1131Smax.romanov@nginx.com             nxt_log(task, NXT_LOG_INFO, "h1p upgrade: bad protocol version");
597*1131Smax.romanov@nginx.com 
598*1131Smax.romanov@nginx.com             return NXT_HTTP_BAD_REQUEST;
599*1131Smax.romanov@nginx.com         }
600*1131Smax.romanov@nginx.com 
601*1131Smax.romanov@nginx.com         if (nxt_slow_path(h1p->websocket_key == NULL)) {
602*1131Smax.romanov@nginx.com             nxt_log(task, NXT_LOG_INFO, "h1p upgrade: bad or absent websocket key");
603*1131Smax.romanov@nginx.com 
604*1131Smax.romanov@nginx.com             return NXT_HTTP_BAD_REQUEST;
605*1131Smax.romanov@nginx.com         }
606*1131Smax.romanov@nginx.com 
607*1131Smax.romanov@nginx.com         if (nxt_slow_path(h1p->websocket_version_ok == 0)) {
608*1131Smax.romanov@nginx.com             nxt_log(task, NXT_LOG_INFO, "h1p upgrade: bad or absent websocket version");
609*1131Smax.romanov@nginx.com 
610*1131Smax.romanov@nginx.com             return NXT_HTTP_UPGRADE_REQUIRED;
611*1131Smax.romanov@nginx.com         }
612*1131Smax.romanov@nginx.com 
613*1131Smax.romanov@nginx.com         r->websocket_handshake = 1;
614*1131Smax.romanov@nginx.com     }
615*1131Smax.romanov@nginx.com 
616*1131Smax.romanov@nginx.com     return ret;
617620Svbart@nginx.com }
618620Svbart@nginx.com 
619620Svbart@nginx.com 
620620Svbart@nginx.com static nxt_int_t
621625Sigor@sysoev.ru nxt_h1p_header_buffer_test(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c,
622625Sigor@sysoev.ru     nxt_socket_conf_t *skcf)
623625Sigor@sysoev.ru {
624625Sigor@sysoev.ru     size_t     size, used;
625625Sigor@sysoev.ru     nxt_buf_t  *in, *b;
626625Sigor@sysoev.ru 
627625Sigor@sysoev.ru     in = c->read;
628625Sigor@sysoev.ru 
629625Sigor@sysoev.ru     if (nxt_buf_mem_free_size(&in->mem) == 0) {
630625Sigor@sysoev.ru         size = skcf->large_header_buffer_size;
631625Sigor@sysoev.ru         used = nxt_buf_mem_used_size(&in->mem);
632625Sigor@sysoev.ru 
633625Sigor@sysoev.ru         if (size <= used || h1p->nbuffers >= skcf->large_header_buffers) {
634625Sigor@sysoev.ru             return NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
635625Sigor@sysoev.ru         }
636625Sigor@sysoev.ru 
637625Sigor@sysoev.ru         b = nxt_buf_mem_alloc(c->mem_pool, size, 0);
638625Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
639625Sigor@sysoev.ru             return NXT_HTTP_INTERNAL_SERVER_ERROR;
640625Sigor@sysoev.ru         }
641625Sigor@sysoev.ru 
642625Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.pos, in->mem.pos, used);
643625Sigor@sysoev.ru 
644625Sigor@sysoev.ru         in->next = h1p->buffers;
645625Sigor@sysoev.ru         h1p->buffers = in;
646625Sigor@sysoev.ru         h1p->nbuffers++;
647625Sigor@sysoev.ru 
648625Sigor@sysoev.ru         c->read = b;
649625Sigor@sysoev.ru     }
650625Sigor@sysoev.ru 
651625Sigor@sysoev.ru     return NXT_OK;
652625Sigor@sysoev.ru }
653625Sigor@sysoev.ru 
654625Sigor@sysoev.ru 
655625Sigor@sysoev.ru static nxt_int_t
656431Sigor@sysoev.ru nxt_h1p_connection(void *ctx, nxt_http_field_t *field, uintptr_t data)
657431Sigor@sysoev.ru {
658*1131Smax.romanov@nginx.com     nxt_http_request_t   *r;
659*1131Smax.romanov@nginx.com     static const u_char  *upgrade = (const u_char *) "upgrade";
660431Sigor@sysoev.ru 
661431Sigor@sysoev.ru     r = ctx;
662431Sigor@sysoev.ru 
663431Sigor@sysoev.ru     if (field->value_length == 5 && nxt_memcmp(field->value, "close", 5) == 0) {
664431Sigor@sysoev.ru         r->proto.h1->keepalive = 0;
665*1131Smax.romanov@nginx.com 
666*1131Smax.romanov@nginx.com     } else if (field->value_length == 7
667*1131Smax.romanov@nginx.com                && nxt_memcasecmp(field->value, upgrade, 7) == 0)
668*1131Smax.romanov@nginx.com     {
669*1131Smax.romanov@nginx.com         r->proto.h1->connection_upgrade = 1;
670*1131Smax.romanov@nginx.com     }
671*1131Smax.romanov@nginx.com 
672*1131Smax.romanov@nginx.com     return NXT_OK;
673*1131Smax.romanov@nginx.com }
674*1131Smax.romanov@nginx.com 
675*1131Smax.romanov@nginx.com 
676*1131Smax.romanov@nginx.com static nxt_int_t
677*1131Smax.romanov@nginx.com nxt_h1p_upgrade(void *ctx, nxt_http_field_t *field, uintptr_t data)
678*1131Smax.romanov@nginx.com {
679*1131Smax.romanov@nginx.com     nxt_http_request_t   *r;
680*1131Smax.romanov@nginx.com     static const u_char  *websocket = (const u_char *) "websocket";
681*1131Smax.romanov@nginx.com 
682*1131Smax.romanov@nginx.com     r = ctx;
683*1131Smax.romanov@nginx.com 
684*1131Smax.romanov@nginx.com     if (field->value_length == 9
685*1131Smax.romanov@nginx.com         && nxt_memcasecmp(field->value, websocket, 9) == 0)
686*1131Smax.romanov@nginx.com     {
687*1131Smax.romanov@nginx.com         r->proto.h1->upgrade_websocket = 1;
688*1131Smax.romanov@nginx.com     }
689*1131Smax.romanov@nginx.com 
690*1131Smax.romanov@nginx.com     return NXT_OK;
691*1131Smax.romanov@nginx.com }
692*1131Smax.romanov@nginx.com 
693*1131Smax.romanov@nginx.com 
694*1131Smax.romanov@nginx.com static nxt_int_t
695*1131Smax.romanov@nginx.com nxt_h1p_websocket_key(void *ctx, nxt_http_field_t *field, uintptr_t data)
696*1131Smax.romanov@nginx.com {
697*1131Smax.romanov@nginx.com     nxt_http_request_t  *r;
698*1131Smax.romanov@nginx.com 
699*1131Smax.romanov@nginx.com     r = ctx;
700*1131Smax.romanov@nginx.com 
701*1131Smax.romanov@nginx.com     if (field->value_length == 24) {
702*1131Smax.romanov@nginx.com         r->proto.h1->websocket_key = field;
703*1131Smax.romanov@nginx.com     }
704*1131Smax.romanov@nginx.com 
705*1131Smax.romanov@nginx.com     return NXT_OK;
706*1131Smax.romanov@nginx.com }
707*1131Smax.romanov@nginx.com 
708*1131Smax.romanov@nginx.com 
709*1131Smax.romanov@nginx.com static nxt_int_t
710*1131Smax.romanov@nginx.com nxt_h1p_websocket_version(void *ctx, nxt_http_field_t *field, uintptr_t data)
711*1131Smax.romanov@nginx.com {
712*1131Smax.romanov@nginx.com     nxt_http_request_t  *r;
713*1131Smax.romanov@nginx.com 
714*1131Smax.romanov@nginx.com     r = ctx;
715*1131Smax.romanov@nginx.com 
716*1131Smax.romanov@nginx.com     if (field->value_length == 2
717*1131Smax.romanov@nginx.com         && field->value[0] == '1' && field->value[1] == '3')
718*1131Smax.romanov@nginx.com     {
719*1131Smax.romanov@nginx.com         r->proto.h1->websocket_version_ok = 1;
720431Sigor@sysoev.ru     }
721431Sigor@sysoev.ru 
722431Sigor@sysoev.ru     return NXT_OK;
723431Sigor@sysoev.ru }
724431Sigor@sysoev.ru 
725431Sigor@sysoev.ru 
726431Sigor@sysoev.ru static nxt_int_t
727431Sigor@sysoev.ru nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field, uintptr_t data)
728431Sigor@sysoev.ru {
729431Sigor@sysoev.ru     nxt_http_te_t       te;
730431Sigor@sysoev.ru     nxt_http_request_t  *r;
731431Sigor@sysoev.ru 
732431Sigor@sysoev.ru     r = ctx;
733431Sigor@sysoev.ru 
734431Sigor@sysoev.ru     if (field->value_length == 7
735431Sigor@sysoev.ru         && nxt_memcmp(field->value, "chunked", 7) == 0)
736431Sigor@sysoev.ru     {
737431Sigor@sysoev.ru         te = NXT_HTTP_TE_CHUNKED;
738431Sigor@sysoev.ru 
739431Sigor@sysoev.ru     } else {
740431Sigor@sysoev.ru         te = NXT_HTTP_TE_UNSUPPORTED;
741431Sigor@sysoev.ru     }
742431Sigor@sysoev.ru 
743431Sigor@sysoev.ru     r->proto.h1->transfer_encoding = te;
744431Sigor@sysoev.ru 
745431Sigor@sysoev.ru     return NXT_OK;
746431Sigor@sysoev.ru }
747431Sigor@sysoev.ru 
748431Sigor@sysoev.ru 
749431Sigor@sysoev.ru static void
750431Sigor@sysoev.ru nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r)
751431Sigor@sysoev.ru {
752527Svbart@nginx.com     size_t             size, body_length;
753459Sigor@sysoev.ru     nxt_buf_t          *in, *b;
754431Sigor@sysoev.ru     nxt_conn_t         *c;
755459Sigor@sysoev.ru     nxt_h1proto_t      *h1p;
756431Sigor@sysoev.ru     nxt_http_status_t  status;
757431Sigor@sysoev.ru 
758459Sigor@sysoev.ru     h1p = r->proto.h1;
759459Sigor@sysoev.ru 
760624Sigor@sysoev.ru     nxt_debug(task, "h1p request body read %O te:%d",
761459Sigor@sysoev.ru               r->content_length_n, h1p->transfer_encoding);
762431Sigor@sysoev.ru 
763459Sigor@sysoev.ru     switch (h1p->transfer_encoding) {
764431Sigor@sysoev.ru 
765431Sigor@sysoev.ru     case NXT_HTTP_TE_CHUNKED:
766431Sigor@sysoev.ru         status = NXT_HTTP_LENGTH_REQUIRED;
767431Sigor@sysoev.ru         goto error;
768431Sigor@sysoev.ru 
769431Sigor@sysoev.ru     case NXT_HTTP_TE_UNSUPPORTED:
770431Sigor@sysoev.ru         status = NXT_HTTP_NOT_IMPLEMENTED;
771431Sigor@sysoev.ru         goto error;
772431Sigor@sysoev.ru 
773431Sigor@sysoev.ru     default:
774431Sigor@sysoev.ru     case NXT_HTTP_TE_NONE:
775431Sigor@sysoev.ru         break;
776431Sigor@sysoev.ru     }
777431Sigor@sysoev.ru 
778431Sigor@sysoev.ru     if (r->content_length_n == -1 || r->content_length_n == 0) {
779431Sigor@sysoev.ru         goto ready;
780431Sigor@sysoev.ru     }
781431Sigor@sysoev.ru 
782683Sigor@sysoev.ru     if (r->content_length_n > (nxt_off_t) r->conf->socket_conf->max_body_size) {
783431Sigor@sysoev.ru         status = NXT_HTTP_PAYLOAD_TOO_LARGE;
784431Sigor@sysoev.ru         goto error;
785431Sigor@sysoev.ru     }
786431Sigor@sysoev.ru 
787527Svbart@nginx.com     body_length = (size_t) r->content_length_n;
788431Sigor@sysoev.ru 
789431Sigor@sysoev.ru     b = r->body;
790431Sigor@sysoev.ru 
791431Sigor@sysoev.ru     if (b == NULL) {
792527Svbart@nginx.com         b = nxt_buf_mem_alloc(r->mem_pool, body_length, 0);
793431Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
794431Sigor@sysoev.ru             status = NXT_HTTP_INTERNAL_SERVER_ERROR;
795431Sigor@sysoev.ru             goto error;
796431Sigor@sysoev.ru         }
797431Sigor@sysoev.ru 
798431Sigor@sysoev.ru         r->body = b;
799431Sigor@sysoev.ru     }
800431Sigor@sysoev.ru 
801459Sigor@sysoev.ru     in = h1p->conn->read;
802431Sigor@sysoev.ru 
803459Sigor@sysoev.ru     size = nxt_buf_mem_used_size(&in->mem);
804431Sigor@sysoev.ru 
805431Sigor@sysoev.ru     if (size != 0) {
806527Svbart@nginx.com         if (size > body_length) {
807527Svbart@nginx.com             size = body_length;
808431Sigor@sysoev.ru         }
809431Sigor@sysoev.ru 
810459Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.free, in->mem.pos, size);
811459Sigor@sysoev.ru         in->mem.pos += size;
812431Sigor@sysoev.ru     }
813431Sigor@sysoev.ru 
814527Svbart@nginx.com     size = nxt_buf_mem_free_size(&b->mem);
815431Sigor@sysoev.ru 
816527Svbart@nginx.com     nxt_debug(task, "h1p body rest: %uz", size);
817431Sigor@sysoev.ru 
818527Svbart@nginx.com     if (size != 0) {
819459Sigor@sysoev.ru         in->next = h1p->buffers;
820459Sigor@sysoev.ru         h1p->buffers = in;
821*1131Smax.romanov@nginx.com         h1p->nbuffers++;
822459Sigor@sysoev.ru 
823459Sigor@sysoev.ru         c = h1p->conn;
824431Sigor@sysoev.ru         c->read = b;
825431Sigor@sysoev.ru         c->read_state = &nxt_h1p_read_body_state;
826431Sigor@sysoev.ru 
827431Sigor@sysoev.ru         nxt_conn_read(task->thread->engine, c);
828431Sigor@sysoev.ru         return;
829431Sigor@sysoev.ru     }
830431Sigor@sysoev.ru 
831431Sigor@sysoev.ru ready:
832431Sigor@sysoev.ru 
833431Sigor@sysoev.ru     nxt_work_queue_add(&task->thread->engine->fast_work_queue,
834431Sigor@sysoev.ru                        r->state->ready_handler, task, r, NULL);
835431Sigor@sysoev.ru 
836431Sigor@sysoev.ru     return;
837431Sigor@sysoev.ru 
838431Sigor@sysoev.ru error:
839431Sigor@sysoev.ru 
840459Sigor@sysoev.ru     h1p->keepalive = 0;
841431Sigor@sysoev.ru 
842431Sigor@sysoev.ru     nxt_http_request_error(task, r, status);
843431Sigor@sysoev.ru }
844431Sigor@sysoev.ru 
845431Sigor@sysoev.ru 
846431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_body_state
847431Sigor@sysoev.ru     nxt_aligned(64) =
848431Sigor@sysoev.ru {
849637Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_request_body_read,
850624Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_request_error,
851624Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_request_error,
852431Sigor@sysoev.ru 
853624Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_request_timeout,
854771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_request_timer_value,
855431Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, body_read_timeout),
856431Sigor@sysoev.ru     .timer_autoreset = 1,
857431Sigor@sysoev.ru };
858431Sigor@sysoev.ru 
859431Sigor@sysoev.ru 
860431Sigor@sysoev.ru static void
861637Sigor@sysoev.ru nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj, void *data)
862431Sigor@sysoev.ru {
863431Sigor@sysoev.ru     size_t              size;
864431Sigor@sysoev.ru     nxt_conn_t          *c;
865431Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
866431Sigor@sysoev.ru     nxt_http_request_t  *r;
867637Sigor@sysoev.ru     nxt_event_engine_t  *engine;
868431Sigor@sysoev.ru 
869431Sigor@sysoev.ru     c = obj;
870431Sigor@sysoev.ru     h1p = data;
871431Sigor@sysoev.ru 
872637Sigor@sysoev.ru     nxt_debug(task, "h1p conn request body read");
873431Sigor@sysoev.ru 
874527Svbart@nginx.com     size = nxt_buf_mem_free_size(&c->read->mem);
875431Sigor@sysoev.ru 
876527Svbart@nginx.com     nxt_debug(task, "h1p body rest: %uz", size);
877431Sigor@sysoev.ru 
878637Sigor@sysoev.ru     engine = task->thread->engine;
879637Sigor@sysoev.ru 
880527Svbart@nginx.com     if (size != 0) {
881637Sigor@sysoev.ru         nxt_conn_read(engine, c);
882431Sigor@sysoev.ru 
883431Sigor@sysoev.ru     } else {
884637Sigor@sysoev.ru         c->read = NULL;
885527Svbart@nginx.com         r = h1p->request;
886637Sigor@sysoev.ru 
887637Sigor@sysoev.ru         nxt_work_queue_add(&engine->fast_work_queue, r->state->ready_handler,
888637Sigor@sysoev.ru                            task, r, NULL);
889431Sigor@sysoev.ru     }
890431Sigor@sysoev.ru }
891431Sigor@sysoev.ru 
892431Sigor@sysoev.ru 
893431Sigor@sysoev.ru static void
894431Sigor@sysoev.ru nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r)
895431Sigor@sysoev.ru {
896431Sigor@sysoev.ru     r->local = nxt_conn_local_addr(task, r->proto.h1->conn);
897431Sigor@sysoev.ru }
898431Sigor@sysoev.ru 
899431Sigor@sysoev.ru 
900*1131Smax.romanov@nginx.com #define NXT_HTTP_LAST_INFORMATIONAL                                           \
901*1131Smax.romanov@nginx.com     (NXT_HTTP_CONTINUE + nxt_nitems(nxt_http_informational) - 1)
902*1131Smax.romanov@nginx.com 
903*1131Smax.romanov@nginx.com static const nxt_str_t  nxt_http_informational[] = {
904*1131Smax.romanov@nginx.com     nxt_string("HTTP/1.1 100 Continue\r\n"),
905*1131Smax.romanov@nginx.com     nxt_string("HTTP/1.1 101 Switching Protocols\r\n"),
906*1131Smax.romanov@nginx.com };
907*1131Smax.romanov@nginx.com 
908*1131Smax.romanov@nginx.com 
909431Sigor@sysoev.ru #define NXT_HTTP_LAST_SUCCESS                                                 \
910431Sigor@sysoev.ru     (NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1)
911431Sigor@sysoev.ru 
912431Sigor@sysoev.ru static const nxt_str_t  nxt_http_success[] = {
913431Sigor@sysoev.ru     nxt_string("HTTP/1.1 200 OK\r\n"),
914431Sigor@sysoev.ru     nxt_string("HTTP/1.1 201 Created\r\n"),
915431Sigor@sysoev.ru     nxt_string("HTTP/1.1 202 Accepted\r\n"),
916431Sigor@sysoev.ru     nxt_string("HTTP/1.1 203 Non-Authoritative Information\r\n"),
917431Sigor@sysoev.ru     nxt_string("HTTP/1.1 204 No Content\r\n"),
918431Sigor@sysoev.ru     nxt_string("HTTP/1.1 205 Reset Content\r\n"),
919431Sigor@sysoev.ru     nxt_string("HTTP/1.1 206 Partial Content\r\n"),
920431Sigor@sysoev.ru };
921431Sigor@sysoev.ru 
922431Sigor@sysoev.ru 
923431Sigor@sysoev.ru #define NXT_HTTP_LAST_REDIRECTION                                             \
924431Sigor@sysoev.ru     (NXT_HTTP_MULTIPLE_CHOICES + nxt_nitems(nxt_http_redirection) - 1)
925431Sigor@sysoev.ru 
926431Sigor@sysoev.ru static const nxt_str_t  nxt_http_redirection[] = {
927431Sigor@sysoev.ru     nxt_string("HTTP/1.1 300 Multiple Choices\r\n"),
928431Sigor@sysoev.ru     nxt_string("HTTP/1.1 301 Moved Permanently\r\n"),
929431Sigor@sysoev.ru     nxt_string("HTTP/1.1 302 Found\r\n"),
930431Sigor@sysoev.ru     nxt_string("HTTP/1.1 303 See Other\r\n"),
931431Sigor@sysoev.ru     nxt_string("HTTP/1.1 304 Not Modified\r\n"),
932431Sigor@sysoev.ru };
933431Sigor@sysoev.ru 
934431Sigor@sysoev.ru 
935431Sigor@sysoev.ru #define NXT_HTTP_LAST_CLIENT_ERROR                                            \
936431Sigor@sysoev.ru     (NXT_HTTP_BAD_REQUEST + nxt_nitems(nxt_http_client_error) - 1)
937431Sigor@sysoev.ru 
938431Sigor@sysoev.ru static const nxt_str_t  nxt_http_client_error[] = {
939431Sigor@sysoev.ru     nxt_string("HTTP/1.1 400 Bad Request\r\n"),
940431Sigor@sysoev.ru     nxt_string("HTTP/1.1 401 Unauthorized\r\n"),
941431Sigor@sysoev.ru     nxt_string("HTTP/1.1 402 Payment Required\r\n"),
942431Sigor@sysoev.ru     nxt_string("HTTP/1.1 403 Forbidden\r\n"),
943431Sigor@sysoev.ru     nxt_string("HTTP/1.1 404 Not Found\r\n"),
944431Sigor@sysoev.ru     nxt_string("HTTP/1.1 405 Method Not Allowed\r\n"),
945431Sigor@sysoev.ru     nxt_string("HTTP/1.1 406 Not Acceptable\r\n"),
946431Sigor@sysoev.ru     nxt_string("HTTP/1.1 407 Proxy Authentication Required\r\n"),
947431Sigor@sysoev.ru     nxt_string("HTTP/1.1 408 Request Timeout\r\n"),
948431Sigor@sysoev.ru     nxt_string("HTTP/1.1 409 Conflict\r\n"),
949431Sigor@sysoev.ru     nxt_string("HTTP/1.1 410 Gone\r\n"),
950431Sigor@sysoev.ru     nxt_string("HTTP/1.1 411 Length Required\r\n"),
951431Sigor@sysoev.ru     nxt_string("HTTP/1.1 412 Precondition Failed\r\n"),
952431Sigor@sysoev.ru     nxt_string("HTTP/1.1 413 Payload Too Large\r\n"),
953431Sigor@sysoev.ru     nxt_string("HTTP/1.1 414 URI Too Long\r\n"),
954431Sigor@sysoev.ru     nxt_string("HTTP/1.1 415 Unsupported Media Type\r\n"),
955431Sigor@sysoev.ru     nxt_string("HTTP/1.1 416 Range Not Satisfiable\r\n"),
956431Sigor@sysoev.ru     nxt_string("HTTP/1.1 417 Expectation Failed\r\n"),
957431Sigor@sysoev.ru     nxt_string("HTTP/1.1 418\r\n"),
958431Sigor@sysoev.ru     nxt_string("HTTP/1.1 419\r\n"),
959431Sigor@sysoev.ru     nxt_string("HTTP/1.1 420\r\n"),
960431Sigor@sysoev.ru     nxt_string("HTTP/1.1 421\r\n"),
961431Sigor@sysoev.ru     nxt_string("HTTP/1.1 422\r\n"),
962431Sigor@sysoev.ru     nxt_string("HTTP/1.1 423\r\n"),
963431Sigor@sysoev.ru     nxt_string("HTTP/1.1 424\r\n"),
964431Sigor@sysoev.ru     nxt_string("HTTP/1.1 425\r\n"),
965*1131Smax.romanov@nginx.com     nxt_string("HTTP/1.1 426 Upgrade Required\r\n"),
966431Sigor@sysoev.ru     nxt_string("HTTP/1.1 427\r\n"),
967431Sigor@sysoev.ru     nxt_string("HTTP/1.1 428\r\n"),
968431Sigor@sysoev.ru     nxt_string("HTTP/1.1 429\r\n"),
969431Sigor@sysoev.ru     nxt_string("HTTP/1.1 430\r\n"),
970431Sigor@sysoev.ru     nxt_string("HTTP/1.1 431 Request Header Fields Too Large\r\n"),
971431Sigor@sysoev.ru };
972431Sigor@sysoev.ru 
973431Sigor@sysoev.ru 
974772Sigor@sysoev.ru #define NXT_HTTP_LAST_NGINX_ERROR                                             \
975772Sigor@sysoev.ru     (NXT_HTTP_TO_HTTPS + nxt_nitems(nxt_http_nginx_error) - 1)
976772Sigor@sysoev.ru 
977772Sigor@sysoev.ru static const nxt_str_t  nxt_http_nginx_error[] = {
978772Sigor@sysoev.ru     nxt_string("HTTP/1.1 400 "
979772Sigor@sysoev.ru                "The plain HTTP request was sent to HTTPS port\r\n"),
980772Sigor@sysoev.ru };
981772Sigor@sysoev.ru 
982772Sigor@sysoev.ru 
983431Sigor@sysoev.ru #define NXT_HTTP_LAST_SERVER_ERROR                                            \
984431Sigor@sysoev.ru     (NXT_HTTP_INTERNAL_SERVER_ERROR + nxt_nitems(nxt_http_server_error) - 1)
985431Sigor@sysoev.ru 
986431Sigor@sysoev.ru static const nxt_str_t  nxt_http_server_error[] = {
987431Sigor@sysoev.ru     nxt_string("HTTP/1.1 500 Internal Server Error\r\n"),
988431Sigor@sysoev.ru     nxt_string("HTTP/1.1 501 Not Implemented\r\n"),
989431Sigor@sysoev.ru     nxt_string("HTTP/1.1 502 Bad Gateway\r\n"),
990431Sigor@sysoev.ru     nxt_string("HTTP/1.1 503 Service Unavailable\r\n"),
991431Sigor@sysoev.ru     nxt_string("HTTP/1.1 504 Gateway Timeout\r\n"),
992482Svbart@nginx.com     nxt_string("HTTP/1.1 505 HTTP Version Not Supported\r\n"),
993431Sigor@sysoev.ru };
994431Sigor@sysoev.ru 
995431Sigor@sysoev.ru 
996703Svbart@nginx.com #define UNKNOWN_STATUS_LENGTH  nxt_length("HTTP/1.1 65536\r\n")
997431Sigor@sysoev.ru 
998431Sigor@sysoev.ru static void
999431Sigor@sysoev.ru nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r)
1000431Sigor@sysoev.ru {
1001431Sigor@sysoev.ru     u_char              *p;
1002431Sigor@sysoev.ru     size_t              size;
1003431Sigor@sysoev.ru     nxt_buf_t           *header;
1004431Sigor@sysoev.ru     nxt_str_t           unknown_status;
1005431Sigor@sysoev.ru     nxt_int_t           conn;
1006431Sigor@sysoev.ru     nxt_uint_t          n;
1007431Sigor@sysoev.ru     nxt_bool_t          http11;
1008431Sigor@sysoev.ru     nxt_conn_t          *c;
1009431Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
1010431Sigor@sysoev.ru     const nxt_str_t     *status;
1011431Sigor@sysoev.ru     nxt_http_field_t    *field;
1012431Sigor@sysoev.ru     u_char              buf[UNKNOWN_STATUS_LENGTH];
1013431Sigor@sysoev.ru 
1014431Sigor@sysoev.ru     static const char   chunked[] = "Transfer-Encoding: chunked\r\n";
1015*1131Smax.romanov@nginx.com     static const char   websocket_version[] = "Sec-WebSocket-Version: 13\r\n";
1016431Sigor@sysoev.ru 
1017*1131Smax.romanov@nginx.com     static const nxt_str_t  connection[3] = {
1018431Sigor@sysoev.ru         nxt_string("Connection: close\r\n"),
1019431Sigor@sysoev.ru         nxt_string("Connection: keep-alive\r\n"),
1020*1131Smax.romanov@nginx.com         nxt_string("Upgrade: websocket\r\n"
1021*1131Smax.romanov@nginx.com                    "Connection: Upgrade\r\n"
1022*1131Smax.romanov@nginx.com                    "Sec-WebSocket-Accept: "),
1023431Sigor@sysoev.ru     };
1024431Sigor@sysoev.ru 
1025431Sigor@sysoev.ru     nxt_debug(task, "h1p request header send");
1026431Sigor@sysoev.ru 
1027431Sigor@sysoev.ru     r->header_sent = 1;
1028431Sigor@sysoev.ru     h1p = r->proto.h1;
1029431Sigor@sysoev.ru     n = r->status;
1030431Sigor@sysoev.ru 
1031*1131Smax.romanov@nginx.com     if (n >= NXT_HTTP_CONTINUE && n <= NXT_HTTP_LAST_INFORMATIONAL) {
1032*1131Smax.romanov@nginx.com         status = &nxt_http_informational[n - NXT_HTTP_CONTINUE];
1033*1131Smax.romanov@nginx.com 
1034*1131Smax.romanov@nginx.com     } else if (n >= NXT_HTTP_OK && n <= NXT_HTTP_LAST_SUCCESS) {
1035431Sigor@sysoev.ru         status = &nxt_http_success[n - NXT_HTTP_OK];
1036431Sigor@sysoev.ru 
1037431Sigor@sysoev.ru     } else if (n >= NXT_HTTP_MULTIPLE_CHOICES
1038431Sigor@sysoev.ru                && n <= NXT_HTTP_LAST_REDIRECTION)
1039431Sigor@sysoev.ru     {
1040431Sigor@sysoev.ru         status = &nxt_http_redirection[n - NXT_HTTP_MULTIPLE_CHOICES];
1041431Sigor@sysoev.ru 
1042431Sigor@sysoev.ru     } else if (n >= NXT_HTTP_BAD_REQUEST && n <= NXT_HTTP_LAST_CLIENT_ERROR) {
1043431Sigor@sysoev.ru         status = &nxt_http_client_error[n - NXT_HTTP_BAD_REQUEST];
1044431Sigor@sysoev.ru 
1045772Sigor@sysoev.ru     } else if (n >= NXT_HTTP_TO_HTTPS && n <= NXT_HTTP_LAST_NGINX_ERROR) {
1046772Sigor@sysoev.ru         status = &nxt_http_nginx_error[n - NXT_HTTP_TO_HTTPS];
1047772Sigor@sysoev.ru 
1048431Sigor@sysoev.ru     } else if (n >= NXT_HTTP_INTERNAL_SERVER_ERROR
1049431Sigor@sysoev.ru                && n <= NXT_HTTP_LAST_SERVER_ERROR)
1050431Sigor@sysoev.ru     {
1051431Sigor@sysoev.ru         status = &nxt_http_server_error[n - NXT_HTTP_INTERNAL_SERVER_ERROR];
1052431Sigor@sysoev.ru 
1053431Sigor@sysoev.ru     } else {
1054431Sigor@sysoev.ru         p = nxt_sprintf(buf, buf + UNKNOWN_STATUS_LENGTH,
1055431Sigor@sysoev.ru                         "HTTP/1.1 %03d\r\n", n);
1056431Sigor@sysoev.ru 
1057431Sigor@sysoev.ru         unknown_status.length = p - buf;
1058431Sigor@sysoev.ru         unknown_status.start = buf;
1059431Sigor@sysoev.ru         status = &unknown_status;
1060431Sigor@sysoev.ru     }
1061431Sigor@sysoev.ru 
1062450Sigor@sysoev.ru     size = status->length;
1063450Sigor@sysoev.ru     /* Trailing CRLF at the end of header. */
1064703Svbart@nginx.com     size += nxt_length("\r\n");
1065431Sigor@sysoev.ru 
1066*1131Smax.romanov@nginx.com     conn = -1;
1067431Sigor@sysoev.ru 
1068*1131Smax.romanov@nginx.com     if (r->websocket_handshake && n == NXT_HTTP_SWITCHING_PROTOCOLS) {
1069*1131Smax.romanov@nginx.com         h1p->websocket = 1;
1070*1131Smax.romanov@nginx.com         h1p->keepalive = 0;
1071*1131Smax.romanov@nginx.com         conn = 2;
1072*1131Smax.romanov@nginx.com         size += NXT_WEBSOCKET_ACCEPT_SIZE + 2;
1073*1131Smax.romanov@nginx.com 
1074*1131Smax.romanov@nginx.com     } else {
1075*1131Smax.romanov@nginx.com         http11 = (h1p->parser.version.s.minor != '0');
1076*1131Smax.romanov@nginx.com 
1077*1131Smax.romanov@nginx.com         if (r->resp.content_length == NULL || r->resp.content_length->skip) {
1078793Sigor@sysoev.ru 
1079*1131Smax.romanov@nginx.com             if (http11) {
1080*1131Smax.romanov@nginx.com                 if (n != NXT_HTTP_NOT_MODIFIED
1081*1131Smax.romanov@nginx.com                     && n != NXT_HTTP_NO_CONTENT
1082*1131Smax.romanov@nginx.com                     && !h1p->websocket)
1083*1131Smax.romanov@nginx.com                 {
1084*1131Smax.romanov@nginx.com                     h1p->chunked = 1;
1085*1131Smax.romanov@nginx.com                     size += nxt_length(chunked);
1086*1131Smax.romanov@nginx.com                     /* Trailing CRLF will be added by the first chunk header. */
1087*1131Smax.romanov@nginx.com                     size -= nxt_length("\r\n");
1088*1131Smax.romanov@nginx.com                 }
1089*1131Smax.romanov@nginx.com 
1090*1131Smax.romanov@nginx.com             } else {
1091*1131Smax.romanov@nginx.com                 h1p->keepalive = 0;
1092797Svbart@nginx.com             }
1093*1131Smax.romanov@nginx.com         }
1094431Sigor@sysoev.ru 
1095*1131Smax.romanov@nginx.com         if (http11 ^ h1p->keepalive) {
1096*1131Smax.romanov@nginx.com             conn = h1p->keepalive;
1097431Sigor@sysoev.ru         }
1098431Sigor@sysoev.ru     }
1099431Sigor@sysoev.ru 
1100*1131Smax.romanov@nginx.com     if (conn >= 0) {
1101431Sigor@sysoev.ru         size += connection[conn].length;
1102431Sigor@sysoev.ru     }
1103431Sigor@sysoev.ru 
1104431Sigor@sysoev.ru     nxt_list_each(field, r->resp.fields) {
1105431Sigor@sysoev.ru 
1106431Sigor@sysoev.ru         if (!field->skip) {
1107431Sigor@sysoev.ru             size += field->name_length + field->value_length;
1108703Svbart@nginx.com             size += nxt_length(": \r\n");
1109431Sigor@sysoev.ru         }
1110431Sigor@sysoev.ru 
1111431Sigor@sysoev.ru     } nxt_list_loop;
1112431Sigor@sysoev.ru 
1113*1131Smax.romanov@nginx.com     if (nxt_slow_path(n == NXT_HTTP_UPGRADE_REQUIRED)) {
1114*1131Smax.romanov@nginx.com         size += nxt_length(websocket_version);
1115*1131Smax.romanov@nginx.com     }
1116*1131Smax.romanov@nginx.com 
1117608Sigor@sysoev.ru     header = nxt_http_buf_mem(task, r, size);
1118431Sigor@sysoev.ru     if (nxt_slow_path(header == NULL)) {
1119725Sigor@sysoev.ru         nxt_h1p_request_error(task, h1p, r);
1120431Sigor@sysoev.ru         return;
1121431Sigor@sysoev.ru     }
1122431Sigor@sysoev.ru 
1123*1131Smax.romanov@nginx.com     p = nxt_cpymem(header->mem.free, status->start, status->length);
1124431Sigor@sysoev.ru 
1125431Sigor@sysoev.ru     nxt_list_each(field, r->resp.fields) {
1126431Sigor@sysoev.ru 
1127431Sigor@sysoev.ru         if (!field->skip) {
1128431Sigor@sysoev.ru             p = nxt_cpymem(p, field->name, field->name_length);
1129431Sigor@sysoev.ru             *p++ = ':'; *p++ = ' ';
1130431Sigor@sysoev.ru             p = nxt_cpymem(p, field->value, field->value_length);
1131431Sigor@sysoev.ru             *p++ = '\r'; *p++ = '\n';
1132431Sigor@sysoev.ru         }
1133431Sigor@sysoev.ru 
1134431Sigor@sysoev.ru     } nxt_list_loop;
1135431Sigor@sysoev.ru 
1136431Sigor@sysoev.ru     if (conn >= 0) {
1137431Sigor@sysoev.ru         p = nxt_cpymem(p, connection[conn].start, connection[conn].length);
1138431Sigor@sysoev.ru     }
1139431Sigor@sysoev.ru 
1140*1131Smax.romanov@nginx.com     if (h1p->websocket) {
1141*1131Smax.romanov@nginx.com         nxt_websocket_accept(p, h1p->websocket_key->value);
1142*1131Smax.romanov@nginx.com         p += NXT_WEBSOCKET_ACCEPT_SIZE;
1143*1131Smax.romanov@nginx.com 
1144*1131Smax.romanov@nginx.com         *p++ = '\r'; *p++ = '\n';
1145*1131Smax.romanov@nginx.com     }
1146*1131Smax.romanov@nginx.com 
1147*1131Smax.romanov@nginx.com     if (nxt_slow_path(n == NXT_HTTP_UPGRADE_REQUIRED)) {
1148*1131Smax.romanov@nginx.com         p = nxt_cpymem(p, websocket_version, nxt_length(websocket_version));
1149*1131Smax.romanov@nginx.com     }
1150*1131Smax.romanov@nginx.com 
1151431Sigor@sysoev.ru     if (h1p->chunked) {
1152703Svbart@nginx.com         p = nxt_cpymem(p, chunked, nxt_length(chunked));
1153450Sigor@sysoev.ru         /* Trailing CRLF will be added by the first chunk header. */
1154431Sigor@sysoev.ru 
1155431Sigor@sysoev.ru     } else {
1156431Sigor@sysoev.ru         *p++ = '\r'; *p++ = '\n';
1157431Sigor@sysoev.ru     }
1158431Sigor@sysoev.ru 
1159431Sigor@sysoev.ru     header->mem.free = p;
1160431Sigor@sysoev.ru 
1161630Svbart@nginx.com     h1p->header_size = nxt_buf_mem_used_size(&header->mem);
1162630Svbart@nginx.com 
1163431Sigor@sysoev.ru     c = h1p->conn;
1164431Sigor@sysoev.ru 
1165431Sigor@sysoev.ru     c->write = header;
1166740Sigor@sysoev.ru     c->write_state = &nxt_h1p_request_send_state;
1167431Sigor@sysoev.ru 
11681127Smax.romanov@nginx.com     nxt_conn_write(task->thread->engine, c);
1169*1131Smax.romanov@nginx.com 
1170*1131Smax.romanov@nginx.com     if (h1p->websocket) {
1171*1131Smax.romanov@nginx.com         nxt_h1p_websocket_first_frame_start(task, r, c->read);
1172*1131Smax.romanov@nginx.com     }
1173*1131Smax.romanov@nginx.com }
1174*1131Smax.romanov@nginx.com 
1175*1131Smax.romanov@nginx.com 
1176*1131Smax.romanov@nginx.com void
1177*1131Smax.romanov@nginx.com nxt_h1p_complete_buffers(nxt_task_t *task, nxt_h1proto_t *h1p)
1178*1131Smax.romanov@nginx.com {
1179*1131Smax.romanov@nginx.com     size_t         size;
1180*1131Smax.romanov@nginx.com     nxt_buf_t      *b, *in, *next;
1181*1131Smax.romanov@nginx.com     nxt_conn_t     *c;
1182*1131Smax.romanov@nginx.com 
1183*1131Smax.romanov@nginx.com     nxt_debug(task, "h1p complete buffers");
1184*1131Smax.romanov@nginx.com 
1185*1131Smax.romanov@nginx.com     b = h1p->buffers;
1186*1131Smax.romanov@nginx.com     c = h1p->conn;
1187*1131Smax.romanov@nginx.com     in = c->read;
1188*1131Smax.romanov@nginx.com 
1189*1131Smax.romanov@nginx.com     if (b != NULL) {
1190*1131Smax.romanov@nginx.com         if (in == NULL) {
1191*1131Smax.romanov@nginx.com             /* A request with large body. */
1192*1131Smax.romanov@nginx.com             in = b;
1193*1131Smax.romanov@nginx.com             c->read = in;
1194*1131Smax.romanov@nginx.com 
1195*1131Smax.romanov@nginx.com             b = in->next;
1196*1131Smax.romanov@nginx.com             in->next = NULL;
1197*1131Smax.romanov@nginx.com         }
1198*1131Smax.romanov@nginx.com 
1199*1131Smax.romanov@nginx.com         while (b != NULL) {
1200*1131Smax.romanov@nginx.com             next = b->next;
1201*1131Smax.romanov@nginx.com 
1202*1131Smax.romanov@nginx.com             nxt_work_queue_add(&task->thread->engine->fast_work_queue,
1203*1131Smax.romanov@nginx.com                                b->completion_handler, task, b, b->parent);
1204*1131Smax.romanov@nginx.com 
1205*1131Smax.romanov@nginx.com             b = next;
1206*1131Smax.romanov@nginx.com         }
1207*1131Smax.romanov@nginx.com 
1208*1131Smax.romanov@nginx.com         h1p->buffers = NULL;
1209*1131Smax.romanov@nginx.com         h1p->nbuffers = 0;
1210*1131Smax.romanov@nginx.com     }
1211*1131Smax.romanov@nginx.com 
1212*1131Smax.romanov@nginx.com     if (in != NULL) {
1213*1131Smax.romanov@nginx.com         size = nxt_buf_mem_used_size(&in->mem);
1214*1131Smax.romanov@nginx.com 
1215*1131Smax.romanov@nginx.com         if (size == 0) {
1216*1131Smax.romanov@nginx.com             nxt_mp_free(c->mem_pool, in);
1217*1131Smax.romanov@nginx.com 
1218*1131Smax.romanov@nginx.com             c->read = NULL;
1219*1131Smax.romanov@nginx.com         }
1220*1131Smax.romanov@nginx.com     }
1221431Sigor@sysoev.ru }
1222431Sigor@sysoev.ru 
1223431Sigor@sysoev.ru 
1224740Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_request_send_state
1225431Sigor@sysoev.ru     nxt_aligned(64) =
1226431Sigor@sysoev.ru {
1227740Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_sent,
1228624Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_request_error,
1229431Sigor@sysoev.ru 
1230626Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_request_send_timeout,
1231771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_request_timer_value,
1232431Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, send_timeout),
1233431Sigor@sysoev.ru     .timer_autoreset = 1,
1234431Sigor@sysoev.ru };
1235431Sigor@sysoev.ru 
1236431Sigor@sysoev.ru 
1237431Sigor@sysoev.ru static void
1238431Sigor@sysoev.ru nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
1239431Sigor@sysoev.ru {
1240725Sigor@sysoev.ru     nxt_conn_t     *c;
1241725Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
1242431Sigor@sysoev.ru 
1243431Sigor@sysoev.ru     nxt_debug(task, "h1p request send");
1244431Sigor@sysoev.ru 
1245725Sigor@sysoev.ru     h1p = r->proto.h1;
1246725Sigor@sysoev.ru     c = h1p->conn;
1247431Sigor@sysoev.ru 
1248725Sigor@sysoev.ru     if (h1p->chunked) {
1249431Sigor@sysoev.ru         out = nxt_h1p_chunk_create(task, r, out);
1250431Sigor@sysoev.ru         if (nxt_slow_path(out == NULL)) {
1251725Sigor@sysoev.ru             nxt_h1p_request_error(task, h1p, r);
1252431Sigor@sysoev.ru             return;
1253431Sigor@sysoev.ru         }
1254431Sigor@sysoev.ru     }
1255431Sigor@sysoev.ru 
1256431Sigor@sysoev.ru     if (c->write == NULL) {
1257431Sigor@sysoev.ru         c->write = out;
1258740Sigor@sysoev.ru         c->write_state = &nxt_h1p_request_send_state;
1259431Sigor@sysoev.ru 
1260431Sigor@sysoev.ru         nxt_conn_write(task->thread->engine, c);
1261431Sigor@sysoev.ru 
1262431Sigor@sysoev.ru     } else {
1263431Sigor@sysoev.ru         nxt_buf_chain_add(&c->write, out);
1264431Sigor@sysoev.ru     }
1265431Sigor@sysoev.ru }
1266431Sigor@sysoev.ru 
1267431Sigor@sysoev.ru 
1268431Sigor@sysoev.ru static nxt_buf_t *
1269431Sigor@sysoev.ru nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
1270431Sigor@sysoev.ru {
1271483Sigor@sysoev.ru     nxt_off_t          size;
1272431Sigor@sysoev.ru     nxt_buf_t          *b, **prev, *header, *tail;
1273431Sigor@sysoev.ru 
1274703Svbart@nginx.com     const size_t       chunk_size = 2 * nxt_length("\r\n") + NXT_OFF_T_HEXLEN;
1275431Sigor@sysoev.ru     static const char  tail_chunk[] = "\r\n0\r\n\r\n";
1276431Sigor@sysoev.ru 
1277431Sigor@sysoev.ru     size = 0;
1278431Sigor@sysoev.ru     prev = &out;
1279431Sigor@sysoev.ru 
1280431Sigor@sysoev.ru     for (b = out; b != NULL; b = b->next) {
1281431Sigor@sysoev.ru 
1282431Sigor@sysoev.ru         if (nxt_buf_is_last(b)) {
1283608Sigor@sysoev.ru             tail = nxt_http_buf_mem(task, r, chunk_size);
1284431Sigor@sysoev.ru             if (nxt_slow_path(tail == NULL)) {
1285431Sigor@sysoev.ru                 return NULL;
1286431Sigor@sysoev.ru             }
1287431Sigor@sysoev.ru 
1288431Sigor@sysoev.ru             *prev = tail;
1289431Sigor@sysoev.ru             tail->next = b;
1290431Sigor@sysoev.ru             /*
1291431Sigor@sysoev.ru              * The tail_chunk size with trailing zero is 8 bytes, so
1292431Sigor@sysoev.ru              * memcpy may be inlined with just single 8 byte move operation.
1293431Sigor@sysoev.ru              */
1294431Sigor@sysoev.ru             nxt_memcpy(tail->mem.free, tail_chunk, sizeof(tail_chunk));
1295703Svbart@nginx.com             tail->mem.free += nxt_length(tail_chunk);
1296431Sigor@sysoev.ru 
1297431Sigor@sysoev.ru             break;
1298431Sigor@sysoev.ru         }
1299431Sigor@sysoev.ru 
1300431Sigor@sysoev.ru         size += nxt_buf_used_size(b);
1301431Sigor@sysoev.ru         prev = &b->next;
1302431Sigor@sysoev.ru     }
1303431Sigor@sysoev.ru 
1304431Sigor@sysoev.ru     if (size == 0) {
1305431Sigor@sysoev.ru         return out;
1306431Sigor@sysoev.ru     }
1307431Sigor@sysoev.ru 
1308608Sigor@sysoev.ru     header = nxt_http_buf_mem(task, r, chunk_size);
1309431Sigor@sysoev.ru     if (nxt_slow_path(header == NULL)) {
1310431Sigor@sysoev.ru         return NULL;
1311431Sigor@sysoev.ru     }
1312431Sigor@sysoev.ru 
1313431Sigor@sysoev.ru     header->next = out;
1314431Sigor@sysoev.ru     header->mem.free = nxt_sprintf(header->mem.free, header->mem.end,
1315431Sigor@sysoev.ru                                    "\r\n%xO\r\n", size);
1316431Sigor@sysoev.ru     return header;
1317431Sigor@sysoev.ru }
1318431Sigor@sysoev.ru 
1319431Sigor@sysoev.ru 
1320630Svbart@nginx.com static nxt_off_t
1321630Svbart@nginx.com nxt_h1p_request_body_bytes_sent(nxt_task_t *task, nxt_http_proto_t proto)
1322630Svbart@nginx.com {
1323630Svbart@nginx.com     nxt_off_t      sent;
1324630Svbart@nginx.com     nxt_h1proto_t  *h1p;
1325630Svbart@nginx.com 
1326630Svbart@nginx.com     h1p = proto.h1;
1327630Svbart@nginx.com 
1328630Svbart@nginx.com     sent = h1p->conn->sent - h1p->header_size;
1329630Svbart@nginx.com 
1330630Svbart@nginx.com     return (sent > 0) ? sent : 0;
1331630Svbart@nginx.com }
1332630Svbart@nginx.com 
1333630Svbart@nginx.com 
1334431Sigor@sysoev.ru static void
1335608Sigor@sysoev.ru nxt_h1p_request_discard(nxt_task_t *task, nxt_http_request_t *r,
1336608Sigor@sysoev.ru     nxt_buf_t *last)
1337608Sigor@sysoev.ru {
1338608Sigor@sysoev.ru     nxt_buf_t         *b;
1339608Sigor@sysoev.ru     nxt_conn_t        *c;
1340608Sigor@sysoev.ru     nxt_h1proto_t     *h1p;
1341608Sigor@sysoev.ru     nxt_work_queue_t  *wq;
1342608Sigor@sysoev.ru 
1343608Sigor@sysoev.ru     nxt_debug(task, "h1p request discard");
1344608Sigor@sysoev.ru 
1345608Sigor@sysoev.ru     h1p = r->proto.h1;
1346608Sigor@sysoev.ru     h1p->keepalive = 0;
1347608Sigor@sysoev.ru 
1348608Sigor@sysoev.ru     c = h1p->conn;
1349608Sigor@sysoev.ru     b = c->write;
1350608Sigor@sysoev.ru     c->write = NULL;
1351608Sigor@sysoev.ru 
1352608Sigor@sysoev.ru     wq = &task->thread->engine->fast_work_queue;
1353608Sigor@sysoev.ru 
1354608Sigor@sysoev.ru     nxt_sendbuf_drain(task, wq, b);
1355608Sigor@sysoev.ru     nxt_sendbuf_drain(task, wq, last);
1356608Sigor@sysoev.ru }
1357608Sigor@sysoev.ru 
1358608Sigor@sysoev.ru 
1359608Sigor@sysoev.ru static void
1360624Sigor@sysoev.ru nxt_h1p_conn_request_error(nxt_task_t *task, void *obj, void *data)
1361624Sigor@sysoev.ru {
1362624Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
1363624Sigor@sysoev.ru     nxt_http_request_t  *r;
1364624Sigor@sysoev.ru 
1365624Sigor@sysoev.ru     h1p = data;
1366624Sigor@sysoev.ru 
1367624Sigor@sysoev.ru     nxt_debug(task, "h1p conn request error");
1368624Sigor@sysoev.ru 
1369624Sigor@sysoev.ru     r = h1p->request;
1370624Sigor@sysoev.ru 
1371*1131Smax.romanov@nginx.com     if (nxt_slow_path(r == NULL)) {
1372*1131Smax.romanov@nginx.com         nxt_h1p_shutdown(task, h1p->conn);
1373*1131Smax.romanov@nginx.com         return;
1374*1131Smax.romanov@nginx.com     }
1375*1131Smax.romanov@nginx.com 
1376624Sigor@sysoev.ru     if (r->fields == NULL) {
1377*1131Smax.romanov@nginx.com         (void) nxt_h1p_header_process(task, h1p, r);
1378624Sigor@sysoev.ru     }
1379624Sigor@sysoev.ru 
1380624Sigor@sysoev.ru     if (r->status == 0) {
1381624Sigor@sysoev.ru         r->status = NXT_HTTP_BAD_REQUEST;
1382624Sigor@sysoev.ru     }
1383624Sigor@sysoev.ru 
1384725Sigor@sysoev.ru     nxt_h1p_request_error(task, h1p, r);
1385624Sigor@sysoev.ru }
1386624Sigor@sysoev.ru 
1387624Sigor@sysoev.ru 
1388624Sigor@sysoev.ru static void
1389624Sigor@sysoev.ru nxt_h1p_conn_request_timeout(nxt_task_t *task, void *obj, void *data)
1390624Sigor@sysoev.ru {
1391624Sigor@sysoev.ru     nxt_conn_t          *c;
1392624Sigor@sysoev.ru     nxt_timer_t         *timer;
1393624Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
1394624Sigor@sysoev.ru     nxt_http_request_t  *r;
1395624Sigor@sysoev.ru 
1396624Sigor@sysoev.ru     timer = obj;
1397624Sigor@sysoev.ru 
1398624Sigor@sysoev.ru     nxt_debug(task, "h1p conn request timeout");
1399624Sigor@sysoev.ru 
1400624Sigor@sysoev.ru     c = nxt_read_timer_conn(timer);
1401979Sigor@sysoev.ru     c->block_read = 1;
1402626Sigor@sysoev.ru     /*
1403626Sigor@sysoev.ru      * Disable SO_LINGER off during socket closing
1404626Sigor@sysoev.ru      * to send "408 Request Timeout" error response.
1405626Sigor@sysoev.ru      */
1406626Sigor@sysoev.ru     c->socket.timedout = 0;
1407626Sigor@sysoev.ru 
1408624Sigor@sysoev.ru     h1p = c->socket.data;
1409725Sigor@sysoev.ru     h1p->keepalive = 0;
1410624Sigor@sysoev.ru     r = h1p->request;
1411624Sigor@sysoev.ru 
1412624Sigor@sysoev.ru     if (r->fields == NULL) {
1413*1131Smax.romanov@nginx.com         (void) nxt_h1p_header_process(task, h1p, r);
1414624Sigor@sysoev.ru     }
1415624Sigor@sysoev.ru 
1416626Sigor@sysoev.ru     nxt_http_request_error(task, r, NXT_HTTP_REQUEST_TIMEOUT);
1417626Sigor@sysoev.ru }
1418626Sigor@sysoev.ru 
1419624Sigor@sysoev.ru 
1420626Sigor@sysoev.ru static void
1421626Sigor@sysoev.ru nxt_h1p_conn_request_send_timeout(nxt_task_t *task, void *obj, void *data)
1422626Sigor@sysoev.ru {
1423626Sigor@sysoev.ru     nxt_conn_t     *c;
1424626Sigor@sysoev.ru     nxt_timer_t    *timer;
1425626Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
1426626Sigor@sysoev.ru 
1427626Sigor@sysoev.ru     timer = obj;
1428626Sigor@sysoev.ru 
1429626Sigor@sysoev.ru     nxt_debug(task, "h1p conn request send timeout");
1430626Sigor@sysoev.ru 
1431724Sigor@sysoev.ru     c = nxt_write_timer_conn(timer);
1432979Sigor@sysoev.ru     c->block_write = 1;
1433626Sigor@sysoev.ru     h1p = c->socket.data;
1434626Sigor@sysoev.ru 
1435725Sigor@sysoev.ru     nxt_h1p_request_error(task, h1p, h1p->request);
1436624Sigor@sysoev.ru }
1437624Sigor@sysoev.ru 
1438624Sigor@sysoev.ru 
1439*1131Smax.romanov@nginx.com nxt_msec_t
1440771Sigor@sysoev.ru nxt_h1p_conn_request_timer_value(nxt_conn_t *c, uintptr_t data)
1441683Sigor@sysoev.ru {
1442683Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
1443683Sigor@sysoev.ru 
1444683Sigor@sysoev.ru     h1p = c->socket.data;
1445683Sigor@sysoev.ru 
1446683Sigor@sysoev.ru     return nxt_value_at(nxt_msec_t, h1p->request->conf->socket_conf, data);
1447683Sigor@sysoev.ru }
1448683Sigor@sysoev.ru 
1449683Sigor@sysoev.ru 
1450624Sigor@sysoev.ru nxt_inline void
1451725Sigor@sysoev.ru nxt_h1p_request_error(nxt_task_t *task, nxt_h1proto_t *h1p,
1452725Sigor@sysoev.ru     nxt_http_request_t *r)
1453624Sigor@sysoev.ru {
1454725Sigor@sysoev.ru     h1p->keepalive = 0;
1455725Sigor@sysoev.ru 
1456725Sigor@sysoev.ru     r->state->error_handler(task, r, h1p);
1457431Sigor@sysoev.ru }
1458771Sigor@sysoev.ru 
1459771Sigor@sysoev.ru 
1460771Sigor@sysoev.ru static void
1461771Sigor@sysoev.ru nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto,
1462771Sigor@sysoev.ru     nxt_socket_conf_joint_t *joint)
1463771Sigor@sysoev.ru {
1464771Sigor@sysoev.ru     nxt_conn_t     *c;
1465771Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
1466771Sigor@sysoev.ru 
1467771Sigor@sysoev.ru     nxt_debug(task, "h1p request close");
1468771Sigor@sysoev.ru 
1469771Sigor@sysoev.ru     h1p = proto.h1;
1470771Sigor@sysoev.ru     h1p->request = NULL;
1471771Sigor@sysoev.ru 
1472771Sigor@sysoev.ru     nxt_router_conf_release(task, joint);
1473771Sigor@sysoev.ru 
1474771Sigor@sysoev.ru     c = h1p->conn;
1475771Sigor@sysoev.ru 
1476771Sigor@sysoev.ru     if (h1p->keepalive) {
1477771Sigor@sysoev.ru         nxt_h1p_keepalive(task, h1p, c);
1478771Sigor@sysoev.ru 
1479771Sigor@sysoev.ru     } else {
1480771Sigor@sysoev.ru         nxt_h1p_shutdown(task, c);
1481771Sigor@sysoev.ru     }
1482771Sigor@sysoev.ru }
1483771Sigor@sysoev.ru 
1484771Sigor@sysoev.ru 
1485771Sigor@sysoev.ru static void
1486771Sigor@sysoev.ru nxt_h1p_conn_sent(nxt_task_t *task, void *obj, void *data)
1487771Sigor@sysoev.ru {
1488771Sigor@sysoev.ru     nxt_conn_t          *c;
1489771Sigor@sysoev.ru     nxt_event_engine_t  *engine;
1490771Sigor@sysoev.ru 
1491771Sigor@sysoev.ru     c = obj;
1492771Sigor@sysoev.ru 
1493771Sigor@sysoev.ru     nxt_debug(task, "h1p conn sent");
1494771Sigor@sysoev.ru 
1495771Sigor@sysoev.ru     engine = task->thread->engine;
1496771Sigor@sysoev.ru 
1497771Sigor@sysoev.ru     c->write = nxt_sendbuf_completion(task, &engine->fast_work_queue, c->write);
1498771Sigor@sysoev.ru 
1499771Sigor@sysoev.ru     if (c->write != NULL) {
1500771Sigor@sysoev.ru         nxt_conn_write(engine, c);
1501771Sigor@sysoev.ru     }
1502771Sigor@sysoev.ru }
1503771Sigor@sysoev.ru 
1504771Sigor@sysoev.ru 
1505771Sigor@sysoev.ru static void
1506771Sigor@sysoev.ru nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data)
1507771Sigor@sysoev.ru {
1508771Sigor@sysoev.ru     nxt_conn_t  *c;
1509771Sigor@sysoev.ru 
1510771Sigor@sysoev.ru     c = obj;
1511771Sigor@sysoev.ru 
1512771Sigor@sysoev.ru     nxt_debug(task, "h1p conn close");
1513771Sigor@sysoev.ru 
1514771Sigor@sysoev.ru     nxt_h1p_shutdown(task, c);
1515771Sigor@sysoev.ru }
1516771Sigor@sysoev.ru 
1517771Sigor@sysoev.ru 
1518771Sigor@sysoev.ru static void
1519771Sigor@sysoev.ru nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data)
1520771Sigor@sysoev.ru {
1521771Sigor@sysoev.ru     nxt_conn_t  *c;
1522771Sigor@sysoev.ru 
1523771Sigor@sysoev.ru     c = obj;
1524771Sigor@sysoev.ru 
1525771Sigor@sysoev.ru     nxt_debug(task, "h1p conn error");
1526771Sigor@sysoev.ru 
1527771Sigor@sysoev.ru     nxt_h1p_shutdown(task, c);
1528771Sigor@sysoev.ru }
1529771Sigor@sysoev.ru 
1530771Sigor@sysoev.ru 
1531771Sigor@sysoev.ru static nxt_msec_t
1532771Sigor@sysoev.ru nxt_h1p_conn_timer_value(nxt_conn_t *c, uintptr_t data)
1533771Sigor@sysoev.ru {
1534771Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
1535771Sigor@sysoev.ru 
1536771Sigor@sysoev.ru     joint = c->listen->socket.data;
1537771Sigor@sysoev.ru 
1538771Sigor@sysoev.ru     return nxt_value_at(nxt_msec_t, joint->socket_conf, data);
1539771Sigor@sysoev.ru }
1540771Sigor@sysoev.ru 
1541771Sigor@sysoev.ru 
1542771Sigor@sysoev.ru static void
1543771Sigor@sysoev.ru nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c)
1544771Sigor@sysoev.ru {
1545771Sigor@sysoev.ru     size_t     size;
1546*1131Smax.romanov@nginx.com     nxt_buf_t  *in;
1547771Sigor@sysoev.ru 
1548771Sigor@sysoev.ru     nxt_debug(task, "h1p keepalive");
1549771Sigor@sysoev.ru 
1550771Sigor@sysoev.ru     if (!c->tcp_nodelay) {
1551771Sigor@sysoev.ru         nxt_conn_tcp_nodelay_on(task, c);
1552771Sigor@sysoev.ru     }
1553771Sigor@sysoev.ru 
1554*1131Smax.romanov@nginx.com     nxt_h1p_complete_buffers(task, h1p);
1555*1131Smax.romanov@nginx.com 
1556*1131Smax.romanov@nginx.com     in = c->read;
1557771Sigor@sysoev.ru 
1558771Sigor@sysoev.ru     nxt_memzero(h1p, offsetof(nxt_h1proto_t, conn));
1559771Sigor@sysoev.ru 
1560771Sigor@sysoev.ru     c->sent = 0;
1561771Sigor@sysoev.ru 
1562771Sigor@sysoev.ru     if (in == NULL) {
1563771Sigor@sysoev.ru         c->read_state = &nxt_h1p_keepalive_state;
1564771Sigor@sysoev.ru 
1565771Sigor@sysoev.ru         nxt_conn_read(task->thread->engine, c);
1566771Sigor@sysoev.ru 
1567771Sigor@sysoev.ru     } else {
1568*1131Smax.romanov@nginx.com         size = nxt_buf_mem_used_size(&in->mem);
1569*1131Smax.romanov@nginx.com 
1570771Sigor@sysoev.ru         nxt_debug(task, "h1p pipelining");
1571771Sigor@sysoev.ru 
1572771Sigor@sysoev.ru         nxt_memmove(in->mem.start, in->mem.pos, size);
1573771Sigor@sysoev.ru 
1574771Sigor@sysoev.ru         in->mem.pos = in->mem.start;
1575771Sigor@sysoev.ru         in->mem.free = in->mem.start + size;
1576771Sigor@sysoev.ru 
1577771Sigor@sysoev.ru         nxt_h1p_conn_request_init(task, c, c->socket.data);
1578771Sigor@sysoev.ru     }
1579771Sigor@sysoev.ru }
1580771Sigor@sysoev.ru 
1581771Sigor@sysoev.ru 
1582771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_keepalive_state
1583771Sigor@sysoev.ru     nxt_aligned(64) =
1584771Sigor@sysoev.ru {
1585771Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_request_init,
1586771Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
1587771Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
1588771Sigor@sysoev.ru 
1589771Sigor@sysoev.ru     .io_read_handler = nxt_h1p_idle_io_read_handler,
1590771Sigor@sysoev.ru 
1591771Sigor@sysoev.ru     .timer_handler = nxt_h1p_idle_timeout,
1592771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_timer_value,
1593771Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, idle_timeout),
1594771Sigor@sysoev.ru     .timer_autoreset = 1,
1595771Sigor@sysoev.ru };
1596771Sigor@sysoev.ru 
1597771Sigor@sysoev.ru 
1598*1131Smax.romanov@nginx.com const nxt_conn_state_t  nxt_h1p_idle_close_state
1599771Sigor@sysoev.ru     nxt_aligned(64) =
1600771Sigor@sysoev.ru {
1601771Sigor@sysoev.ru     .close_handler = nxt_h1p_idle_close,
1602771Sigor@sysoev.ru };
1603771Sigor@sysoev.ru 
1604771Sigor@sysoev.ru 
1605771Sigor@sysoev.ru static void
1606771Sigor@sysoev.ru nxt_h1p_idle_close(nxt_task_t *task, void *obj, void *data)
1607771Sigor@sysoev.ru {
1608771Sigor@sysoev.ru     nxt_conn_t  *c;
1609771Sigor@sysoev.ru 
1610771Sigor@sysoev.ru     c = obj;
1611771Sigor@sysoev.ru 
1612771Sigor@sysoev.ru     nxt_debug(task, "h1p idle close");
1613771Sigor@sysoev.ru 
1614771Sigor@sysoev.ru     nxt_h1p_idle_response(task, c);
1615771Sigor@sysoev.ru }
1616771Sigor@sysoev.ru 
1617771Sigor@sysoev.ru 
1618771Sigor@sysoev.ru static void
1619771Sigor@sysoev.ru nxt_h1p_idle_timeout(nxt_task_t *task, void *obj, void *data)
1620771Sigor@sysoev.ru {
1621771Sigor@sysoev.ru     nxt_conn_t   *c;
1622771Sigor@sysoev.ru     nxt_timer_t  *timer;
1623771Sigor@sysoev.ru 
1624771Sigor@sysoev.ru     timer = obj;
1625771Sigor@sysoev.ru 
1626771Sigor@sysoev.ru     nxt_debug(task, "h1p idle timeout");
1627771Sigor@sysoev.ru 
1628771Sigor@sysoev.ru     c = nxt_read_timer_conn(timer);
1629979Sigor@sysoev.ru     c->block_read = 1;
1630771Sigor@sysoev.ru 
1631771Sigor@sysoev.ru     nxt_h1p_idle_response(task, c);
1632771Sigor@sysoev.ru }
1633771Sigor@sysoev.ru 
1634771Sigor@sysoev.ru 
1635771Sigor@sysoev.ru #define NXT_H1P_IDLE_TIMEOUT                                                  \
1636771Sigor@sysoev.ru      "HTTP/1.1 408 Request Timeout\r\n"                                       \
1637771Sigor@sysoev.ru      "Server: " NXT_SERVER "\r\n"                                             \
1638771Sigor@sysoev.ru      "Connection: close\r\n"                                                  \
1639771Sigor@sysoev.ru      "Content-Length: 0\r\n"                                                  \
1640771Sigor@sysoev.ru      "Date: "
1641771Sigor@sysoev.ru 
1642771Sigor@sysoev.ru 
1643771Sigor@sysoev.ru static void
1644771Sigor@sysoev.ru nxt_h1p_idle_response(nxt_task_t *task, nxt_conn_t *c)
1645771Sigor@sysoev.ru {
1646771Sigor@sysoev.ru     u_char     *p;
1647771Sigor@sysoev.ru     size_t     size;
1648771Sigor@sysoev.ru     nxt_buf_t  *out, *last;
1649771Sigor@sysoev.ru 
1650771Sigor@sysoev.ru     size = nxt_length(NXT_H1P_IDLE_TIMEOUT)
1651771Sigor@sysoev.ru            + nxt_http_date_cache.size
1652771Sigor@sysoev.ru            + nxt_length("\r\n\r\n");
1653771Sigor@sysoev.ru 
1654771Sigor@sysoev.ru     out = nxt_buf_mem_alloc(c->mem_pool, size, 0);
1655771Sigor@sysoev.ru     if (nxt_slow_path(out == NULL)) {
1656771Sigor@sysoev.ru         goto fail;
1657771Sigor@sysoev.ru     }
1658771Sigor@sysoev.ru 
1659771Sigor@sysoev.ru     p = nxt_cpymem(out->mem.free, NXT_H1P_IDLE_TIMEOUT,
1660771Sigor@sysoev.ru                    nxt_length(NXT_H1P_IDLE_TIMEOUT));
1661771Sigor@sysoev.ru 
1662771Sigor@sysoev.ru     p = nxt_thread_time_string(task->thread, &nxt_http_date_cache, p);
1663771Sigor@sysoev.ru 
1664771Sigor@sysoev.ru     out->mem.free = nxt_cpymem(p, "\r\n\r\n", 4);
1665771Sigor@sysoev.ru 
1666771Sigor@sysoev.ru     last = nxt_mp_zget(c->mem_pool, NXT_BUF_SYNC_SIZE);
1667771Sigor@sysoev.ru     if (nxt_slow_path(last == NULL)) {
1668771Sigor@sysoev.ru         goto fail;
1669771Sigor@sysoev.ru     }
1670771Sigor@sysoev.ru 
1671771Sigor@sysoev.ru     out->next = last;
1672771Sigor@sysoev.ru     nxt_buf_set_sync(last);
1673771Sigor@sysoev.ru     nxt_buf_set_last(last);
1674771Sigor@sysoev.ru 
1675771Sigor@sysoev.ru     last->completion_handler = nxt_h1p_idle_response_sent;
1676771Sigor@sysoev.ru     last->parent = c;
1677771Sigor@sysoev.ru 
1678771Sigor@sysoev.ru     c->write = out;
1679771Sigor@sysoev.ru     c->write_state = &nxt_h1p_timeout_response_state;
1680771Sigor@sysoev.ru 
1681771Sigor@sysoev.ru     nxt_conn_write(task->thread->engine, c);
1682771Sigor@sysoev.ru     return;
1683771Sigor@sysoev.ru 
1684771Sigor@sysoev.ru fail:
1685771Sigor@sysoev.ru 
1686771Sigor@sysoev.ru     nxt_h1p_shutdown(task, c);
1687771Sigor@sysoev.ru }
1688771Sigor@sysoev.ru 
1689771Sigor@sysoev.ru 
1690771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_timeout_response_state
1691771Sigor@sysoev.ru     nxt_aligned(64) =
1692771Sigor@sysoev.ru {
1693771Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_sent,
1694771Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
1695771Sigor@sysoev.ru 
1696771Sigor@sysoev.ru     .timer_handler = nxt_h1p_idle_response_timeout,
1697771Sigor@sysoev.ru     .timer_value = nxt_h1p_idle_response_timer_value,
1698771Sigor@sysoev.ru };
1699771Sigor@sysoev.ru 
1700771Sigor@sysoev.ru 
1701771Sigor@sysoev.ru static void
1702771Sigor@sysoev.ru nxt_h1p_idle_response_sent(nxt_task_t *task, void *obj, void *data)
1703771Sigor@sysoev.ru {
1704771Sigor@sysoev.ru     nxt_conn_t  *c;
1705771Sigor@sysoev.ru 
1706771Sigor@sysoev.ru     c = data;
1707771Sigor@sysoev.ru 
1708771Sigor@sysoev.ru     nxt_debug(task, "h1p idle timeout response sent");
1709771Sigor@sysoev.ru 
1710771Sigor@sysoev.ru     nxt_h1p_shutdown(task, c);
1711771Sigor@sysoev.ru }
1712771Sigor@sysoev.ru 
1713771Sigor@sysoev.ru 
1714771Sigor@sysoev.ru static void
1715771Sigor@sysoev.ru nxt_h1p_idle_response_timeout(nxt_task_t *task, void *obj, void *data)
1716771Sigor@sysoev.ru {
1717771Sigor@sysoev.ru     nxt_conn_t   *c;
1718771Sigor@sysoev.ru     nxt_timer_t  *timer;
1719771Sigor@sysoev.ru 
1720771Sigor@sysoev.ru     timer = obj;
1721771Sigor@sysoev.ru 
1722771Sigor@sysoev.ru     nxt_debug(task, "h1p idle timeout response timeout");
1723771Sigor@sysoev.ru 
1724771Sigor@sysoev.ru     c = nxt_read_timer_conn(timer);
1725979Sigor@sysoev.ru     c->block_write = 1;
1726771Sigor@sysoev.ru 
1727771Sigor@sysoev.ru     nxt_h1p_shutdown(task, c);
1728771Sigor@sysoev.ru }
1729771Sigor@sysoev.ru 
1730771Sigor@sysoev.ru 
1731771Sigor@sysoev.ru static nxt_msec_t
1732771Sigor@sysoev.ru nxt_h1p_idle_response_timer_value(nxt_conn_t *c, uintptr_t data)
1733771Sigor@sysoev.ru {
1734771Sigor@sysoev.ru     return 10 * 1000;
1735771Sigor@sysoev.ru }
1736771Sigor@sysoev.ru 
1737771Sigor@sysoev.ru 
1738771Sigor@sysoev.ru static void
1739771Sigor@sysoev.ru nxt_h1p_shutdown(nxt_task_t *task, nxt_conn_t *c)
1740771Sigor@sysoev.ru {
1741*1131Smax.romanov@nginx.com     nxt_timer_t    *timer;
1742*1131Smax.romanov@nginx.com     nxt_h1proto_t  *h1p;
1743*1131Smax.romanov@nginx.com 
1744771Sigor@sysoev.ru     nxt_debug(task, "h1p shutdown");
1745771Sigor@sysoev.ru 
1746*1131Smax.romanov@nginx.com     h1p = c->socket.data;
1747*1131Smax.romanov@nginx.com 
1748*1131Smax.romanov@nginx.com     if (nxt_slow_path(h1p != NULL && h1p->websocket_timer != NULL)) {
1749*1131Smax.romanov@nginx.com         timer = &h1p->websocket_timer->timer;
1750*1131Smax.romanov@nginx.com 
1751*1131Smax.romanov@nginx.com         if (timer->handler != nxt_h1p_conn_ws_shutdown) {
1752*1131Smax.romanov@nginx.com             timer->handler = nxt_h1p_conn_ws_shutdown;
1753*1131Smax.romanov@nginx.com             nxt_timer_add(task->thread->engine, timer, 0);
1754*1131Smax.romanov@nginx.com 
1755*1131Smax.romanov@nginx.com         } else {
1756*1131Smax.romanov@nginx.com             nxt_debug(task, "h1p already scheduled ws shutdown");
1757*1131Smax.romanov@nginx.com         }
1758*1131Smax.romanov@nginx.com 
1759*1131Smax.romanov@nginx.com     } else {
1760*1131Smax.romanov@nginx.com         nxt_h1p_shutdown_(task, c);
1761*1131Smax.romanov@nginx.com     }
1762*1131Smax.romanov@nginx.com }
1763*1131Smax.romanov@nginx.com 
1764*1131Smax.romanov@nginx.com 
1765*1131Smax.romanov@nginx.com static void
1766*1131Smax.romanov@nginx.com nxt_h1p_shutdown_(nxt_task_t *task, nxt_conn_t *c)
1767*1131Smax.romanov@nginx.com {
1768771Sigor@sysoev.ru     c->socket.data = NULL;
1769771Sigor@sysoev.ru 
1770771Sigor@sysoev.ru #if (NXT_TLS)
1771771Sigor@sysoev.ru 
1772771Sigor@sysoev.ru     if (c->u.tls != NULL) {
1773771Sigor@sysoev.ru         c->write_state = &nxt_h1p_shutdown_state;
1774771Sigor@sysoev.ru 
1775771Sigor@sysoev.ru         c->io->shutdown(task, c, NULL);
1776771Sigor@sysoev.ru         return;
1777771Sigor@sysoev.ru     }
1778771Sigor@sysoev.ru 
1779771Sigor@sysoev.ru #endif
1780771Sigor@sysoev.ru 
1781771Sigor@sysoev.ru     nxt_h1p_conn_closing(task, c, NULL);
1782771Sigor@sysoev.ru }
1783771Sigor@sysoev.ru 
1784771Sigor@sysoev.ru 
1785771Sigor@sysoev.ru #if (NXT_TLS)
1786771Sigor@sysoev.ru 
1787771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_shutdown_state
1788771Sigor@sysoev.ru     nxt_aligned(64) =
1789771Sigor@sysoev.ru {
1790771Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_closing,
1791771Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_closing,
1792771Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_closing,
1793771Sigor@sysoev.ru };
1794771Sigor@sysoev.ru 
1795771Sigor@sysoev.ru #endif
1796771Sigor@sysoev.ru 
1797771Sigor@sysoev.ru 
1798771Sigor@sysoev.ru static void
1799*1131Smax.romanov@nginx.com nxt_h1p_conn_ws_shutdown(nxt_task_t *task, void *obj, void *data)
1800*1131Smax.romanov@nginx.com {
1801*1131Smax.romanov@nginx.com     nxt_timer_t                *timer;
1802*1131Smax.romanov@nginx.com     nxt_h1p_websocket_timer_t  *ws_timer;
1803*1131Smax.romanov@nginx.com 
1804*1131Smax.romanov@nginx.com     nxt_debug(task, "h1p conn ws shutdown");
1805*1131Smax.romanov@nginx.com 
1806*1131Smax.romanov@nginx.com     timer = obj;
1807*1131Smax.romanov@nginx.com     ws_timer = nxt_timer_data(timer, nxt_h1p_websocket_timer_t, timer);
1808*1131Smax.romanov@nginx.com 
1809*1131Smax.romanov@nginx.com     nxt_h1p_shutdown_(task, ws_timer->h1p->conn);
1810*1131Smax.romanov@nginx.com }
1811*1131Smax.romanov@nginx.com 
1812*1131Smax.romanov@nginx.com 
1813*1131Smax.romanov@nginx.com static void
1814771Sigor@sysoev.ru nxt_h1p_conn_closing(nxt_task_t *task, void *obj, void *data)
1815771Sigor@sysoev.ru {
1816771Sigor@sysoev.ru     nxt_conn_t  *c;
1817771Sigor@sysoev.ru 
1818771Sigor@sysoev.ru     c = obj;
1819771Sigor@sysoev.ru 
1820771Sigor@sysoev.ru     nxt_debug(task, "h1p conn closing");
1821771Sigor@sysoev.ru 
1822771Sigor@sysoev.ru     c->write_state = &nxt_h1p_close_state;
1823771Sigor@sysoev.ru 
1824771Sigor@sysoev.ru     nxt_conn_close(task->thread->engine, c);
1825771Sigor@sysoev.ru }
1826771Sigor@sysoev.ru 
1827771Sigor@sysoev.ru 
1828771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_close_state
1829771Sigor@sysoev.ru     nxt_aligned(64) =
1830771Sigor@sysoev.ru {
1831771Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_free,
1832771Sigor@sysoev.ru };
1833771Sigor@sysoev.ru 
1834771Sigor@sysoev.ru 
1835771Sigor@sysoev.ru static void
1836771Sigor@sysoev.ru nxt_h1p_conn_free(nxt_task_t *task, void *obj, void *data)
1837771Sigor@sysoev.ru {
1838771Sigor@sysoev.ru     nxt_conn_t          *c;
1839771Sigor@sysoev.ru     nxt_listen_event_t  *lev;
1840771Sigor@sysoev.ru     nxt_event_engine_t  *engine;
1841771Sigor@sysoev.ru 
1842771Sigor@sysoev.ru     c = obj;
1843771Sigor@sysoev.ru 
1844771Sigor@sysoev.ru     nxt_debug(task, "h1p conn free");
1845771Sigor@sysoev.ru 
1846771Sigor@sysoev.ru     nxt_queue_remove(&c->link);
1847771Sigor@sysoev.ru 
1848771Sigor@sysoev.ru     engine = task->thread->engine;
1849771Sigor@sysoev.ru 
1850771Sigor@sysoev.ru     nxt_sockaddr_cache_free(engine, c);
1851771Sigor@sysoev.ru 
1852771Sigor@sysoev.ru     lev = c->listen;
1853771Sigor@sysoev.ru 
1854771Sigor@sysoev.ru     nxt_conn_free(task, c);
1855771Sigor@sysoev.ru 
1856771Sigor@sysoev.ru     nxt_router_listen_event_release(&engine->task, lev, NULL);
1857771Sigor@sysoev.ru }
1858