xref: /unit/src/nxt_h1proto.c (revision 2133:46433e3cef45)
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>
91394Sigor@sysoev.ru #include <nxt_upstream.h>
101131Smax.romanov@nginx.com #include <nxt_h1proto.h>
111131Smax.romanov@nginx.com #include <nxt_websocket.h>
121131Smax.romanov@nginx.com #include <nxt_websocket_header.h>
131113Sigor@sysoev.ru 
141113Sigor@sysoev.ru 
15624Sigor@sysoev.ru /*
16771Sigor@sysoev.ru  * nxt_http_conn_ and nxt_h1p_conn_ prefixes are used for connection handlers.
17771Sigor@sysoev.ru  * nxt_h1p_idle_ prefix is used for idle connection handlers.
18624Sigor@sysoev.ru  * nxt_h1p_request_ prefix is used for HTTP/1 protocol request methods.
19624Sigor@sysoev.ru  */
20624Sigor@sysoev.ru 
21771Sigor@sysoev.ru #if (NXT_TLS)
221268Sigor@sysoev.ru static ssize_t nxt_http_idle_io_read_handler(nxt_task_t *task, nxt_conn_t *c);
23771Sigor@sysoev.ru static void nxt_http_conn_test(nxt_task_t *task, void *obj, void *data);
24771Sigor@sysoev.ru #endif
251268Sigor@sysoev.ru static ssize_t nxt_h1p_idle_io_read_handler(nxt_task_t *task, nxt_conn_t *c);
26624Sigor@sysoev.ru static void nxt_h1p_conn_proto_init(nxt_task_t *task, void *obj, void *data);
27624Sigor@sysoev.ru static void nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data);
28683Sigor@sysoev.ru static void nxt_h1p_conn_request_header_parse(nxt_task_t *task, void *obj,
29683Sigor@sysoev.ru     void *data);
301131Smax.romanov@nginx.com static nxt_int_t nxt_h1p_header_process(nxt_task_t *task, nxt_h1proto_t *h1p,
31620Svbart@nginx.com     nxt_http_request_t *r);
32625Sigor@sysoev.ru static nxt_int_t nxt_h1p_header_buffer_test(nxt_task_t *task,
33625Sigor@sysoev.ru     nxt_h1proto_t *h1p, nxt_conn_t *c, nxt_socket_conf_t *skcf);
34431Sigor@sysoev.ru static nxt_int_t nxt_h1p_connection(void *ctx, nxt_http_field_t *field,
35431Sigor@sysoev.ru     uintptr_t data);
361131Smax.romanov@nginx.com static nxt_int_t nxt_h1p_upgrade(void *ctx, nxt_http_field_t *field,
371131Smax.romanov@nginx.com     uintptr_t data);
381131Smax.romanov@nginx.com static nxt_int_t nxt_h1p_websocket_key(void *ctx, nxt_http_field_t *field,
391131Smax.romanov@nginx.com     uintptr_t data);
401131Smax.romanov@nginx.com static nxt_int_t nxt_h1p_websocket_version(void *ctx, nxt_http_field_t *field,
411131Smax.romanov@nginx.com     uintptr_t data);
42431Sigor@sysoev.ru static nxt_int_t nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field,
43431Sigor@sysoev.ru     uintptr_t data);
44431Sigor@sysoev.ru static void nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r);
45637Sigor@sysoev.ru static void nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj,
46637Sigor@sysoev.ru     void *data);
47431Sigor@sysoev.ru static void nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r);
48431Sigor@sysoev.ru static void nxt_h1p_request_header_send(nxt_task_t *task,
491270Sigor@sysoev.ru     nxt_http_request_t *r, nxt_work_handler_t body_handler, void *data);
50431Sigor@sysoev.ru static void nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r,
51431Sigor@sysoev.ru     nxt_buf_t *out);
52431Sigor@sysoev.ru static nxt_buf_t *nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r,
53431Sigor@sysoev.ru     nxt_buf_t *out);
54630Svbart@nginx.com static nxt_off_t nxt_h1p_request_body_bytes_sent(nxt_task_t *task,
55630Svbart@nginx.com     nxt_http_proto_t proto);
56608Sigor@sysoev.ru static void nxt_h1p_request_discard(nxt_task_t *task, nxt_http_request_t *r,
57608Sigor@sysoev.ru     nxt_buf_t *last);
58624Sigor@sysoev.ru static void nxt_h1p_conn_request_error(nxt_task_t *task, void *obj, void *data);
59624Sigor@sysoev.ru static void nxt_h1p_conn_request_timeout(nxt_task_t *task, void *obj,
60624Sigor@sysoev.ru     void *data);
61626Sigor@sysoev.ru static void nxt_h1p_conn_request_send_timeout(nxt_task_t *task, void *obj,
62626Sigor@sysoev.ru     void *data);
63725Sigor@sysoev.ru nxt_inline void nxt_h1p_request_error(nxt_task_t *task, nxt_h1proto_t *h1p,
64725Sigor@sysoev.ru     nxt_http_request_t *r);
65771Sigor@sysoev.ru static void nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto,
66771Sigor@sysoev.ru     nxt_socket_conf_joint_t *joint);
67771Sigor@sysoev.ru static void nxt_h1p_conn_sent(nxt_task_t *task, void *obj, void *data);
68771Sigor@sysoev.ru static void nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data);
69771Sigor@sysoev.ru static void nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data);
70771Sigor@sysoev.ru static nxt_msec_t nxt_h1p_conn_timer_value(nxt_conn_t *c, uintptr_t data);
71771Sigor@sysoev.ru static void nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p,
72771Sigor@sysoev.ru     nxt_conn_t *c);
73771Sigor@sysoev.ru static void nxt_h1p_idle_close(nxt_task_t *task, void *obj, void *data);
74771Sigor@sysoev.ru static void nxt_h1p_idle_timeout(nxt_task_t *task, void *obj, void *data);
75771Sigor@sysoev.ru static void nxt_h1p_idle_response(nxt_task_t *task, nxt_conn_t *c);
76771Sigor@sysoev.ru static void nxt_h1p_idle_response_sent(nxt_task_t *task, void *obj, void *data);
771457Sigor@sysoev.ru static void nxt_h1p_idle_response_error(nxt_task_t *task, void *obj,
781457Sigor@sysoev.ru     void *data);
79771Sigor@sysoev.ru static void nxt_h1p_idle_response_timeout(nxt_task_t *task, void *obj,
80771Sigor@sysoev.ru     void *data);
81771Sigor@sysoev.ru static nxt_msec_t nxt_h1p_idle_response_timer_value(nxt_conn_t *c,
82771Sigor@sysoev.ru     uintptr_t data);
83771Sigor@sysoev.ru static void nxt_h1p_shutdown(nxt_task_t *task, nxt_conn_t *c);
841270Sigor@sysoev.ru static void nxt_h1p_closing(nxt_task_t *task, nxt_conn_t *c);
851131Smax.romanov@nginx.com static void nxt_h1p_conn_ws_shutdown(nxt_task_t *task, void *obj, void *data);
86771Sigor@sysoev.ru static void nxt_h1p_conn_closing(nxt_task_t *task, void *obj, void *data);
87771Sigor@sysoev.ru static void nxt_h1p_conn_free(nxt_task_t *task, void *obj, void *data);
88431Sigor@sysoev.ru 
891270Sigor@sysoev.ru static void nxt_h1p_peer_connect(nxt_task_t *task, nxt_http_peer_t *peer);
901270Sigor@sysoev.ru static void nxt_h1p_peer_connected(nxt_task_t *task, void *obj, void *data);
911270Sigor@sysoev.ru static void nxt_h1p_peer_refused(nxt_task_t *task, void *obj, void *data);
921270Sigor@sysoev.ru static void nxt_h1p_peer_header_send(nxt_task_t *task, nxt_http_peer_t *peer);
931270Sigor@sysoev.ru static void nxt_h1p_peer_header_sent(nxt_task_t *task, void *obj, void *data);
941270Sigor@sysoev.ru static void nxt_h1p_peer_header_read(nxt_task_t *task, nxt_http_peer_t *peer);
951270Sigor@sysoev.ru static ssize_t nxt_h1p_peer_io_read_handler(nxt_task_t *task, nxt_conn_t *c);
961270Sigor@sysoev.ru static void nxt_h1p_peer_header_read_done(nxt_task_t *task, void *obj,
971270Sigor@sysoev.ru     void *data);
981270Sigor@sysoev.ru static nxt_int_t nxt_h1p_peer_header_parse(nxt_http_peer_t *peer,
991270Sigor@sysoev.ru     nxt_buf_mem_t *bm);
1001270Sigor@sysoev.ru static void nxt_h1p_peer_read(nxt_task_t *task, nxt_http_peer_t *peer);
1011270Sigor@sysoev.ru static void nxt_h1p_peer_read_done(nxt_task_t *task, void *obj, void *data);
1021505Sigor@sysoev.ru static void nxt_h1p_peer_body_process(nxt_task_t *task, nxt_http_peer_t *peer, nxt_buf_t *out);
1031270Sigor@sysoev.ru static void nxt_h1p_peer_closed(nxt_task_t *task, void *obj, void *data);
1041270Sigor@sysoev.ru static void nxt_h1p_peer_error(nxt_task_t *task, void *obj, void *data);
1051270Sigor@sysoev.ru static void nxt_h1p_peer_send_timeout(nxt_task_t *task, void *obj, void *data);
1061270Sigor@sysoev.ru static void nxt_h1p_peer_read_timeout(nxt_task_t *task, void *obj, void *data);
1071270Sigor@sysoev.ru static nxt_msec_t nxt_h1p_peer_timer_value(nxt_conn_t *c, uintptr_t data);
1081270Sigor@sysoev.ru static void nxt_h1p_peer_close(nxt_task_t *task, nxt_http_peer_t *peer);
1091270Sigor@sysoev.ru static void nxt_h1p_peer_free(nxt_task_t *task, void *obj, void *data);
1101505Sigor@sysoev.ru static nxt_int_t nxt_h1p_peer_transfer_encoding(void *ctx,
1111505Sigor@sysoev.ru     nxt_http_field_t *field, uintptr_t data);
1121270Sigor@sysoev.ru 
113771Sigor@sysoev.ru #if (NXT_TLS)
114771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_http_idle_state;
115771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_shutdown_state;
116771Sigor@sysoev.ru #endif
117431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_state;
118624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_header_parse_state;
119431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_body_state;
120740Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_request_send_state;
121771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_timeout_response_state;
122624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_keepalive_state;
123683Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_close_state;
1241270Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_peer_connect_state;
1251270Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_peer_header_send_state;
1261270Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_peer_header_body_send_state;
1271270Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_peer_header_read_state;
1281270Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_peer_header_read_timer_state;
1291270Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_peer_read_state;
1301270Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_peer_close_state;
131431Sigor@sysoev.ru 
132431Sigor@sysoev.ru 
1331112Sigor@sysoev.ru const nxt_http_proto_table_t  nxt_http_proto[3] = {
1341112Sigor@sysoev.ru     /* NXT_HTTP_PROTO_H1 */
1351112Sigor@sysoev.ru     {
1361112Sigor@sysoev.ru         .body_read        = nxt_h1p_request_body_read,
1371112Sigor@sysoev.ru         .local_addr       = nxt_h1p_request_local_addr,
1381112Sigor@sysoev.ru         .header_send      = nxt_h1p_request_header_send,
1391112Sigor@sysoev.ru         .send             = nxt_h1p_request_send,
1401112Sigor@sysoev.ru         .body_bytes_sent  = nxt_h1p_request_body_bytes_sent,
1411112Sigor@sysoev.ru         .discard          = nxt_h1p_request_discard,
1421112Sigor@sysoev.ru         .close            = nxt_h1p_request_close,
1431270Sigor@sysoev.ru 
1441270Sigor@sysoev.ru         .peer_connect     = nxt_h1p_peer_connect,
1451270Sigor@sysoev.ru         .peer_header_send = nxt_h1p_peer_header_send,
1461270Sigor@sysoev.ru         .peer_header_read = nxt_h1p_peer_header_read,
1471270Sigor@sysoev.ru         .peer_read        = nxt_h1p_peer_read,
1481270Sigor@sysoev.ru         .peer_close       = nxt_h1p_peer_close,
1491270Sigor@sysoev.ru 
1501131Smax.romanov@nginx.com         .ws_frame_start   = nxt_h1p_websocket_frame_start,
1511112Sigor@sysoev.ru     },
1521112Sigor@sysoev.ru     /* NXT_HTTP_PROTO_H2      */
1531112Sigor@sysoev.ru     /* NXT_HTTP_PROTO_DEVNULL */
154431Sigor@sysoev.ru };
155431Sigor@sysoev.ru 
156431Sigor@sysoev.ru 
1571270Sigor@sysoev.ru static nxt_lvlhsh_t                    nxt_h1p_fields_hash;
1581270Sigor@sysoev.ru 
1591270Sigor@sysoev.ru static nxt_http_field_proc_t           nxt_h1p_fields[] = {
160431Sigor@sysoev.ru     { nxt_string("Connection"),        &nxt_h1p_connection, 0 },
1611131Smax.romanov@nginx.com     { nxt_string("Upgrade"),           &nxt_h1p_upgrade, 0 },
1621131Smax.romanov@nginx.com     { nxt_string("Sec-WebSocket-Key"), &nxt_h1p_websocket_key, 0 },
1631131Smax.romanov@nginx.com     { nxt_string("Sec-WebSocket-Version"),
1641131Smax.romanov@nginx.com                                        &nxt_h1p_websocket_version, 0 },
165431Sigor@sysoev.ru     { nxt_string("Transfer-Encoding"), &nxt_h1p_transfer_encoding, 0 },
166431Sigor@sysoev.ru 
167431Sigor@sysoev.ru     { nxt_string("Host"),              &nxt_http_request_host, 0 },
168431Sigor@sysoev.ru     { nxt_string("Cookie"),            &nxt_http_request_field,
169431Sigor@sysoev.ru         offsetof(nxt_http_request_t, cookie) },
170630Svbart@nginx.com     { nxt_string("Referer"),           &nxt_http_request_field,
171630Svbart@nginx.com         offsetof(nxt_http_request_t, referer) },
172630Svbart@nginx.com     { nxt_string("User-Agent"),        &nxt_http_request_field,
173630Svbart@nginx.com         offsetof(nxt_http_request_t, user_agent) },
174431Sigor@sysoev.ru     { nxt_string("Content-Type"),      &nxt_http_request_field,
175431Sigor@sysoev.ru         offsetof(nxt_http_request_t, content_type) },
176431Sigor@sysoev.ru     { nxt_string("Content-Length"),    &nxt_http_request_content_length, 0 },
1771733Svbart@nginx.com     { nxt_string("Authorization"),     &nxt_http_request_field,
1781733Svbart@nginx.com         offsetof(nxt_http_request_t, authorization) },
179431Sigor@sysoev.ru };
180431Sigor@sysoev.ru 
181431Sigor@sysoev.ru 
1821270Sigor@sysoev.ru static nxt_lvlhsh_t                    nxt_h1p_peer_fields_hash;
1831270Sigor@sysoev.ru 
1841270Sigor@sysoev.ru static nxt_http_field_proc_t           nxt_h1p_peer_fields[] = {
1851270Sigor@sysoev.ru     { nxt_string("Connection"),        &nxt_http_proxy_skip, 0 },
1861505Sigor@sysoev.ru     { nxt_string("Transfer-Encoding"), &nxt_h1p_peer_transfer_encoding, 0 },
1871270Sigor@sysoev.ru     { nxt_string("Server"),            &nxt_http_proxy_skip, 0 },
1881270Sigor@sysoev.ru     { nxt_string("Date"),              &nxt_http_proxy_date, 0 },
1891270Sigor@sysoev.ru     { nxt_string("Content-Length"),    &nxt_http_proxy_content_length, 0 },
1901270Sigor@sysoev.ru };
1911270Sigor@sysoev.ru 
1921270Sigor@sysoev.ru 
193431Sigor@sysoev.ru nxt_int_t
nxt_h1p_init(nxt_task_t * task)1941459Smax.romanov@nginx.com nxt_h1p_init(nxt_task_t *task)
195431Sigor@sysoev.ru {
1961270Sigor@sysoev.ru     nxt_int_t  ret;
1971270Sigor@sysoev.ru 
1981459Smax.romanov@nginx.com     ret = nxt_http_fields_hash(&nxt_h1p_fields_hash,
1991270Sigor@sysoev.ru                                nxt_h1p_fields, nxt_nitems(nxt_h1p_fields));
2001270Sigor@sysoev.ru 
2011270Sigor@sysoev.ru     if (nxt_fast_path(ret == NXT_OK)) {
2021270Sigor@sysoev.ru         ret = nxt_http_fields_hash(&nxt_h1p_peer_fields_hash,
2031459Smax.romanov@nginx.com                                    nxt_h1p_peer_fields,
2041270Sigor@sysoev.ru                                    nxt_nitems(nxt_h1p_peer_fields));
2051270Sigor@sysoev.ru     }
2061270Sigor@sysoev.ru 
2071270Sigor@sysoev.ru     return ret;
208431Sigor@sysoev.ru }
209431Sigor@sysoev.ru 
210431Sigor@sysoev.ru 
211431Sigor@sysoev.ru void
nxt_http_conn_init(nxt_task_t * task,void * obj,void * data)212431Sigor@sysoev.ru nxt_http_conn_init(nxt_task_t *task, void *obj, void *data)
213431Sigor@sysoev.ru {
214431Sigor@sysoev.ru     nxt_conn_t               *c;
215431Sigor@sysoev.ru     nxt_socket_conf_t        *skcf;
216431Sigor@sysoev.ru     nxt_event_engine_t       *engine;
217683Sigor@sysoev.ru     nxt_listen_event_t       *lev;
218431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
219431Sigor@sysoev.ru 
220431Sigor@sysoev.ru     c = obj;
221683Sigor@sysoev.ru     lev = data;
222431Sigor@sysoev.ru 
223431Sigor@sysoev.ru     nxt_debug(task, "http conn init");
224431Sigor@sysoev.ru 
225683Sigor@sysoev.ru     joint = lev->socket.data;
226431Sigor@sysoev.ru     skcf = joint->socket_conf;
227431Sigor@sysoev.ru     c->local = skcf->sockaddr;
228431Sigor@sysoev.ru 
229431Sigor@sysoev.ru     engine = task->thread->engine;
230431Sigor@sysoev.ru     c->read_work_queue = &engine->fast_work_queue;
231431Sigor@sysoev.ru     c->write_work_queue = &engine->fast_work_queue;
232431Sigor@sysoev.ru 
233431Sigor@sysoev.ru     c->read_state = &nxt_h1p_idle_state;
234431Sigor@sysoev.ru 
235771Sigor@sysoev.ru #if (NXT_TLS)
236771Sigor@sysoev.ru     if (skcf->tls != NULL) {
237771Sigor@sysoev.ru         c->read_state = &nxt_http_idle_state;
238771Sigor@sysoev.ru     }
239771Sigor@sysoev.ru #endif
240771Sigor@sysoev.ru 
241637Sigor@sysoev.ru     nxt_conn_read(engine, c);
242431Sigor@sysoev.ru }
243431Sigor@sysoev.ru 
244431Sigor@sysoev.ru 
245771Sigor@sysoev.ru #if (NXT_TLS)
246771Sigor@sysoev.ru 
247771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_http_idle_state
248431Sigor@sysoev.ru     nxt_aligned(64) =
249431Sigor@sysoev.ru {
250771Sigor@sysoev.ru     .ready_handler = nxt_http_conn_test,
251771Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
252431Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
253431Sigor@sysoev.ru 
254771Sigor@sysoev.ru     .io_read_handler = nxt_http_idle_io_read_handler,
255431Sigor@sysoev.ru 
256771Sigor@sysoev.ru     .timer_handler = nxt_h1p_idle_timeout,
257771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_timer_value,
258635Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, idle_timeout),
259431Sigor@sysoev.ru };
260431Sigor@sysoev.ru 
261431Sigor@sysoev.ru 
262629Sigor@sysoev.ru static ssize_t
nxt_http_idle_io_read_handler(nxt_task_t * task,nxt_conn_t * c)2631268Sigor@sysoev.ru nxt_http_idle_io_read_handler(nxt_task_t *task, nxt_conn_t *c)
264629Sigor@sysoev.ru {
265629Sigor@sysoev.ru     size_t                   size;
266629Sigor@sysoev.ru     ssize_t                  n;
267629Sigor@sysoev.ru     nxt_buf_t                *b;
268629Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
269629Sigor@sysoev.ru 
270683Sigor@sysoev.ru     joint = c->listen->socket.data;
271741Sigor@sysoev.ru 
272771Sigor@sysoev.ru     if (nxt_slow_path(joint == NULL)) {
273741Sigor@sysoev.ru         /*
274741Sigor@sysoev.ru          * Listening socket had been closed while
275741Sigor@sysoev.ru          * connection was in keep-alive state.
276741Sigor@sysoev.ru          */
277741Sigor@sysoev.ru         c->read_state = &nxt_h1p_idle_close_state;
278741Sigor@sysoev.ru         return 0;
279741Sigor@sysoev.ru     }
280741Sigor@sysoev.ru 
281629Sigor@sysoev.ru     size = joint->socket_conf->header_buffer_size;
282629Sigor@sysoev.ru 
2831268Sigor@sysoev.ru     b = nxt_event_engine_buf_mem_alloc(task->thread->engine, size);
284629Sigor@sysoev.ru     if (nxt_slow_path(b == NULL)) {
285629Sigor@sysoev.ru         c->socket.error = NXT_ENOMEM;
286629Sigor@sysoev.ru         return NXT_ERROR;
287629Sigor@sysoev.ru     }
288629Sigor@sysoev.ru 
289771Sigor@sysoev.ru     /*
290771Sigor@sysoev.ru      * 1 byte is enough to distinguish between SSLv3/TLS and plain HTTP.
291771Sigor@sysoev.ru      * 11 bytes are enough to log supported SSLv3/TLS version.
292771Sigor@sysoev.ru      * 16 bytes are just for more optimized kernel copy-out operation.
293771Sigor@sysoev.ru      */
294771Sigor@sysoev.ru     n = c->io->recv(c, b->mem.pos, 16, MSG_PEEK);
295629Sigor@sysoev.ru 
296629Sigor@sysoev.ru     if (n > 0) {
297629Sigor@sysoev.ru         c->read = b;
298629Sigor@sysoev.ru 
299629Sigor@sysoev.ru     } else {
300771Sigor@sysoev.ru         c->read = NULL;
3011268Sigor@sysoev.ru         nxt_event_engine_buf_mem_free(task->thread->engine, b);
302629Sigor@sysoev.ru     }
303629Sigor@sysoev.ru 
304629Sigor@sysoev.ru     return n;
305629Sigor@sysoev.ru }
306629Sigor@sysoev.ru 
307629Sigor@sysoev.ru 
308771Sigor@sysoev.ru static void
nxt_http_conn_test(nxt_task_t * task,void * obj,void * data)309771Sigor@sysoev.ru nxt_http_conn_test(nxt_task_t *task, void *obj, void *data)
310771Sigor@sysoev.ru {
311771Sigor@sysoev.ru     u_char                   *p;
312771Sigor@sysoev.ru     nxt_buf_t                *b;
313771Sigor@sysoev.ru     nxt_conn_t               *c;
314771Sigor@sysoev.ru     nxt_tls_conf_t           *tls;
3151268Sigor@sysoev.ru     nxt_event_engine_t       *engine;
316771Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
317771Sigor@sysoev.ru 
318771Sigor@sysoev.ru     c = obj;
319771Sigor@sysoev.ru 
320771Sigor@sysoev.ru     nxt_debug(task, "h1p conn https test");
321771Sigor@sysoev.ru 
3221268Sigor@sysoev.ru     engine = task->thread->engine;
323771Sigor@sysoev.ru     b = c->read;
324771Sigor@sysoev.ru     p = b->mem.pos;
325771Sigor@sysoev.ru 
326771Sigor@sysoev.ru     c->read_state = &nxt_h1p_idle_state;
327771Sigor@sysoev.ru 
328771Sigor@sysoev.ru     if (p[0] != 0x16) {
329771Sigor@sysoev.ru         b->mem.free = b->mem.pos;
330771Sigor@sysoev.ru 
3311268Sigor@sysoev.ru         nxt_conn_read(engine, c);
332771Sigor@sysoev.ru         return;
333771Sigor@sysoev.ru     }
334771Sigor@sysoev.ru 
335771Sigor@sysoev.ru     /* SSLv3/TLS ClientHello message. */
336771Sigor@sysoev.ru 
337771Sigor@sysoev.ru #if (NXT_DEBUG)
338771Sigor@sysoev.ru     if (nxt_buf_mem_used_size(&b->mem) >= 11) {
339771Sigor@sysoev.ru         u_char      major, minor;
340771Sigor@sysoev.ru         const char  *protocol;
341771Sigor@sysoev.ru 
342771Sigor@sysoev.ru         major = p[9];
343771Sigor@sysoev.ru         minor = p[10];
344771Sigor@sysoev.ru 
345771Sigor@sysoev.ru         if (major == 3) {
346771Sigor@sysoev.ru             if (minor == 0) {
347771Sigor@sysoev.ru                 protocol = "SSLv";
348771Sigor@sysoev.ru 
349771Sigor@sysoev.ru             } else {
350771Sigor@sysoev.ru                 protocol = "TLSv";
351771Sigor@sysoev.ru                 major -= 2;
352771Sigor@sysoev.ru                 minor -= 1;
353771Sigor@sysoev.ru             }
354771Sigor@sysoev.ru 
355771Sigor@sysoev.ru             nxt_debug(task, "SSL/TLS: %s%ud.%ud", protocol, major, minor);
356771Sigor@sysoev.ru         }
357771Sigor@sysoev.ru     }
358771Sigor@sysoev.ru #endif
359771Sigor@sysoev.ru 
360771Sigor@sysoev.ru     c->read = NULL;
3611268Sigor@sysoev.ru     nxt_event_engine_buf_mem_free(engine, b);
362771Sigor@sysoev.ru 
363771Sigor@sysoev.ru     joint = c->listen->socket.data;
364771Sigor@sysoev.ru 
365771Sigor@sysoev.ru     if (nxt_slow_path(joint == NULL)) {
366771Sigor@sysoev.ru         /*
367771Sigor@sysoev.ru          * Listening socket had been closed while
368771Sigor@sysoev.ru          * connection was in keep-alive state.
369771Sigor@sysoev.ru          */
3701270Sigor@sysoev.ru         nxt_h1p_closing(task, c);
371771Sigor@sysoev.ru         return;
372771Sigor@sysoev.ru     }
373771Sigor@sysoev.ru 
374771Sigor@sysoev.ru     tls = joint->socket_conf->tls;
375771Sigor@sysoev.ru 
376771Sigor@sysoev.ru     tls->conn_init(task, tls, c);
377771Sigor@sysoev.ru }
378771Sigor@sysoev.ru 
379771Sigor@sysoev.ru #endif
380771Sigor@sysoev.ru 
381771Sigor@sysoev.ru 
382771Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_idle_state
383741Sigor@sysoev.ru     nxt_aligned(64) =
384741Sigor@sysoev.ru {
385771Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_proto_init,
386741Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_close,
387771Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_error,
388771Sigor@sysoev.ru 
389771Sigor@sysoev.ru     .io_read_handler = nxt_h1p_idle_io_read_handler,
390771Sigor@sysoev.ru 
391771Sigor@sysoev.ru     .timer_handler = nxt_h1p_idle_timeout,
392771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_timer_value,
393771Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, idle_timeout),
394771Sigor@sysoev.ru     .timer_autoreset = 1,
395741Sigor@sysoev.ru };
396741Sigor@sysoev.ru 
397741Sigor@sysoev.ru 
398771Sigor@sysoev.ru static ssize_t
nxt_h1p_idle_io_read_handler(nxt_task_t * task,nxt_conn_t * c)3991268Sigor@sysoev.ru nxt_h1p_idle_io_read_handler(nxt_task_t *task, nxt_conn_t *c)
400771Sigor@sysoev.ru {
401771Sigor@sysoev.ru     size_t                   size;
402771Sigor@sysoev.ru     ssize_t                  n;
403771Sigor@sysoev.ru     nxt_buf_t                *b;
404771Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
405771Sigor@sysoev.ru 
406771Sigor@sysoev.ru     joint = c->listen->socket.data;
407771Sigor@sysoev.ru 
408771Sigor@sysoev.ru     if (nxt_slow_path(joint == NULL)) {
409771Sigor@sysoev.ru         /*
410771Sigor@sysoev.ru          * Listening socket had been closed while
411771Sigor@sysoev.ru          * connection was in keep-alive state.
412771Sigor@sysoev.ru          */
413771Sigor@sysoev.ru         c->read_state = &nxt_h1p_idle_close_state;
414771Sigor@sysoev.ru         return 0;
415771Sigor@sysoev.ru     }
416771Sigor@sysoev.ru 
417771Sigor@sysoev.ru     b = c->read;
418771Sigor@sysoev.ru 
419771Sigor@sysoev.ru     if (b == NULL) {
420771Sigor@sysoev.ru         size = joint->socket_conf->header_buffer_size;
421771Sigor@sysoev.ru 
4221268Sigor@sysoev.ru         b = nxt_event_engine_buf_mem_alloc(task->thread->engine, size);
423771Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
424771Sigor@sysoev.ru             c->socket.error = NXT_ENOMEM;
425771Sigor@sysoev.ru             return NXT_ERROR;
426771Sigor@sysoev.ru         }
427771Sigor@sysoev.ru     }
428771Sigor@sysoev.ru 
429771Sigor@sysoev.ru     n = c->io->recvbuf(c, b);
430771Sigor@sysoev.ru 
431771Sigor@sysoev.ru     if (n > 0) {
432771Sigor@sysoev.ru         c->read = b;
433771Sigor@sysoev.ru 
434771Sigor@sysoev.ru     } else {
435771Sigor@sysoev.ru         c->read = NULL;
4361268Sigor@sysoev.ru         nxt_event_engine_buf_mem_free(task->thread->engine, b);
437771Sigor@sysoev.ru     }
438771Sigor@sysoev.ru 
439771Sigor@sysoev.ru     return n;
440771Sigor@sysoev.ru }
441771Sigor@sysoev.ru 
442771Sigor@sysoev.ru 
443431Sigor@sysoev.ru static void
nxt_h1p_conn_proto_init(nxt_task_t * task,void * obj,void * data)444624Sigor@sysoev.ru nxt_h1p_conn_proto_init(nxt_task_t *task, void *obj, void *data)
445431Sigor@sysoev.ru {
446624Sigor@sysoev.ru     nxt_conn_t     *c;
447624Sigor@sysoev.ru     nxt_h1proto_t  *h1p;
448624Sigor@sysoev.ru 
449624Sigor@sysoev.ru     c = obj;
450624Sigor@sysoev.ru 
451624Sigor@sysoev.ru     nxt_debug(task, "h1p conn proto init");
452624Sigor@sysoev.ru 
453624Sigor@sysoev.ru     h1p = nxt_mp_zget(c->mem_pool, sizeof(nxt_h1proto_t));
454624Sigor@sysoev.ru     if (nxt_slow_path(h1p == NULL)) {
4551270Sigor@sysoev.ru         nxt_h1p_closing(task, c);
456624Sigor@sysoev.ru         return;
457624Sigor@sysoev.ru     }
458624Sigor@sysoev.ru 
459624Sigor@sysoev.ru     c->socket.data = h1p;
460624Sigor@sysoev.ru     h1p->conn = c;
461624Sigor@sysoev.ru 
462624Sigor@sysoev.ru     nxt_h1p_conn_request_init(task, c, h1p);
463624Sigor@sysoev.ru }
464624Sigor@sysoev.ru 
465624Sigor@sysoev.ru 
466624Sigor@sysoev.ru static void
nxt_h1p_conn_request_init(nxt_task_t * task,void * obj,void * data)467624Sigor@sysoev.ru nxt_h1p_conn_request_init(nxt_task_t *task, void *obj, void *data)
468624Sigor@sysoev.ru {
469431Sigor@sysoev.ru     nxt_int_t                ret;
470431Sigor@sysoev.ru     nxt_conn_t               *c;
471431Sigor@sysoev.ru     nxt_h1proto_t            *h1p;
4721709Svbart@nginx.com     nxt_socket_conf_t        *skcf;
473431Sigor@sysoev.ru     nxt_http_request_t       *r;
474431Sigor@sysoev.ru     nxt_socket_conf_joint_t  *joint;
475431Sigor@sysoev.ru 
476431Sigor@sysoev.ru     c = obj;
477431Sigor@sysoev.ru     h1p = data;
478431Sigor@sysoev.ru 
479624Sigor@sysoev.ru     nxt_debug(task, "h1p conn request init");
480431Sigor@sysoev.ru 
4811457Sigor@sysoev.ru     nxt_queue_remove(&c->link);
4821457Sigor@sysoev.ru 
483624Sigor@sysoev.ru     r = nxt_http_request_create(task);
484431Sigor@sysoev.ru 
485624Sigor@sysoev.ru     if (nxt_fast_path(r != NULL)) {
486431Sigor@sysoev.ru         h1p->request = r;
487431Sigor@sysoev.ru         r->proto.h1 = h1p;
488624Sigor@sysoev.ru 
4891112Sigor@sysoev.ru         /* r->protocol = NXT_HTTP_PROTO_H1 is done by zeroing. */
490431Sigor@sysoev.ru         r->remote = c->remote;
491431Sigor@sysoev.ru 
4921110Saxel.duch@nginx.com #if (NXT_TLS)
493*2133Sz.hong@f5.com         r->tls = (c->u.tls != NULL);
4941110Saxel.duch@nginx.com #endif
4951110Saxel.duch@nginx.com 
4961265Sigor@sysoev.ru         r->task = c->task;
4971265Sigor@sysoev.ru         task = &r->task;
4981265Sigor@sysoev.ru         c->socket.task = task;
4991265Sigor@sysoev.ru         c->read_timer.task = task;
5001265Sigor@sysoev.ru         c->write_timer.task = task;
5011265Sigor@sysoev.ru 
502431Sigor@sysoev.ru         ret = nxt_http_parse_request_init(&h1p->parser, r->mem_pool);
503624Sigor@sysoev.ru 
504624Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_OK)) {
505683Sigor@sysoev.ru             joint = c->listen->socket.data;
506683Sigor@sysoev.ru             joint->count++;
507683Sigor@sysoev.ru 
508683Sigor@sysoev.ru             r->conf = joint;
5091709Svbart@nginx.com             skcf = joint->socket_conf;
5101619Smax.romanov@nginx.com 
5111619Smax.romanov@nginx.com             if (c->local == NULL) {
5121709Svbart@nginx.com                 c->local = skcf->sockaddr;
5131619Smax.romanov@nginx.com             }
514683Sigor@sysoev.ru 
5151709Svbart@nginx.com             h1p->parser.discard_unsafe_fields = skcf->discard_unsafe_fields;
5161709Svbart@nginx.com 
517683Sigor@sysoev.ru             nxt_h1p_conn_request_header_parse(task, c, h1p);
518624Sigor@sysoev.ru             return;
519431Sigor@sysoev.ru         }
520624Sigor@sysoev.ru 
521624Sigor@sysoev.ru         /*
522624Sigor@sysoev.ru          * The request is very incomplete here,
523624Sigor@sysoev.ru          * so "internal server error" useless here.
524624Sigor@sysoev.ru          */
525624Sigor@sysoev.ru         nxt_mp_release(r->mem_pool);
526431Sigor@sysoev.ru     }
527431Sigor@sysoev.ru 
5281270Sigor@sysoev.ru     nxt_h1p_closing(task, c);
529624Sigor@sysoev.ru }
530624Sigor@sysoev.ru 
531624Sigor@sysoev.ru 
532624Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_header_parse_state
533624Sigor@sysoev.ru     nxt_aligned(64) =
534624Sigor@sysoev.ru {
535683Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_request_header_parse,
536624Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_request_error,
537624Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_request_error,
538624Sigor@sysoev.ru 
539624Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_request_timeout,
540771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_request_timer_value,
541624Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, header_read_timeout),
542624Sigor@sysoev.ru };
543624Sigor@sysoev.ru 
544624Sigor@sysoev.ru 
545624Sigor@sysoev.ru static void
nxt_h1p_conn_request_header_parse(nxt_task_t * task,void * obj,void * data)546683Sigor@sysoev.ru nxt_h1p_conn_request_header_parse(nxt_task_t *task, void *obj, void *data)
547624Sigor@sysoev.ru {
548624Sigor@sysoev.ru     nxt_int_t           ret;
549624Sigor@sysoev.ru     nxt_conn_t          *c;
550624Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
551624Sigor@sysoev.ru     nxt_http_status_t   status;
552624Sigor@sysoev.ru     nxt_http_request_t  *r;
553624Sigor@sysoev.ru 
554624Sigor@sysoev.ru     c = obj;
555624Sigor@sysoev.ru     h1p = data;
556624Sigor@sysoev.ru 
557624Sigor@sysoev.ru     nxt_debug(task, "h1p conn header parse");
558624Sigor@sysoev.ru 
559431Sigor@sysoev.ru     ret = nxt_http_parse_request(&h1p->parser, &c->read->mem);
560431Sigor@sysoev.ru 
561636Sigor@sysoev.ru     ret = nxt_expect(NXT_DONE, ret);
562624Sigor@sysoev.ru 
563636Sigor@sysoev.ru     if (ret != NXT_AGAIN) {
564636Sigor@sysoev.ru         nxt_timer_disable(task->thread->engine, &c->read_timer);
565636Sigor@sysoev.ru     }
566636Sigor@sysoev.ru 
567636Sigor@sysoev.ru     r = h1p->request;
568625Sigor@sysoev.ru 
569625Sigor@sysoev.ru     switch (ret) {
570625Sigor@sysoev.ru 
571625Sigor@sysoev.ru     case NXT_DONE:
572431Sigor@sysoev.ru         /*
573431Sigor@sysoev.ru          * By default the keepalive mode is disabled in HTTP/1.0 and
574431Sigor@sysoev.ru          * enabled in HTTP/1.1.  The mode can be overridden later by
575431Sigor@sysoev.ru          * the "Connection" field processed in nxt_h1p_connection().
576431Sigor@sysoev.ru          */
577481Svbart@nginx.com         h1p->keepalive = (h1p->parser.version.s.minor != '0');
578431Sigor@sysoev.ru 
5791131Smax.romanov@nginx.com         ret = nxt_h1p_header_process(task, h1p, r);
580431Sigor@sysoev.ru 
581431Sigor@sysoev.ru         if (nxt_fast_path(ret == NXT_OK)) {
582772Sigor@sysoev.ru 
583772Sigor@sysoev.ru #if (NXT_TLS)
584772Sigor@sysoev.ru             if (c->u.tls == NULL && r->conf->socket_conf->tls != NULL) {
585772Sigor@sysoev.ru                 status = NXT_HTTP_TO_HTTPS;
586772Sigor@sysoev.ru                 goto error;
587772Sigor@sysoev.ru             }
588772Sigor@sysoev.ru #endif
589772Sigor@sysoev.ru 
590431Sigor@sysoev.ru             r->state->ready_handler(task, r, NULL);
591431Sigor@sysoev.ru             return;
592431Sigor@sysoev.ru         }
593431Sigor@sysoev.ru 
594945Svbart@nginx.com         status = ret;
595625Sigor@sysoev.ru         goto error;
596431Sigor@sysoev.ru 
597625Sigor@sysoev.ru     case NXT_AGAIN:
598683Sigor@sysoev.ru         status = nxt_h1p_header_buffer_test(task, h1p, c, r->conf->socket_conf);
599431Sigor@sysoev.ru 
600625Sigor@sysoev.ru         if (nxt_fast_path(status == NXT_OK)) {
601625Sigor@sysoev.ru             c->read_state = &nxt_h1p_header_parse_state;
602431Sigor@sysoev.ru 
603625Sigor@sysoev.ru             nxt_conn_read(task->thread->engine, c);
604625Sigor@sysoev.ru             return;
605431Sigor@sysoev.ru         }
606431Sigor@sysoev.ru 
607625Sigor@sysoev.ru         break;
608480Svbart@nginx.com 
609480Svbart@nginx.com     case NXT_HTTP_PARSE_INVALID:
610480Svbart@nginx.com         status = NXT_HTTP_BAD_REQUEST;
611480Svbart@nginx.com         break;
612431Sigor@sysoev.ru 
613482Svbart@nginx.com     case NXT_HTTP_PARSE_UNSUPPORTED_VERSION:
614482Svbart@nginx.com         status = NXT_HTTP_VERSION_NOT_SUPPORTED;
615482Svbart@nginx.com         break;
616482Svbart@nginx.com 
617480Svbart@nginx.com     case NXT_HTTP_PARSE_TOO_LARGE_FIELD:
618480Svbart@nginx.com         status = NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
619480Svbart@nginx.com         break;
620480Svbart@nginx.com 
621480Svbart@nginx.com     default:
622480Svbart@nginx.com     case NXT_ERROR:
623480Svbart@nginx.com         status = NXT_HTTP_INTERNAL_SERVER_ERROR;
624480Svbart@nginx.com         break;
625480Svbart@nginx.com     }
626480Svbart@nginx.com 
6271131Smax.romanov@nginx.com     (void) nxt_h1p_header_process(task, h1p, r);
628625Sigor@sysoev.ru 
629625Sigor@sysoev.ru error:
630625Sigor@sysoev.ru 
631960Sigor@sysoev.ru     h1p->keepalive = 0;
632960Sigor@sysoev.ru 
633480Svbart@nginx.com     nxt_http_request_error(task, r, status);
634431Sigor@sysoev.ru }
635431Sigor@sysoev.ru 
636431Sigor@sysoev.ru 
637431Sigor@sysoev.ru static nxt_int_t
nxt_h1p_header_process(nxt_task_t * task,nxt_h1proto_t * h1p,nxt_http_request_t * r)6381131Smax.romanov@nginx.com nxt_h1p_header_process(nxt_task_t *task, nxt_h1proto_t *h1p,
6391131Smax.romanov@nginx.com     nxt_http_request_t *r)
640620Svbart@nginx.com {
6411131Smax.romanov@nginx.com     u_char     *m;
6421131Smax.romanov@nginx.com     nxt_int_t  ret;
6431131Smax.romanov@nginx.com 
644620Svbart@nginx.com     r->target.start = h1p->parser.target_start;
645620Svbart@nginx.com     r->target.length = h1p->parser.target_end - h1p->parser.target_start;
646620Svbart@nginx.com 
647620Svbart@nginx.com     if (h1p->parser.version.ui64 != 0) {
648620Svbart@nginx.com         r->version.start = h1p->parser.version.str;
649620Svbart@nginx.com         r->version.length = sizeof(h1p->parser.version.str);
650620Svbart@nginx.com     }
651620Svbart@nginx.com 
652620Svbart@nginx.com     r->method = &h1p->parser.method;
653620Svbart@nginx.com     r->path = &h1p->parser.path;
654620Svbart@nginx.com     r->args = &h1p->parser.args;
655620Svbart@nginx.com 
656620Svbart@nginx.com     r->fields = h1p->parser.fields;
657620Svbart@nginx.com 
6581131Smax.romanov@nginx.com     ret = nxt_http_fields_process(r->fields, &nxt_h1p_fields_hash, r);
6591131Smax.romanov@nginx.com     if (nxt_slow_path(ret != NXT_OK)) {
6601131Smax.romanov@nginx.com         return ret;
6611131Smax.romanov@nginx.com     }
6621131Smax.romanov@nginx.com 
6631131Smax.romanov@nginx.com     if (h1p->connection_upgrade && h1p->upgrade_websocket) {
6641131Smax.romanov@nginx.com         m = h1p->parser.method.start;
6651131Smax.romanov@nginx.com 
6661131Smax.romanov@nginx.com         if (nxt_slow_path(h1p->parser.method.length != 3
6671131Smax.romanov@nginx.com                           || m[0] != 'G'
6681131Smax.romanov@nginx.com                           || m[1] != 'E'
6691131Smax.romanov@nginx.com                           || m[2] != 'T'))
6701131Smax.romanov@nginx.com         {
6711131Smax.romanov@nginx.com             nxt_log(task, NXT_LOG_INFO, "h1p upgrade: bad method");
6721131Smax.romanov@nginx.com 
6731131Smax.romanov@nginx.com             return NXT_HTTP_BAD_REQUEST;
6741131Smax.romanov@nginx.com         }
6751131Smax.romanov@nginx.com 
6761131Smax.romanov@nginx.com         if (nxt_slow_path(h1p->parser.version.s.minor != '1')) {
6771131Smax.romanov@nginx.com             nxt_log(task, NXT_LOG_INFO, "h1p upgrade: bad protocol version");
6781131Smax.romanov@nginx.com 
6791131Smax.romanov@nginx.com             return NXT_HTTP_BAD_REQUEST;
6801131Smax.romanov@nginx.com         }
6811131Smax.romanov@nginx.com 
6821131Smax.romanov@nginx.com         if (nxt_slow_path(h1p->websocket_key == NULL)) {
6831235Sigor@sysoev.ru             nxt_log(task, NXT_LOG_INFO,
6841235Sigor@sysoev.ru                     "h1p upgrade: bad or absent websocket key");
6851131Smax.romanov@nginx.com 
6861131Smax.romanov@nginx.com             return NXT_HTTP_BAD_REQUEST;
6871131Smax.romanov@nginx.com         }
6881131Smax.romanov@nginx.com 
6891131Smax.romanov@nginx.com         if (nxt_slow_path(h1p->websocket_version_ok == 0)) {
6901235Sigor@sysoev.ru             nxt_log(task, NXT_LOG_INFO,
6911235Sigor@sysoev.ru                     "h1p upgrade: bad or absent websocket version");
6921131Smax.romanov@nginx.com 
6931131Smax.romanov@nginx.com             return NXT_HTTP_UPGRADE_REQUIRED;
6941131Smax.romanov@nginx.com         }
6951131Smax.romanov@nginx.com 
6961131Smax.romanov@nginx.com         r->websocket_handshake = 1;
6971131Smax.romanov@nginx.com     }
6981131Smax.romanov@nginx.com 
6991131Smax.romanov@nginx.com     return ret;
700620Svbart@nginx.com }
701620Svbart@nginx.com 
702620Svbart@nginx.com 
703620Svbart@nginx.com static nxt_int_t
nxt_h1p_header_buffer_test(nxt_task_t * task,nxt_h1proto_t * h1p,nxt_conn_t * c,nxt_socket_conf_t * skcf)704625Sigor@sysoev.ru nxt_h1p_header_buffer_test(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c,
705625Sigor@sysoev.ru     nxt_socket_conf_t *skcf)
706625Sigor@sysoev.ru {
707625Sigor@sysoev.ru     size_t     size, used;
708625Sigor@sysoev.ru     nxt_buf_t  *in, *b;
709625Sigor@sysoev.ru 
710625Sigor@sysoev.ru     in = c->read;
711625Sigor@sysoev.ru 
712625Sigor@sysoev.ru     if (nxt_buf_mem_free_size(&in->mem) == 0) {
713625Sigor@sysoev.ru         size = skcf->large_header_buffer_size;
714625Sigor@sysoev.ru         used = nxt_buf_mem_used_size(&in->mem);
715625Sigor@sysoev.ru 
716625Sigor@sysoev.ru         if (size <= used || h1p->nbuffers >= skcf->large_header_buffers) {
717625Sigor@sysoev.ru             return NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
718625Sigor@sysoev.ru         }
719625Sigor@sysoev.ru 
720625Sigor@sysoev.ru         b = nxt_buf_mem_alloc(c->mem_pool, size, 0);
721625Sigor@sysoev.ru         if (nxt_slow_path(b == NULL)) {
722625Sigor@sysoev.ru             return NXT_HTTP_INTERNAL_SERVER_ERROR;
723625Sigor@sysoev.ru         }
724625Sigor@sysoev.ru 
725625Sigor@sysoev.ru         b->mem.free = nxt_cpymem(b->mem.pos, in->mem.pos, used);
726625Sigor@sysoev.ru 
727625Sigor@sysoev.ru         in->next = h1p->buffers;
728625Sigor@sysoev.ru         h1p->buffers = in;
729625Sigor@sysoev.ru         h1p->nbuffers++;
730625Sigor@sysoev.ru 
731625Sigor@sysoev.ru         c->read = b;
732625Sigor@sysoev.ru     }
733625Sigor@sysoev.ru 
734625Sigor@sysoev.ru     return NXT_OK;
735625Sigor@sysoev.ru }
736625Sigor@sysoev.ru 
737625Sigor@sysoev.ru 
738625Sigor@sysoev.ru static nxt_int_t
nxt_h1p_connection(void * ctx,nxt_http_field_t * field,uintptr_t data)739431Sigor@sysoev.ru nxt_h1p_connection(void *ctx, nxt_http_field_t *field, uintptr_t data)
740431Sigor@sysoev.ru {
7411234Sigor@sysoev.ru     nxt_http_request_t  *r;
742431Sigor@sysoev.ru 
743431Sigor@sysoev.ru     r = ctx;
7441270Sigor@sysoev.ru     field->hopbyhop = 1;
745431Sigor@sysoev.ru 
7461614Smax.romanov@nginx.com     if (field->value_length == 5
7471614Smax.romanov@nginx.com         && nxt_memcasecmp(field->value, "close", 5) == 0)
7481614Smax.romanov@nginx.com     {
749431Sigor@sysoev.ru         r->proto.h1->keepalive = 0;
7501131Smax.romanov@nginx.com 
7511614Smax.romanov@nginx.com     } else if (field->value_length == 10
7521614Smax.romanov@nginx.com                && nxt_memcasecmp(field->value, "keep-alive", 10) == 0)
7531614Smax.romanov@nginx.com     {
7541614Smax.romanov@nginx.com         r->proto.h1->keepalive = 1;
7551614Smax.romanov@nginx.com 
7561131Smax.romanov@nginx.com     } else if (field->value_length == 7
7571234Sigor@sysoev.ru                && nxt_memcasecmp(field->value, "upgrade", 7) == 0)
7581131Smax.romanov@nginx.com     {
7591131Smax.romanov@nginx.com         r->proto.h1->connection_upgrade = 1;
7601131Smax.romanov@nginx.com     }
7611131Smax.romanov@nginx.com 
7621131Smax.romanov@nginx.com     return NXT_OK;
7631131Smax.romanov@nginx.com }
7641131Smax.romanov@nginx.com 
7651131Smax.romanov@nginx.com 
7661131Smax.romanov@nginx.com static nxt_int_t
nxt_h1p_upgrade(void * ctx,nxt_http_field_t * field,uintptr_t data)7671131Smax.romanov@nginx.com nxt_h1p_upgrade(void *ctx, nxt_http_field_t *field, uintptr_t data)
7681131Smax.romanov@nginx.com {
7691234Sigor@sysoev.ru     nxt_http_request_t  *r;
7701131Smax.romanov@nginx.com 
7711131Smax.romanov@nginx.com     r = ctx;
7721131Smax.romanov@nginx.com 
7731131Smax.romanov@nginx.com     if (field->value_length == 9
7741234Sigor@sysoev.ru         && nxt_memcasecmp(field->value, "websocket", 9) == 0)
7751131Smax.romanov@nginx.com     {
7761131Smax.romanov@nginx.com         r->proto.h1->upgrade_websocket = 1;
7771131Smax.romanov@nginx.com     }
7781131Smax.romanov@nginx.com 
7791131Smax.romanov@nginx.com     return NXT_OK;
7801131Smax.romanov@nginx.com }
7811131Smax.romanov@nginx.com 
7821131Smax.romanov@nginx.com 
7831131Smax.romanov@nginx.com static nxt_int_t
nxt_h1p_websocket_key(void * ctx,nxt_http_field_t * field,uintptr_t data)7841131Smax.romanov@nginx.com nxt_h1p_websocket_key(void *ctx, nxt_http_field_t *field, uintptr_t data)
7851131Smax.romanov@nginx.com {
7861131Smax.romanov@nginx.com     nxt_http_request_t  *r;
7871131Smax.romanov@nginx.com 
7881131Smax.romanov@nginx.com     r = ctx;
7891131Smax.romanov@nginx.com 
7901131Smax.romanov@nginx.com     if (field->value_length == 24) {
7911131Smax.romanov@nginx.com         r->proto.h1->websocket_key = field;
7921131Smax.romanov@nginx.com     }
7931131Smax.romanov@nginx.com 
7941131Smax.romanov@nginx.com     return NXT_OK;
7951131Smax.romanov@nginx.com }
7961131Smax.romanov@nginx.com 
7971131Smax.romanov@nginx.com 
7981131Smax.romanov@nginx.com static nxt_int_t
nxt_h1p_websocket_version(void * ctx,nxt_http_field_t * field,uintptr_t data)7991131Smax.romanov@nginx.com nxt_h1p_websocket_version(void *ctx, nxt_http_field_t *field, uintptr_t data)
8001131Smax.romanov@nginx.com {
8011131Smax.romanov@nginx.com     nxt_http_request_t  *r;
8021131Smax.romanov@nginx.com 
8031131Smax.romanov@nginx.com     r = ctx;
8041131Smax.romanov@nginx.com 
8051131Smax.romanov@nginx.com     if (field->value_length == 2
8061131Smax.romanov@nginx.com         && field->value[0] == '1' && field->value[1] == '3')
8071131Smax.romanov@nginx.com     {
8081131Smax.romanov@nginx.com         r->proto.h1->websocket_version_ok = 1;
809431Sigor@sysoev.ru     }
810431Sigor@sysoev.ru 
811431Sigor@sysoev.ru     return NXT_OK;
812431Sigor@sysoev.ru }
813431Sigor@sysoev.ru 
814431Sigor@sysoev.ru 
815431Sigor@sysoev.ru static nxt_int_t
nxt_h1p_transfer_encoding(void * ctx,nxt_http_field_t * field,uintptr_t data)816431Sigor@sysoev.ru nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field, uintptr_t data)
817431Sigor@sysoev.ru {
818431Sigor@sysoev.ru     nxt_http_te_t       te;
819431Sigor@sysoev.ru     nxt_http_request_t  *r;
820431Sigor@sysoev.ru 
821431Sigor@sysoev.ru     r = ctx;
8221270Sigor@sysoev.ru     field->skip = 1;
8231270Sigor@sysoev.ru     field->hopbyhop = 1;
824431Sigor@sysoev.ru 
825431Sigor@sysoev.ru     if (field->value_length == 7
826431Sigor@sysoev.ru         && nxt_memcmp(field->value, "chunked", 7) == 0)
827431Sigor@sysoev.ru     {
828431Sigor@sysoev.ru         te = NXT_HTTP_TE_CHUNKED;
829431Sigor@sysoev.ru 
830431Sigor@sysoev.ru     } else {
831431Sigor@sysoev.ru         te = NXT_HTTP_TE_UNSUPPORTED;
832431Sigor@sysoev.ru     }
833431Sigor@sysoev.ru 
834431Sigor@sysoev.ru     r->proto.h1->transfer_encoding = te;
835431Sigor@sysoev.ru 
836431Sigor@sysoev.ru     return NXT_OK;
837431Sigor@sysoev.ru }
838431Sigor@sysoev.ru 
839431Sigor@sysoev.ru 
840431Sigor@sysoev.ru static void
nxt_h1p_request_body_read(nxt_task_t * task,nxt_http_request_t * r)841431Sigor@sysoev.ru nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r)
842431Sigor@sysoev.ru {
8431403Smax.romanov@nginx.com     size_t             size, body_length, body_buffer_size, body_rest;
8441403Smax.romanov@nginx.com     ssize_t            res;
8451403Smax.romanov@nginx.com     nxt_str_t          *tmp_path, tmp_name;
846459Sigor@sysoev.ru     nxt_buf_t          *in, *b;
847431Sigor@sysoev.ru     nxt_conn_t         *c;
848459Sigor@sysoev.ru     nxt_h1proto_t      *h1p;
849431Sigor@sysoev.ru     nxt_http_status_t  status;
850431Sigor@sysoev.ru 
8511403Smax.romanov@nginx.com     static const nxt_str_t tmp_name_pattern = nxt_string("/req-XXXXXXXX");
8521403Smax.romanov@nginx.com 
853459Sigor@sysoev.ru     h1p = r->proto.h1;
854459Sigor@sysoev.ru 
855624Sigor@sysoev.ru     nxt_debug(task, "h1p request body read %O te:%d",
856459Sigor@sysoev.ru               r->content_length_n, h1p->transfer_encoding);
857431Sigor@sysoev.ru 
858459Sigor@sysoev.ru     switch (h1p->transfer_encoding) {
859431Sigor@sysoev.ru 
860431Sigor@sysoev.ru     case NXT_HTTP_TE_CHUNKED:
861431Sigor@sysoev.ru         status = NXT_HTTP_LENGTH_REQUIRED;
862431Sigor@sysoev.ru         goto error;
863431Sigor@sysoev.ru 
864431Sigor@sysoev.ru     case NXT_HTTP_TE_UNSUPPORTED:
865431Sigor@sysoev.ru         status = NXT_HTTP_NOT_IMPLEMENTED;
866431Sigor@sysoev.ru         goto error;
867431Sigor@sysoev.ru 
868431Sigor@sysoev.ru     default:
869431Sigor@sysoev.ru     case NXT_HTTP_TE_NONE:
870431Sigor@sysoev.ru         break;
871431Sigor@sysoev.ru     }
872431Sigor@sysoev.ru 
873431Sigor@sysoev.ru     if (r->content_length_n == -1 || r->content_length_n == 0) {
874431Sigor@sysoev.ru         goto ready;
875431Sigor@sysoev.ru     }
876431Sigor@sysoev.ru 
877527Svbart@nginx.com     body_length = (size_t) r->content_length_n;
878431Sigor@sysoev.ru 
8791403Smax.romanov@nginx.com     body_buffer_size = nxt_min(r->conf->socket_conf->body_buffer_size,
8801403Smax.romanov@nginx.com                                body_length);
8811403Smax.romanov@nginx.com 
8821403Smax.romanov@nginx.com     if (body_length > body_buffer_size) {
8831403Smax.romanov@nginx.com         tmp_path = &r->conf->socket_conf->body_temp_path;
8841403Smax.romanov@nginx.com 
8851403Smax.romanov@nginx.com         tmp_name.length = tmp_path->length + tmp_name_pattern.length;
8861403Smax.romanov@nginx.com 
8871403Smax.romanov@nginx.com         b = nxt_buf_file_alloc(r->mem_pool,
8881403Smax.romanov@nginx.com                                body_buffer_size + sizeof(nxt_file_t)
8891403Smax.romanov@nginx.com                                + tmp_name.length + 1, 0);
8901403Smax.romanov@nginx.com 
8911403Smax.romanov@nginx.com     } else {
8921403Smax.romanov@nginx.com         /* This initialization required for CentOS 6, gcc 4.4.7. */
8931403Smax.romanov@nginx.com         tmp_path = NULL;
8941403Smax.romanov@nginx.com         tmp_name.length = 0;
8951403Smax.romanov@nginx.com 
8961403Smax.romanov@nginx.com         b = nxt_buf_mem_alloc(r->mem_pool, body_buffer_size, 0);
8971403Smax.romanov@nginx.com     }
8981403Smax.romanov@nginx.com 
8991403Smax.romanov@nginx.com     if (nxt_slow_path(b == NULL)) {
9001403Smax.romanov@nginx.com         status = NXT_HTTP_INTERNAL_SERVER_ERROR;
9011403Smax.romanov@nginx.com         goto error;
9021403Smax.romanov@nginx.com     }
9031403Smax.romanov@nginx.com 
9041403Smax.romanov@nginx.com     r->body = b;
9051403Smax.romanov@nginx.com 
9061403Smax.romanov@nginx.com     if (body_length > body_buffer_size) {
9071403Smax.romanov@nginx.com         tmp_name.start = nxt_pointer_to(b->mem.start, sizeof(nxt_file_t));
9081403Smax.romanov@nginx.com 
9091403Smax.romanov@nginx.com         memcpy(tmp_name.start, tmp_path->start, tmp_path->length);
9101403Smax.romanov@nginx.com         memcpy(tmp_name.start + tmp_path->length, tmp_name_pattern.start,
9111403Smax.romanov@nginx.com                tmp_name_pattern.length);
9121403Smax.romanov@nginx.com         tmp_name.start[tmp_name.length] = '\0';
9131403Smax.romanov@nginx.com 
9141403Smax.romanov@nginx.com         b->file = (nxt_file_t *) b->mem.start;
9151403Smax.romanov@nginx.com         nxt_memzero(b->file, sizeof(nxt_file_t));
9161403Smax.romanov@nginx.com         b->file->fd = -1;
9171403Smax.romanov@nginx.com         b->file->size = body_length;
9181403Smax.romanov@nginx.com 
9191403Smax.romanov@nginx.com         b->mem.start += sizeof(nxt_file_t) + tmp_name.length + 1;
9201403Smax.romanov@nginx.com         b->mem.pos = b->mem.start;
9211403Smax.romanov@nginx.com         b->mem.free = b->mem.start;
9221403Smax.romanov@nginx.com 
9231403Smax.romanov@nginx.com         b->file->fd = mkstemp((char *) tmp_name.start);
9241403Smax.romanov@nginx.com         if (nxt_slow_path(b->file->fd == -1)) {
9251534Svbart@nginx.com             nxt_alert(task, "mkstemp(%s) failed %E", tmp_name.start, nxt_errno);
9261403Smax.romanov@nginx.com 
927431Sigor@sysoev.ru             status = NXT_HTTP_INTERNAL_SERVER_ERROR;
928431Sigor@sysoev.ru             goto error;
929431Sigor@sysoev.ru         }
930431Sigor@sysoev.ru 
9311403Smax.romanov@nginx.com         nxt_debug(task, "create body tmp file \"%V\", %d",
9321403Smax.romanov@nginx.com                   &tmp_name, b->file->fd);
9331403Smax.romanov@nginx.com 
9341403Smax.romanov@nginx.com         unlink((char *) tmp_name.start);
935431Sigor@sysoev.ru     }
936431Sigor@sysoev.ru 
9371403Smax.romanov@nginx.com     body_rest = body_length;
9381403Smax.romanov@nginx.com 
939459Sigor@sysoev.ru     in = h1p->conn->read;
940431Sigor@sysoev.ru 
941459Sigor@sysoev.ru     size = nxt_buf_mem_used_size(&in->mem);
942431Sigor@sysoev.ru 
943431Sigor@sysoev.ru     if (size != 0) {
9441403Smax.romanov@nginx.com         size = nxt_min(size, body_length);
9451403Smax.romanov@nginx.com 
9461403Smax.romanov@nginx.com         if (nxt_buf_is_file(b)) {
9471403Smax.romanov@nginx.com             res = nxt_fd_write(b->file->fd, in->mem.pos, size);
9481403Smax.romanov@nginx.com             if (nxt_slow_path(res < (ssize_t) size)) {
9491403Smax.romanov@nginx.com                 status = NXT_HTTP_INTERNAL_SERVER_ERROR;
9501403Smax.romanov@nginx.com                 goto error;
9511403Smax.romanov@nginx.com             }
9521403Smax.romanov@nginx.com 
9531403Smax.romanov@nginx.com             b->file_end += size;
9541403Smax.romanov@nginx.com 
9551403Smax.romanov@nginx.com         } else {
9561403Smax.romanov@nginx.com             size = nxt_min(body_buffer_size, size);
9571403Smax.romanov@nginx.com             b->mem.free = nxt_cpymem(b->mem.free, in->mem.pos, size);
958431Sigor@sysoev.ru         }
959431Sigor@sysoev.ru 
960459Sigor@sysoev.ru         in->mem.pos += size;
9611403Smax.romanov@nginx.com         body_rest -= size;
962431Sigor@sysoev.ru     }
963431Sigor@sysoev.ru 
9641403Smax.romanov@nginx.com     nxt_debug(task, "h1p body rest: %uz", body_rest);
9651403Smax.romanov@nginx.com 
9661403Smax.romanov@nginx.com     if (body_rest != 0) {
967459Sigor@sysoev.ru         in->next = h1p->buffers;
968459Sigor@sysoev.ru         h1p->buffers = in;
9691131Smax.romanov@nginx.com         h1p->nbuffers++;
970459Sigor@sysoev.ru 
971459Sigor@sysoev.ru         c = h1p->conn;
972431Sigor@sysoev.ru         c->read = b;
973431Sigor@sysoev.ru         c->read_state = &nxt_h1p_read_body_state;
974431Sigor@sysoev.ru 
975431Sigor@sysoev.ru         nxt_conn_read(task->thread->engine, c);
976431Sigor@sysoev.ru         return;
977431Sigor@sysoev.ru     }
978431Sigor@sysoev.ru 
9791403Smax.romanov@nginx.com     if (nxt_buf_is_file(b)) {
9801403Smax.romanov@nginx.com         b->mem.start = NULL;
9811403Smax.romanov@nginx.com         b->mem.end = NULL;
9821403Smax.romanov@nginx.com         b->mem.pos = NULL;
9831403Smax.romanov@nginx.com         b->mem.free = NULL;
9841403Smax.romanov@nginx.com     }
9851403Smax.romanov@nginx.com 
986431Sigor@sysoev.ru ready:
987431Sigor@sysoev.ru 
9881155Smax.romanov@nginx.com     r->state->ready_handler(task, r, NULL);
989431Sigor@sysoev.ru 
990431Sigor@sysoev.ru     return;
991431Sigor@sysoev.ru 
992431Sigor@sysoev.ru error:
993431Sigor@sysoev.ru 
994459Sigor@sysoev.ru     h1p->keepalive = 0;
995431Sigor@sysoev.ru 
996431Sigor@sysoev.ru     nxt_http_request_error(task, r, status);
997431Sigor@sysoev.ru }
998431Sigor@sysoev.ru 
999431Sigor@sysoev.ru 
1000431Sigor@sysoev.ru static const nxt_conn_state_t  nxt_h1p_read_body_state
1001431Sigor@sysoev.ru     nxt_aligned(64) =
1002431Sigor@sysoev.ru {
1003637Sigor@sysoev.ru     .ready_handler = nxt_h1p_conn_request_body_read,
1004624Sigor@sysoev.ru     .close_handler = nxt_h1p_conn_request_error,
1005624Sigor@sysoev.ru     .error_handler = nxt_h1p_conn_request_error,
1006431Sigor@sysoev.ru 
1007624Sigor@sysoev.ru     .timer_handler = nxt_h1p_conn_request_timeout,
1008771Sigor@sysoev.ru     .timer_value = nxt_h1p_conn_request_timer_value,
1009431Sigor@sysoev.ru     .timer_data = offsetof(nxt_socket_conf_t, body_read_timeout),
1010431Sigor@sysoev.ru     .timer_autoreset = 1,
1011431Sigor@sysoev.ru };
1012431Sigor@sysoev.ru 
1013431Sigor@sysoev.ru 
1014431Sigor@sysoev.ru static void
nxt_h1p_conn_request_body_read(nxt_task_t * task,void * obj,void * data)1015637Sigor@sysoev.ru nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj, void *data)
1016431Sigor@sysoev.ru {
10171403Smax.romanov@nginx.com     size_t              size, body_rest;
10181403Smax.romanov@nginx.com     ssize_t             res;
10191403Smax.romanov@nginx.com     nxt_buf_t           *b;
1020431Sigor@sysoev.ru     nxt_conn_t          *c;
1021431Sigor@sysoev.ru     nxt_h1proto_t       *h1p;
1022431Sigor@sysoev.ru     nxt_http_request_t  *r;
1023637Sigor@sysoev.ru     nxt_event_engine_t  *engine;
1024431Sigor@sysoev.ru 
1025431Sigor@sysoev.ru     c = obj;
1026431Sigor@sysoev.ru     h1p = data;
1027431Sigor@sysoev.ru 
1028637Sigor@sysoev.ru     nxt_debug(task, "h1p conn request body read");
1029431Sigor@sysoev.ru 
10301403Smax.romanov@nginx.com     r = h1p->request;
1031431Sigor@sysoev.ru 
1032637Sigor@sysoev.ru     engine = task->thread->engine;
1033637Sigor@sysoev.ru 
10341403Smax.romanov@nginx.com     b = c->read;
10351403Smax.romanov@nginx.com 
10361403Smax.romanov@nginx.com     if (nxt_buf_is_file(b)) {
10371403Smax.romanov@nginx.com         body_rest = b->file->size - b->file_end;
10381403Smax.romanov@nginx.com 
10391403Smax.romanov@nginx.com         size = nxt_buf_mem_used_size(&b->mem);
1040