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