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 }, 177*1733Svbart@nginx.com { nxt_string("Authorization"), &nxt_http_request_field, 178*1733Svbart@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 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 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 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 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 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 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 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) 4931110Saxel.duch@nginx.com r->tls = c->u.tls; 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 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 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 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 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 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 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 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 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 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); 9581403Smax.romanov@nginx.com body_buffer_size -= size; 959431Sigor@sysoev.ru } 960431Sigor@sysoev.ru 961459Sigor@sysoev.ru in->mem.pos += size; 9621403Smax.romanov@nginx.com body_rest -= size; 963431Sigor@sysoev.ru } 964431Sigor@sysoev.ru 9651403Smax.romanov@nginx.com nxt_debug(task, "h1p body rest: %uz", body_rest); 9661403Smax.romanov@nginx.com 9671403Smax.romanov@nginx.com if (body_rest != 0) { 968459Sigor@sysoev.ru in->next = h1p->buffers; 969459Sigor@sysoev.ru h1p->buffers = in; 9701131Smax.romanov@nginx.com h1p->nbuffers++; 971459Sigor@sysoev.ru 972459Sigor@sysoev.ru c = h1p->conn; 973431Sigor@sysoev.ru c->read = b; 974431Sigor@sysoev.ru c->read_state = &nxt_h1p_read_body_state; 975431Sigor@sysoev.ru 976431Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 977431Sigor@sysoev.ru return; 978431Sigor@sysoev.ru } 979431Sigor@sysoev.ru 9801403Smax.romanov@nginx.com if (nxt_buf_is_file(b)) { 9811403Smax.romanov@nginx.com b->mem.start = NULL; 9821403Smax.romanov@nginx.com b->mem.end = NULL; 9831403Smax.romanov@nginx.com b->mem.pos = NULL; 9841403Smax.romanov@nginx.com b->mem.free = NULL; 9851403Smax.romanov@nginx.com } 9861403Smax.romanov@nginx.com 987431Sigor@sysoev.ru ready: 988431Sigor@sysoev.ru 9891155Smax.romanov@nginx.com r->state->ready_handler(task, r, NULL); 990431Sigor@sysoev.ru 991431Sigor@sysoev.ru return; 992431Sigor@sysoev.ru 993431Sigor@sysoev.ru error: 994431Sigor@sysoev.ru 995459Sigor@sysoev.ru h1p->keepalive = 0; 996431Sigor@sysoev.ru 997431Sigor@sysoev.ru nxt_http_request_error(task, r, status); 998431Sigor@sysoev.ru } 999431Sigor@sysoev.ru 1000431Sigor@sysoev.ru 1001431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_read_body_state 1002431Sigor@sysoev.ru nxt_aligned(64) = 1003431Sigor@sysoev.ru { 1004637Sigor@sysoev.ru .ready_handler = nxt_h1p_conn_request_body_read, 1005624Sigor@sysoev.ru .close_handler = nxt_h1p_conn_request_error, 1006624Sigor@sysoev.ru .error_handler = nxt_h1p_conn_request_error, 1007431Sigor@sysoev.ru 1008624Sigor@sysoev.ru .timer_handler = nxt_h1p_conn_request_timeout, 1009771Sigor@sysoev.ru .timer_value = nxt_h1p_conn_request_timer_value, 1010431Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, body_read_timeout), 1011431Sigor@sysoev.ru .timer_autoreset = 1, 1012431Sigor@sysoev.ru }; 1013431Sigor@sysoev.ru 1014431Sigor@sysoev.ru 1015431Sigor@sysoev.ru static void 1016637Sigor@sysoev.ru nxt_h1p_conn_request_body_read(nxt_task_t *task, void *obj, void *data) 1017431Sigor@sysoev.ru { 10181403Smax.romanov@nginx.com size_t size, body_rest; 10191403Smax.romanov@nginx.com ssize_t res; 10201403Smax.romanov@nginx.com nxt_buf_t *b; 1021431Sigor@sysoev.ru nxt_conn_t *c; 1022431Sigor@sysoev.ru nxt_h1proto_t *h1p; 1023431Sigor@sysoev.ru nxt_http_request_t *r; 1024637Sigor@sysoev.ru nxt_event_engine_t *engine; 1025431Sigor@sysoev.ru 1026431Sigor@sysoev.ru c = obj; 1027431Sigor@sysoev.ru h1p = data; 1028431Sigor@sysoev.ru 1029637Sigor@sysoev.ru nxt_debug(task, "h1p conn request body read"); 1030431Sigor@sysoev.ru 10311403Smax.romanov@nginx.com r = h1p->request; 1032431Sigor@sysoev.ru 1033637Sigor@sysoev.ru engine = task->thread->engine; 1034637Sigor@sysoev.ru 10351403Smax.romanov@nginx.com b = c->read; 10361403Smax.romanov@nginx.com 10371403Smax.romanov@nginx.com if (nxt_buf_is_file(b)) { 10381403Smax.romanov@nginx.com body_rest = b->file->size - b->file_end; 10391403Smax.romanov@nginx.com 10401403Smax.romanov@nginx.com size = nxt_buf_mem_used_size(&b->mem); 10411403Smax.romanov@nginx.com size = nxt_min(size, body_rest); 10421403Smax.romanov@nginx.com 10431403Smax.romanov@nginx.com res = nxt_fd_write(b->file->fd, b->mem.pos, size); 10441403Smax.romanov@nginx.com if (nxt_slow_path(res < (ssize_t) size)) { 10451403Smax.romanov@nginx.com nxt_h1p_request_error(task, h1p, r); 10461403Smax.romanov@nginx.com return; 10471403Smax.romanov@nginx.com } 10481403Smax.romanov@nginx.com 10491403Smax.romanov@nginx.com b->file_end += size; 10501403Smax.romanov@nginx.com body_rest -= res; 10511403Smax.romanov@nginx.com 10521403Smax.romanov@nginx.com b->mem.pos += size; 10531403Smax.romanov@nginx.com 10541403Smax.romanov@nginx.com if (b->mem.pos == b->mem.free) { 10551403Smax.romanov@nginx.com if (body_rest >= (size_t) nxt_buf_mem_size(&b->mem)) { 10561403Smax.romanov@nginx.com b->mem.free = b->mem.start; 10571403Smax.romanov@nginx.com 10581403Smax.romanov@nginx.com } else { 10591403Smax.romanov@nginx.com /* This required to avoid reading next request. */ 10601403Smax.romanov@nginx.com b->mem.free = b->mem.end - body_rest; 10611403Smax.romanov@nginx.com } 10621403Smax.romanov@nginx.com 10631403Smax.romanov@nginx.com b->mem.pos = b->mem.free; 10641403Smax.romanov@nginx.com } 10651403Smax.romanov@nginx.com 10661403Smax.romanov@nginx.com } else { 10671403Smax.romanov@nginx.com body_rest = nxt_buf_mem_free_size(&c->read->mem); 10681403Smax.romanov@nginx.com } 10691403Smax.romanov@nginx.com 10701403Smax.romanov@nginx.com nxt_debug(task, "h1p body rest: %uz", body_rest); 10711403Smax.romanov@nginx.com 10721403Smax.romanov@nginx.com if (body_rest != 0) { 1073637Sigor@sysoev.ru nxt_conn_read(engine, c); 1074431Sigor@sysoev.ru 1075431Sigor@sysoev.ru } else { 10761403Smax.romanov@nginx.com if (nxt_buf_is_file(b)) { 10771403Smax.romanov@nginx.com b->mem.start = NULL; 10781403Smax.romanov@nginx.com b->mem.end = NULL; 10791403Smax.romanov@nginx.com b->mem.pos = NULL; 10801403Smax.romanov@nginx.com b->mem.free = NULL; 10811403Smax.romanov@nginx.com } 10821403Smax.romanov@nginx.com 1083637Sigor@sysoev.ru c->read = NULL; 1084637Sigor@sysoev.ru 10851155Smax.romanov@nginx.com r->state->ready_handler(task, r, NULL); 1086431Sigor@sysoev.ru } 1087431Sigor@sysoev.ru } 1088431Sigor@sysoev.ru 1089431Sigor@sysoev.ru 1090431Sigor@sysoev.ru static void 1091431Sigor@sysoev.ru nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r) 1092431Sigor@sysoev.ru { 1093431Sigor@sysoev.ru r->local = nxt_conn_local_addr(task, r->proto.h1->conn); 1094431Sigor@sysoev.ru } 1095431Sigor@sysoev.ru 1096431Sigor@sysoev.ru 10971131Smax.romanov@nginx.com #define NXT_HTTP_LAST_INFORMATIONAL \ 10981131Smax.romanov@nginx.com (NXT_HTTP_CONTINUE + nxt_nitems(nxt_http_informational) - 1) 10991131Smax.romanov@nginx.com 11001131Smax.romanov@nginx.com static const nxt_str_t nxt_http_informational[] = { 11011131Smax.romanov@nginx.com nxt_string("HTTP/1.1 100 Continue\r\n"), 11021131Smax.romanov@nginx.com nxt_string("HTTP/1.1 101 Switching Protocols\r\n"), 11031131Smax.romanov@nginx.com }; 11041131Smax.romanov@nginx.com 11051131Smax.romanov@nginx.com 1106431Sigor@sysoev.ru #define NXT_HTTP_LAST_SUCCESS \ 1107431Sigor@sysoev.ru (NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1) 1108431Sigor@sysoev.ru 1109431Sigor@sysoev.ru static const nxt_str_t nxt_http_success[] = { 1110431Sigor@sysoev.ru nxt_string("HTTP/1.1 200 OK\r\n"), 1111431Sigor@sysoev.ru nxt_string("HTTP/1.1 201 Created\r\n"), 1112431Sigor@sysoev.ru nxt_string("HTTP/1.1 202 Accepted\r\n"), 1113431Sigor@sysoev.ru nxt_string("HTTP/1.1 203 Non-Authoritative Information\r\n"), 1114431Sigor@sysoev.ru nxt_string("HTTP/1.1 204 No Content\r\n"), 1115431Sigor@sysoev.ru nxt_string("HTTP/1.1 205 Reset Content\r\n"), 1116431Sigor@sysoev.ru nxt_string("HTTP/1.1 206 Partial Content\r\n"), 1117431Sigor@sysoev.ru }; 1118431Sigor@sysoev.ru 1119431Sigor@sysoev.ru 1120431Sigor@sysoev.ru #define NXT_HTTP_LAST_REDIRECTION \ 1121431Sigor@sysoev.ru (NXT_HTTP_MULTIPLE_CHOICES + nxt_nitems(nxt_http_redirection) - 1) 1122431Sigor@sysoev.ru 1123431Sigor@sysoev.ru static const nxt_str_t nxt_http_redirection[] = { 1124431Sigor@sysoev.ru nxt_string("HTTP/1.1 300 Multiple Choices\r\n"), 1125431Sigor@sysoev.ru nxt_string("HTTP/1.1 301 Moved Permanently\r\n"), 1126431Sigor@sysoev.ru nxt_string("HTTP/1.1 302 Found\r\n"), 1127431Sigor@sysoev.ru nxt_string("HTTP/1.1 303 See Other\r\n"), 1128431Sigor@sysoev.ru nxt_string("HTTP/1.1 304 Not Modified\r\n"), 11291432Svbart@nginx.com nxt_string("HTTP/1.1 307 Temporary Redirect\r\n"), 11301432Svbart@nginx.com nxt_string("HTTP/1.1 308 Permanent Redirect\r\n"), 1131431Sigor@sysoev.ru }; 1132431Sigor@sysoev.ru 1133431Sigor@sysoev.ru 1134431Sigor@sysoev.ru #define NXT_HTTP_LAST_CLIENT_ERROR \ 1135431Sigor@sysoev.ru (NXT_HTTP_BAD_REQUEST + nxt_nitems(nxt_http_client_error) - 1) 1136431Sigor@sysoev.ru 1137431Sigor@sysoev.ru static const nxt_str_t nxt_http_client_error[] = { 1138431Sigor@sysoev.ru nxt_string("HTTP/1.1 400 Bad Request\r\n"), 1139431Sigor@sysoev.ru nxt_string("HTTP/1.1 401 Unauthorized\r\n"), 1140431Sigor@sysoev.ru nxt_string("HTTP/1.1 402 Payment Required\r\n"), 1141431Sigor@sysoev.ru nxt_string("HTTP/1.1 403 Forbidden\r\n"), 1142431Sigor@sysoev.ru nxt_string("HTTP/1.1 404 Not Found\r\n"), 1143431Sigor@sysoev.ru nxt_string("HTTP/1.1 405 Method Not Allowed\r\n"), 1144431Sigor@sysoev.ru nxt_string("HTTP/1.1 406 Not Acceptable\r\n"), 1145431Sigor@sysoev.ru nxt_string("HTTP/1.1 407 Proxy Authentication Required\r\n"), 1146431Sigor@sysoev.ru nxt_string("HTTP/1.1 408 Request Timeout\r\n"), 1147431Sigor@sysoev.ru nxt_string("HTTP/1.1 409 Conflict\r\n"), 1148431Sigor@sysoev.ru nxt_string("HTTP/1.1 410 Gone\r\n"), 1149431Sigor@sysoev.ru nxt_string("HTTP/1.1 411 Length Required\r\n"), 1150431Sigor@sysoev.ru nxt_string("HTTP/1.1 412 Precondition Failed\r\n"), 1151431Sigor@sysoev.ru nxt_string("HTTP/1.1 413 Payload Too Large\r\n"), 1152431Sigor@sysoev.ru nxt_string("HTTP/1.1 414 URI Too Long\r\n"), 1153431Sigor@sysoev.ru nxt_string("HTTP/1.1 415 Unsupported Media Type\r\n"), 1154431Sigor@sysoev.ru nxt_string("HTTP/1.1 416 Range Not Satisfiable\r\n"), 1155431Sigor@sysoev.ru nxt_string("HTTP/1.1 417 Expectation Failed\r\n"), 11561732Svbart@nginx.com nxt_string("HTTP/1.1 418 I'm a teapot\r\n"), 11571732Svbart@nginx.com nxt_string("HTTP/1.1 419 \r\n"), 11581732Svbart@nginx.com nxt_string("HTTP/1.1 420 \r\n"), 11591732Svbart@nginx.com nxt_string("HTTP/1.1 421 Misdirected Request\r\n"), 11601732Svbart@nginx.com nxt_string("HTTP/1.1 422 Unprocessable Entity\r\n"), 11611732Svbart@nginx.com nxt_string("HTTP/1.1 423 Locked\r\n"), 11621732Svbart@nginx.com nxt_string("HTTP/1.1 424 Failed Dependency\r\n"), 11631732Svbart@nginx.com nxt_string("HTTP/1.1 425 \r\n"), 11641131Smax.romanov@nginx.com nxt_string("HTTP/1.1 426 Upgrade Required\r\n"), 11651732Svbart@nginx.com nxt_string("HTTP/1.1 427 \r\n"), 11661732Svbart@nginx.com nxt_string("HTTP/1.1 428 \r\n"), 11671732Svbart@nginx.com nxt_string("HTTP/1.1 429 \r\n"), 11681732Svbart@nginx.com nxt_string("HTTP/1.1 430 \r\n"), 1169431Sigor@sysoev.ru nxt_string("HTTP/1.1 431 Request Header Fields Too Large\r\n"), 1170431Sigor@sysoev.ru }; 1171431Sigor@sysoev.ru 1172431Sigor@sysoev.ru 1173772Sigor@sysoev.ru #define NXT_HTTP_LAST_NGINX_ERROR \ 1174772Sigor@sysoev.ru (NXT_HTTP_TO_HTTPS + nxt_nitems(nxt_http_nginx_error) - 1) 1175772Sigor@sysoev.ru 1176772Sigor@sysoev.ru static const nxt_str_t nxt_http_nginx_error[] = { 1177772Sigor@sysoev.ru nxt_string("HTTP/1.1 400 " 1178772Sigor@sysoev.ru "The plain HTTP request was sent to HTTPS port\r\n"), 1179772Sigor@sysoev.ru }; 1180772Sigor@sysoev.ru 1181772Sigor@sysoev.ru 1182431Sigor@sysoev.ru #define NXT_HTTP_LAST_SERVER_ERROR \ 1183431Sigor@sysoev.ru (NXT_HTTP_INTERNAL_SERVER_ERROR + nxt_nitems(nxt_http_server_error) - 1) 1184431Sigor@sysoev.ru 1185431Sigor@sysoev.ru static const nxt_str_t nxt_http_server_error[] = { 1186431Sigor@sysoev.ru nxt_string("HTTP/1.1 500 Internal Server Error\r\n"), 1187431Sigor@sysoev.ru nxt_string("HTTP/1.1 501 Not Implemented\r\n"), 1188431Sigor@sysoev.ru nxt_string("HTTP/1.1 502 Bad Gateway\r\n"), 1189431Sigor@sysoev.ru nxt_string("HTTP/1.1 503 Service Unavailable\r\n"), 1190431Sigor@sysoev.ru nxt_string("HTTP/1.1 504 Gateway Timeout\r\n"), 1191482Svbart@nginx.com nxt_string("HTTP/1.1 505 HTTP Version Not Supported\r\n"), 1192431Sigor@sysoev.ru }; 1193431Sigor@sysoev.ru 1194431Sigor@sysoev.ru 11951732Svbart@nginx.com #define UNKNOWN_STATUS_LENGTH nxt_length("HTTP/1.1 999 \r\n") 1196431Sigor@sysoev.ru 1197431Sigor@sysoev.ru static void 11981148Sigor@sysoev.ru nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r, 11991270Sigor@sysoev.ru nxt_work_handler_t body_handler, void *data) 1200431Sigor@sysoev.ru { 1201431Sigor@sysoev.ru u_char *p; 1202431Sigor@sysoev.ru size_t size; 1203431Sigor@sysoev.ru nxt_buf_t *header; 1204431Sigor@sysoev.ru nxt_str_t unknown_status; 1205431Sigor@sysoev.ru nxt_int_t conn; 1206431Sigor@sysoev.ru nxt_uint_t n; 1207431Sigor@sysoev.ru nxt_bool_t http11; 1208431Sigor@sysoev.ru nxt_conn_t *c; 1209431Sigor@sysoev.ru nxt_h1proto_t *h1p; 1210431Sigor@sysoev.ru const nxt_str_t *status; 1211431Sigor@sysoev.ru nxt_http_field_t *field; 1212431Sigor@sysoev.ru u_char buf[UNKNOWN_STATUS_LENGTH]; 1213431Sigor@sysoev.ru 1214431Sigor@sysoev.ru static const char chunked[] = "Transfer-Encoding: chunked\r\n"; 12151131Smax.romanov@nginx.com static const char websocket_version[] = "Sec-WebSocket-Version: 13\r\n"; 1216431Sigor@sysoev.ru 12171131Smax.romanov@nginx.com static const nxt_str_t connection[3] = { 1218431Sigor@sysoev.ru nxt_string("Connection: close\r\n"), 1219431Sigor@sysoev.ru nxt_string("Connection: keep-alive\r\n"), 12201131Smax.romanov@nginx.com nxt_string("Upgrade: websocket\r\n" 12211131Smax.romanov@nginx.com "Connection: Upgrade\r\n" 12221131Smax.romanov@nginx.com "Sec-WebSocket-Accept: "), 1223431Sigor@sysoev.ru }; 1224431Sigor@sysoev.ru 1225431Sigor@sysoev.ru nxt_debug(task, "h1p request header send"); 1226431Sigor@sysoev.ru 1227431Sigor@sysoev.ru r->header_sent = 1; 1228431Sigor@sysoev.ru h1p = r->proto.h1; 1229431Sigor@sysoev.ru n = r->status; 1230431Sigor@sysoev.ru 12311131Smax.romanov@nginx.com if (n >= NXT_HTTP_CONTINUE && n <= NXT_HTTP_LAST_INFORMATIONAL) { 12321131Smax.romanov@nginx.com status = &nxt_http_informational[n - NXT_HTTP_CONTINUE]; 12331131Smax.romanov@nginx.com 12341131Smax.romanov@nginx.com } else if (n >= NXT_HTTP_OK && n <= NXT_HTTP_LAST_SUCCESS) { 1235431Sigor@sysoev.ru status = &nxt_http_success[n - NXT_HTTP_OK]; 1236431Sigor@sysoev.ru 1237431Sigor@sysoev.ru } else if (n >= NXT_HTTP_MULTIPLE_CHOICES 1238431Sigor@sysoev.ru && n <= NXT_HTTP_LAST_REDIRECTION) 1239431Sigor@sysoev.ru { 1240431Sigor@sysoev.ru status = &nxt_http_redirection[n - NXT_HTTP_MULTIPLE_CHOICES]; 1241431Sigor@sysoev.ru 1242431Sigor@sysoev.ru } else if (n >= NXT_HTTP_BAD_REQUEST && n <= NXT_HTTP_LAST_CLIENT_ERROR) { 1243431Sigor@sysoev.ru status = &nxt_http_client_error[n - NXT_HTTP_BAD_REQUEST]; 1244431Sigor@sysoev.ru 1245772Sigor@sysoev.ru } else if (n >= NXT_HTTP_TO_HTTPS && n <= NXT_HTTP_LAST_NGINX_ERROR) { 1246772Sigor@sysoev.ru status = &nxt_http_nginx_error[n - NXT_HTTP_TO_HTTPS]; 1247772Sigor@sysoev.ru 1248431Sigor@sysoev.ru } else if (n >= NXT_HTTP_INTERNAL_SERVER_ERROR 1249431Sigor@sysoev.ru && n <= NXT_HTTP_LAST_SERVER_ERROR) 1250431Sigor@sysoev.ru { 1251431Sigor@sysoev.ru status = &nxt_http_server_error[n - NXT_HTTP_INTERNAL_SERVER_ERROR]; 1252431Sigor@sysoev.ru 12531732Svbart@nginx.com } else if (n <= NXT_HTTP_STATUS_MAX) { 12541732Svbart@nginx.com (void) nxt_sprintf(buf, buf + UNKNOWN_STATUS_LENGTH, 12551732Svbart@nginx.com "HTTP/1.1 %03d \r\n", n); 12561732Svbart@nginx.com 12571732Svbart@nginx.com unknown_status.length = UNKNOWN_STATUS_LENGTH; 1258431Sigor@sysoev.ru unknown_status.start = buf; 1259431Sigor@sysoev.ru status = &unknown_status; 12601732Svbart@nginx.com 12611732Svbart@nginx.com } else { 12621732Svbart@nginx.com status = &nxt_http_server_error[0]; 1263431Sigor@sysoev.ru } 1264431Sigor@sysoev.ru 1265450Sigor@sysoev.ru size = status->length; 1266450Sigor@sysoev.ru /* Trailing CRLF at the end of header. */ 1267703Svbart@nginx.com size += nxt_length("\r\n"); 1268431Sigor@sysoev.ru 12691131Smax.romanov@nginx.com conn = -1; 1270431Sigor@sysoev.ru 12711131Smax.romanov@nginx.com if (r->websocket_handshake && n == NXT_HTTP_SWITCHING_PROTOCOLS) { 12721131Smax.romanov@nginx.com h1p->websocket = 1; 12731131Smax.romanov@nginx.com h1p->keepalive = 0; 12741131Smax.romanov@nginx.com conn = 2; 12751131Smax.romanov@nginx.com size += NXT_WEBSOCKET_ACCEPT_SIZE + 2; 12761131Smax.romanov@nginx.com 12771131Smax.romanov@nginx.com } else { 12781131Smax.romanov@nginx.com http11 = (h1p->parser.version.s.minor != '0'); 12791131Smax.romanov@nginx.com 12801131Smax.romanov@nginx.com if (r->resp.content_length == NULL || r->resp.content_length->skip) { 1281793Sigor@sysoev.ru 12821131Smax.romanov@nginx.com if (http11) { 12831131Smax.romanov@nginx.com if (n != NXT_HTTP_NOT_MODIFIED 12841131Smax.romanov@nginx.com && n != NXT_HTTP_NO_CONTENT 12851148Sigor@sysoev.ru && body_handler != NULL 12861131Smax.romanov@nginx.com && !h1p->websocket) 12871131Smax.romanov@nginx.com { 12881131Smax.romanov@nginx.com h1p->chunked = 1; 12891131Smax.romanov@nginx.com size += nxt_length(chunked); 12901131Smax.romanov@nginx.com /* Trailing CRLF will be added by the first chunk header. */ 12911131Smax.romanov@nginx.com size -= nxt_length("\r\n"); 12921131Smax.romanov@nginx.com } 12931131Smax.romanov@nginx.com 12941131Smax.romanov@nginx.com } else { 12951131Smax.romanov@nginx.com h1p->keepalive = 0; 1296797Svbart@nginx.com } 12971131Smax.romanov@nginx.com } 1298431Sigor@sysoev.ru 12991131Smax.romanov@nginx.com if (http11 ^ h1p->keepalive) { 13001131Smax.romanov@nginx.com conn = h1p->keepalive; 1301431Sigor@sysoev.ru } 1302431Sigor@sysoev.ru } 1303431Sigor@sysoev.ru 13041131Smax.romanov@nginx.com if (conn >= 0) { 1305431Sigor@sysoev.ru size += connection[conn].length; 1306431Sigor@sysoev.ru } 1307431Sigor@sysoev.ru 1308431Sigor@sysoev.ru nxt_list_each(field, r->resp.fields) { 1309431Sigor@sysoev.ru 1310431Sigor@sysoev.ru if (!field->skip) { 1311431Sigor@sysoev.ru size += field->name_length + field->value_length; 1312703Svbart@nginx.com size += nxt_length(": \r\n"); 1313431Sigor@sysoev.ru } 1314431Sigor@sysoev.ru 1315431Sigor@sysoev.ru } nxt_list_loop; 1316431Sigor@sysoev.ru 13171131Smax.romanov@nginx.com if (nxt_slow_path(n == NXT_HTTP_UPGRADE_REQUIRED)) { 13181131Smax.romanov@nginx.com size += nxt_length(websocket_version); 13191131Smax.romanov@nginx.com } 13201131Smax.romanov@nginx.com 1321608Sigor@sysoev.ru header = nxt_http_buf_mem(task, r, size); 1322431Sigor@sysoev.ru if (nxt_slow_path(header == NULL)) { 1323725Sigor@sysoev.ru nxt_h1p_request_error(task, h1p, r); 1324431Sigor@sysoev.ru return; 1325431Sigor@sysoev.ru } 1326431Sigor@sysoev.ru 13271131Smax.romanov@nginx.com p = nxt_cpymem(header->mem.free, status->start, status->length); 1328431Sigor@sysoev.ru 1329431Sigor@sysoev.ru nxt_list_each(field, r->resp.fields) { 1330431Sigor@sysoev.ru 1331431Sigor@sysoev.ru if (!field->skip) { 1332431Sigor@sysoev.ru p = nxt_cpymem(p, field->name, field->name_length); 1333431Sigor@sysoev.ru *p++ = ':'; *p++ = ' '; 1334431Sigor@sysoev.ru p = nxt_cpymem(p, field->value, field->value_length); 1335431Sigor@sysoev.ru *p++ = '\r'; *p++ = '\n'; 1336431Sigor@sysoev.ru } 1337431Sigor@sysoev.ru 1338431Sigor@sysoev.ru } nxt_list_loop; 1339431Sigor@sysoev.ru 1340431Sigor@sysoev.ru if (conn >= 0) { 1341431Sigor@sysoev.ru p = nxt_cpymem(p, connection[conn].start, connection[conn].length); 1342431Sigor@sysoev.ru } 1343431Sigor@sysoev.ru 13441131Smax.romanov@nginx.com if (h1p->websocket) { 13451131Smax.romanov@nginx.com nxt_websocket_accept(p, h1p->websocket_key->value); 13461131Smax.romanov@nginx.com p += NXT_WEBSOCKET_ACCEPT_SIZE; 13471131Smax.romanov@nginx.com 13481131Smax.romanov@nginx.com *p++ = '\r'; *p++ = '\n'; 13491131Smax.romanov@nginx.com } 13501131Smax.romanov@nginx.com 13511131Smax.romanov@nginx.com if (nxt_slow_path(n == NXT_HTTP_UPGRADE_REQUIRED)) { 13521131Smax.romanov@nginx.com p = nxt_cpymem(p, websocket_version, nxt_length(websocket_version)); 13531131Smax.romanov@nginx.com } 13541131Smax.romanov@nginx.com 1355431Sigor@sysoev.ru if (h1p->chunked) { 1356703Svbart@nginx.com p = nxt_cpymem(p, chunked, nxt_length(chunked)); 1357450Sigor@sysoev.ru /* Trailing CRLF will be added by the first chunk header. */ 1358431Sigor@sysoev.ru 1359431Sigor@sysoev.ru } else { 1360431Sigor@sysoev.ru *p++ = '\r'; *p++ = '\n'; 1361431Sigor@sysoev.ru } 1362431Sigor@sysoev.ru 1363431Sigor@sysoev.ru header->mem.free = p; 1364431Sigor@sysoev.ru 1365630Svbart@nginx.com h1p->header_size = nxt_buf_mem_used_size(&header->mem); 1366630Svbart@nginx.com 1367431Sigor@sysoev.ru c = h1p->conn; 1368431Sigor@sysoev.ru 1369431Sigor@sysoev.ru c->write = header; 13701318Smax.romanov@nginx.com h1p->conn_write_tail = &header->next; 1371740Sigor@sysoev.ru c->write_state = &nxt_h1p_request_send_state; 1372431Sigor@sysoev.ru 13731148Sigor@sysoev.ru if (body_handler != NULL) { 13741148Sigor@sysoev.ru /* 13751148Sigor@sysoev.ru * The body handler will run before c->io->write() handler, 13761148Sigor@sysoev.ru * because the latter was inqueued by nxt_conn_write() 13771148Sigor@sysoev.ru * in engine->write_work_queue. 13781148Sigor@sysoev.ru */ 13791148Sigor@sysoev.ru nxt_work_queue_add(&task->thread->engine->fast_work_queue, 13801270Sigor@sysoev.ru body_handler, task, r, data); 13811148Sigor@sysoev.ru 13821148Sigor@sysoev.ru } else { 13831148Sigor@sysoev.ru header->next = nxt_http_buf_last(r); 13841148Sigor@sysoev.ru } 13851148Sigor@sysoev.ru 13861127Smax.romanov@nginx.com nxt_conn_write(task->thread->engine, c); 13871131Smax.romanov@nginx.com 13881131Smax.romanov@nginx.com if (h1p->websocket) { 13891131Smax.romanov@nginx.com nxt_h1p_websocket_first_frame_start(task, r, c->read); 13901131Smax.romanov@nginx.com } 13911131Smax.romanov@nginx.com } 13921131Smax.romanov@nginx.com 13931131Smax.romanov@nginx.com 13941131Smax.romanov@nginx.com void 13951417Smax.romanov@nginx.com nxt_h1p_complete_buffers(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_bool_t all) 13961131Smax.romanov@nginx.com { 13971417Smax.romanov@nginx.com size_t size; 13981417Smax.romanov@nginx.com nxt_buf_t *b, *in, *next; 13991417Smax.romanov@nginx.com nxt_conn_t *c; 14001131Smax.romanov@nginx.com 14011131Smax.romanov@nginx.com nxt_debug(task, "h1p complete buffers"); 14021131Smax.romanov@nginx.com 14031131Smax.romanov@nginx.com b = h1p->buffers; 14041131Smax.romanov@nginx.com c = h1p->conn; 14051131Smax.romanov@nginx.com in = c->read; 14061131Smax.romanov@nginx.com 14071131Smax.romanov@nginx.com if (b != NULL) { 14081131Smax.romanov@nginx.com if (in == NULL) { 14091131Smax.romanov@nginx.com /* A request with large body. */ 14101131Smax.romanov@nginx.com in = b; 14111131Smax.romanov@nginx.com c->read = in; 14121131Smax.romanov@nginx.com 14131131Smax.romanov@nginx.com b = in->next; 14141131Smax.romanov@nginx.com in->next = NULL; 14151131Smax.romanov@nginx.com } 14161131Smax.romanov@nginx.com 14171131Smax.romanov@nginx.com while (b != NULL) { 14181131Smax.romanov@nginx.com next = b->next; 14191269Sigor@sysoev.ru b->next = NULL; 14201131Smax.romanov@nginx.com 14211418Smax.romanov@nginx.com b->completion_handler(task, b, b->parent); 14221131Smax.romanov@nginx.com 14231131Smax.romanov@nginx.com b = next; 14241131Smax.romanov@nginx.com } 14251131Smax.romanov@nginx.com 14261131Smax.romanov@nginx.com h1p->buffers = NULL; 14271131Smax.romanov@nginx.com h1p->nbuffers = 0; 14281131Smax.romanov@nginx.com } 14291131Smax.romanov@nginx.com 14301131Smax.romanov@nginx.com if (in != NULL) { 14311131Smax.romanov@nginx.com size = nxt_buf_mem_used_size(&in->mem); 14321131Smax.romanov@nginx.com 14331417Smax.romanov@nginx.com if (size == 0 || all) { 14341418Smax.romanov@nginx.com in->completion_handler(task, in, in->parent); 14351131Smax.romanov@nginx.com 14361131Smax.romanov@nginx.com c->read = NULL; 14371131Smax.romanov@nginx.com } 14381131Smax.romanov@nginx.com } 1439431Sigor@sysoev.ru } 1440431Sigor@sysoev.ru 1441431Sigor@sysoev.ru 1442740Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_request_send_state 1443431Sigor@sysoev.ru nxt_aligned(64) = 1444431Sigor@sysoev.ru { 1445740Sigor@sysoev.ru .ready_handler = nxt_h1p_conn_sent, 1446624Sigor@sysoev.ru .error_handler = nxt_h1p_conn_request_error, 1447431Sigor@sysoev.ru 1448626Sigor@sysoev.ru .timer_handler = nxt_h1p_conn_request_send_timeout, 1449771Sigor@sysoev.ru .timer_value = nxt_h1p_conn_request_timer_value, 1450431Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, send_timeout), 1451431Sigor@sysoev.ru .timer_autoreset = 1, 1452431Sigor@sysoev.ru }; 1453431Sigor@sysoev.ru 1454431Sigor@sysoev.ru 1455431Sigor@sysoev.ru static void 1456431Sigor@sysoev.ru nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out) 1457431Sigor@sysoev.ru { 1458725Sigor@sysoev.ru nxt_conn_t *c; 1459725Sigor@sysoev.ru nxt_h1proto_t *h1p; 1460431Sigor@sysoev.ru 1461431Sigor@sysoev.ru nxt_debug(task, "h1p request send"); 1462431Sigor@sysoev.ru 1463725Sigor@sysoev.ru h1p = r->proto.h1; 1464725Sigor@sysoev.ru c = h1p->conn; 1465431Sigor@sysoev.ru 1466725Sigor@sysoev.ru if (h1p->chunked) { 1467431Sigor@sysoev.ru out = nxt_h1p_chunk_create(task, r, out); 1468431Sigor@sysoev.ru if (nxt_slow_path(out == NULL)) { 1469725Sigor@sysoev.ru nxt_h1p_request_error(task, h1p, r); 1470431Sigor@sysoev.ru return; 1471431Sigor@sysoev.ru } 1472431Sigor@sysoev.ru } 1473431Sigor@sysoev.ru 1474431Sigor@sysoev.ru if (c->write == NULL) { 1475431Sigor@sysoev.ru c->write = out; 1476740Sigor@sysoev.ru c->write_state = &nxt_h1p_request_send_state; 1477431Sigor@sysoev.ru 1478431Sigor@sysoev.ru nxt_conn_write(task->thread->engine, c); 1479431Sigor@sysoev.ru 1480431Sigor@sysoev.ru } else { 14811318Smax.romanov@nginx.com *h1p->conn_write_tail = out; 1482431Sigor@sysoev.ru } 14831318Smax.romanov@nginx.com 14841318Smax.romanov@nginx.com while (out->next != NULL) { 14851318Smax.romanov@nginx.com out = out->next; 14861318Smax.romanov@nginx.com } 14871318Smax.romanov@nginx.com 14881318Smax.romanov@nginx.com h1p->conn_write_tail = &out->next; 1489431Sigor@sysoev.ru } 1490431Sigor@sysoev.ru 1491431Sigor@sysoev.ru 1492431Sigor@sysoev.ru static nxt_buf_t * 1493431Sigor@sysoev.ru nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out) 1494431Sigor@sysoev.ru { 1495483Sigor@sysoev.ru nxt_off_t size; 1496431Sigor@sysoev.ru nxt_buf_t *b, **prev, *header, *tail; 1497431Sigor@sysoev.ru 1498703Svbart@nginx.com const size_t chunk_size = 2 * nxt_length("\r\n") + NXT_OFF_T_HEXLEN; 1499431Sigor@sysoev.ru static const char tail_chunk[] = "\r\n0\r\n\r\n"; 1500431Sigor@sysoev.ru 1501431Sigor@sysoev.ru size = 0; 1502431Sigor@sysoev.ru prev = &out; 1503431Sigor@sysoev.ru 1504431Sigor@sysoev.ru for (b = out; b != NULL; b = b->next) { 1505431Sigor@sysoev.ru 1506431Sigor@sysoev.ru if (nxt_buf_is_last(b)) { 15071215Svbart@nginx.com tail = nxt_http_buf_mem(task, r, sizeof(tail_chunk)); 1508431Sigor@sysoev.ru if (nxt_slow_path(tail == NULL)) { 1509431Sigor@sysoev.ru return NULL; 1510431Sigor@sysoev.ru } 1511431Sigor@sysoev.ru 1512431Sigor@sysoev.ru *prev = tail; 1513431Sigor@sysoev.ru tail->next = b; 1514431Sigor@sysoev.ru /* 1515431Sigor@sysoev.ru * The tail_chunk size with trailing zero is 8 bytes, so 1516431Sigor@sysoev.ru * memcpy may be inlined with just single 8 byte move operation. 1517431Sigor@sysoev.ru */ 1518431Sigor@sysoev.ru nxt_memcpy(tail->mem.free, tail_chunk, sizeof(tail_chunk)); 1519703Svbart@nginx.com tail->mem.free += nxt_length(tail_chunk); 1520431Sigor@sysoev.ru 1521431Sigor@sysoev.ru break; 1522431Sigor@sysoev.ru } 1523431Sigor@sysoev.ru 1524431Sigor@sysoev.ru size += nxt_buf_used_size(b); 1525431Sigor@sysoev.ru prev = &b->next; 1526431Sigor@sysoev.ru } 1527431Sigor@sysoev.ru 1528431Sigor@sysoev.ru if (size == 0) { 1529431Sigor@sysoev.ru return out; 1530431Sigor@sysoev.ru } 1531431Sigor@sysoev.ru 1532608Sigor@sysoev.ru header = nxt_http_buf_mem(task, r, chunk_size); 1533431Sigor@sysoev.ru if (nxt_slow_path(header == NULL)) { 1534431Sigor@sysoev.ru return NULL; 1535431Sigor@sysoev.ru } 1536431Sigor@sysoev.ru 1537431Sigor@sysoev.ru header->next = out; 1538431Sigor@sysoev.ru header->mem.free = nxt_sprintf(header->mem.free, header->mem.end, 1539431Sigor@sysoev.ru "\r\n%xO\r\n", size); 1540431Sigor@sysoev.ru return header; 1541431Sigor@sysoev.ru } 1542431Sigor@sysoev.ru 1543431Sigor@sysoev.ru 1544630Svbart@nginx.com static nxt_off_t 1545630Svbart@nginx.com nxt_h1p_request_body_bytes_sent(nxt_task_t *task, nxt_http_proto_t proto) 1546630Svbart@nginx.com { 1547630Svbart@nginx.com nxt_off_t sent; 1548630Svbart@nginx.com nxt_h1proto_t *h1p; 1549630Svbart@nginx.com 1550630Svbart@nginx.com h1p = proto.h1; 1551630Svbart@nginx.com 1552630Svbart@nginx.com sent = h1p->conn->sent - h1p->header_size; 1553630Svbart@nginx.com 1554630Svbart@nginx.com return (sent > 0) ? sent : 0; 1555630Svbart@nginx.com } 1556630Svbart@nginx.com 1557630Svbart@nginx.com 1558431Sigor@sysoev.ru static void 1559608Sigor@sysoev.ru nxt_h1p_request_discard(nxt_task_t *task, nxt_http_request_t *r, 1560608Sigor@sysoev.ru nxt_buf_t *last) 1561608Sigor@sysoev.ru { 1562608Sigor@sysoev.ru nxt_buf_t *b; 1563608Sigor@sysoev.ru nxt_conn_t *c; 1564608Sigor@sysoev.ru nxt_h1proto_t *h1p; 1565608Sigor@sysoev.ru nxt_work_queue_t *wq; 1566608Sigor@sysoev.ru 1567608Sigor@sysoev.ru nxt_debug(task, "h1p request discard"); 1568608Sigor@sysoev.ru 1569608Sigor@sysoev.ru h1p = r->proto.h1; 1570608Sigor@sysoev.ru h1p->keepalive = 0; 1571608Sigor@sysoev.ru 1572608Sigor@sysoev.ru c = h1p->conn; 1573608Sigor@sysoev.ru b = c->write; 1574608Sigor@sysoev.ru c->write = NULL; 1575608Sigor@sysoev.ru 1576608Sigor@sysoev.ru wq = &task->thread->engine->fast_work_queue; 1577608Sigor@sysoev.ru 1578608Sigor@sysoev.ru nxt_sendbuf_drain(task, wq, b); 1579608Sigor@sysoev.ru nxt_sendbuf_drain(task, wq, last); 1580608Sigor@sysoev.ru } 1581608Sigor@sysoev.ru 1582608Sigor@sysoev.ru 1583608Sigor@sysoev.ru static void 1584624Sigor@sysoev.ru nxt_h1p_conn_request_error(nxt_task_t *task, void *obj, void *data) 1585624Sigor@sysoev.ru { 1586624Sigor@sysoev.ru nxt_h1proto_t *h1p; 1587624Sigor@sysoev.ru nxt_http_request_t *r; 1588624Sigor@sysoev.ru 1589624Sigor@sysoev.ru h1p = data; 1590624Sigor@sysoev.ru 1591624Sigor@sysoev.ru nxt_debug(task, "h1p conn request error"); 1592624Sigor@sysoev.ru 1593624Sigor@sysoev.ru r = h1p->request; 1594624Sigor@sysoev.ru 15951131Smax.romanov@nginx.com if (nxt_slow_path(r == NULL)) { 15961131Smax.romanov@nginx.com nxt_h1p_shutdown(task, h1p->conn); 15971131Smax.romanov@nginx.com return; 15981131Smax.romanov@nginx.com } 15991131Smax.romanov@nginx.com 1600624Sigor@sysoev.ru if (r->fields == NULL) { 16011131Smax.romanov@nginx.com (void) nxt_h1p_header_process(task, h1p, r); 1602624Sigor@sysoev.ru } 1603624Sigor@sysoev.ru 1604624Sigor@sysoev.ru if (r->status == 0) { 1605624Sigor@sysoev.ru r->status = NXT_HTTP_BAD_REQUEST; 1606624Sigor@sysoev.ru } 1607624Sigor@sysoev.ru 1608725Sigor@sysoev.ru nxt_h1p_request_error(task, h1p, r); 1609624Sigor@sysoev.ru } 1610624Sigor@sysoev.ru 1611624Sigor@sysoev.ru 1612624Sigor@sysoev.ru static void 1613624Sigor@sysoev.ru nxt_h1p_conn_request_timeout(nxt_task_t *task, void *obj, void *data) 1614624Sigor@sysoev.ru { 1615624Sigor@sysoev.ru nxt_conn_t *c; 1616624Sigor@sysoev.ru nxt_timer_t *timer; 1617624Sigor@sysoev.ru nxt_h1proto_t *h1p; 1618624Sigor@sysoev.ru nxt_http_request_t *r; 1619624Sigor@sysoev.ru 1620624Sigor@sysoev.ru timer = obj; 1621624Sigor@sysoev.ru 1622624Sigor@sysoev.ru nxt_debug(task, "h1p conn request timeout"); 1623624Sigor@sysoev.ru 1624624Sigor@sysoev.ru c = nxt_read_timer_conn(timer); 1625979Sigor@sysoev.ru c->block_read = 1; 1626626Sigor@sysoev.ru /* 1627626Sigor@sysoev.ru * Disable SO_LINGER off during socket closing 1628626Sigor@sysoev.ru * to send "408 Request Timeout" error response. 1629626Sigor@sysoev.ru */ 1630626Sigor@sysoev.ru c->socket.timedout = 0; 1631626Sigor@sysoev.ru 1632624Sigor@sysoev.ru h1p = c->socket.data; 1633725Sigor@sysoev.ru h1p->keepalive = 0; 1634624Sigor@sysoev.ru r = h1p->request; 1635624Sigor@sysoev.ru 1636624Sigor@sysoev.ru if (r->fields == NULL) { 16371131Smax.romanov@nginx.com (void) nxt_h1p_header_process(task, h1p, r); 1638624Sigor@sysoev.ru } 1639624Sigor@sysoev.ru 1640626Sigor@sysoev.ru nxt_http_request_error(task, r, NXT_HTTP_REQUEST_TIMEOUT); 1641626Sigor@sysoev.ru } 1642626Sigor@sysoev.ru 1643624Sigor@sysoev.ru 1644626Sigor@sysoev.ru static void 1645626Sigor@sysoev.ru nxt_h1p_conn_request_send_timeout(nxt_task_t *task, void *obj, void *data) 1646626Sigor@sysoev.ru { 1647626Sigor@sysoev.ru nxt_conn_t *c; 1648626Sigor@sysoev.ru nxt_timer_t *timer; 1649626Sigor@sysoev.ru nxt_h1proto_t *h1p; 1650626Sigor@sysoev.ru 1651626Sigor@sysoev.ru timer = obj; 1652626Sigor@sysoev.ru 1653626Sigor@sysoev.ru nxt_debug(task, "h1p conn request send timeout"); 1654626Sigor@sysoev.ru 1655724Sigor@sysoev.ru c = nxt_write_timer_conn(timer); 1656979Sigor@sysoev.ru c->block_write = 1; 1657626Sigor@sysoev.ru h1p = c->socket.data; 1658626Sigor@sysoev.ru 1659725Sigor@sysoev.ru nxt_h1p_request_error(task, h1p, h1p->request); 1660624Sigor@sysoev.ru } 1661624Sigor@sysoev.ru 1662624Sigor@sysoev.ru 16631131Smax.romanov@nginx.com nxt_msec_t 1664771Sigor@sysoev.ru nxt_h1p_conn_request_timer_value(nxt_conn_t *c, uintptr_t data) 1665683Sigor@sysoev.ru { 1666683Sigor@sysoev.ru nxt_h1proto_t *h1p; 1667683Sigor@sysoev.ru 1668683Sigor@sysoev.ru h1p = c->socket.data; 1669683Sigor@sysoev.ru 1670683Sigor@sysoev.ru return nxt_value_at(nxt_msec_t, h1p->request->conf->socket_conf, data); 1671683Sigor@sysoev.ru } 1672683Sigor@sysoev.ru 1673683Sigor@sysoev.ru 1674624Sigor@sysoev.ru nxt_inline void 1675725Sigor@sysoev.ru nxt_h1p_request_error(nxt_task_t *task, nxt_h1proto_t *h1p, 1676725Sigor@sysoev.ru nxt_http_request_t *r) 1677624Sigor@sysoev.ru { 1678725Sigor@sysoev.ru h1p->keepalive = 0; 1679725Sigor@sysoev.ru 1680725Sigor@sysoev.ru r->state->error_handler(task, r, h1p); 1681431Sigor@sysoev.ru } 1682771Sigor@sysoev.ru 1683771Sigor@sysoev.ru 1684771Sigor@sysoev.ru static void 1685771Sigor@sysoev.ru nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto, 1686771Sigor@sysoev.ru nxt_socket_conf_joint_t *joint) 1687771Sigor@sysoev.ru { 1688771Sigor@sysoev.ru nxt_conn_t *c; 1689771Sigor@sysoev.ru nxt_h1proto_t *h1p; 1690771Sigor@sysoev.ru 1691771Sigor@sysoev.ru nxt_debug(task, "h1p request close"); 1692771Sigor@sysoev.ru 1693771Sigor@sysoev.ru h1p = proto.h1; 16941271Sigor@sysoev.ru h1p->keepalive &= !h1p->request->inconsistent; 1695771Sigor@sysoev.ru h1p->request = NULL; 1696771Sigor@sysoev.ru 1697771Sigor@sysoev.ru nxt_router_conf_release(task, joint); 1698771Sigor@sysoev.ru 1699771Sigor@sysoev.ru c = h1p->conn; 17001265Sigor@sysoev.ru task = &c->task; 17011265Sigor@sysoev.ru c->socket.task = task; 17021265Sigor@sysoev.ru c->read_timer.task = task; 17031265Sigor@sysoev.ru c->write_timer.task = task; 1704771Sigor@sysoev.ru 1705771Sigor@sysoev.ru if (h1p->keepalive) { 1706771Sigor@sysoev.ru nxt_h1p_keepalive(task, h1p, c); 1707771Sigor@sysoev.ru 1708771Sigor@sysoev.ru } else { 1709771Sigor@sysoev.ru nxt_h1p_shutdown(task, c); 1710771Sigor@sysoev.ru } 1711771Sigor@sysoev.ru } 1712771Sigor@sysoev.ru 1713771Sigor@sysoev.ru 1714771Sigor@sysoev.ru static void 1715771Sigor@sysoev.ru nxt_h1p_conn_sent(nxt_task_t *task, void *obj, void *data) 1716771Sigor@sysoev.ru { 1717771Sigor@sysoev.ru nxt_conn_t *c; 1718771Sigor@sysoev.ru nxt_event_engine_t *engine; 1719771Sigor@sysoev.ru 1720771Sigor@sysoev.ru c = obj; 1721771Sigor@sysoev.ru 1722771Sigor@sysoev.ru nxt_debug(task, "h1p conn sent"); 1723771Sigor@sysoev.ru 1724771Sigor@sysoev.ru engine = task->thread->engine; 1725771Sigor@sysoev.ru 1726771Sigor@sysoev.ru c->write = nxt_sendbuf_completion(task, &engine->fast_work_queue, c->write); 1727771Sigor@sysoev.ru 1728771Sigor@sysoev.ru if (c->write != NULL) { 1729771Sigor@sysoev.ru nxt_conn_write(engine, c); 1730771Sigor@sysoev.ru } 1731771Sigor@sysoev.ru } 1732771Sigor@sysoev.ru 1733771Sigor@sysoev.ru 1734771Sigor@sysoev.ru static void 1735771Sigor@sysoev.ru nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data) 1736771Sigor@sysoev.ru { 1737771Sigor@sysoev.ru nxt_conn_t *c; 1738771Sigor@sysoev.ru 1739771Sigor@sysoev.ru c = obj; 1740771Sigor@sysoev.ru 1741771Sigor@sysoev.ru nxt_debug(task, "h1p conn close"); 1742771Sigor@sysoev.ru 17431457Sigor@sysoev.ru nxt_queue_remove(&c->link); 17441457Sigor@sysoev.ru 1745771Sigor@sysoev.ru nxt_h1p_shutdown(task, c); 1746771Sigor@sysoev.ru } 1747771Sigor@sysoev.ru 1748771Sigor@sysoev.ru 1749771Sigor@sysoev.ru static void 1750771Sigor@sysoev.ru nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data) 1751771Sigor@sysoev.ru { 1752771Sigor@sysoev.ru nxt_conn_t *c; 1753771Sigor@sysoev.ru 1754771Sigor@sysoev.ru c = obj; 1755771Sigor@sysoev.ru 1756771Sigor@sysoev.ru nxt_debug(task, "h1p conn error"); 1757771Sigor@sysoev.ru 17581457Sigor@sysoev.ru nxt_queue_remove(&c->link); 17591457Sigor@sysoev.ru 1760771Sigor@sysoev.ru nxt_h1p_shutdown(task, c); 1761771Sigor@sysoev.ru } 1762771Sigor@sysoev.ru 1763771Sigor@sysoev.ru 1764771Sigor@sysoev.ru static nxt_msec_t 1765771Sigor@sysoev.ru nxt_h1p_conn_timer_value(nxt_conn_t *c, uintptr_t data) 1766771Sigor@sysoev.ru { 1767771Sigor@sysoev.ru nxt_socket_conf_joint_t *joint; 1768771Sigor@sysoev.ru 1769771Sigor@sysoev.ru joint = c->listen->socket.data; 1770771Sigor@sysoev.ru 17711599Sigor@sysoev.ru if (nxt_fast_path(joint != NULL)) { 17721599Sigor@sysoev.ru return nxt_value_at(nxt_msec_t, joint->socket_conf, data); 17731599Sigor@sysoev.ru } 17741599Sigor@sysoev.ru 17751599Sigor@sysoev.ru /* 17761599Sigor@sysoev.ru * Listening socket had been closed while 17771599Sigor@sysoev.ru * connection was in keep-alive state. 17781599Sigor@sysoev.ru */ 17791599Sigor@sysoev.ru return 1; 1780771Sigor@sysoev.ru } 1781771Sigor@sysoev.ru 1782771Sigor@sysoev.ru 1783771Sigor@sysoev.ru static void 1784771Sigor@sysoev.ru nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c) 1785771Sigor@sysoev.ru { 17861457Sigor@sysoev.ru size_t size; 17871457Sigor@sysoev.ru nxt_buf_t *in; 17881457Sigor@sysoev.ru nxt_event_engine_t *engine; 1789771Sigor@sysoev.ru 1790771Sigor@sysoev.ru nxt_debug(task, "h1p keepalive"); 1791771Sigor@sysoev.ru 1792771Sigor@sysoev.ru if (!c->tcp_nodelay) { 1793771Sigor@sysoev.ru nxt_conn_tcp_nodelay_on(task, c); 1794771Sigor@sysoev.ru } 1795771Sigor@sysoev.ru 17961417Smax.romanov@nginx.com nxt_h1p_complete_buffers(task, h1p, 0); 17971131Smax.romanov@nginx.com 17981131Smax.romanov@nginx.com in = c->read; 1799771Sigor@sysoev.ru 1800771Sigor@sysoev.ru nxt_memzero(h1p, offsetof(nxt_h1proto_t, conn)); 1801771Sigor@sysoev.ru 1802771Sigor@sysoev.ru c->sent = 0; 1803771Sigor@sysoev.ru 18041457Sigor@sysoev.ru engine = task->thread->engine; 18051457Sigor@sysoev.ru nxt_queue_insert_head(&engine->idle_connections, &c->link); 18061457Sigor@sysoev.ru 1807771Sigor@sysoev.ru if (in == NULL) { 1808771Sigor@sysoev.ru c->read_state = &nxt_h1p_keepalive_state; 1809771Sigor@sysoev.ru 18101457Sigor@sysoev.ru nxt_conn_read(engine, c); 1811771Sigor@sysoev.ru 1812771Sigor@sysoev.ru } else { 18131131Smax.romanov@nginx.com size = nxt_buf_mem_used_size(&in->mem); 18141131Smax.romanov@nginx.com 1815771Sigor@sysoev.ru nxt_debug(task, "h1p pipelining"); 1816771Sigor@sysoev.ru 1817771Sigor@sysoev.ru nxt_memmove(in->mem.start, in->mem.pos, size); 1818771Sigor@sysoev.ru 1819771Sigor@sysoev.ru in->mem.pos = in->mem.start; 1820771Sigor@sysoev.ru in->mem.free = in->mem.start + size; 1821771Sigor@sysoev.ru 1822771Sigor@sysoev.ru nxt_h1p_conn_request_init(task, c, c->socket.data); 1823771Sigor@sysoev.ru } 1824771Sigor@sysoev.ru } 1825771Sigor@sysoev.ru 1826771Sigor@sysoev.ru 1827771Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_keepalive_state 1828771Sigor@sysoev.ru nxt_aligned(64) = 1829771Sigor@sysoev.ru { 1830771Sigor@sysoev.ru .ready_handler = nxt_h1p_conn_request_init, 1831771Sigor@sysoev.ru .close_handler = nxt_h1p_conn_close, 1832771Sigor@sysoev.ru .error_handler = nxt_h1p_conn_error, 1833771Sigor@sysoev.ru 1834771Sigor@sysoev.ru .io_read_handler = nxt_h1p_idle_io_read_handler, 1835771Sigor@sysoev.ru 1836771Sigor@sysoev.ru .timer_handler = nxt_h1p_idle_timeout, 1837771Sigor@sysoev.ru .timer_value = nxt_h1p_conn_timer_value, 1838771Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, idle_timeout), 1839771Sigor@sysoev.ru .timer_autoreset = 1, 1840771Sigor@sysoev.ru }; 1841771Sigor@sysoev.ru 1842771Sigor@sysoev.ru 18431131Smax.romanov@nginx.com const nxt_conn_state_t nxt_h1p_idle_close_state 1844771Sigor@sysoev.ru nxt_aligned(64) = 1845771Sigor@sysoev.ru { 1846771Sigor@sysoev.ru .close_handler = nxt_h1p_idle_close, 1847771Sigor@sysoev.ru }; 1848771Sigor@sysoev.ru 1849771Sigor@sysoev.ru 1850771Sigor@sysoev.ru static void 1851771Sigor@sysoev.ru nxt_h1p_idle_close(nxt_task_t *task, void *obj, void *data) 1852771Sigor@sysoev.ru { 1853771Sigor@sysoev.ru nxt_conn_t *c; 1854771Sigor@sysoev.ru 1855771Sigor@sysoev.ru c = obj; 1856771Sigor@sysoev.ru 1857771Sigor@sysoev.ru nxt_debug(task, "h1p idle close"); 1858771Sigor@sysoev.ru 18591598Sigor@sysoev.ru nxt_queue_remove(&c->link); 18601598Sigor@sysoev.ru 1861771Sigor@sysoev.ru nxt_h1p_idle_response(task, c); 1862771Sigor@sysoev.ru } 1863771Sigor@sysoev.ru 1864771Sigor@sysoev.ru 1865771Sigor@sysoev.ru static void 1866771Sigor@sysoev.ru nxt_h1p_idle_timeout(nxt_task_t *task, void *obj, void *data) 1867771Sigor@sysoev.ru { 1868771Sigor@sysoev.ru nxt_conn_t *c; 1869771Sigor@sysoev.ru nxt_timer_t *timer; 1870771Sigor@sysoev.ru 1871771Sigor@sysoev.ru timer = obj; 1872771Sigor@sysoev.ru 1873771Sigor@sysoev.ru nxt_debug(task, "h1p idle timeout"); 1874771Sigor@sysoev.ru 1875771Sigor@sysoev.ru c = nxt_read_timer_conn(timer); 1876979Sigor@sysoev.ru c->block_read = 1; 1877771Sigor@sysoev.ru 18781457Sigor@sysoev.ru nxt_queue_remove(&c->link); 18791457Sigor@sysoev.ru 1880771Sigor@sysoev.ru nxt_h1p_idle_response(task, c); 1881771Sigor@sysoev.ru } 1882771Sigor@sysoev.ru 1883771Sigor@sysoev.ru 1884771Sigor@sysoev.ru #define NXT_H1P_IDLE_TIMEOUT \ 1885771Sigor@sysoev.ru "HTTP/1.1 408 Request Timeout\r\n" \ 1886771Sigor@sysoev.ru "Server: " NXT_SERVER "\r\n" \ 1887771Sigor@sysoev.ru "Connection: close\r\n" \ 1888771Sigor@sysoev.ru "Content-Length: 0\r\n" \ 1889771Sigor@sysoev.ru "Date: " 1890771Sigor@sysoev.ru 1891771Sigor@sysoev.ru 1892771Sigor@sysoev.ru static void 1893771Sigor@sysoev.ru nxt_h1p_idle_response(nxt_task_t *task, nxt_conn_t *c) 1894771Sigor@sysoev.ru { 18951600Sigor@sysoev.ru u_char *p; 18961600Sigor@sysoev.ru size_t size; 18971600Sigor@sysoev.ru nxt_buf_t *out, *last; 1898771Sigor@sysoev.ru 1899771Sigor@sysoev.ru size = nxt_length(NXT_H1P_IDLE_TIMEOUT) 1900771Sigor@sysoev.ru + nxt_http_date_cache.size 1901771Sigor@sysoev.ru + nxt_length("\r\n\r\n"); 1902771Sigor@sysoev.ru 1903771Sigor@sysoev.ru out = nxt_buf_mem_alloc(c->mem_pool, size, 0); 1904771Sigor@sysoev.ru if (nxt_slow_path(out == NULL)) { 1905771Sigor@sysoev.ru goto fail; 1906771Sigor@sysoev.ru } 1907771Sigor@sysoev.ru 1908771Sigor@sysoev.ru p = nxt_cpymem(out->mem.free, NXT_H1P_IDLE_TIMEOUT, 1909771Sigor@sysoev.ru nxt_length(NXT_H1P_IDLE_TIMEOUT)); 1910771Sigor@sysoev.ru 1911771Sigor@sysoev.ru p = nxt_thread_time_string(task->thread, &nxt_http_date_cache, p); 1912771Sigor@sysoev.ru 1913771Sigor@sysoev.ru out->mem.free = nxt_cpymem(p, "\r\n\r\n", 4); 1914771Sigor@sysoev.ru 1915771Sigor@sysoev.ru last = nxt_mp_zget(c->mem_pool, NXT_BUF_SYNC_SIZE); 1916771Sigor@sysoev.ru if (nxt_slow_path(last == NULL)) { 1917771Sigor@sysoev.ru goto fail; 1918771Sigor@sysoev.ru } 1919771Sigor@sysoev.ru 1920771Sigor@sysoev.ru out->next = last; 1921771Sigor@sysoev.ru nxt_buf_set_sync(last); 1922771Sigor@sysoev.ru nxt_buf_set_last(last); 1923771Sigor@sysoev.ru 1924771Sigor@sysoev.ru last->completion_handler = nxt_h1p_idle_response_sent; 1925771Sigor@sysoev.ru last->parent = c; 1926771Sigor@sysoev.ru 1927771Sigor@sysoev.ru c->write = out; 1928771Sigor@sysoev.ru c->write_state = &nxt_h1p_timeout_response_state; 1929771Sigor@sysoev.ru 1930771Sigor@sysoev.ru nxt_conn_write(task->thread->engine, c); 1931771Sigor@sysoev.ru return; 1932771Sigor@sysoev.ru 1933771Sigor@sysoev.ru fail: 1934771Sigor@sysoev.ru 1935771Sigor@sysoev.ru nxt_h1p_shutdown(task, c); 1936771Sigor@sysoev.ru } 1937771Sigor@sysoev.ru 1938771Sigor@sysoev.ru 1939771Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_timeout_response_state 1940771Sigor@sysoev.ru nxt_aligned(64) = 1941771Sigor@sysoev.ru { 1942771Sigor@sysoev.ru .ready_handler = nxt_h1p_conn_sent, 19431457Sigor@sysoev.ru .error_handler = nxt_h1p_idle_response_error, 1944771Sigor@sysoev.ru 1945771Sigor@sysoev.ru .timer_handler = nxt_h1p_idle_response_timeout, 1946771Sigor@sysoev.ru .timer_value = nxt_h1p_idle_response_timer_value, 1947771Sigor@sysoev.ru }; 1948771Sigor@sysoev.ru 1949771Sigor@sysoev.ru 1950771Sigor@sysoev.ru static void 1951771Sigor@sysoev.ru nxt_h1p_idle_response_sent(nxt_task_t *task, void *obj, void *data) 1952771Sigor@sysoev.ru { 1953771Sigor@sysoev.ru nxt_conn_t *c; 1954771Sigor@sysoev.ru 1955771Sigor@sysoev.ru c = data; 1956771Sigor@sysoev.ru 1957771Sigor@sysoev.ru nxt_debug(task, "h1p idle timeout response sent"); 1958771Sigor@sysoev.ru 1959771Sigor@sysoev.ru nxt_h1p_shutdown(task, c); 1960771Sigor@sysoev.ru } 1961771Sigor@sysoev.ru 1962771Sigor@sysoev.ru 1963771Sigor@sysoev.ru static void 19641457Sigor@sysoev.ru nxt_h1p_idle_response_error(nxt_task_t *task, void *obj, void *data) 19651457Sigor@sysoev.ru { 19661457Sigor@sysoev.ru nxt_conn_t *c; 19671457Sigor@sysoev.ru 19681457Sigor@sysoev.ru c = obj; 19691457Sigor@sysoev.ru 19701457Sigor@sysoev.ru nxt_debug(task, "h1p response error"); 19711457Sigor@sysoev.ru 19721457Sigor@sysoev.ru nxt_h1p_shutdown(task, c); 19731457Sigor@sysoev.ru } 19741457Sigor@sysoev.ru 19751457Sigor@sysoev.ru 19761457Sigor@sysoev.ru static void 1977771Sigor@sysoev.ru nxt_h1p_idle_response_timeout(nxt_task_t *task, void *obj, void *data) 1978771Sigor@sysoev.ru { 1979771Sigor@sysoev.ru nxt_conn_t *c; 1980771Sigor@sysoev.ru nxt_timer_t *timer; 1981771Sigor@sysoev.ru 1982771Sigor@sysoev.ru timer = obj; 1983771Sigor@sysoev.ru 1984771Sigor@sysoev.ru nxt_debug(task, "h1p idle timeout response timeout"); 1985771Sigor@sysoev.ru 1986771Sigor@sysoev.ru c = nxt_read_timer_conn(timer); 1987979Sigor@sysoev.ru c->block_write = 1; 1988771Sigor@sysoev.ru 1989771Sigor@sysoev.ru nxt_h1p_shutdown(task, c); 1990771Sigor@sysoev.ru } 1991771Sigor@sysoev.ru 1992771Sigor@sysoev.ru 1993771Sigor@sysoev.ru static nxt_msec_t 1994771Sigor@sysoev.ru nxt_h1p_idle_response_timer_value(nxt_conn_t *c, uintptr_t data) 1995771Sigor@sysoev.ru { 1996771Sigor@sysoev.ru return 10 * 1000; 1997771Sigor@sysoev.ru } 1998771Sigor@sysoev.ru 1999771Sigor@sysoev.ru 2000771Sigor@sysoev.ru static void 2001771Sigor@sysoev.ru nxt_h1p_shutdown(nxt_task_t *task, nxt_conn_t *c) 2002771Sigor@sysoev.ru { 20031131Smax.romanov@nginx.com nxt_timer_t *timer; 20041131Smax.romanov@nginx.com nxt_h1proto_t *h1p; 20051131Smax.romanov@nginx.com 2006771Sigor@sysoev.ru nxt_debug(task, "h1p shutdown"); 2007771Sigor@sysoev.ru 20081131Smax.romanov@nginx.com h1p = c->socket.data; 20091131Smax.romanov@nginx.com 20101417Smax.romanov@nginx.com if (h1p != NULL) { 20111417Smax.romanov@nginx.com nxt_h1p_complete_buffers(task, h1p, 1); 20121417Smax.romanov@nginx.com 20131417Smax.romanov@nginx.com if (nxt_slow_path(h1p->websocket_timer != NULL)) { 20141417Smax.romanov@nginx.com timer = &h1p->websocket_timer->timer; 20151417Smax.romanov@nginx.com 20161417Smax.romanov@nginx.com if (timer->handler != nxt_h1p_conn_ws_shutdown) { 20171417Smax.romanov@nginx.com timer->handler = nxt_h1p_conn_ws_shutdown; 20181417Smax.romanov@nginx.com nxt_timer_add(task->thread->engine, timer, 0); 20191417Smax.romanov@nginx.com 20201417Smax.romanov@nginx.com } else { 20211417Smax.romanov@nginx.com nxt_debug(task, "h1p already scheduled ws shutdown"); 20221417Smax.romanov@nginx.com } 20231417Smax.romanov@nginx.com 20241417Smax.romanov@nginx.com return; 20251131Smax.romanov@nginx.com } 20261131Smax.romanov@nginx.com } 20271417Smax.romanov@nginx.com 20281417Smax.romanov@nginx.com nxt_h1p_closing(task, c); 20291131Smax.romanov@nginx.com } 20301131Smax.romanov@nginx.com 20311131Smax.romanov@nginx.com 20321131Smax.romanov@nginx.com static void 20331270Sigor@sysoev.ru nxt_h1p_conn_ws_shutdown(nxt_task_t *task, void *obj, void *data) 20341131Smax.romanov@nginx.com { 20351270Sigor@sysoev.ru nxt_timer_t *timer; 20361270Sigor@sysoev.ru nxt_h1p_websocket_timer_t *ws_timer; 20371270Sigor@sysoev.ru 20381270Sigor@sysoev.ru nxt_debug(task, "h1p conn ws shutdown"); 20391270Sigor@sysoev.ru 20401270Sigor@sysoev.ru timer = obj; 20411270Sigor@sysoev.ru ws_timer = nxt_timer_data(timer, nxt_h1p_websocket_timer_t, timer); 20421270Sigor@sysoev.ru 20431270Sigor@sysoev.ru nxt_h1p_closing(task, ws_timer->h1p->conn); 20441270Sigor@sysoev.ru } 20451270Sigor@sysoev.ru 20461270Sigor@sysoev.ru 20471270Sigor@sysoev.ru static void 20481270Sigor@sysoev.ru nxt_h1p_closing(nxt_task_t *task, nxt_conn_t *c) 20491270Sigor@sysoev.ru { 20501270Sigor@sysoev.ru nxt_debug(task, "h1p closing"); 20511270Sigor@sysoev.ru 2052771Sigor@sysoev.ru c->socket.data = NULL; 2053771Sigor@sysoev.ru 2054771Sigor@sysoev.ru #if (NXT_TLS) 2055771Sigor@sysoev.ru 2056771Sigor@sysoev.ru if (c->u.tls != NULL) { 2057771Sigor@sysoev.ru c->write_state = &nxt_h1p_shutdown_state; 2058771Sigor@sysoev.ru 2059771Sigor@sysoev.ru c->io->shutdown(task, c, NULL); 2060771Sigor@sysoev.ru return; 2061771Sigor@sysoev.ru } 2062771Sigor@sysoev.ru 2063771Sigor@sysoev.ru #endif 2064771Sigor@sysoev.ru 2065771Sigor@sysoev.ru nxt_h1p_conn_closing(task, c, NULL); 2066771Sigor@sysoev.ru } 2067771Sigor@sysoev.ru 2068771Sigor@sysoev.ru 2069771Sigor@sysoev.ru #if (NXT_TLS) 2070771Sigor@sysoev.ru 2071771Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_shutdown_state 2072771Sigor@sysoev.ru nxt_aligned(64) = 2073771Sigor@sysoev.ru { 2074771Sigor@sysoev.ru .ready_handler = nxt_h1p_conn_closing, 2075771Sigor@sysoev.ru .close_handler = nxt_h1p_conn_closing, 2076771Sigor@sysoev.ru .error_handler = nxt_h1p_conn_closing, 2077771Sigor@sysoev.ru }; 2078771Sigor@sysoev.ru 2079771Sigor@sysoev.ru #endif 2080771Sigor@sysoev.ru 2081771Sigor@sysoev.ru 2082771Sigor@sysoev.ru static void 2083771Sigor@sysoev.ru nxt_h1p_conn_closing(nxt_task_t *task, void *obj, void *data) 2084771Sigor@sysoev.ru { 2085771Sigor@sysoev.ru nxt_conn_t *c; 2086771Sigor@sysoev.ru 2087771Sigor@sysoev.ru c = obj; 2088771Sigor@sysoev.ru 2089771Sigor@sysoev.ru nxt_debug(task, "h1p conn closing"); 2090771Sigor@sysoev.ru 2091771Sigor@sysoev.ru c->write_state = &nxt_h1p_close_state; 2092771Sigor@sysoev.ru 2093771Sigor@sysoev.ru nxt_conn_close(task->thread->engine, c); 2094771Sigor@sysoev.ru } 2095771Sigor@sysoev.ru 2096771Sigor@sysoev.ru 2097771Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_close_state 2098771Sigor@sysoev.ru nxt_aligned(64) = 2099771Sigor@sysoev.ru { 2100771Sigor@sysoev.ru .ready_handler = nxt_h1p_conn_free, 2101771Sigor@sysoev.ru }; 2102771Sigor@sysoev.ru 2103771Sigor@sysoev.ru 2104771Sigor@sysoev.ru static void 2105771Sigor@sysoev.ru nxt_h1p_conn_free(nxt_task_t *task, void *obj, void *data) 2106771Sigor@sysoev.ru { 2107771Sigor@sysoev.ru nxt_conn_t *c; 2108771Sigor@sysoev.ru nxt_listen_event_t *lev; 2109771Sigor@sysoev.ru nxt_event_engine_t *engine; 2110771Sigor@sysoev.ru 2111771Sigor@sysoev.ru c = obj; 2112771Sigor@sysoev.ru 2113771Sigor@sysoev.ru nxt_debug(task, "h1p conn free"); 2114771Sigor@sysoev.ru 2115771Sigor@sysoev.ru engine = task->thread->engine; 2116771Sigor@sysoev.ru 2117771Sigor@sysoev.ru nxt_sockaddr_cache_free(engine, c); 2118771Sigor@sysoev.ru 2119771Sigor@sysoev.ru lev = c->listen; 2120771Sigor@sysoev.ru 2121771Sigor@sysoev.ru nxt_conn_free(task, c); 2122771Sigor@sysoev.ru 2123771Sigor@sysoev.ru nxt_router_listen_event_release(&engine->task, lev, NULL); 2124771Sigor@sysoev.ru } 21251270Sigor@sysoev.ru 21261270Sigor@sysoev.ru 21271270Sigor@sysoev.ru static void 21281270Sigor@sysoev.ru nxt_h1p_peer_connect(nxt_task_t *task, nxt_http_peer_t *peer) 21291270Sigor@sysoev.ru { 21301270Sigor@sysoev.ru nxt_mp_t *mp; 21311270Sigor@sysoev.ru nxt_int_t ret; 21321270Sigor@sysoev.ru nxt_conn_t *c, *client; 21331270Sigor@sysoev.ru nxt_h1proto_t *h1p; 21341270Sigor@sysoev.ru nxt_fd_event_t *socket; 21351270Sigor@sysoev.ru nxt_work_queue_t *wq; 21361270Sigor@sysoev.ru nxt_http_request_t *r; 21371270Sigor@sysoev.ru 21381270Sigor@sysoev.ru nxt_debug(task, "h1p peer connect"); 21391270Sigor@sysoev.ru 21401270Sigor@sysoev.ru peer->status = NXT_HTTP_UNSET; 21411270Sigor@sysoev.ru r = peer->request; 21421270Sigor@sysoev.ru 21431270Sigor@sysoev.ru mp = nxt_mp_create(1024, 128, 256, 32); 21441270Sigor@sysoev.ru 21451270Sigor@sysoev.ru if (nxt_slow_path(mp == NULL)) { 21461270Sigor@sysoev.ru goto fail; 21471270Sigor@sysoev.ru } 21481270Sigor@sysoev.ru 21491270Sigor@sysoev.ru h1p = nxt_mp_zalloc(mp, sizeof(nxt_h1proto_t)); 21501270Sigor@sysoev.ru if (nxt_slow_path(h1p == NULL)) { 21511270Sigor@sysoev.ru goto fail; 21521270Sigor@sysoev.ru } 21531270Sigor@sysoev.ru 21541270Sigor@sysoev.ru ret = nxt_http_parse_request_init(&h1p->parser, r->mem_pool); 21551270Sigor@sysoev.ru if (nxt_slow_path(ret != NXT_OK)) { 21561270Sigor@sysoev.ru goto fail; 21571270Sigor@sysoev.ru } 21581270Sigor@sysoev.ru 21591270Sigor@sysoev.ru c = nxt_conn_create(mp, task); 21601270Sigor@sysoev.ru if (nxt_slow_path(c == NULL)) { 21611270Sigor@sysoev.ru goto fail; 21621270Sigor@sysoev.ru } 21631270Sigor@sysoev.ru 21641270Sigor@sysoev.ru c->mem_pool = mp; 21651270Sigor@sysoev.ru h1p->conn = c; 21661270Sigor@sysoev.ru 21671270Sigor@sysoev.ru peer->proto.h1 = h1p; 21681270Sigor@sysoev.ru h1p->request = r; 21691270Sigor@sysoev.ru 21701270Sigor@sysoev.ru c->socket.data = peer; 21711394Sigor@sysoev.ru c->remote = peer->server->sockaddr; 21721270Sigor@sysoev.ru 21731270Sigor@sysoev.ru c->socket.write_ready = 1; 21741270Sigor@sysoev.ru c->write_state = &nxt_h1p_peer_connect_state; 21751270Sigor@sysoev.ru 21761270Sigor@sysoev.ru /* 21771270Sigor@sysoev.ru * TODO: queues should be implemented via client proto interface. 21781270Sigor@sysoev.ru */ 21791270Sigor@sysoev.ru client = r->proto.h1->conn; 21801270Sigor@sysoev.ru 21811270Sigor@sysoev.ru socket = &client->socket; 21821270Sigor@sysoev.ru wq = socket->read_work_queue; 21831270Sigor@sysoev.ru c->read_work_queue = wq; 21841270Sigor@sysoev.ru c->socket.read_work_queue = wq; 21851270Sigor@sysoev.ru c->read_timer.work_queue = wq; 21861270Sigor@sysoev.ru 21871270Sigor@sysoev.ru wq = socket->write_work_queue; 21881270Sigor@sysoev.ru c->write_work_queue = wq; 21891270Sigor@sysoev.ru c->socket.write_work_queue = wq; 21901270Sigor@sysoev.ru c->write_timer.work_queue = wq; 21911270Sigor@sysoev.ru /* TODO END */ 21921270Sigor@sysoev.ru 21931270Sigor@sysoev.ru nxt_conn_connect(task->thread->engine, c); 21941270Sigor@sysoev.ru 21951270Sigor@sysoev.ru return; 21961270Sigor@sysoev.ru 21971270Sigor@sysoev.ru fail: 21981270Sigor@sysoev.ru 21991270Sigor@sysoev.ru peer->status = NXT_HTTP_INTERNAL_SERVER_ERROR; 22001270Sigor@sysoev.ru 22011270Sigor@sysoev.ru r->state->error_handler(task, r, peer); 22021270Sigor@sysoev.ru } 22031270Sigor@sysoev.ru 22041270Sigor@sysoev.ru 22051270Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_peer_connect_state 22061270Sigor@sysoev.ru nxt_aligned(64) = 22071270Sigor@sysoev.ru { 22081270Sigor@sysoev.ru .ready_handler = nxt_h1p_peer_connected, 22091270Sigor@sysoev.ru .close_handler = nxt_h1p_peer_refused, 22101270Sigor@sysoev.ru .error_handler = nxt_h1p_peer_error, 22111270Sigor@sysoev.ru 22121270Sigor@sysoev.ru .timer_handler = nxt_h1p_peer_send_timeout, 22131270Sigor@sysoev.ru .timer_value = nxt_h1p_peer_timer_value, 22141270Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, proxy_timeout), 22151270Sigor@sysoev.ru }; 22161270Sigor@sysoev.ru 22171270Sigor@sysoev.ru 22181270Sigor@sysoev.ru static void 22191270Sigor@sysoev.ru nxt_h1p_peer_connected(nxt_task_t *task, void *obj, void *data) 22201270Sigor@sysoev.ru { 22211270Sigor@sysoev.ru nxt_http_peer_t *peer; 22221270Sigor@sysoev.ru nxt_http_request_t *r; 22231270Sigor@sysoev.ru 22241270Sigor@sysoev.ru peer = data; 22251270Sigor@sysoev.ru 22261270Sigor@sysoev.ru nxt_debug(task, "h1p peer connected"); 22271270Sigor@sysoev.ru 22281270Sigor@sysoev.ru r = peer->request; 22291270Sigor@sysoev.ru r->state->ready_handler(task, r, peer); 22301270Sigor@sysoev.ru } 22311270Sigor@sysoev.ru 22321270Sigor@sysoev.ru 22331270Sigor@sysoev.ru static void 22341270Sigor@sysoev.ru nxt_h1p_peer_refused(nxt_task_t *task, void *obj, void *data) 22351270Sigor@sysoev.ru { 22361270Sigor@sysoev.ru nxt_http_peer_t *peer; 22371270Sigor@sysoev.ru nxt_http_request_t *r; 22381270Sigor@sysoev.ru 22391270Sigor@sysoev.ru peer = data; 22401270Sigor@sysoev.ru 22411270Sigor@sysoev.ru nxt_debug(task, "h1p peer refused"); 22421270Sigor@sysoev.ru 22431270Sigor@sysoev.ru //peer->status = NXT_HTTP_SERVICE_UNAVAILABLE; 22441270Sigor@sysoev.ru peer->status = NXT_HTTP_BAD_GATEWAY; 22451270Sigor@sysoev.ru 22461270Sigor@sysoev.ru r = peer->request; 22471270Sigor@sysoev.ru r->state->error_handler(task, r, peer); 22481270Sigor@sysoev.ru } 22491270Sigor@sysoev.ru 22501270Sigor@sysoev.ru 22511270Sigor@sysoev.ru static void 22521270Sigor@sysoev.ru nxt_h1p_peer_header_send(nxt_task_t *task, nxt_http_peer_t *peer) 22531270Sigor@sysoev.ru { 22541270Sigor@sysoev.ru u_char *p; 22551270Sigor@sysoev.ru size_t size; 22561270Sigor@sysoev.ru nxt_buf_t *header, *body; 22571270Sigor@sysoev.ru nxt_conn_t *c; 22581270Sigor@sysoev.ru nxt_http_field_t *field; 22591270Sigor@sysoev.ru nxt_http_request_t *r; 22601270Sigor@sysoev.ru 22611270Sigor@sysoev.ru nxt_debug(task, "h1p peer header send"); 22621270Sigor@sysoev.ru 22631270Sigor@sysoev.ru r = peer->request; 22641270Sigor@sysoev.ru 22651270Sigor@sysoev.ru size = r->method->length + sizeof(" ") + r->target.length 22661505Sigor@sysoev.ru + sizeof(" HTTP/1.1\r\n") 22671505Sigor@sysoev.ru + sizeof("Connection: close\r\n") 22681270Sigor@sysoev.ru + sizeof("\r\n"); 22691270Sigor@sysoev.ru 22701270Sigor@sysoev.ru nxt_list_each(field, r->fields) { 22711270Sigor@sysoev.ru 22721270Sigor@sysoev.ru if (!field->hopbyhop) { 22731270Sigor@sysoev.ru size += field->name_length + field->value_length; 22741270Sigor@sysoev.ru size += nxt_length(": \r\n"); 22751270Sigor@sysoev.ru } 22761270Sigor@sysoev.ru 22771270Sigor@sysoev.ru } nxt_list_loop; 22781270Sigor@sysoev.ru 22791270Sigor@sysoev.ru header = nxt_http_buf_mem(task, r, size); 22801270Sigor@sysoev.ru if (nxt_slow_path(header == NULL)) { 22811270Sigor@sysoev.ru r->state->error_handler(task, r, peer); 22821270Sigor@sysoev.ru return; 22831270Sigor@sysoev.ru } 22841270Sigor@sysoev.ru 22851270Sigor@sysoev.ru p = header->mem.free; 22861270Sigor@sysoev.ru 22871270Sigor@sysoev.ru p = nxt_cpymem(p, r->method->start, r->method->length); 22881270Sigor@sysoev.ru *p++ = ' '; 22891270Sigor@sysoev.ru p = nxt_cpymem(p, r->target.start, r->target.length); 22901505Sigor@sysoev.ru p = nxt_cpymem(p, " HTTP/1.1\r\n", 11); 22911505Sigor@sysoev.ru p = nxt_cpymem(p, "Connection: close\r\n", 19); 22921270Sigor@sysoev.ru 22931270Sigor@sysoev.ru nxt_list_each(field, r->fields) { 22941270Sigor@sysoev.ru 22951270Sigor@sysoev.ru if (!field->hopbyhop) { 22961270Sigor@sysoev.ru p = nxt_cpymem(p, field->name, field->name_length); 22971270Sigor@sysoev.ru *p++ = ':'; *p++ = ' '; 22981270Sigor@sysoev.ru p = nxt_cpymem(p, field->value, field->value_length); 22991270Sigor@sysoev.ru *p++ = '\r'; *p++ = '\n'; 23001270Sigor@sysoev.ru } 23011270Sigor@sysoev.ru 23021270Sigor@sysoev.ru } nxt_list_loop; 23031270Sigor@sysoev.ru 23041270Sigor@sysoev.ru *p++ = '\r'; *p++ = '\n'; 23051270Sigor@sysoev.ru header->mem.free = p; 23061270Sigor@sysoev.ru size = p - header->mem.pos; 23071270Sigor@sysoev.ru 23081270Sigor@sysoev.ru c = peer->proto.h1->conn; 23091270Sigor@sysoev.ru c->write = header; 23101270Sigor@sysoev.ru c->write_state = &nxt_h1p_peer_header_send_state; 23111270Sigor@sysoev.ru 23121270Sigor@sysoev.ru if (r->body != NULL) { 23131403Smax.romanov@nginx.com if (nxt_buf_is_file(r->body)) { 23141403Smax.romanov@nginx.com body = nxt_buf_file_alloc(r->mem_pool, 0, 0); 23151403Smax.romanov@nginx.com 23161403Smax.romanov@nginx.com } else { 23171403Smax.romanov@nginx.com body = nxt_buf_mem_alloc(r->mem_pool, 0, 0); 23181403Smax.romanov@nginx.com } 23191403Smax.romanov@nginx.com 23201270Sigor@sysoev.ru if (nxt_slow_path(body == NULL)) { 23211270Sigor@sysoev.ru r->state->error_handler(task, r, peer); 23221270Sigor@sysoev.ru return; 23231270Sigor@sysoev.ru } 23241270Sigor@sysoev.ru 23251270Sigor@sysoev.ru header->next = body; 23261270Sigor@sysoev.ru 23271403Smax.romanov@nginx.com if (nxt_buf_is_file(r->body)) { 23281403Smax.romanov@nginx.com body->file = r->body->file; 23291403Smax.romanov@nginx.com body->file_end = r->body->file_end; 23301403Smax.romanov@nginx.com 23311403Smax.romanov@nginx.com } else { 23321403Smax.romanov@nginx.com body->mem = r->body->mem; 23331403Smax.romanov@nginx.com } 23341403Smax.romanov@nginx.com 23351403Smax.romanov@nginx.com size += nxt_buf_used_size(body); 23361270Sigor@sysoev.ru 23371270Sigor@sysoev.ru // nxt_mp_retain(r->mem_pool); 23381270Sigor@sysoev.ru } 23391270Sigor@sysoev.ru 23401270Sigor@sysoev.ru if (size > 16384) { 23411270Sigor@sysoev.ru /* Use proxy_send_timeout instead of proxy_timeout. */ 23421270Sigor@sysoev.ru c->write_state = &nxt_h1p_peer_header_body_send_state; 23431270Sigor@sysoev.ru } 23441270Sigor@sysoev.ru 23451270Sigor@sysoev.ru nxt_conn_write(task->thread->engine, c); 23461270Sigor@sysoev.ru } 23471270Sigor@sysoev.ru 23481270Sigor@sysoev.ru 23491270Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_peer_header_send_state 23501270Sigor@sysoev.ru nxt_aligned(64) = 23511270Sigor@sysoev.ru { 23521270Sigor@sysoev.ru .ready_handler = nxt_h1p_peer_header_sent, 23531270Sigor@sysoev.ru .error_handler = nxt_h1p_peer_error, 23541270Sigor@sysoev.ru 23551270Sigor@sysoev.ru .timer_handler = nxt_h1p_peer_send_timeout, 23561270Sigor@sysoev.ru .timer_value = nxt_h1p_peer_timer_value, 23571270Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, proxy_timeout), 23581270Sigor@sysoev.ru }; 23591270Sigor@sysoev.ru 23601270Sigor@sysoev.ru 23611270Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_peer_header_body_send_state 23621270Sigor@sysoev.ru nxt_aligned(64) = 23631270Sigor@sysoev.ru { 23641270Sigor@sysoev.ru .ready_handler = nxt_h1p_peer_header_sent, 23651270Sigor@sysoev.ru .error_handler = nxt_h1p_peer_error, 23661270Sigor@sysoev.ru 23671270Sigor@sysoev.ru .timer_handler = nxt_h1p_peer_send_timeout, 23681270Sigor@sysoev.ru .timer_value = nxt_h1p_peer_timer_value, 23691270Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, proxy_send_timeout), 23701270Sigor@sysoev.ru .timer_autoreset = 1, 23711270Sigor@sysoev.ru }; 23721270Sigor@sysoev.ru 23731270Sigor@sysoev.ru 23741270Sigor@sysoev.ru static void 23751270Sigor@sysoev.ru nxt_h1p_peer_header_sent(nxt_task_t *task, void *obj, void *data) 23761270Sigor@sysoev.ru { 23771270Sigor@sysoev.ru nxt_conn_t *c; 23781270Sigor@sysoev.ru nxt_http_peer_t *peer; 23791270Sigor@sysoev.ru nxt_http_request_t *r; 23801270Sigor@sysoev.ru nxt_event_engine_t *engine; 23811270Sigor@sysoev.ru 23821270Sigor@sysoev.ru c = obj; 23831270Sigor@sysoev.ru peer = data; 23841270Sigor@sysoev.ru 23851270Sigor@sysoev.ru nxt_debug(task, "h1p peer header sent"); 23861270Sigor@sysoev.ru 23871270Sigor@sysoev.ru engine = task->thread->engine; 23881270Sigor@sysoev.ru 23891270Sigor@sysoev.ru c->write = nxt_sendbuf_completion(task, &engine->fast_work_queue, c->write); 23901270Sigor@sysoev.ru 23911403Smax.romanov@nginx.com if (c->write != NULL) { 23921403Smax.romanov@nginx.com nxt_conn_write(engine, c); 23931270Sigor@sysoev.ru return; 23941270Sigor@sysoev.ru } 23951270Sigor@sysoev.ru 23961403Smax.romanov@nginx.com r = peer->request; 23971403Smax.romanov@nginx.com r->state->ready_handler(task, r, peer); 23981270Sigor@sysoev.ru } 23991270Sigor@sysoev.ru 24001270Sigor@sysoev.ru 24011270Sigor@sysoev.ru static void 24021270Sigor@sysoev.ru nxt_h1p_peer_header_read(nxt_task_t *task, nxt_http_peer_t *peer) 24031270Sigor@sysoev.ru { 24041270Sigor@sysoev.ru nxt_conn_t *c; 24051270Sigor@sysoev.ru 24061270Sigor@sysoev.ru nxt_debug(task, "h1p peer header read"); 24071270Sigor@sysoev.ru 24081270Sigor@sysoev.ru c = peer->proto.h1->conn; 24091270Sigor@sysoev.ru 24101270Sigor@sysoev.ru if (c->write_timer.enabled) { 24111270Sigor@sysoev.ru c->read_state = &nxt_h1p_peer_header_read_state; 24121270Sigor@sysoev.ru 24131270Sigor@sysoev.ru } else { 24141270Sigor@sysoev.ru c->read_state = &nxt_h1p_peer_header_read_timer_state; 24151270Sigor@sysoev.ru } 24161270Sigor@sysoev.ru 24171270Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 24181270Sigor@sysoev.ru } 24191270Sigor@sysoev.ru 24201270Sigor@sysoev.ru 24211270Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_peer_header_read_state 24221270Sigor@sysoev.ru nxt_aligned(64) = 24231270Sigor@sysoev.ru { 24241270Sigor@sysoev.ru .ready_handler = nxt_h1p_peer_header_read_done, 24251270Sigor@sysoev.ru .close_handler = nxt_h1p_peer_closed, 24261270Sigor@sysoev.ru .error_handler = nxt_h1p_peer_error, 24271270Sigor@sysoev.ru 24281270Sigor@sysoev.ru .io_read_handler = nxt_h1p_peer_io_read_handler, 24291270Sigor@sysoev.ru }; 24301270Sigor@sysoev.ru 24311270Sigor@sysoev.ru 24321270Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_peer_header_read_timer_state 24331270Sigor@sysoev.ru nxt_aligned(64) = 24341270Sigor@sysoev.ru { 24351270Sigor@sysoev.ru .ready_handler = nxt_h1p_peer_header_read_done, 24361270Sigor@sysoev.ru .close_handler = nxt_h1p_peer_closed, 24371270Sigor@sysoev.ru .error_handler = nxt_h1p_peer_error, 24381270Sigor@sysoev.ru 24391270Sigor@sysoev.ru .io_read_handler = nxt_h1p_peer_io_read_handler, 24401270Sigor@sysoev.ru 24411270Sigor@sysoev.ru .timer_handler = nxt_h1p_peer_read_timeout, 24421270Sigor@sysoev.ru .timer_value = nxt_h1p_peer_timer_value, 24431270Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, proxy_timeout), 24441270Sigor@sysoev.ru }; 24451270Sigor@sysoev.ru 24461270Sigor@sysoev.ru 24471270Sigor@sysoev.ru static ssize_t 24481270Sigor@sysoev.ru nxt_h1p_peer_io_read_handler(nxt_task_t *task, nxt_conn_t *c) 24491270Sigor@sysoev.ru { 24501270Sigor@sysoev.ru size_t size; 24511270Sigor@sysoev.ru ssize_t n; 24521270Sigor@sysoev.ru nxt_buf_t *b; 24531270Sigor@sysoev.ru nxt_http_peer_t *peer; 24541270Sigor@sysoev.ru nxt_socket_conf_t *skcf; 24551270Sigor@sysoev.ru nxt_http_request_t *r; 24561270Sigor@sysoev.ru 24571270Sigor@sysoev.ru peer = c->socket.data; 24581270Sigor@sysoev.ru r = peer->request; 24591270Sigor@sysoev.ru b = c->read; 24601270Sigor@sysoev.ru 24611270Sigor@sysoev.ru if (b == NULL) { 24621270Sigor@sysoev.ru skcf = r->conf->socket_conf; 24631270Sigor@sysoev.ru 24641270Sigor@sysoev.ru size = (peer->header_received) ? skcf->proxy_buffer_size 24651270Sigor@sysoev.ru : skcf->proxy_header_buffer_size; 24661270Sigor@sysoev.ru 24671270Sigor@sysoev.ru nxt_debug(task, "h1p peer io read: %z", size); 24681270Sigor@sysoev.ru 24691270Sigor@sysoev.ru b = nxt_http_proxy_buf_mem_alloc(task, r, size); 24701270Sigor@sysoev.ru if (nxt_slow_path(b == NULL)) { 24711270Sigor@sysoev.ru c->socket.error = NXT_ENOMEM; 24721270Sigor@sysoev.ru return NXT_ERROR; 24731270Sigor@sysoev.ru } 24741270Sigor@sysoev.ru } 24751270Sigor@sysoev.ru 24761270Sigor@sysoev.ru n = c->io->recvbuf(c, b); 24771270Sigor@sysoev.ru 24781270Sigor@sysoev.ru if (n > 0) { 24791270Sigor@sysoev.ru c->read = b; 24801270Sigor@sysoev.ru 24811270Sigor@sysoev.ru } else { 24821270Sigor@sysoev.ru c->read = NULL; 24831270Sigor@sysoev.ru nxt_http_proxy_buf_mem_free(task, r, b); 24841270Sigor@sysoev.ru } 24851270Sigor@sysoev.ru 24861270Sigor@sysoev.ru return n; 24871270Sigor@sysoev.ru } 24881270Sigor@sysoev.ru 24891270Sigor@sysoev.ru 24901270Sigor@sysoev.ru static void 24911270Sigor@sysoev.ru nxt_h1p_peer_header_read_done(nxt_task_t *task, void *obj, void *data) 24921270Sigor@sysoev.ru { 24931270Sigor@sysoev.ru nxt_int_t ret; 24941270Sigor@sysoev.ru nxt_buf_t *b; 24951270Sigor@sysoev.ru nxt_conn_t *c; 24961505Sigor@sysoev.ru nxt_h1proto_t *h1p; 24971270Sigor@sysoev.ru nxt_http_peer_t *peer; 24981270Sigor@sysoev.ru nxt_http_request_t *r; 24991270Sigor@sysoev.ru nxt_event_engine_t *engine; 25001270Sigor@sysoev.ru 25011270Sigor@sysoev.ru c = obj; 25021270Sigor@sysoev.ru peer = data; 25031270Sigor@sysoev.ru 25041270Sigor@sysoev.ru nxt_debug(task, "h1p peer header read done"); 25051270Sigor@sysoev.ru 25061270Sigor@sysoev.ru b = c->read; 25071270Sigor@sysoev.ru 25081270Sigor@sysoev.ru ret = nxt_h1p_peer_header_parse(peer, &b->mem); 25091270Sigor@sysoev.ru 25101270Sigor@sysoev.ru r = peer->request; 25111270Sigor@sysoev.ru 25121270Sigor@sysoev.ru ret = nxt_expect(NXT_DONE, ret); 25131270Sigor@sysoev.ru 25141270Sigor@sysoev.ru if (ret != NXT_AGAIN) { 25151270Sigor@sysoev.ru engine = task->thread->engine; 25161270Sigor@sysoev.ru nxt_timer_disable(engine, &c->write_timer); 25171270Sigor@sysoev.ru nxt_timer_disable(engine, &c->read_timer); 25181270Sigor@sysoev.ru } 25191270Sigor@sysoev.ru 25201270Sigor@sysoev.ru switch (ret) { 25211270Sigor@sysoev.ru 25221270Sigor@sysoev.ru case NXT_DONE: 25231270Sigor@sysoev.ru peer->fields = peer->proto.h1->parser.fields; 25241270Sigor@sysoev.ru 25251270Sigor@sysoev.ru ret = nxt_http_fields_process(peer->fields, 25261270Sigor@sysoev.ru &nxt_h1p_peer_fields_hash, r); 25271270Sigor@sysoev.ru if (nxt_slow_path(ret != NXT_OK)) { 25281270Sigor@sysoev.ru peer->status = NXT_HTTP_INTERNAL_SERVER_ERROR; 25291270Sigor@sysoev.ru break; 25301270Sigor@sysoev.ru } 25311270Sigor@sysoev.ru 25321270Sigor@sysoev.ru c->read = NULL; 25331270Sigor@sysoev.ru 25341505Sigor@sysoev.ru peer->header_received = 1; 25351505Sigor@sysoev.ru 25361505Sigor@sysoev.ru h1p = peer->proto.h1; 25371505Sigor@sysoev.ru 25381505Sigor@sysoev.ru if (h1p->chunked) { 25391505Sigor@sysoev.ru if (r->resp.content_length != NULL) { 25401505Sigor@sysoev.ru peer->status = NXT_HTTP_BAD_GATEWAY; 25411505Sigor@sysoev.ru break; 25421505Sigor@sysoev.ru } 25431505Sigor@sysoev.ru 25441505Sigor@sysoev.ru h1p->chunked_parse.mem_pool = c->mem_pool; 25451505Sigor@sysoev.ru 25461505Sigor@sysoev.ru } else if (r->resp.content_length_n > 0) { 25471505Sigor@sysoev.ru h1p->remainder = r->resp.content_length_n; 25481270Sigor@sysoev.ru } 25491270Sigor@sysoev.ru 25501505Sigor@sysoev.ru if (nxt_buf_mem_used_size(&b->mem) != 0) { 25511505Sigor@sysoev.ru nxt_h1p_peer_body_process(task, peer, b); 25521505Sigor@sysoev.ru return; 25531505Sigor@sysoev.ru } 25541270Sigor@sysoev.ru 25551270Sigor@sysoev.ru r->state->ready_handler(task, r, peer); 25561270Sigor@sysoev.ru return; 25571270Sigor@sysoev.ru 25581270Sigor@sysoev.ru case NXT_AGAIN: 25591270Sigor@sysoev.ru if (nxt_buf_mem_free_size(&b->mem) != 0) { 25601270Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 25611270Sigor@sysoev.ru return; 25621270Sigor@sysoev.ru } 25631270Sigor@sysoev.ru 25641270Sigor@sysoev.ru /* Fall through. */ 25651270Sigor@sysoev.ru 25661270Sigor@sysoev.ru default: 25671270Sigor@sysoev.ru case NXT_ERROR: 25681270Sigor@sysoev.ru case NXT_HTTP_PARSE_INVALID: 25691270Sigor@sysoev.ru case NXT_HTTP_PARSE_UNSUPPORTED_VERSION: 25701270Sigor@sysoev.ru case NXT_HTTP_PARSE_TOO_LARGE_FIELD: 25711270Sigor@sysoev.ru peer->status = NXT_HTTP_BAD_GATEWAY; 25721270Sigor@sysoev.ru break; 25731270Sigor@sysoev.ru } 25741270Sigor@sysoev.ru 25751270Sigor@sysoev.ru nxt_http_proxy_buf_mem_free(task, r, b); 25761270Sigor@sysoev.ru 25771270Sigor@sysoev.ru r->state->error_handler(task, r, peer); 25781270Sigor@sysoev.ru } 25791270Sigor@sysoev.ru 25801270Sigor@sysoev.ru 25811270Sigor@sysoev.ru static nxt_int_t 25821270Sigor@sysoev.ru nxt_h1p_peer_header_parse(nxt_http_peer_t *peer, nxt_buf_mem_t *bm) 25831270Sigor@sysoev.ru { 25841270Sigor@sysoev.ru u_char *p; 25851270Sigor@sysoev.ru size_t length; 25861270Sigor@sysoev.ru nxt_int_t status; 25871270Sigor@sysoev.ru 25881270Sigor@sysoev.ru if (peer->status < 0) { 25891270Sigor@sysoev.ru length = nxt_buf_mem_used_size(bm); 25901270Sigor@sysoev.ru 25911270Sigor@sysoev.ru if (nxt_slow_path(length < 12)) { 25921270Sigor@sysoev.ru return NXT_AGAIN; 25931270Sigor@sysoev.ru } 25941270Sigor@sysoev.ru 25951270Sigor@sysoev.ru p = bm->pos; 25961270Sigor@sysoev.ru 25971270Sigor@sysoev.ru if (nxt_slow_path(nxt_memcmp(p, "HTTP/1.", 7) != 0 25981270Sigor@sysoev.ru || (p[7] != '0' && p[7] != '1'))) 25991270Sigor@sysoev.ru { 26001270Sigor@sysoev.ru return NXT_ERROR; 26011270Sigor@sysoev.ru } 26021270Sigor@sysoev.ru 26031270Sigor@sysoev.ru status = nxt_int_parse(&p[9], 3); 26041270Sigor@sysoev.ru 26051270Sigor@sysoev.ru if (nxt_slow_path(status < 0)) { 26061270Sigor@sysoev.ru return NXT_ERROR; 26071270Sigor@sysoev.ru } 26081270Sigor@sysoev.ru 26091270Sigor@sysoev.ru p += 12; 26101270Sigor@sysoev.ru length -= 12; 26111270Sigor@sysoev.ru 26121270Sigor@sysoev.ru p = nxt_memchr(p, '\n', length); 26131270Sigor@sysoev.ru 26141270Sigor@sysoev.ru if (nxt_slow_path(p == NULL)) { 26151270Sigor@sysoev.ru return NXT_AGAIN; 26161270Sigor@sysoev.ru } 26171270Sigor@sysoev.ru 26181270Sigor@sysoev.ru bm->pos = p + 1; 26191270Sigor@sysoev.ru peer->status = status; 26201270Sigor@sysoev.ru } 26211270Sigor@sysoev.ru 26221270Sigor@sysoev.ru return nxt_http_parse_fields(&peer->proto.h1->parser, bm); 26231270Sigor@sysoev.ru } 26241270Sigor@sysoev.ru 26251270Sigor@sysoev.ru 26261270Sigor@sysoev.ru static void 26271270Sigor@sysoev.ru nxt_h1p_peer_read(nxt_task_t *task, nxt_http_peer_t *peer) 26281270Sigor@sysoev.ru { 26291270Sigor@sysoev.ru nxt_conn_t *c; 26301270Sigor@sysoev.ru 26311270Sigor@sysoev.ru nxt_debug(task, "h1p peer read"); 26321270Sigor@sysoev.ru 26331270Sigor@sysoev.ru c = peer->proto.h1->conn; 26341270Sigor@sysoev.ru c->read_state = &nxt_h1p_peer_read_state; 26351270Sigor@sysoev.ru 26361270Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 26371270Sigor@sysoev.ru } 26381270Sigor@sysoev.ru 26391270Sigor@sysoev.ru 26401270Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_peer_read_state 26411270Sigor@sysoev.ru nxt_aligned(64) = 26421270Sigor@sysoev.ru { 26431270Sigor@sysoev.ru .ready_handler = nxt_h1p_peer_read_done, 26441270Sigor@sysoev.ru .close_handler = nxt_h1p_peer_closed, 26451270Sigor@sysoev.ru .error_handler = nxt_h1p_peer_error, 26461270Sigor@sysoev.ru 26471270Sigor@sysoev.ru .io_read_handler = nxt_h1p_peer_io_read_handler, 26481270Sigor@sysoev.ru 26491270Sigor@sysoev.ru .timer_handler = nxt_h1p_peer_read_timeout, 26501270Sigor@sysoev.ru .timer_value = nxt_h1p_peer_timer_value, 26511270Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, proxy_read_timeout), 26521270Sigor@sysoev.ru .timer_autoreset = 1, 26531270Sigor@sysoev.ru }; 26541270Sigor@sysoev.ru 26551270Sigor@sysoev.ru 26561270Sigor@sysoev.ru static void 26571270Sigor@sysoev.ru nxt_h1p_peer_read_done(nxt_task_t *task, void *obj, void *data) 26581270Sigor@sysoev.ru { 26591505Sigor@sysoev.ru nxt_buf_t *out; 26601505Sigor@sysoev.ru nxt_conn_t *c; 26611505Sigor@sysoev.ru nxt_http_peer_t *peer; 26621270Sigor@sysoev.ru 26631270Sigor@sysoev.ru c = obj; 26641270Sigor@sysoev.ru peer = data; 26651270Sigor@sysoev.ru 26661270Sigor@sysoev.ru nxt_debug(task, "h1p peer read done"); 26671270Sigor@sysoev.ru 26681505Sigor@sysoev.ru out = c->read; 26691270Sigor@sysoev.ru c->read = NULL; 26701270Sigor@sysoev.ru 26711505Sigor@sysoev.ru nxt_h1p_peer_body_process(task, peer, out); 26721505Sigor@sysoev.ru } 26731505Sigor@sysoev.ru 26741505Sigor@sysoev.ru 26751505Sigor@sysoev.ru static void 26761505Sigor@sysoev.ru nxt_h1p_peer_body_process(nxt_task_t *task, nxt_http_peer_t *peer, 26771505Sigor@sysoev.ru nxt_buf_t *out) 26781505Sigor@sysoev.ru { 26791505Sigor@sysoev.ru size_t length; 26801505Sigor@sysoev.ru nxt_h1proto_t *h1p; 26811505Sigor@sysoev.ru nxt_http_request_t *r; 26821505Sigor@sysoev.ru 26831505Sigor@sysoev.ru h1p = peer->proto.h1; 26841505Sigor@sysoev.ru 26851505Sigor@sysoev.ru if (h1p->chunked) { 26861505Sigor@sysoev.ru out = nxt_http_chunk_parse(task, &h1p->chunked_parse, out); 26871505Sigor@sysoev.ru 26881505Sigor@sysoev.ru if (h1p->chunked_parse.chunk_error || h1p->chunked_parse.error) { 26891505Sigor@sysoev.ru peer->status = NXT_HTTP_BAD_GATEWAY; 26901505Sigor@sysoev.ru r = peer->request; 26911505Sigor@sysoev.ru r->state->error_handler(task, r, peer); 26921505Sigor@sysoev.ru return; 26931505Sigor@sysoev.ru } 26941505Sigor@sysoev.ru 26951505Sigor@sysoev.ru if (h1p->chunked_parse.last) { 26961505Sigor@sysoev.ru nxt_buf_chain_add(&out, nxt_http_buf_last(peer->request)); 26971505Sigor@sysoev.ru peer->closed = 1; 26981505Sigor@sysoev.ru } 26991505Sigor@sysoev.ru 27001505Sigor@sysoev.ru } else if (h1p->remainder > 0) { 27011505Sigor@sysoev.ru length = nxt_buf_chain_length(out); 27021505Sigor@sysoev.ru h1p->remainder -= length; 27031505Sigor@sysoev.ru } 27041505Sigor@sysoev.ru 27051505Sigor@sysoev.ru peer->body = out; 27061505Sigor@sysoev.ru 27071270Sigor@sysoev.ru r = peer->request; 27081270Sigor@sysoev.ru r->state->ready_handler(task, r, peer); 27091270Sigor@sysoev.ru } 27101270Sigor@sysoev.ru 27111270Sigor@sysoev.ru 27121270Sigor@sysoev.ru static void 27131270Sigor@sysoev.ru nxt_h1p_peer_closed(nxt_task_t *task, void *obj, void *data) 27141270Sigor@sysoev.ru { 27151270Sigor@sysoev.ru nxt_http_peer_t *peer; 27161270Sigor@sysoev.ru nxt_http_request_t *r; 27171270Sigor@sysoev.ru 27181270Sigor@sysoev.ru peer = data; 27191270Sigor@sysoev.ru 27201270Sigor@sysoev.ru nxt_debug(task, "h1p peer closed"); 27211270Sigor@sysoev.ru 27221270Sigor@sysoev.ru r = peer->request; 27231270Sigor@sysoev.ru 27241270Sigor@sysoev.ru if (peer->header_received) { 27251270Sigor@sysoev.ru peer->body = nxt_http_buf_last(r); 27261270Sigor@sysoev.ru peer->closed = 1; 27271505Sigor@sysoev.ru r->inconsistent = (peer->proto.h1->remainder != 0); 27281270Sigor@sysoev.ru 27291270Sigor@sysoev.ru r->state->ready_handler(task, r, peer); 27301270Sigor@sysoev.ru 27311270Sigor@sysoev.ru } else { 27321270Sigor@sysoev.ru peer->status = NXT_HTTP_BAD_GATEWAY; 27331270Sigor@sysoev.ru 27341270Sigor@sysoev.ru r->state->error_handler(task, r, peer); 27351270Sigor@sysoev.ru } 27361270Sigor@sysoev.ru } 27371270Sigor@sysoev.ru 27381270Sigor@sysoev.ru 27391270Sigor@sysoev.ru static void 27401270Sigor@sysoev.ru nxt_h1p_peer_error(nxt_task_t *task, void *obj, void *data) 27411270Sigor@sysoev.ru { 27421270Sigor@sysoev.ru nxt_http_peer_t *peer; 27431270Sigor@sysoev.ru nxt_http_request_t *r; 27441270Sigor@sysoev.ru 27451270Sigor@sysoev.ru peer = data; 27461270Sigor@sysoev.ru 27471270Sigor@sysoev.ru nxt_debug(task, "h1p peer error"); 27481270Sigor@sysoev.ru 27491270Sigor@sysoev.ru peer->status = NXT_HTTP_BAD_GATEWAY; 27501270Sigor@sysoev.ru 27511270Sigor@sysoev.ru r = peer->request; 27521270Sigor@sysoev.ru r->state->error_handler(task, r, peer); 27531270Sigor@sysoev.ru } 27541270Sigor@sysoev.ru 27551270Sigor@sysoev.ru 27561270Sigor@sysoev.ru static void 27571270Sigor@sysoev.ru nxt_h1p_peer_send_timeout(nxt_task_t *task, void *obj, void *data) 27581270Sigor@sysoev.ru { 27591270Sigor@sysoev.ru nxt_conn_t *c; 27601270Sigor@sysoev.ru nxt_timer_t *timer; 27611270Sigor@sysoev.ru nxt_http_peer_t *peer; 27621270Sigor@sysoev.ru nxt_http_request_t *r; 27631270Sigor@sysoev.ru 27641270Sigor@sysoev.ru timer = obj; 27651270Sigor@sysoev.ru 27661270Sigor@sysoev.ru nxt_debug(task, "h1p peer send timeout"); 27671270Sigor@sysoev.ru 27681270Sigor@sysoev.ru c = nxt_write_timer_conn(timer); 27691270Sigor@sysoev.ru c->block_write = 1; 27701270Sigor@sysoev.ru c->block_read = 1; 27711270Sigor@sysoev.ru 27721270Sigor@sysoev.ru peer = c->socket.data; 27731270Sigor@sysoev.ru peer->status = NXT_HTTP_GATEWAY_TIMEOUT; 27741270Sigor@sysoev.ru 27751270Sigor@sysoev.ru r = peer->request; 27761270Sigor@sysoev.ru r->state->error_handler(task, r, peer); 27771270Sigor@sysoev.ru } 27781270Sigor@sysoev.ru 27791270Sigor@sysoev.ru 27801270Sigor@sysoev.ru static void 27811270Sigor@sysoev.ru nxt_h1p_peer_read_timeout(nxt_task_t *task, void *obj, void *data) 27821270Sigor@sysoev.ru { 27831270Sigor@sysoev.ru nxt_conn_t *c; 27841270Sigor@sysoev.ru nxt_timer_t *timer; 27851270Sigor@sysoev.ru nxt_http_peer_t *peer; 27861270Sigor@sysoev.ru nxt_http_request_t *r; 27871270Sigor@sysoev.ru 27881270Sigor@sysoev.ru timer = obj; 27891270Sigor@sysoev.ru 27901270Sigor@sysoev.ru nxt_debug(task, "h1p peer read timeout"); 27911270Sigor@sysoev.ru 27921270Sigor@sysoev.ru c = nxt_read_timer_conn(timer); 27931270Sigor@sysoev.ru c->block_write = 1; 27941270Sigor@sysoev.ru c->block_read = 1; 27951270Sigor@sysoev.ru 27961270Sigor@sysoev.ru peer = c->socket.data; 27971270Sigor@sysoev.ru peer->status = NXT_HTTP_GATEWAY_TIMEOUT; 27981270Sigor@sysoev.ru 27991270Sigor@sysoev.ru r = peer->request; 28001270Sigor@sysoev.ru r->state->error_handler(task, r, peer); 28011270Sigor@sysoev.ru } 28021270Sigor@sysoev.ru 28031270Sigor@sysoev.ru 28041270Sigor@sysoev.ru static nxt_msec_t 28051270Sigor@sysoev.ru nxt_h1p_peer_timer_value(nxt_conn_t *c, uintptr_t data) 28061270Sigor@sysoev.ru { 28071270Sigor@sysoev.ru nxt_http_peer_t *peer; 28081270Sigor@sysoev.ru 28091270Sigor@sysoev.ru peer = c->socket.data; 28101270Sigor@sysoev.ru 28111270Sigor@sysoev.ru return nxt_value_at(nxt_msec_t, peer->request->conf->socket_conf, data); 28121270Sigor@sysoev.ru } 28131270Sigor@sysoev.ru 28141270Sigor@sysoev.ru 28151270Sigor@sysoev.ru static void 28161270Sigor@sysoev.ru nxt_h1p_peer_close(nxt_task_t *task, nxt_http_peer_t *peer) 28171270Sigor@sysoev.ru { 28181270Sigor@sysoev.ru nxt_conn_t *c; 28191270Sigor@sysoev.ru 28201270Sigor@sysoev.ru nxt_debug(task, "h1p peer close"); 28211270Sigor@sysoev.ru 28221270Sigor@sysoev.ru peer->closed = 1; 28231270Sigor@sysoev.ru 28241270Sigor@sysoev.ru c = peer->proto.h1->conn; 28251270Sigor@sysoev.ru task = &c->task; 28261270Sigor@sysoev.ru c->socket.task = task; 28271270Sigor@sysoev.ru c->read_timer.task = task; 28281270Sigor@sysoev.ru c->write_timer.task = task; 28291270Sigor@sysoev.ru 28301270Sigor@sysoev.ru if (c->socket.fd != -1) { 28311270Sigor@sysoev.ru c->write_state = &nxt_h1p_peer_close_state; 28321270Sigor@sysoev.ru 28331270Sigor@sysoev.ru nxt_conn_close(task->thread->engine, c); 28341270Sigor@sysoev.ru 28351270Sigor@sysoev.ru } else { 28361270Sigor@sysoev.ru nxt_h1p_peer_free(task, c, NULL); 28371270Sigor@sysoev.ru } 28381270Sigor@sysoev.ru } 28391270Sigor@sysoev.ru 28401270Sigor@sysoev.ru 28411270Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_peer_close_state 28421270Sigor@sysoev.ru nxt_aligned(64) = 28431270Sigor@sysoev.ru { 28441270Sigor@sysoev.ru .ready_handler = nxt_h1p_peer_free, 28451270Sigor@sysoev.ru }; 28461270Sigor@sysoev.ru 28471270Sigor@sysoev.ru 28481270Sigor@sysoev.ru static void 28491270Sigor@sysoev.ru nxt_h1p_peer_free(nxt_task_t *task, void *obj, void *data) 28501270Sigor@sysoev.ru { 28511270Sigor@sysoev.ru nxt_conn_t *c; 28521270Sigor@sysoev.ru 28531270Sigor@sysoev.ru c = obj; 28541270Sigor@sysoev.ru 28551270Sigor@sysoev.ru nxt_debug(task, "h1p peer free"); 28561270Sigor@sysoev.ru 28571270Sigor@sysoev.ru nxt_conn_free(task, c); 28581270Sigor@sysoev.ru } 28591505Sigor@sysoev.ru 28601505Sigor@sysoev.ru 28611505Sigor@sysoev.ru static nxt_int_t 28621505Sigor@sysoev.ru nxt_h1p_peer_transfer_encoding(void *ctx, nxt_http_field_t *field, 28631505Sigor@sysoev.ru uintptr_t data) 28641505Sigor@sysoev.ru { 28651505Sigor@sysoev.ru nxt_http_request_t *r; 28661505Sigor@sysoev.ru 28671505Sigor@sysoev.ru r = ctx; 28681505Sigor@sysoev.ru field->skip = 1; 28691505Sigor@sysoev.ru 28701505Sigor@sysoev.ru if (field->value_length == 7 28711505Sigor@sysoev.ru && nxt_memcmp(field->value, "chunked", 7) == 0) 28721505Sigor@sysoev.ru { 28731505Sigor@sysoev.ru r->peer->proto.h1->chunked = 1; 28741505Sigor@sysoev.ru } 28751505Sigor@sysoev.ru 28761505Sigor@sysoev.ru return NXT_OK; 28771505Sigor@sysoev.ru } 2878