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