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 11431Sigor@sysoev.ru static void nxt_h1p_read_header(nxt_task_t *task, void *obj, void *data); 12431Sigor@sysoev.ru static void nxt_h1p_header_parse(nxt_task_t *task, void *obj, void *data); 13431Sigor@sysoev.ru static nxt_int_t nxt_h1p_connection(void *ctx, nxt_http_field_t *field, 14431Sigor@sysoev.ru uintptr_t data); 15431Sigor@sysoev.ru static nxt_int_t nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field, 16431Sigor@sysoev.ru uintptr_t data); 17431Sigor@sysoev.ru static void nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r); 18431Sigor@sysoev.ru static void nxt_h1p_body_read(nxt_task_t *task, void *obj, void *data); 19431Sigor@sysoev.ru static void nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r); 20431Sigor@sysoev.ru static void nxt_h1p_request_header_send(nxt_task_t *task, 21431Sigor@sysoev.ru nxt_http_request_t *r); 22431Sigor@sysoev.ru static void nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, 23431Sigor@sysoev.ru nxt_buf_t *out); 24431Sigor@sysoev.ru static nxt_buf_t *nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r, 25431Sigor@sysoev.ru nxt_buf_t *out); 26431Sigor@sysoev.ru static void nxt_h1p_sent(nxt_task_t *task, void *obj, void *data); 27431Sigor@sysoev.ru static void nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto); 28431Sigor@sysoev.ru static void nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, 29431Sigor@sysoev.ru nxt_conn_t *c); 30431Sigor@sysoev.ru static void nxt_h1p_close(nxt_task_t *task, nxt_conn_t *c); 31431Sigor@sysoev.ru static void nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data); 32431Sigor@sysoev.ru static void nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data); 33431Sigor@sysoev.ru static void nxt_h1p_conn_timeout(nxt_task_t *task, void *obj, void *data); 34431Sigor@sysoev.ru static nxt_msec_t nxt_h1p_timeout_value(nxt_conn_t *c, uintptr_t data); 35431Sigor@sysoev.ru 36431Sigor@sysoev.ru 37431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_idle_state; 38431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_read_header_state; 39431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_read_body_state; 40431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_send_state; 41431Sigor@sysoev.ru 42431Sigor@sysoev.ru 43431Sigor@sysoev.ru const nxt_http_proto_body_read_t nxt_http_proto_body_read[3] = { 44431Sigor@sysoev.ru nxt_h1p_request_body_read, 45431Sigor@sysoev.ru NULL, 46431Sigor@sysoev.ru NULL, 47431Sigor@sysoev.ru }; 48431Sigor@sysoev.ru 49431Sigor@sysoev.ru 50431Sigor@sysoev.ru const nxt_http_proto_local_addr_t nxt_http_proto_local_addr[3] = { 51431Sigor@sysoev.ru nxt_h1p_request_local_addr, 52431Sigor@sysoev.ru NULL, 53431Sigor@sysoev.ru NULL, 54431Sigor@sysoev.ru }; 55431Sigor@sysoev.ru 56431Sigor@sysoev.ru 57431Sigor@sysoev.ru const nxt_http_proto_header_send_t nxt_http_proto_header_send[3] = { 58431Sigor@sysoev.ru nxt_h1p_request_header_send, 59431Sigor@sysoev.ru NULL, 60431Sigor@sysoev.ru NULL, 61431Sigor@sysoev.ru }; 62431Sigor@sysoev.ru 63431Sigor@sysoev.ru 64431Sigor@sysoev.ru const nxt_http_proto_send_t nxt_http_proto_send[3] = { 65431Sigor@sysoev.ru nxt_h1p_request_send, 66431Sigor@sysoev.ru NULL, 67431Sigor@sysoev.ru NULL, 68431Sigor@sysoev.ru }; 69431Sigor@sysoev.ru 70431Sigor@sysoev.ru 71431Sigor@sysoev.ru const nxt_http_proto_close_t nxt_http_proto_close[3] = { 72431Sigor@sysoev.ru nxt_h1p_request_close, 73431Sigor@sysoev.ru NULL, 74431Sigor@sysoev.ru NULL, 75431Sigor@sysoev.ru }; 76431Sigor@sysoev.ru 77431Sigor@sysoev.ru 78431Sigor@sysoev.ru static nxt_lvlhsh_t nxt_h1p_fields_hash; 79431Sigor@sysoev.ru 80431Sigor@sysoev.ru static nxt_http_field_proc_t nxt_h1p_fields[] = { 81431Sigor@sysoev.ru { nxt_string("Connection"), &nxt_h1p_connection, 0 }, 82431Sigor@sysoev.ru { nxt_string("Transfer-Encoding"), &nxt_h1p_transfer_encoding, 0 }, 83431Sigor@sysoev.ru 84431Sigor@sysoev.ru { nxt_string("Host"), &nxt_http_request_host, 0 }, 85431Sigor@sysoev.ru { nxt_string("Cookie"), &nxt_http_request_field, 86431Sigor@sysoev.ru offsetof(nxt_http_request_t, cookie) }, 87431Sigor@sysoev.ru { nxt_string("Content-Type"), &nxt_http_request_field, 88431Sigor@sysoev.ru offsetof(nxt_http_request_t, content_type) }, 89431Sigor@sysoev.ru { nxt_string("Content-Length"), &nxt_http_request_content_length, 0 }, 90431Sigor@sysoev.ru }; 91431Sigor@sysoev.ru 92431Sigor@sysoev.ru 93431Sigor@sysoev.ru nxt_int_t 94431Sigor@sysoev.ru nxt_h1p_init(nxt_task_t *task, nxt_runtime_t *rt) 95431Sigor@sysoev.ru { 96431Sigor@sysoev.ru return nxt_http_fields_hash(&nxt_h1p_fields_hash, rt->mem_pool, 97431Sigor@sysoev.ru nxt_h1p_fields, nxt_nitems(nxt_h1p_fields)); 98431Sigor@sysoev.ru } 99431Sigor@sysoev.ru 100431Sigor@sysoev.ru 101431Sigor@sysoev.ru void 102431Sigor@sysoev.ru nxt_http_conn_init(nxt_task_t *task, void *obj, void *data) 103431Sigor@sysoev.ru { 104431Sigor@sysoev.ru nxt_conn_t *c; 105431Sigor@sysoev.ru nxt_socket_conf_t *skcf; 106431Sigor@sysoev.ru nxt_event_engine_t *engine; 107431Sigor@sysoev.ru nxt_socket_conf_joint_t *joint; 108431Sigor@sysoev.ru 109431Sigor@sysoev.ru c = obj; 110431Sigor@sysoev.ru joint = data; 111431Sigor@sysoev.ru 112431Sigor@sysoev.ru nxt_debug(task, "http conn init"); 113431Sigor@sysoev.ru 114431Sigor@sysoev.ru c->joint = joint; 115431Sigor@sysoev.ru joint->count++; 116431Sigor@sysoev.ru 117431Sigor@sysoev.ru skcf = joint->socket_conf; 118431Sigor@sysoev.ru c->local = skcf->sockaddr; 119431Sigor@sysoev.ru c->socket.data = NULL; 120431Sigor@sysoev.ru 121431Sigor@sysoev.ru engine = task->thread->engine; 122431Sigor@sysoev.ru c->read_work_queue = &engine->fast_work_queue; 123431Sigor@sysoev.ru c->write_work_queue = &engine->fast_work_queue; 124431Sigor@sysoev.ru 125431Sigor@sysoev.ru c->read_state = &nxt_h1p_idle_state; 126431Sigor@sysoev.ru 127431Sigor@sysoev.ru nxt_conn_wait(c); 128431Sigor@sysoev.ru } 129431Sigor@sysoev.ru 130431Sigor@sysoev.ru 131431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_idle_state 132431Sigor@sysoev.ru nxt_aligned(64) = 133431Sigor@sysoev.ru { 134431Sigor@sysoev.ru .ready_handler = nxt_h1p_read_header, 135431Sigor@sysoev.ru .close_handler = nxt_h1p_conn_close, 136431Sigor@sysoev.ru .error_handler = nxt_h1p_conn_error, 137431Sigor@sysoev.ru 138431Sigor@sysoev.ru .timer_handler = nxt_h1p_conn_timeout, 139431Sigor@sysoev.ru .timer_value = nxt_h1p_timeout_value, 140431Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, idle_timeout), 141431Sigor@sysoev.ru }; 142431Sigor@sysoev.ru 143431Sigor@sysoev.ru 144431Sigor@sysoev.ru static void 145431Sigor@sysoev.ru nxt_h1p_read_header(nxt_task_t *task, void *obj, void *data) 146431Sigor@sysoev.ru { 147431Sigor@sysoev.ru size_t size; 148431Sigor@sysoev.ru nxt_conn_t *c; 149431Sigor@sysoev.ru nxt_socket_conf_joint_t *joint; 150431Sigor@sysoev.ru 151431Sigor@sysoev.ru c = obj; 152431Sigor@sysoev.ru 153431Sigor@sysoev.ru nxt_debug(task, "h1p read header"); 154431Sigor@sysoev.ru 155431Sigor@sysoev.ru if (c->read == NULL) { 156431Sigor@sysoev.ru joint = c->joint; 157431Sigor@sysoev.ru size = joint->socket_conf->header_buffer_size; 158431Sigor@sysoev.ru 159431Sigor@sysoev.ru c->read = nxt_buf_mem_alloc(c->mem_pool, size, 0); 160431Sigor@sysoev.ru if (nxt_slow_path(c->read == NULL)) { 161431Sigor@sysoev.ru nxt_h1p_conn_error(task, c, c->socket.data); 162431Sigor@sysoev.ru return; 163431Sigor@sysoev.ru } 164431Sigor@sysoev.ru } 165431Sigor@sysoev.ru 166431Sigor@sysoev.ru c->read_state = &nxt_h1p_read_header_state; 167431Sigor@sysoev.ru 168431Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 169431Sigor@sysoev.ru } 170431Sigor@sysoev.ru 171431Sigor@sysoev.ru 172431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_read_header_state 173431Sigor@sysoev.ru nxt_aligned(64) = 174431Sigor@sysoev.ru { 175431Sigor@sysoev.ru .ready_handler = nxt_h1p_header_parse, 176431Sigor@sysoev.ru .close_handler = nxt_h1p_conn_close, 177431Sigor@sysoev.ru .error_handler = nxt_h1p_conn_error, 178431Sigor@sysoev.ru 179431Sigor@sysoev.ru .timer_handler = nxt_h1p_conn_timeout, 180431Sigor@sysoev.ru .timer_value = nxt_h1p_timeout_value, 181431Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, header_read_timeout), 182431Sigor@sysoev.ru }; 183431Sigor@sysoev.ru 184431Sigor@sysoev.ru 185431Sigor@sysoev.ru static void 186431Sigor@sysoev.ru nxt_h1p_header_parse(nxt_task_t *task, void *obj, void *data) 187431Sigor@sysoev.ru { 188431Sigor@sysoev.ru size_t size; 189431Sigor@sysoev.ru nxt_int_t ret; 190431Sigor@sysoev.ru nxt_buf_t *in, *b; 191431Sigor@sysoev.ru nxt_conn_t *c; 192431Sigor@sysoev.ru nxt_h1proto_t *h1p; 193*480Svbart@nginx.com nxt_http_status_t status; 194431Sigor@sysoev.ru nxt_http_request_t *r; 195431Sigor@sysoev.ru nxt_socket_conf_joint_t *joint; 196431Sigor@sysoev.ru 197431Sigor@sysoev.ru c = obj; 198431Sigor@sysoev.ru h1p = data; 199431Sigor@sysoev.ru 200431Sigor@sysoev.ru nxt_debug(task, "h1p header parse"); 201431Sigor@sysoev.ru 202431Sigor@sysoev.ru if (h1p == NULL) { 203431Sigor@sysoev.ru h1p = nxt_mp_zget(c->mem_pool, sizeof(nxt_h1proto_t)); 204431Sigor@sysoev.ru if (nxt_slow_path(h1p == NULL)) { 205431Sigor@sysoev.ru goto fail; 206431Sigor@sysoev.ru } 207431Sigor@sysoev.ru 208431Sigor@sysoev.ru c->socket.data = h1p; 209431Sigor@sysoev.ru h1p->conn = c; 210431Sigor@sysoev.ru } 211431Sigor@sysoev.ru 212431Sigor@sysoev.ru r = h1p->request; 213431Sigor@sysoev.ru 214431Sigor@sysoev.ru if (r == NULL) { 215431Sigor@sysoev.ru r = nxt_http_request_create(task); 216431Sigor@sysoev.ru if (nxt_slow_path(r == NULL)) { 217431Sigor@sysoev.ru goto fail; 218431Sigor@sysoev.ru } 219431Sigor@sysoev.ru 220431Sigor@sysoev.ru h1p->request = r; 221431Sigor@sysoev.ru r->proto.h1 = h1p; 222431Sigor@sysoev.ru joint = c->joint; 223431Sigor@sysoev.ru r->socket_conf = joint->socket_conf; 224431Sigor@sysoev.ru 225431Sigor@sysoev.ru r->remote = c->remote; 226431Sigor@sysoev.ru 227431Sigor@sysoev.ru ret = nxt_http_parse_request_init(&h1p->parser, r->mem_pool); 228431Sigor@sysoev.ru if (nxt_slow_path(ret != NXT_OK)) { 229431Sigor@sysoev.ru /* 230431Sigor@sysoev.ru * The request is very uncomplete here, 231431Sigor@sysoev.ru * so "internal server error" useless here. 232431Sigor@sysoev.ru */ 233431Sigor@sysoev.ru nxt_mp_release(r->mem_pool); 234431Sigor@sysoev.ru h1p->request = NULL; 235431Sigor@sysoev.ru goto fail; 236431Sigor@sysoev.ru } 237431Sigor@sysoev.ru } 238431Sigor@sysoev.ru 239431Sigor@sysoev.ru ret = nxt_http_parse_request(&h1p->parser, &c->read->mem); 240431Sigor@sysoev.ru 241431Sigor@sysoev.ru if (nxt_fast_path(ret == NXT_DONE)) { 242431Sigor@sysoev.ru r->target.start = h1p->parser.target_start; 243431Sigor@sysoev.ru r->target.length = h1p->parser.target_end - h1p->parser.target_start; 244431Sigor@sysoev.ru 245431Sigor@sysoev.ru r->version.start = h1p->parser.version.str; 246431Sigor@sysoev.ru r->version.length = sizeof(h1p->parser.version.str); 247431Sigor@sysoev.ru 248431Sigor@sysoev.ru r->method = &h1p->parser.method; 249431Sigor@sysoev.ru r->path = &h1p->parser.path; 250431Sigor@sysoev.ru r->args = &h1p->parser.args; 251431Sigor@sysoev.ru 252431Sigor@sysoev.ru /* 253431Sigor@sysoev.ru * By default the keepalive mode is disabled in HTTP/1.0 and 254431Sigor@sysoev.ru * enabled in HTTP/1.1. The mode can be overridden later by 255431Sigor@sysoev.ru * the "Connection" field processed in nxt_h1p_connection(). 256431Sigor@sysoev.ru */ 257431Sigor@sysoev.ru h1p->keepalive = (h1p->parser.version.str[7] != '0'); 258431Sigor@sysoev.ru 259431Sigor@sysoev.ru r->fields = h1p->parser.fields; 260431Sigor@sysoev.ru 261431Sigor@sysoev.ru ret = nxt_http_fields_process(r->fields, &nxt_h1p_fields_hash, r); 262431Sigor@sysoev.ru 263431Sigor@sysoev.ru if (nxt_fast_path(ret == NXT_OK)) { 264431Sigor@sysoev.ru r->state->ready_handler(task, r, NULL); 265431Sigor@sysoev.ru return; 266431Sigor@sysoev.ru } 267431Sigor@sysoev.ru 268*480Svbart@nginx.com /* ret == NXT_ERROR */ 269*480Svbart@nginx.com 270*480Svbart@nginx.com nxt_http_request_error(task, r, NXT_HTTP_BAD_REQUEST); 271*480Svbart@nginx.com return; 272*480Svbart@nginx.com } 273*480Svbart@nginx.com 274*480Svbart@nginx.com if (ret == NXT_AGAIN) { 275431Sigor@sysoev.ru in = c->read; 276431Sigor@sysoev.ru 277431Sigor@sysoev.ru if (nxt_buf_mem_free_size(&in->mem) == 0) { 278431Sigor@sysoev.ru size = r->socket_conf->large_header_buffer_size; 279431Sigor@sysoev.ru 280431Sigor@sysoev.ru if (size <= (size_t) nxt_buf_mem_used_size(&in->mem) 281431Sigor@sysoev.ru || h1p->nbuffers >= r->socket_conf->large_header_buffers) 282431Sigor@sysoev.ru { 283431Sigor@sysoev.ru nxt_http_request_error(task, r, 284431Sigor@sysoev.ru NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE); 285431Sigor@sysoev.ru return; 286431Sigor@sysoev.ru } 287431Sigor@sysoev.ru 288431Sigor@sysoev.ru b = nxt_buf_mem_alloc(c->mem_pool, size, 0); 289431Sigor@sysoev.ru if (nxt_slow_path(b == NULL)) { 290431Sigor@sysoev.ru nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); 291431Sigor@sysoev.ru return; 292431Sigor@sysoev.ru } 293431Sigor@sysoev.ru 294431Sigor@sysoev.ru h1p->nbuffers++; 295431Sigor@sysoev.ru 296431Sigor@sysoev.ru size = nxt_buf_mem_used_size(&in->mem); 297431Sigor@sysoev.ru b->mem.free = nxt_cpymem(b->mem.pos, in->mem.pos, size); 298431Sigor@sysoev.ru 299459Sigor@sysoev.ru in->next = h1p->buffers; 300459Sigor@sysoev.ru h1p->buffers = in; 301431Sigor@sysoev.ru c->read = b; 302431Sigor@sysoev.ru } 303431Sigor@sysoev.ru 304431Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 305431Sigor@sysoev.ru return; 306431Sigor@sysoev.ru } 307431Sigor@sysoev.ru 308*480Svbart@nginx.com switch (ret) { 309*480Svbart@nginx.com 310*480Svbart@nginx.com case NXT_HTTP_PARSE_INVALID: 311*480Svbart@nginx.com status = NXT_HTTP_BAD_REQUEST; 312*480Svbart@nginx.com break; 313431Sigor@sysoev.ru 314*480Svbart@nginx.com case NXT_HTTP_PARSE_TOO_LARGE_FIELD: 315*480Svbart@nginx.com status = NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; 316*480Svbart@nginx.com break; 317*480Svbart@nginx.com 318*480Svbart@nginx.com default: 319*480Svbart@nginx.com case NXT_ERROR: 320*480Svbart@nginx.com status = NXT_HTTP_INTERNAL_SERVER_ERROR; 321*480Svbart@nginx.com break; 322*480Svbart@nginx.com } 323*480Svbart@nginx.com 324*480Svbart@nginx.com nxt_http_request_error(task, r, status); 325431Sigor@sysoev.ru return; 326431Sigor@sysoev.ru 327431Sigor@sysoev.ru fail: 328431Sigor@sysoev.ru 329431Sigor@sysoev.ru nxt_h1p_conn_close(task, c, h1p); 330431Sigor@sysoev.ru } 331431Sigor@sysoev.ru 332431Sigor@sysoev.ru 333431Sigor@sysoev.ru static nxt_int_t 334431Sigor@sysoev.ru nxt_h1p_connection(void *ctx, nxt_http_field_t *field, uintptr_t data) 335431Sigor@sysoev.ru { 336431Sigor@sysoev.ru nxt_http_request_t *r; 337431Sigor@sysoev.ru 338431Sigor@sysoev.ru r = ctx; 339431Sigor@sysoev.ru 340431Sigor@sysoev.ru if (field->value_length == 5 && nxt_memcmp(field->value, "close", 5) == 0) { 341431Sigor@sysoev.ru r->proto.h1->keepalive = 0; 342431Sigor@sysoev.ru } 343431Sigor@sysoev.ru 344431Sigor@sysoev.ru return NXT_OK; 345431Sigor@sysoev.ru } 346431Sigor@sysoev.ru 347431Sigor@sysoev.ru 348431Sigor@sysoev.ru static nxt_int_t 349431Sigor@sysoev.ru nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field, uintptr_t data) 350431Sigor@sysoev.ru { 351431Sigor@sysoev.ru nxt_http_te_t te; 352431Sigor@sysoev.ru nxt_http_request_t *r; 353431Sigor@sysoev.ru 354431Sigor@sysoev.ru r = ctx; 355431Sigor@sysoev.ru 356431Sigor@sysoev.ru if (field->value_length == 7 357431Sigor@sysoev.ru && nxt_memcmp(field->value, "chunked", 7) == 0) 358431Sigor@sysoev.ru { 359431Sigor@sysoev.ru te = NXT_HTTP_TE_CHUNKED; 360431Sigor@sysoev.ru 361431Sigor@sysoev.ru } else { 362431Sigor@sysoev.ru te = NXT_HTTP_TE_UNSUPPORTED; 363431Sigor@sysoev.ru } 364431Sigor@sysoev.ru 365431Sigor@sysoev.ru r->proto.h1->transfer_encoding = te; 366431Sigor@sysoev.ru 367431Sigor@sysoev.ru return NXT_OK; 368431Sigor@sysoev.ru } 369431Sigor@sysoev.ru 370431Sigor@sysoev.ru 371431Sigor@sysoev.ru static void 372431Sigor@sysoev.ru nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r) 373431Sigor@sysoev.ru { 374431Sigor@sysoev.ru size_t size, rest_length; 375459Sigor@sysoev.ru nxt_buf_t *in, *b; 376431Sigor@sysoev.ru nxt_conn_t *c; 377459Sigor@sysoev.ru nxt_h1proto_t *h1p; 378431Sigor@sysoev.ru nxt_http_status_t status; 379431Sigor@sysoev.ru 380459Sigor@sysoev.ru h1p = r->proto.h1; 381459Sigor@sysoev.ru 382431Sigor@sysoev.ru nxt_debug(task, "h1p body read %O te:%d", 383459Sigor@sysoev.ru r->content_length_n, h1p->transfer_encoding); 384431Sigor@sysoev.ru 385459Sigor@sysoev.ru switch (h1p->transfer_encoding) { 386431Sigor@sysoev.ru 387431Sigor@sysoev.ru case NXT_HTTP_TE_CHUNKED: 388431Sigor@sysoev.ru status = NXT_HTTP_LENGTH_REQUIRED; 389431Sigor@sysoev.ru goto error; 390431Sigor@sysoev.ru 391431Sigor@sysoev.ru case NXT_HTTP_TE_UNSUPPORTED: 392431Sigor@sysoev.ru status = NXT_HTTP_NOT_IMPLEMENTED; 393431Sigor@sysoev.ru goto error; 394431Sigor@sysoev.ru 395431Sigor@sysoev.ru default: 396431Sigor@sysoev.ru case NXT_HTTP_TE_NONE: 397431Sigor@sysoev.ru break; 398431Sigor@sysoev.ru } 399431Sigor@sysoev.ru 400431Sigor@sysoev.ru if (r->content_length_n == -1 || r->content_length_n == 0) { 401431Sigor@sysoev.ru goto ready; 402431Sigor@sysoev.ru } 403431Sigor@sysoev.ru 404431Sigor@sysoev.ru if (r->content_length_n > (nxt_off_t) r->socket_conf->max_body_size) { 405431Sigor@sysoev.ru status = NXT_HTTP_PAYLOAD_TOO_LARGE; 406431Sigor@sysoev.ru goto error; 407431Sigor@sysoev.ru } 408431Sigor@sysoev.ru 409431Sigor@sysoev.ru rest_length = (size_t) r->content_length_n; 410431Sigor@sysoev.ru 411431Sigor@sysoev.ru b = r->body; 412431Sigor@sysoev.ru 413431Sigor@sysoev.ru if (b == NULL) { 414431Sigor@sysoev.ru b = nxt_buf_mem_alloc(r->mem_pool, rest_length, 0); 415431Sigor@sysoev.ru if (nxt_slow_path(b == NULL)) { 416431Sigor@sysoev.ru status = NXT_HTTP_INTERNAL_SERVER_ERROR; 417431Sigor@sysoev.ru goto error; 418431Sigor@sysoev.ru } 419431Sigor@sysoev.ru 420431Sigor@sysoev.ru r->body = b; 421431Sigor@sysoev.ru } 422431Sigor@sysoev.ru 423459Sigor@sysoev.ru in = h1p->conn->read; 424431Sigor@sysoev.ru 425459Sigor@sysoev.ru size = nxt_buf_mem_used_size(&in->mem); 426431Sigor@sysoev.ru 427431Sigor@sysoev.ru if (size != 0) { 428431Sigor@sysoev.ru if (size >= rest_length) { 429431Sigor@sysoev.ru size = rest_length; 430431Sigor@sysoev.ru rest_length = 0; 431431Sigor@sysoev.ru 432431Sigor@sysoev.ru } else { 433431Sigor@sysoev.ru rest_length -= size; 434431Sigor@sysoev.ru } 435431Sigor@sysoev.ru 436459Sigor@sysoev.ru b->mem.free = nxt_cpymem(b->mem.free, in->mem.pos, size); 437459Sigor@sysoev.ru in->mem.pos += size; 438431Sigor@sysoev.ru } 439431Sigor@sysoev.ru 440431Sigor@sysoev.ru nxt_debug(task, "h1p body rest: %O", rest_length); 441431Sigor@sysoev.ru 442431Sigor@sysoev.ru r->rest_length = rest_length; 443431Sigor@sysoev.ru 444431Sigor@sysoev.ru if (rest_length != 0) { 445459Sigor@sysoev.ru in->next = h1p->buffers; 446459Sigor@sysoev.ru h1p->buffers = in; 447459Sigor@sysoev.ru 448459Sigor@sysoev.ru c = h1p->conn; 449431Sigor@sysoev.ru c->read = b; 450431Sigor@sysoev.ru c->read_state = &nxt_h1p_read_body_state; 451431Sigor@sysoev.ru 452431Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 453431Sigor@sysoev.ru return; 454431Sigor@sysoev.ru } 455431Sigor@sysoev.ru 456431Sigor@sysoev.ru ready: 457431Sigor@sysoev.ru 458431Sigor@sysoev.ru nxt_work_queue_add(&task->thread->engine->fast_work_queue, 459431Sigor@sysoev.ru r->state->ready_handler, task, r, NULL); 460431Sigor@sysoev.ru 461431Sigor@sysoev.ru return; 462431Sigor@sysoev.ru 463431Sigor@sysoev.ru error: 464431Sigor@sysoev.ru 465459Sigor@sysoev.ru h1p->keepalive = 0; 466431Sigor@sysoev.ru 467431Sigor@sysoev.ru nxt_http_request_error(task, r, status); 468431Sigor@sysoev.ru } 469431Sigor@sysoev.ru 470431Sigor@sysoev.ru 471431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_read_body_state 472431Sigor@sysoev.ru nxt_aligned(64) = 473431Sigor@sysoev.ru { 474431Sigor@sysoev.ru .ready_handler = nxt_h1p_body_read, 475431Sigor@sysoev.ru .close_handler = nxt_h1p_conn_close, 476431Sigor@sysoev.ru .error_handler = nxt_h1p_conn_error, 477431Sigor@sysoev.ru 478431Sigor@sysoev.ru .timer_handler = nxt_h1p_conn_timeout, 479431Sigor@sysoev.ru .timer_value = nxt_h1p_timeout_value, 480431Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, body_read_timeout), 481431Sigor@sysoev.ru .timer_autoreset = 1, 482431Sigor@sysoev.ru }; 483431Sigor@sysoev.ru 484431Sigor@sysoev.ru 485431Sigor@sysoev.ru static void 486431Sigor@sysoev.ru nxt_h1p_body_read(nxt_task_t *task, void *obj, void *data) 487431Sigor@sysoev.ru { 488431Sigor@sysoev.ru size_t size; 489431Sigor@sysoev.ru nxt_conn_t *c; 490431Sigor@sysoev.ru nxt_h1proto_t *h1p; 491431Sigor@sysoev.ru nxt_http_request_t *r; 492431Sigor@sysoev.ru 493431Sigor@sysoev.ru c = obj; 494431Sigor@sysoev.ru h1p = data; 495431Sigor@sysoev.ru 496431Sigor@sysoev.ru nxt_debug(task, "h1p body read"); 497431Sigor@sysoev.ru 498431Sigor@sysoev.ru r = h1p->request; 499431Sigor@sysoev.ru size = nxt_buf_mem_used_size(&c->read->mem); 500431Sigor@sysoev.ru 501431Sigor@sysoev.ru r->rest_length -= size; 502431Sigor@sysoev.ru 503431Sigor@sysoev.ru nxt_debug(task, "h1p body rest: %O", r->rest_length); 504431Sigor@sysoev.ru 505431Sigor@sysoev.ru if (r->rest_length != 0) { 506431Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 507431Sigor@sysoev.ru 508431Sigor@sysoev.ru } else { 509459Sigor@sysoev.ru c->read = NULL; 510431Sigor@sysoev.ru nxt_work_queue_add(&task->thread->engine->fast_work_queue, 511431Sigor@sysoev.ru r->state->ready_handler, task, r, NULL); 512431Sigor@sysoev.ru } 513431Sigor@sysoev.ru } 514431Sigor@sysoev.ru 515431Sigor@sysoev.ru 516431Sigor@sysoev.ru static void 517431Sigor@sysoev.ru nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r) 518431Sigor@sysoev.ru { 519431Sigor@sysoev.ru r->local = nxt_conn_local_addr(task, r->proto.h1->conn); 520431Sigor@sysoev.ru } 521431Sigor@sysoev.ru 522431Sigor@sysoev.ru 523431Sigor@sysoev.ru #define NXT_HTTP_LAST_SUCCESS \ 524431Sigor@sysoev.ru (NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1) 525431Sigor@sysoev.ru 526431Sigor@sysoev.ru static const nxt_str_t nxt_http_success[] = { 527431Sigor@sysoev.ru nxt_string("HTTP/1.1 200 OK\r\n"), 528431Sigor@sysoev.ru nxt_string("HTTP/1.1 201 Created\r\n"), 529431Sigor@sysoev.ru nxt_string("HTTP/1.1 202 Accepted\r\n"), 530431Sigor@sysoev.ru nxt_string("HTTP/1.1 203 Non-Authoritative Information\r\n"), 531431Sigor@sysoev.ru nxt_string("HTTP/1.1 204 No Content\r\n"), 532431Sigor@sysoev.ru nxt_string("HTTP/1.1 205 Reset Content\r\n"), 533431Sigor@sysoev.ru nxt_string("HTTP/1.1 206 Partial Content\r\n"), 534431Sigor@sysoev.ru }; 535431Sigor@sysoev.ru 536431Sigor@sysoev.ru 537431Sigor@sysoev.ru #define NXT_HTTP_LAST_REDIRECTION \ 538431Sigor@sysoev.ru (NXT_HTTP_MULTIPLE_CHOICES + nxt_nitems(nxt_http_redirection) - 1) 539431Sigor@sysoev.ru 540431Sigor@sysoev.ru static const nxt_str_t nxt_http_redirection[] = { 541431Sigor@sysoev.ru nxt_string("HTTP/1.1 300 Multiple Choices\r\n"), 542431Sigor@sysoev.ru nxt_string("HTTP/1.1 301 Moved Permanently\r\n"), 543431Sigor@sysoev.ru nxt_string("HTTP/1.1 302 Found\r\n"), 544431Sigor@sysoev.ru nxt_string("HTTP/1.1 303 See Other\r\n"), 545431Sigor@sysoev.ru nxt_string("HTTP/1.1 304 Not Modified\r\n"), 546431Sigor@sysoev.ru }; 547431Sigor@sysoev.ru 548431Sigor@sysoev.ru 549431Sigor@sysoev.ru #define NXT_HTTP_LAST_CLIENT_ERROR \ 550431Sigor@sysoev.ru (NXT_HTTP_BAD_REQUEST + nxt_nitems(nxt_http_client_error) - 1) 551431Sigor@sysoev.ru 552431Sigor@sysoev.ru static const nxt_str_t nxt_http_client_error[] = { 553431Sigor@sysoev.ru nxt_string("HTTP/1.1 400 Bad Request\r\n"), 554431Sigor@sysoev.ru nxt_string("HTTP/1.1 401 Unauthorized\r\n"), 555431Sigor@sysoev.ru nxt_string("HTTP/1.1 402 Payment Required\r\n"), 556431Sigor@sysoev.ru nxt_string("HTTP/1.1 403 Forbidden\r\n"), 557431Sigor@sysoev.ru nxt_string("HTTP/1.1 404 Not Found\r\n"), 558431Sigor@sysoev.ru nxt_string("HTTP/1.1 405 Method Not Allowed\r\n"), 559431Sigor@sysoev.ru nxt_string("HTTP/1.1 406 Not Acceptable\r\n"), 560431Sigor@sysoev.ru nxt_string("HTTP/1.1 407 Proxy Authentication Required\r\n"), 561431Sigor@sysoev.ru nxt_string("HTTP/1.1 408 Request Timeout\r\n"), 562431Sigor@sysoev.ru nxt_string("HTTP/1.1 409 Conflict\r\n"), 563431Sigor@sysoev.ru nxt_string("HTTP/1.1 410 Gone\r\n"), 564431Sigor@sysoev.ru nxt_string("HTTP/1.1 411 Length Required\r\n"), 565431Sigor@sysoev.ru nxt_string("HTTP/1.1 412 Precondition Failed\r\n"), 566431Sigor@sysoev.ru nxt_string("HTTP/1.1 413 Payload Too Large\r\n"), 567431Sigor@sysoev.ru nxt_string("HTTP/1.1 414 URI Too Long\r\n"), 568431Sigor@sysoev.ru nxt_string("HTTP/1.1 415 Unsupported Media Type\r\n"), 569431Sigor@sysoev.ru nxt_string("HTTP/1.1 416 Range Not Satisfiable\r\n"), 570431Sigor@sysoev.ru nxt_string("HTTP/1.1 417 Expectation Failed\r\n"), 571431Sigor@sysoev.ru nxt_string("HTTP/1.1 418\r\n"), 572431Sigor@sysoev.ru nxt_string("HTTP/1.1 419\r\n"), 573431Sigor@sysoev.ru nxt_string("HTTP/1.1 420\r\n"), 574431Sigor@sysoev.ru nxt_string("HTTP/1.1 421\r\n"), 575431Sigor@sysoev.ru nxt_string("HTTP/1.1 422\r\n"), 576431Sigor@sysoev.ru nxt_string("HTTP/1.1 423\r\n"), 577431Sigor@sysoev.ru nxt_string("HTTP/1.1 424\r\n"), 578431Sigor@sysoev.ru nxt_string("HTTP/1.1 425\r\n"), 579431Sigor@sysoev.ru nxt_string("HTTP/1.1 426\r\n"), 580431Sigor@sysoev.ru nxt_string("HTTP/1.1 427\r\n"), 581431Sigor@sysoev.ru nxt_string("HTTP/1.1 428\r\n"), 582431Sigor@sysoev.ru nxt_string("HTTP/1.1 429\r\n"), 583431Sigor@sysoev.ru nxt_string("HTTP/1.1 430\r\n"), 584431Sigor@sysoev.ru nxt_string("HTTP/1.1 431 Request Header Fields Too Large\r\n"), 585431Sigor@sysoev.ru }; 586431Sigor@sysoev.ru 587431Sigor@sysoev.ru 588431Sigor@sysoev.ru #define NXT_HTTP_LAST_SERVER_ERROR \ 589431Sigor@sysoev.ru (NXT_HTTP_INTERNAL_SERVER_ERROR + nxt_nitems(nxt_http_server_error) - 1) 590431Sigor@sysoev.ru 591431Sigor@sysoev.ru static const nxt_str_t nxt_http_server_error[] = { 592431Sigor@sysoev.ru nxt_string("HTTP/1.1 500 Internal Server Error\r\n"), 593431Sigor@sysoev.ru nxt_string("HTTP/1.1 501 Not Implemented\r\n"), 594431Sigor@sysoev.ru nxt_string("HTTP/1.1 502 Bad Gateway\r\n"), 595431Sigor@sysoev.ru nxt_string("HTTP/1.1 503 Service Unavailable\r\n"), 596431Sigor@sysoev.ru nxt_string("HTTP/1.1 504 Gateway Timeout\r\n"), 597431Sigor@sysoev.ru }; 598431Sigor@sysoev.ru 599431Sigor@sysoev.ru 600431Sigor@sysoev.ru #define UNKNOWN_STATUS_LENGTH (sizeof("HTTP/1.1 65536\r\n") - 1) 601431Sigor@sysoev.ru 602431Sigor@sysoev.ru static void 603431Sigor@sysoev.ru nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r) 604431Sigor@sysoev.ru { 605431Sigor@sysoev.ru u_char *p; 606431Sigor@sysoev.ru size_t size; 607431Sigor@sysoev.ru nxt_buf_t *header; 608431Sigor@sysoev.ru nxt_str_t unknown_status; 609431Sigor@sysoev.ru nxt_int_t conn; 610431Sigor@sysoev.ru nxt_uint_t n; 611431Sigor@sysoev.ru nxt_bool_t http11; 612431Sigor@sysoev.ru nxt_conn_t *c; 613431Sigor@sysoev.ru nxt_h1proto_t *h1p; 614431Sigor@sysoev.ru const nxt_str_t *status; 615431Sigor@sysoev.ru nxt_http_field_t *field; 616431Sigor@sysoev.ru nxt_event_engine_t *engine; 617431Sigor@sysoev.ru u_char buf[UNKNOWN_STATUS_LENGTH]; 618431Sigor@sysoev.ru 619431Sigor@sysoev.ru static const char chunked[] = "Transfer-Encoding: chunked\r\n"; 620431Sigor@sysoev.ru 621431Sigor@sysoev.ru static const nxt_str_t connection[2] = { 622431Sigor@sysoev.ru nxt_string("Connection: close\r\n"), 623431Sigor@sysoev.ru nxt_string("Connection: keep-alive\r\n"), 624431Sigor@sysoev.ru }; 625431Sigor@sysoev.ru 626431Sigor@sysoev.ru nxt_debug(task, "h1p request header send"); 627431Sigor@sysoev.ru 628431Sigor@sysoev.ru r->header_sent = 1; 629431Sigor@sysoev.ru h1p = r->proto.h1; 630431Sigor@sysoev.ru n = r->status; 631431Sigor@sysoev.ru 632431Sigor@sysoev.ru if (n >= NXT_HTTP_OK && n <= NXT_HTTP_LAST_SUCCESS) { 633431Sigor@sysoev.ru status = &nxt_http_success[n - NXT_HTTP_OK]; 634431Sigor@sysoev.ru 635431Sigor@sysoev.ru } else if (n >= NXT_HTTP_MULTIPLE_CHOICES 636431Sigor@sysoev.ru && n <= NXT_HTTP_LAST_REDIRECTION) 637431Sigor@sysoev.ru { 638431Sigor@sysoev.ru status = &nxt_http_redirection[n - NXT_HTTP_MULTIPLE_CHOICES]; 639431Sigor@sysoev.ru 640431Sigor@sysoev.ru } else if (n >= NXT_HTTP_BAD_REQUEST && n <= NXT_HTTP_LAST_CLIENT_ERROR) { 641431Sigor@sysoev.ru status = &nxt_http_client_error[n - NXT_HTTP_BAD_REQUEST]; 642431Sigor@sysoev.ru 643431Sigor@sysoev.ru } else if (n >= NXT_HTTP_INTERNAL_SERVER_ERROR 644431Sigor@sysoev.ru && n <= NXT_HTTP_LAST_SERVER_ERROR) 645431Sigor@sysoev.ru { 646431Sigor@sysoev.ru status = &nxt_http_server_error[n - NXT_HTTP_INTERNAL_SERVER_ERROR]; 647431Sigor@sysoev.ru 648431Sigor@sysoev.ru } else { 649431Sigor@sysoev.ru p = nxt_sprintf(buf, buf + UNKNOWN_STATUS_LENGTH, 650431Sigor@sysoev.ru "HTTP/1.1 %03d\r\n", n); 651431Sigor@sysoev.ru 652431Sigor@sysoev.ru unknown_status.length = p - buf; 653431Sigor@sysoev.ru unknown_status.start = buf; 654431Sigor@sysoev.ru status = &unknown_status; 655431Sigor@sysoev.ru } 656431Sigor@sysoev.ru 657450Sigor@sysoev.ru size = status->length; 658450Sigor@sysoev.ru /* Trailing CRLF at the end of header. */ 659450Sigor@sysoev.ru size += sizeof("\r\n") - 1; 660431Sigor@sysoev.ru 661431Sigor@sysoev.ru http11 = (h1p->parser.version.str[7] != '0'); 662431Sigor@sysoev.ru 663431Sigor@sysoev.ru if (r->resp.content_length == NULL || r->resp.content_length->skip) { 664431Sigor@sysoev.ru if (http11) { 665431Sigor@sysoev.ru h1p->chunked = 1; 666431Sigor@sysoev.ru size += sizeof(chunked) - 1; 667450Sigor@sysoev.ru /* Trailing CRLF will be added by the first chunk header. */ 668450Sigor@sysoev.ru size -= sizeof("\r\n") - 1; 669431Sigor@sysoev.ru 670431Sigor@sysoev.ru } else { 671431Sigor@sysoev.ru h1p->keepalive = 0; 672431Sigor@sysoev.ru } 673431Sigor@sysoev.ru } 674431Sigor@sysoev.ru 675431Sigor@sysoev.ru conn = -1; 676431Sigor@sysoev.ru 677431Sigor@sysoev.ru if (http11 ^ h1p->keepalive) { 678431Sigor@sysoev.ru conn = h1p->keepalive; 679431Sigor@sysoev.ru size += connection[conn].length; 680431Sigor@sysoev.ru } 681431Sigor@sysoev.ru 682431Sigor@sysoev.ru nxt_list_each(field, r->resp.fields) { 683431Sigor@sysoev.ru 684431Sigor@sysoev.ru if (!field->skip) { 685431Sigor@sysoev.ru size += field->name_length + field->value_length; 686431Sigor@sysoev.ru size += sizeof(": \r\n") - 1; 687431Sigor@sysoev.ru } 688431Sigor@sysoev.ru 689431Sigor@sysoev.ru } nxt_list_loop; 690431Sigor@sysoev.ru 691431Sigor@sysoev.ru header = nxt_buf_mem_alloc(r->mem_pool, size, 0); 692431Sigor@sysoev.ru if (nxt_slow_path(header == NULL)) { 693431Sigor@sysoev.ru /* The internal server error is set just for logging. */ 694431Sigor@sysoev.ru r->status = NXT_HTTP_INTERNAL_SERVER_ERROR; 695431Sigor@sysoev.ru nxt_h1p_conn_close(task, h1p->conn, h1p); 696431Sigor@sysoev.ru return; 697431Sigor@sysoev.ru } 698431Sigor@sysoev.ru 699431Sigor@sysoev.ru p = header->mem.free; 700431Sigor@sysoev.ru 701431Sigor@sysoev.ru p = nxt_cpymem(p, status->start, status->length); 702431Sigor@sysoev.ru 703431Sigor@sysoev.ru nxt_list_each(field, r->resp.fields) { 704431Sigor@sysoev.ru 705431Sigor@sysoev.ru if (!field->skip) { 706431Sigor@sysoev.ru p = nxt_cpymem(p, field->name, field->name_length); 707431Sigor@sysoev.ru *p++ = ':'; *p++ = ' '; 708431Sigor@sysoev.ru p = nxt_cpymem(p, field->value, field->value_length); 709431Sigor@sysoev.ru *p++ = '\r'; *p++ = '\n'; 710431Sigor@sysoev.ru } 711431Sigor@sysoev.ru 712431Sigor@sysoev.ru } nxt_list_loop; 713431Sigor@sysoev.ru 714431Sigor@sysoev.ru if (conn >= 0) { 715431Sigor@sysoev.ru p = nxt_cpymem(p, connection[conn].start, connection[conn].length); 716431Sigor@sysoev.ru } 717431Sigor@sysoev.ru 718431Sigor@sysoev.ru if (h1p->chunked) { 719431Sigor@sysoev.ru p = nxt_cpymem(p, chunked, sizeof(chunked) - 1); 720450Sigor@sysoev.ru /* Trailing CRLF will be added by the first chunk header. */ 721431Sigor@sysoev.ru 722431Sigor@sysoev.ru } else { 723431Sigor@sysoev.ru *p++ = '\r'; *p++ = '\n'; 724431Sigor@sysoev.ru } 725431Sigor@sysoev.ru 726431Sigor@sysoev.ru header->mem.free = p; 727431Sigor@sysoev.ru 728431Sigor@sysoev.ru c = h1p->conn; 729431Sigor@sysoev.ru 730431Sigor@sysoev.ru c->write = header; 731431Sigor@sysoev.ru c->write_state = &nxt_h1p_send_state; 732431Sigor@sysoev.ru 733431Sigor@sysoev.ru engine = task->thread->engine; 734431Sigor@sysoev.ru 735431Sigor@sysoev.ru nxt_work_queue_add(&engine->fast_work_queue, r->state->ready_handler, 736431Sigor@sysoev.ru task, r, NULL); 737431Sigor@sysoev.ru 738431Sigor@sysoev.ru nxt_conn_write(engine, c); 739431Sigor@sysoev.ru } 740431Sigor@sysoev.ru 741431Sigor@sysoev.ru 742431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_send_state 743431Sigor@sysoev.ru nxt_aligned(64) = 744431Sigor@sysoev.ru { 745431Sigor@sysoev.ru .ready_handler = nxt_h1p_sent, 746431Sigor@sysoev.ru .close_handler = nxt_h1p_conn_close, 747431Sigor@sysoev.ru .error_handler = nxt_h1p_conn_error, 748431Sigor@sysoev.ru 749431Sigor@sysoev.ru .timer_handler = nxt_h1p_conn_timeout, 750431Sigor@sysoev.ru .timer_value = nxt_h1p_timeout_value, 751431Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, send_timeout), 752431Sigor@sysoev.ru .timer_autoreset = 1, 753431Sigor@sysoev.ru }; 754431Sigor@sysoev.ru 755431Sigor@sysoev.ru 756431Sigor@sysoev.ru static void 757431Sigor@sysoev.ru nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out) 758431Sigor@sysoev.ru { 759431Sigor@sysoev.ru nxt_conn_t *c; 760431Sigor@sysoev.ru 761431Sigor@sysoev.ru nxt_debug(task, "h1p request send"); 762431Sigor@sysoev.ru 763431Sigor@sysoev.ru c = r->proto.h1->conn; 764431Sigor@sysoev.ru 765431Sigor@sysoev.ru if (r->proto.h1->chunked) { 766431Sigor@sysoev.ru out = nxt_h1p_chunk_create(task, r, out); 767431Sigor@sysoev.ru if (nxt_slow_path(out == NULL)) { 768431Sigor@sysoev.ru nxt_h1p_conn_error(task, c, c->socket.data); 769431Sigor@sysoev.ru return; 770431Sigor@sysoev.ru } 771431Sigor@sysoev.ru } 772431Sigor@sysoev.ru 773431Sigor@sysoev.ru if (c->write == NULL) { 774431Sigor@sysoev.ru c->write = out; 775431Sigor@sysoev.ru c->write_state = &nxt_h1p_send_state; 776431Sigor@sysoev.ru 777431Sigor@sysoev.ru nxt_conn_write(task->thread->engine, c); 778431Sigor@sysoev.ru 779431Sigor@sysoev.ru } else { 780431Sigor@sysoev.ru nxt_buf_chain_add(&c->write, out); 781431Sigor@sysoev.ru } 782431Sigor@sysoev.ru } 783431Sigor@sysoev.ru 784431Sigor@sysoev.ru 785431Sigor@sysoev.ru static nxt_buf_t * 786431Sigor@sysoev.ru nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out) 787431Sigor@sysoev.ru { 788431Sigor@sysoev.ru size_t size; 789431Sigor@sysoev.ru nxt_buf_t *b, **prev, *header, *tail; 790431Sigor@sysoev.ru 791431Sigor@sysoev.ru const size_t chunk_size = 2 * (sizeof("\r\n") - 1) + NXT_OFF_T_HEXLEN; 792431Sigor@sysoev.ru static const char tail_chunk[] = "\r\n0\r\n\r\n"; 793431Sigor@sysoev.ru 794431Sigor@sysoev.ru size = 0; 795431Sigor@sysoev.ru prev = &out; 796431Sigor@sysoev.ru 797431Sigor@sysoev.ru for (b = out; b != NULL; b = b->next) { 798431Sigor@sysoev.ru 799431Sigor@sysoev.ru if (nxt_buf_is_last(b)) { 800431Sigor@sysoev.ru tail = nxt_buf_mem_alloc(r->mem_pool, chunk_size, 0); 801431Sigor@sysoev.ru if (nxt_slow_path(tail == NULL)) { 802431Sigor@sysoev.ru return NULL; 803431Sigor@sysoev.ru } 804431Sigor@sysoev.ru 805431Sigor@sysoev.ru *prev = tail; 806431Sigor@sysoev.ru tail->next = b; 807431Sigor@sysoev.ru /* 808431Sigor@sysoev.ru * The tail_chunk size with trailing zero is 8 bytes, so 809431Sigor@sysoev.ru * memcpy may be inlined with just single 8 byte move operation. 810431Sigor@sysoev.ru */ 811431Sigor@sysoev.ru nxt_memcpy(tail->mem.free, tail_chunk, sizeof(tail_chunk)); 812431Sigor@sysoev.ru tail->mem.free += sizeof(tail_chunk) - 1; 813431Sigor@sysoev.ru 814431Sigor@sysoev.ru break; 815431Sigor@sysoev.ru } 816431Sigor@sysoev.ru 817431Sigor@sysoev.ru size += nxt_buf_used_size(b); 818431Sigor@sysoev.ru prev = &b->next; 819431Sigor@sysoev.ru } 820431Sigor@sysoev.ru 821431Sigor@sysoev.ru if (size == 0) { 822431Sigor@sysoev.ru return out; 823431Sigor@sysoev.ru } 824431Sigor@sysoev.ru 825431Sigor@sysoev.ru header = nxt_buf_mem_alloc(r->mem_pool, chunk_size, 0); 826431Sigor@sysoev.ru if (nxt_slow_path(header == NULL)) { 827431Sigor@sysoev.ru return NULL; 828431Sigor@sysoev.ru } 829431Sigor@sysoev.ru 830431Sigor@sysoev.ru header->next = out; 831431Sigor@sysoev.ru header->mem.free = nxt_sprintf(header->mem.free, header->mem.end, 832431Sigor@sysoev.ru "\r\n%xO\r\n", size); 833431Sigor@sysoev.ru return header; 834431Sigor@sysoev.ru } 835431Sigor@sysoev.ru 836431Sigor@sysoev.ru 837431Sigor@sysoev.ru static void 838431Sigor@sysoev.ru nxt_h1p_sent(nxt_task_t *task, void *obj, void *data) 839431Sigor@sysoev.ru { 840431Sigor@sysoev.ru nxt_conn_t *c; 841431Sigor@sysoev.ru nxt_event_engine_t *engine; 842431Sigor@sysoev.ru 843431Sigor@sysoev.ru c = obj; 844431Sigor@sysoev.ru 845431Sigor@sysoev.ru nxt_debug(task, "h1p sent"); 846431Sigor@sysoev.ru 847431Sigor@sysoev.ru engine = task->thread->engine; 848431Sigor@sysoev.ru 849431Sigor@sysoev.ru c->write = nxt_sendbuf_completion0(task, &engine->fast_work_queue, 850431Sigor@sysoev.ru c->write); 851431Sigor@sysoev.ru if (c->write != NULL) { 852431Sigor@sysoev.ru nxt_conn_write(engine, c); 853431Sigor@sysoev.ru } 854431Sigor@sysoev.ru } 855431Sigor@sysoev.ru 856431Sigor@sysoev.ru 857431Sigor@sysoev.ru static void 858431Sigor@sysoev.ru nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto) 859431Sigor@sysoev.ru { 860431Sigor@sysoev.ru nxt_conn_t *c; 861431Sigor@sysoev.ru nxt_h1proto_t *h1p; 862431Sigor@sysoev.ru 863431Sigor@sysoev.ru nxt_debug(task, "h1p request close"); 864431Sigor@sysoev.ru 865431Sigor@sysoev.ru h1p = proto.h1; 866431Sigor@sysoev.ru h1p->request = NULL; 867431Sigor@sysoev.ru 868431Sigor@sysoev.ru c = h1p->conn; 869431Sigor@sysoev.ru 870431Sigor@sysoev.ru if (h1p->keepalive) { 871431Sigor@sysoev.ru nxt_h1p_keepalive(task, h1p, c); 872431Sigor@sysoev.ru 873431Sigor@sysoev.ru } else { 874431Sigor@sysoev.ru nxt_h1p_close(task, c); 875431Sigor@sysoev.ru } 876431Sigor@sysoev.ru } 877431Sigor@sysoev.ru 878431Sigor@sysoev.ru 879431Sigor@sysoev.ru static void 880431Sigor@sysoev.ru nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c) 881431Sigor@sysoev.ru { 882431Sigor@sysoev.ru size_t size; 883431Sigor@sysoev.ru nxt_buf_t *in, *b, *next; 884431Sigor@sysoev.ru 885431Sigor@sysoev.ru nxt_debug(task, "h1p keepalive"); 886431Sigor@sysoev.ru 887436Sigor@sysoev.ru if (!c->tcp_nodelay) { 888436Sigor@sysoev.ru nxt_conn_tcp_nodelay_on(task, c); 889436Sigor@sysoev.ru } 890436Sigor@sysoev.ru 891431Sigor@sysoev.ru b = h1p->buffers; 892431Sigor@sysoev.ru 893452Sigor@sysoev.ru nxt_memzero(h1p, offsetof(nxt_h1proto_t, conn)); 894431Sigor@sysoev.ru 895431Sigor@sysoev.ru in = c->read; 896431Sigor@sysoev.ru 897459Sigor@sysoev.ru if (in == NULL) { 898459Sigor@sysoev.ru /* A request with large body. */ 899459Sigor@sysoev.ru in = b; 900459Sigor@sysoev.ru c->read = in; 901459Sigor@sysoev.ru 902459Sigor@sysoev.ru b = in->next; 903459Sigor@sysoev.ru in->next = NULL; 904459Sigor@sysoev.ru } 905459Sigor@sysoev.ru 906459Sigor@sysoev.ru while (b != NULL) { 907459Sigor@sysoev.ru next = b->next; 908459Sigor@sysoev.ru nxt_mp_free(c->mem_pool, b); 909459Sigor@sysoev.ru b = next; 910459Sigor@sysoev.ru } 911459Sigor@sysoev.ru 912431Sigor@sysoev.ru size = nxt_buf_mem_used_size(&in->mem); 913431Sigor@sysoev.ru 914431Sigor@sysoev.ru if (size == 0) { 915431Sigor@sysoev.ru in->mem.pos = in->mem.start; 916431Sigor@sysoev.ru in->mem.free = in->mem.start; 917431Sigor@sysoev.ru 918431Sigor@sysoev.ru if (c->socket.read_ready) { 919431Sigor@sysoev.ru c->read_state = &nxt_h1p_read_header_state; 920431Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 921431Sigor@sysoev.ru 922431Sigor@sysoev.ru } else { 923431Sigor@sysoev.ru c->read_state = &nxt_h1p_idle_state; 924431Sigor@sysoev.ru nxt_conn_wait(c); 925431Sigor@sysoev.ru } 926431Sigor@sysoev.ru 927431Sigor@sysoev.ru } else { 928431Sigor@sysoev.ru nxt_debug(task, "h1p pipelining"); 929431Sigor@sysoev.ru 930431Sigor@sysoev.ru nxt_memmove(in->mem.start, in->mem.pos, size); 931431Sigor@sysoev.ru 932431Sigor@sysoev.ru in->mem.pos = in->mem.start; 933431Sigor@sysoev.ru in->mem.free = in->mem.start + size; 934431Sigor@sysoev.ru 935431Sigor@sysoev.ru nxt_h1p_header_parse(task, c, c->socket.data); 936431Sigor@sysoev.ru } 937431Sigor@sysoev.ru } 938431Sigor@sysoev.ru 939431Sigor@sysoev.ru 940431Sigor@sysoev.ru static void 941431Sigor@sysoev.ru nxt_h1p_close(nxt_task_t *task, nxt_conn_t *c) 942431Sigor@sysoev.ru { 943431Sigor@sysoev.ru nxt_debug(task, "h1p close"); 944431Sigor@sysoev.ru 945431Sigor@sysoev.ru c->socket.data = NULL; 946431Sigor@sysoev.ru 947431Sigor@sysoev.ru if (c->socket.fd != -1) { 948431Sigor@sysoev.ru c->write_state = &nxt_router_conn_close_state; 949431Sigor@sysoev.ru 950431Sigor@sysoev.ru nxt_conn_close(task->thread->engine, c); 951431Sigor@sysoev.ru } 952431Sigor@sysoev.ru } 953431Sigor@sysoev.ru 954431Sigor@sysoev.ru 955431Sigor@sysoev.ru static void 956431Sigor@sysoev.ru nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data) 957431Sigor@sysoev.ru { 958431Sigor@sysoev.ru nxt_conn_t *c; 959431Sigor@sysoev.ru nxt_h1proto_t *h1p; 960431Sigor@sysoev.ru nxt_http_request_t *r; 961431Sigor@sysoev.ru 962431Sigor@sysoev.ru c = obj; 963431Sigor@sysoev.ru h1p = data; 964431Sigor@sysoev.ru 965431Sigor@sysoev.ru nxt_debug(task, "h1p conn close"); 966431Sigor@sysoev.ru 967431Sigor@sysoev.ru if (h1p != NULL) { 968431Sigor@sysoev.ru r = h1p->request; 969431Sigor@sysoev.ru 970431Sigor@sysoev.ru if (r != NULL) { 971431Sigor@sysoev.ru r->state->error_handler(task, r, r->proto.h1); 972431Sigor@sysoev.ru return; 973431Sigor@sysoev.ru } 974431Sigor@sysoev.ru } 975431Sigor@sysoev.ru 976431Sigor@sysoev.ru nxt_h1p_close(task, c); 977431Sigor@sysoev.ru } 978431Sigor@sysoev.ru 979431Sigor@sysoev.ru 980431Sigor@sysoev.ru static void 981431Sigor@sysoev.ru nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data) 982431Sigor@sysoev.ru { 983431Sigor@sysoev.ru nxt_conn_t *c; 984431Sigor@sysoev.ru nxt_h1proto_t *h1p; 985431Sigor@sysoev.ru 986431Sigor@sysoev.ru c = obj; 987431Sigor@sysoev.ru h1p = data; 988431Sigor@sysoev.ru 989431Sigor@sysoev.ru nxt_debug(task, "h1p conn error"); 990431Sigor@sysoev.ru 991431Sigor@sysoev.ru nxt_h1p_conn_close(task, c, h1p); 992431Sigor@sysoev.ru } 993431Sigor@sysoev.ru 994431Sigor@sysoev.ru 995431Sigor@sysoev.ru static void 996431Sigor@sysoev.ru nxt_h1p_conn_timeout(nxt_task_t *task, void *obj, void *data) 997431Sigor@sysoev.ru { 998431Sigor@sysoev.ru nxt_conn_t *c; 999431Sigor@sysoev.ru nxt_timer_t *timer; 1000431Sigor@sysoev.ru 1001431Sigor@sysoev.ru timer = obj; 1002431Sigor@sysoev.ru 1003431Sigor@sysoev.ru nxt_debug(task, "h1p conn timeout"); 1004431Sigor@sysoev.ru 1005431Sigor@sysoev.ru c = nxt_read_timer_conn(timer); 1006431Sigor@sysoev.ru 1007431Sigor@sysoev.ru nxt_h1p_conn_close(task, c, c->socket.data); 1008431Sigor@sysoev.ru } 1009431Sigor@sysoev.ru 1010431Sigor@sysoev.ru 1011431Sigor@sysoev.ru static nxt_msec_t 1012431Sigor@sysoev.ru nxt_h1p_timeout_value(nxt_conn_t *c, uintptr_t data) 1013431Sigor@sysoev.ru { 1014431Sigor@sysoev.ru nxt_socket_conf_joint_t *joint; 1015431Sigor@sysoev.ru 1016431Sigor@sysoev.ru joint = c->joint; 1017431Sigor@sysoev.ru 1018431Sigor@sysoev.ru return nxt_value_at(nxt_msec_t, joint->socket_conf, data); 1019431Sigor@sysoev.ru } 1020