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; 193480Svbart@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 */ 257481Svbart@nginx.com h1p->keepalive = (h1p->parser.version.s.minor != '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 268480Svbart@nginx.com /* ret == NXT_ERROR */ 269480Svbart@nginx.com 270480Svbart@nginx.com nxt_http_request_error(task, r, NXT_HTTP_BAD_REQUEST); 271480Svbart@nginx.com return; 272480Svbart@nginx.com } 273480Svbart@nginx.com 274480Svbart@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 308480Svbart@nginx.com switch (ret) { 309480Svbart@nginx.com 310480Svbart@nginx.com case NXT_HTTP_PARSE_INVALID: 311480Svbart@nginx.com status = NXT_HTTP_BAD_REQUEST; 312480Svbart@nginx.com break; 313431Sigor@sysoev.ru 314*482Svbart@nginx.com case NXT_HTTP_PARSE_UNSUPPORTED_VERSION: 315*482Svbart@nginx.com status = NXT_HTTP_VERSION_NOT_SUPPORTED; 316*482Svbart@nginx.com break; 317*482Svbart@nginx.com 318480Svbart@nginx.com case NXT_HTTP_PARSE_TOO_LARGE_FIELD: 319480Svbart@nginx.com status = NXT_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; 320480Svbart@nginx.com break; 321480Svbart@nginx.com 322480Svbart@nginx.com default: 323480Svbart@nginx.com case NXT_ERROR: 324480Svbart@nginx.com status = NXT_HTTP_INTERNAL_SERVER_ERROR; 325480Svbart@nginx.com break; 326480Svbart@nginx.com } 327480Svbart@nginx.com 328480Svbart@nginx.com nxt_http_request_error(task, r, status); 329431Sigor@sysoev.ru return; 330431Sigor@sysoev.ru 331431Sigor@sysoev.ru fail: 332431Sigor@sysoev.ru 333431Sigor@sysoev.ru nxt_h1p_conn_close(task, c, h1p); 334431Sigor@sysoev.ru } 335431Sigor@sysoev.ru 336431Sigor@sysoev.ru 337431Sigor@sysoev.ru static nxt_int_t 338431Sigor@sysoev.ru nxt_h1p_connection(void *ctx, nxt_http_field_t *field, uintptr_t data) 339431Sigor@sysoev.ru { 340431Sigor@sysoev.ru nxt_http_request_t *r; 341431Sigor@sysoev.ru 342431Sigor@sysoev.ru r = ctx; 343431Sigor@sysoev.ru 344431Sigor@sysoev.ru if (field->value_length == 5 && nxt_memcmp(field->value, "close", 5) == 0) { 345431Sigor@sysoev.ru r->proto.h1->keepalive = 0; 346431Sigor@sysoev.ru } 347431Sigor@sysoev.ru 348431Sigor@sysoev.ru return NXT_OK; 349431Sigor@sysoev.ru } 350431Sigor@sysoev.ru 351431Sigor@sysoev.ru 352431Sigor@sysoev.ru static nxt_int_t 353431Sigor@sysoev.ru nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field, uintptr_t data) 354431Sigor@sysoev.ru { 355431Sigor@sysoev.ru nxt_http_te_t te; 356431Sigor@sysoev.ru nxt_http_request_t *r; 357431Sigor@sysoev.ru 358431Sigor@sysoev.ru r = ctx; 359431Sigor@sysoev.ru 360431Sigor@sysoev.ru if (field->value_length == 7 361431Sigor@sysoev.ru && nxt_memcmp(field->value, "chunked", 7) == 0) 362431Sigor@sysoev.ru { 363431Sigor@sysoev.ru te = NXT_HTTP_TE_CHUNKED; 364431Sigor@sysoev.ru 365431Sigor@sysoev.ru } else { 366431Sigor@sysoev.ru te = NXT_HTTP_TE_UNSUPPORTED; 367431Sigor@sysoev.ru } 368431Sigor@sysoev.ru 369431Sigor@sysoev.ru r->proto.h1->transfer_encoding = te; 370431Sigor@sysoev.ru 371431Sigor@sysoev.ru return NXT_OK; 372431Sigor@sysoev.ru } 373431Sigor@sysoev.ru 374431Sigor@sysoev.ru 375431Sigor@sysoev.ru static void 376431Sigor@sysoev.ru nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r) 377431Sigor@sysoev.ru { 378431Sigor@sysoev.ru size_t size, rest_length; 379459Sigor@sysoev.ru nxt_buf_t *in, *b; 380431Sigor@sysoev.ru nxt_conn_t *c; 381459Sigor@sysoev.ru nxt_h1proto_t *h1p; 382431Sigor@sysoev.ru nxt_http_status_t status; 383431Sigor@sysoev.ru 384459Sigor@sysoev.ru h1p = r->proto.h1; 385459Sigor@sysoev.ru 386431Sigor@sysoev.ru nxt_debug(task, "h1p body read %O te:%d", 387459Sigor@sysoev.ru r->content_length_n, h1p->transfer_encoding); 388431Sigor@sysoev.ru 389459Sigor@sysoev.ru switch (h1p->transfer_encoding) { 390431Sigor@sysoev.ru 391431Sigor@sysoev.ru case NXT_HTTP_TE_CHUNKED: 392431Sigor@sysoev.ru status = NXT_HTTP_LENGTH_REQUIRED; 393431Sigor@sysoev.ru goto error; 394431Sigor@sysoev.ru 395431Sigor@sysoev.ru case NXT_HTTP_TE_UNSUPPORTED: 396431Sigor@sysoev.ru status = NXT_HTTP_NOT_IMPLEMENTED; 397431Sigor@sysoev.ru goto error; 398431Sigor@sysoev.ru 399431Sigor@sysoev.ru default: 400431Sigor@sysoev.ru case NXT_HTTP_TE_NONE: 401431Sigor@sysoev.ru break; 402431Sigor@sysoev.ru } 403431Sigor@sysoev.ru 404431Sigor@sysoev.ru if (r->content_length_n == -1 || r->content_length_n == 0) { 405431Sigor@sysoev.ru goto ready; 406431Sigor@sysoev.ru } 407431Sigor@sysoev.ru 408431Sigor@sysoev.ru if (r->content_length_n > (nxt_off_t) r->socket_conf->max_body_size) { 409431Sigor@sysoev.ru status = NXT_HTTP_PAYLOAD_TOO_LARGE; 410431Sigor@sysoev.ru goto error; 411431Sigor@sysoev.ru } 412431Sigor@sysoev.ru 413431Sigor@sysoev.ru rest_length = (size_t) r->content_length_n; 414431Sigor@sysoev.ru 415431Sigor@sysoev.ru b = r->body; 416431Sigor@sysoev.ru 417431Sigor@sysoev.ru if (b == NULL) { 418431Sigor@sysoev.ru b = nxt_buf_mem_alloc(r->mem_pool, rest_length, 0); 419431Sigor@sysoev.ru if (nxt_slow_path(b == NULL)) { 420431Sigor@sysoev.ru status = NXT_HTTP_INTERNAL_SERVER_ERROR; 421431Sigor@sysoev.ru goto error; 422431Sigor@sysoev.ru } 423431Sigor@sysoev.ru 424431Sigor@sysoev.ru r->body = b; 425431Sigor@sysoev.ru } 426431Sigor@sysoev.ru 427459Sigor@sysoev.ru in = h1p->conn->read; 428431Sigor@sysoev.ru 429459Sigor@sysoev.ru size = nxt_buf_mem_used_size(&in->mem); 430431Sigor@sysoev.ru 431431Sigor@sysoev.ru if (size != 0) { 432431Sigor@sysoev.ru if (size >= rest_length) { 433431Sigor@sysoev.ru size = rest_length; 434431Sigor@sysoev.ru rest_length = 0; 435431Sigor@sysoev.ru 436431Sigor@sysoev.ru } else { 437431Sigor@sysoev.ru rest_length -= size; 438431Sigor@sysoev.ru } 439431Sigor@sysoev.ru 440459Sigor@sysoev.ru b->mem.free = nxt_cpymem(b->mem.free, in->mem.pos, size); 441459Sigor@sysoev.ru in->mem.pos += size; 442431Sigor@sysoev.ru } 443431Sigor@sysoev.ru 444431Sigor@sysoev.ru nxt_debug(task, "h1p body rest: %O", rest_length); 445431Sigor@sysoev.ru 446431Sigor@sysoev.ru r->rest_length = rest_length; 447431Sigor@sysoev.ru 448431Sigor@sysoev.ru if (rest_length != 0) { 449459Sigor@sysoev.ru in->next = h1p->buffers; 450459Sigor@sysoev.ru h1p->buffers = in; 451459Sigor@sysoev.ru 452459Sigor@sysoev.ru c = h1p->conn; 453431Sigor@sysoev.ru c->read = b; 454431Sigor@sysoev.ru c->read_state = &nxt_h1p_read_body_state; 455431Sigor@sysoev.ru 456431Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 457431Sigor@sysoev.ru return; 458431Sigor@sysoev.ru } 459431Sigor@sysoev.ru 460431Sigor@sysoev.ru ready: 461431Sigor@sysoev.ru 462431Sigor@sysoev.ru nxt_work_queue_add(&task->thread->engine->fast_work_queue, 463431Sigor@sysoev.ru r->state->ready_handler, task, r, NULL); 464431Sigor@sysoev.ru 465431Sigor@sysoev.ru return; 466431Sigor@sysoev.ru 467431Sigor@sysoev.ru error: 468431Sigor@sysoev.ru 469459Sigor@sysoev.ru h1p->keepalive = 0; 470431Sigor@sysoev.ru 471431Sigor@sysoev.ru nxt_http_request_error(task, r, status); 472431Sigor@sysoev.ru } 473431Sigor@sysoev.ru 474431Sigor@sysoev.ru 475431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_read_body_state 476431Sigor@sysoev.ru nxt_aligned(64) = 477431Sigor@sysoev.ru { 478431Sigor@sysoev.ru .ready_handler = nxt_h1p_body_read, 479431Sigor@sysoev.ru .close_handler = nxt_h1p_conn_close, 480431Sigor@sysoev.ru .error_handler = nxt_h1p_conn_error, 481431Sigor@sysoev.ru 482431Sigor@sysoev.ru .timer_handler = nxt_h1p_conn_timeout, 483431Sigor@sysoev.ru .timer_value = nxt_h1p_timeout_value, 484431Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, body_read_timeout), 485431Sigor@sysoev.ru .timer_autoreset = 1, 486431Sigor@sysoev.ru }; 487431Sigor@sysoev.ru 488431Sigor@sysoev.ru 489431Sigor@sysoev.ru static void 490431Sigor@sysoev.ru nxt_h1p_body_read(nxt_task_t *task, void *obj, void *data) 491431Sigor@sysoev.ru { 492431Sigor@sysoev.ru size_t size; 493431Sigor@sysoev.ru nxt_conn_t *c; 494431Sigor@sysoev.ru nxt_h1proto_t *h1p; 495431Sigor@sysoev.ru nxt_http_request_t *r; 496431Sigor@sysoev.ru 497431Sigor@sysoev.ru c = obj; 498431Sigor@sysoev.ru h1p = data; 499431Sigor@sysoev.ru 500431Sigor@sysoev.ru nxt_debug(task, "h1p body read"); 501431Sigor@sysoev.ru 502431Sigor@sysoev.ru r = h1p->request; 503431Sigor@sysoev.ru size = nxt_buf_mem_used_size(&c->read->mem); 504431Sigor@sysoev.ru 505431Sigor@sysoev.ru r->rest_length -= size; 506431Sigor@sysoev.ru 507431Sigor@sysoev.ru nxt_debug(task, "h1p body rest: %O", r->rest_length); 508431Sigor@sysoev.ru 509431Sigor@sysoev.ru if (r->rest_length != 0) { 510431Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 511431Sigor@sysoev.ru 512431Sigor@sysoev.ru } else { 513459Sigor@sysoev.ru c->read = NULL; 514431Sigor@sysoev.ru nxt_work_queue_add(&task->thread->engine->fast_work_queue, 515431Sigor@sysoev.ru r->state->ready_handler, task, r, NULL); 516431Sigor@sysoev.ru } 517431Sigor@sysoev.ru } 518431Sigor@sysoev.ru 519431Sigor@sysoev.ru 520431Sigor@sysoev.ru static void 521431Sigor@sysoev.ru nxt_h1p_request_local_addr(nxt_task_t *task, nxt_http_request_t *r) 522431Sigor@sysoev.ru { 523431Sigor@sysoev.ru r->local = nxt_conn_local_addr(task, r->proto.h1->conn); 524431Sigor@sysoev.ru } 525431Sigor@sysoev.ru 526431Sigor@sysoev.ru 527431Sigor@sysoev.ru #define NXT_HTTP_LAST_SUCCESS \ 528431Sigor@sysoev.ru (NXT_HTTP_OK + nxt_nitems(nxt_http_success) - 1) 529431Sigor@sysoev.ru 530431Sigor@sysoev.ru static const nxt_str_t nxt_http_success[] = { 531431Sigor@sysoev.ru nxt_string("HTTP/1.1 200 OK\r\n"), 532431Sigor@sysoev.ru nxt_string("HTTP/1.1 201 Created\r\n"), 533431Sigor@sysoev.ru nxt_string("HTTP/1.1 202 Accepted\r\n"), 534431Sigor@sysoev.ru nxt_string("HTTP/1.1 203 Non-Authoritative Information\r\n"), 535431Sigor@sysoev.ru nxt_string("HTTP/1.1 204 No Content\r\n"), 536431Sigor@sysoev.ru nxt_string("HTTP/1.1 205 Reset Content\r\n"), 537431Sigor@sysoev.ru nxt_string("HTTP/1.1 206 Partial Content\r\n"), 538431Sigor@sysoev.ru }; 539431Sigor@sysoev.ru 540431Sigor@sysoev.ru 541431Sigor@sysoev.ru #define NXT_HTTP_LAST_REDIRECTION \ 542431Sigor@sysoev.ru (NXT_HTTP_MULTIPLE_CHOICES + nxt_nitems(nxt_http_redirection) - 1) 543431Sigor@sysoev.ru 544431Sigor@sysoev.ru static const nxt_str_t nxt_http_redirection[] = { 545431Sigor@sysoev.ru nxt_string("HTTP/1.1 300 Multiple Choices\r\n"), 546431Sigor@sysoev.ru nxt_string("HTTP/1.1 301 Moved Permanently\r\n"), 547431Sigor@sysoev.ru nxt_string("HTTP/1.1 302 Found\r\n"), 548431Sigor@sysoev.ru nxt_string("HTTP/1.1 303 See Other\r\n"), 549431Sigor@sysoev.ru nxt_string("HTTP/1.1 304 Not Modified\r\n"), 550431Sigor@sysoev.ru }; 551431Sigor@sysoev.ru 552431Sigor@sysoev.ru 553431Sigor@sysoev.ru #define NXT_HTTP_LAST_CLIENT_ERROR \ 554431Sigor@sysoev.ru (NXT_HTTP_BAD_REQUEST + nxt_nitems(nxt_http_client_error) - 1) 555431Sigor@sysoev.ru 556431Sigor@sysoev.ru static const nxt_str_t nxt_http_client_error[] = { 557431Sigor@sysoev.ru nxt_string("HTTP/1.1 400 Bad Request\r\n"), 558431Sigor@sysoev.ru nxt_string("HTTP/1.1 401 Unauthorized\r\n"), 559431Sigor@sysoev.ru nxt_string("HTTP/1.1 402 Payment Required\r\n"), 560431Sigor@sysoev.ru nxt_string("HTTP/1.1 403 Forbidden\r\n"), 561431Sigor@sysoev.ru nxt_string("HTTP/1.1 404 Not Found\r\n"), 562431Sigor@sysoev.ru nxt_string("HTTP/1.1 405 Method Not Allowed\r\n"), 563431Sigor@sysoev.ru nxt_string("HTTP/1.1 406 Not Acceptable\r\n"), 564431Sigor@sysoev.ru nxt_string("HTTP/1.1 407 Proxy Authentication Required\r\n"), 565431Sigor@sysoev.ru nxt_string("HTTP/1.1 408 Request Timeout\r\n"), 566431Sigor@sysoev.ru nxt_string("HTTP/1.1 409 Conflict\r\n"), 567431Sigor@sysoev.ru nxt_string("HTTP/1.1 410 Gone\r\n"), 568431Sigor@sysoev.ru nxt_string("HTTP/1.1 411 Length Required\r\n"), 569431Sigor@sysoev.ru nxt_string("HTTP/1.1 412 Precondition Failed\r\n"), 570431Sigor@sysoev.ru nxt_string("HTTP/1.1 413 Payload Too Large\r\n"), 571431Sigor@sysoev.ru nxt_string("HTTP/1.1 414 URI Too Long\r\n"), 572431Sigor@sysoev.ru nxt_string("HTTP/1.1 415 Unsupported Media Type\r\n"), 573431Sigor@sysoev.ru nxt_string("HTTP/1.1 416 Range Not Satisfiable\r\n"), 574431Sigor@sysoev.ru nxt_string("HTTP/1.1 417 Expectation Failed\r\n"), 575431Sigor@sysoev.ru nxt_string("HTTP/1.1 418\r\n"), 576431Sigor@sysoev.ru nxt_string("HTTP/1.1 419\r\n"), 577431Sigor@sysoev.ru nxt_string("HTTP/1.1 420\r\n"), 578431Sigor@sysoev.ru nxt_string("HTTP/1.1 421\r\n"), 579431Sigor@sysoev.ru nxt_string("HTTP/1.1 422\r\n"), 580431Sigor@sysoev.ru nxt_string("HTTP/1.1 423\r\n"), 581431Sigor@sysoev.ru nxt_string("HTTP/1.1 424\r\n"), 582431Sigor@sysoev.ru nxt_string("HTTP/1.1 425\r\n"), 583431Sigor@sysoev.ru nxt_string("HTTP/1.1 426\r\n"), 584431Sigor@sysoev.ru nxt_string("HTTP/1.1 427\r\n"), 585431Sigor@sysoev.ru nxt_string("HTTP/1.1 428\r\n"), 586431Sigor@sysoev.ru nxt_string("HTTP/1.1 429\r\n"), 587431Sigor@sysoev.ru nxt_string("HTTP/1.1 430\r\n"), 588431Sigor@sysoev.ru nxt_string("HTTP/1.1 431 Request Header Fields Too Large\r\n"), 589431Sigor@sysoev.ru }; 590431Sigor@sysoev.ru 591431Sigor@sysoev.ru 592431Sigor@sysoev.ru #define NXT_HTTP_LAST_SERVER_ERROR \ 593431Sigor@sysoev.ru (NXT_HTTP_INTERNAL_SERVER_ERROR + nxt_nitems(nxt_http_server_error) - 1) 594431Sigor@sysoev.ru 595431Sigor@sysoev.ru static const nxt_str_t nxt_http_server_error[] = { 596431Sigor@sysoev.ru nxt_string("HTTP/1.1 500 Internal Server Error\r\n"), 597431Sigor@sysoev.ru nxt_string("HTTP/1.1 501 Not Implemented\r\n"), 598431Sigor@sysoev.ru nxt_string("HTTP/1.1 502 Bad Gateway\r\n"), 599431Sigor@sysoev.ru nxt_string("HTTP/1.1 503 Service Unavailable\r\n"), 600431Sigor@sysoev.ru nxt_string("HTTP/1.1 504 Gateway Timeout\r\n"), 601*482Svbart@nginx.com nxt_string("HTTP/1.1 505 HTTP Version Not Supported\r\n"), 602431Sigor@sysoev.ru }; 603431Sigor@sysoev.ru 604431Sigor@sysoev.ru 605431Sigor@sysoev.ru #define UNKNOWN_STATUS_LENGTH (sizeof("HTTP/1.1 65536\r\n") - 1) 606431Sigor@sysoev.ru 607431Sigor@sysoev.ru static void 608431Sigor@sysoev.ru nxt_h1p_request_header_send(nxt_task_t *task, nxt_http_request_t *r) 609431Sigor@sysoev.ru { 610431Sigor@sysoev.ru u_char *p; 611431Sigor@sysoev.ru size_t size; 612431Sigor@sysoev.ru nxt_buf_t *header; 613431Sigor@sysoev.ru nxt_str_t unknown_status; 614431Sigor@sysoev.ru nxt_int_t conn; 615431Sigor@sysoev.ru nxt_uint_t n; 616431Sigor@sysoev.ru nxt_bool_t http11; 617431Sigor@sysoev.ru nxt_conn_t *c; 618431Sigor@sysoev.ru nxt_h1proto_t *h1p; 619431Sigor@sysoev.ru const nxt_str_t *status; 620431Sigor@sysoev.ru nxt_http_field_t *field; 621431Sigor@sysoev.ru nxt_event_engine_t *engine; 622431Sigor@sysoev.ru u_char buf[UNKNOWN_STATUS_LENGTH]; 623431Sigor@sysoev.ru 624431Sigor@sysoev.ru static const char chunked[] = "Transfer-Encoding: chunked\r\n"; 625431Sigor@sysoev.ru 626431Sigor@sysoev.ru static const nxt_str_t connection[2] = { 627431Sigor@sysoev.ru nxt_string("Connection: close\r\n"), 628431Sigor@sysoev.ru nxt_string("Connection: keep-alive\r\n"), 629431Sigor@sysoev.ru }; 630431Sigor@sysoev.ru 631431Sigor@sysoev.ru nxt_debug(task, "h1p request header send"); 632431Sigor@sysoev.ru 633431Sigor@sysoev.ru r->header_sent = 1; 634431Sigor@sysoev.ru h1p = r->proto.h1; 635431Sigor@sysoev.ru n = r->status; 636431Sigor@sysoev.ru 637431Sigor@sysoev.ru if (n >= NXT_HTTP_OK && n <= NXT_HTTP_LAST_SUCCESS) { 638431Sigor@sysoev.ru status = &nxt_http_success[n - NXT_HTTP_OK]; 639431Sigor@sysoev.ru 640431Sigor@sysoev.ru } else if (n >= NXT_HTTP_MULTIPLE_CHOICES 641431Sigor@sysoev.ru && n <= NXT_HTTP_LAST_REDIRECTION) 642431Sigor@sysoev.ru { 643431Sigor@sysoev.ru status = &nxt_http_redirection[n - NXT_HTTP_MULTIPLE_CHOICES]; 644431Sigor@sysoev.ru 645431Sigor@sysoev.ru } else if (n >= NXT_HTTP_BAD_REQUEST && n <= NXT_HTTP_LAST_CLIENT_ERROR) { 646431Sigor@sysoev.ru status = &nxt_http_client_error[n - NXT_HTTP_BAD_REQUEST]; 647431Sigor@sysoev.ru 648431Sigor@sysoev.ru } else if (n >= NXT_HTTP_INTERNAL_SERVER_ERROR 649431Sigor@sysoev.ru && n <= NXT_HTTP_LAST_SERVER_ERROR) 650431Sigor@sysoev.ru { 651431Sigor@sysoev.ru status = &nxt_http_server_error[n - NXT_HTTP_INTERNAL_SERVER_ERROR]; 652431Sigor@sysoev.ru 653431Sigor@sysoev.ru } else { 654431Sigor@sysoev.ru p = nxt_sprintf(buf, buf + UNKNOWN_STATUS_LENGTH, 655431Sigor@sysoev.ru "HTTP/1.1 %03d\r\n", n); 656431Sigor@sysoev.ru 657431Sigor@sysoev.ru unknown_status.length = p - buf; 658431Sigor@sysoev.ru unknown_status.start = buf; 659431Sigor@sysoev.ru status = &unknown_status; 660431Sigor@sysoev.ru } 661431Sigor@sysoev.ru 662450Sigor@sysoev.ru size = status->length; 663450Sigor@sysoev.ru /* Trailing CRLF at the end of header. */ 664450Sigor@sysoev.ru size += sizeof("\r\n") - 1; 665431Sigor@sysoev.ru 666481Svbart@nginx.com http11 = (h1p->parser.version.s.minor != '0'); 667431Sigor@sysoev.ru 668431Sigor@sysoev.ru if (r->resp.content_length == NULL || r->resp.content_length->skip) { 669431Sigor@sysoev.ru if (http11) { 670431Sigor@sysoev.ru h1p->chunked = 1; 671431Sigor@sysoev.ru size += sizeof(chunked) - 1; 672450Sigor@sysoev.ru /* Trailing CRLF will be added by the first chunk header. */ 673450Sigor@sysoev.ru size -= sizeof("\r\n") - 1; 674431Sigor@sysoev.ru 675431Sigor@sysoev.ru } else { 676431Sigor@sysoev.ru h1p->keepalive = 0; 677431Sigor@sysoev.ru } 678431Sigor@sysoev.ru } 679431Sigor@sysoev.ru 680431Sigor@sysoev.ru conn = -1; 681431Sigor@sysoev.ru 682431Sigor@sysoev.ru if (http11 ^ h1p->keepalive) { 683431Sigor@sysoev.ru conn = h1p->keepalive; 684431Sigor@sysoev.ru size += connection[conn].length; 685431Sigor@sysoev.ru } 686431Sigor@sysoev.ru 687431Sigor@sysoev.ru nxt_list_each(field, r->resp.fields) { 688431Sigor@sysoev.ru 689431Sigor@sysoev.ru if (!field->skip) { 690431Sigor@sysoev.ru size += field->name_length + field->value_length; 691431Sigor@sysoev.ru size += sizeof(": \r\n") - 1; 692431Sigor@sysoev.ru } 693431Sigor@sysoev.ru 694431Sigor@sysoev.ru } nxt_list_loop; 695431Sigor@sysoev.ru 696431Sigor@sysoev.ru header = nxt_buf_mem_alloc(r->mem_pool, size, 0); 697431Sigor@sysoev.ru if (nxt_slow_path(header == NULL)) { 698431Sigor@sysoev.ru /* The internal server error is set just for logging. */ 699431Sigor@sysoev.ru r->status = NXT_HTTP_INTERNAL_SERVER_ERROR; 700431Sigor@sysoev.ru nxt_h1p_conn_close(task, h1p->conn, h1p); 701431Sigor@sysoev.ru return; 702431Sigor@sysoev.ru } 703431Sigor@sysoev.ru 704431Sigor@sysoev.ru p = header->mem.free; 705431Sigor@sysoev.ru 706431Sigor@sysoev.ru p = nxt_cpymem(p, status->start, status->length); 707431Sigor@sysoev.ru 708431Sigor@sysoev.ru nxt_list_each(field, r->resp.fields) { 709431Sigor@sysoev.ru 710431Sigor@sysoev.ru if (!field->skip) { 711431Sigor@sysoev.ru p = nxt_cpymem(p, field->name, field->name_length); 712431Sigor@sysoev.ru *p++ = ':'; *p++ = ' '; 713431Sigor@sysoev.ru p = nxt_cpymem(p, field->value, field->value_length); 714431Sigor@sysoev.ru *p++ = '\r'; *p++ = '\n'; 715431Sigor@sysoev.ru } 716431Sigor@sysoev.ru 717431Sigor@sysoev.ru } nxt_list_loop; 718431Sigor@sysoev.ru 719431Sigor@sysoev.ru if (conn >= 0) { 720431Sigor@sysoev.ru p = nxt_cpymem(p, connection[conn].start, connection[conn].length); 721431Sigor@sysoev.ru } 722431Sigor@sysoev.ru 723431Sigor@sysoev.ru if (h1p->chunked) { 724431Sigor@sysoev.ru p = nxt_cpymem(p, chunked, sizeof(chunked) - 1); 725450Sigor@sysoev.ru /* Trailing CRLF will be added by the first chunk header. */ 726431Sigor@sysoev.ru 727431Sigor@sysoev.ru } else { 728431Sigor@sysoev.ru *p++ = '\r'; *p++ = '\n'; 729431Sigor@sysoev.ru } 730431Sigor@sysoev.ru 731431Sigor@sysoev.ru header->mem.free = p; 732431Sigor@sysoev.ru 733431Sigor@sysoev.ru c = h1p->conn; 734431Sigor@sysoev.ru 735431Sigor@sysoev.ru c->write = header; 736431Sigor@sysoev.ru c->write_state = &nxt_h1p_send_state; 737431Sigor@sysoev.ru 738431Sigor@sysoev.ru engine = task->thread->engine; 739431Sigor@sysoev.ru 740431Sigor@sysoev.ru nxt_work_queue_add(&engine->fast_work_queue, r->state->ready_handler, 741431Sigor@sysoev.ru task, r, NULL); 742431Sigor@sysoev.ru 743431Sigor@sysoev.ru nxt_conn_write(engine, c); 744431Sigor@sysoev.ru } 745431Sigor@sysoev.ru 746431Sigor@sysoev.ru 747431Sigor@sysoev.ru static const nxt_conn_state_t nxt_h1p_send_state 748431Sigor@sysoev.ru nxt_aligned(64) = 749431Sigor@sysoev.ru { 750431Sigor@sysoev.ru .ready_handler = nxt_h1p_sent, 751431Sigor@sysoev.ru .close_handler = nxt_h1p_conn_close, 752431Sigor@sysoev.ru .error_handler = nxt_h1p_conn_error, 753431Sigor@sysoev.ru 754431Sigor@sysoev.ru .timer_handler = nxt_h1p_conn_timeout, 755431Sigor@sysoev.ru .timer_value = nxt_h1p_timeout_value, 756431Sigor@sysoev.ru .timer_data = offsetof(nxt_socket_conf_t, send_timeout), 757431Sigor@sysoev.ru .timer_autoreset = 1, 758431Sigor@sysoev.ru }; 759431Sigor@sysoev.ru 760431Sigor@sysoev.ru 761431Sigor@sysoev.ru static void 762431Sigor@sysoev.ru nxt_h1p_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out) 763431Sigor@sysoev.ru { 764431Sigor@sysoev.ru nxt_conn_t *c; 765431Sigor@sysoev.ru 766431Sigor@sysoev.ru nxt_debug(task, "h1p request send"); 767431Sigor@sysoev.ru 768431Sigor@sysoev.ru c = r->proto.h1->conn; 769431Sigor@sysoev.ru 770431Sigor@sysoev.ru if (r->proto.h1->chunked) { 771431Sigor@sysoev.ru out = nxt_h1p_chunk_create(task, r, out); 772431Sigor@sysoev.ru if (nxt_slow_path(out == NULL)) { 773431Sigor@sysoev.ru nxt_h1p_conn_error(task, c, c->socket.data); 774431Sigor@sysoev.ru return; 775431Sigor@sysoev.ru } 776431Sigor@sysoev.ru } 777431Sigor@sysoev.ru 778431Sigor@sysoev.ru if (c->write == NULL) { 779431Sigor@sysoev.ru c->write = out; 780431Sigor@sysoev.ru c->write_state = &nxt_h1p_send_state; 781431Sigor@sysoev.ru 782431Sigor@sysoev.ru nxt_conn_write(task->thread->engine, c); 783431Sigor@sysoev.ru 784431Sigor@sysoev.ru } else { 785431Sigor@sysoev.ru nxt_buf_chain_add(&c->write, out); 786431Sigor@sysoev.ru } 787431Sigor@sysoev.ru } 788431Sigor@sysoev.ru 789431Sigor@sysoev.ru 790431Sigor@sysoev.ru static nxt_buf_t * 791431Sigor@sysoev.ru nxt_h1p_chunk_create(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out) 792431Sigor@sysoev.ru { 793431Sigor@sysoev.ru size_t size; 794431Sigor@sysoev.ru nxt_buf_t *b, **prev, *header, *tail; 795431Sigor@sysoev.ru 796431Sigor@sysoev.ru const size_t chunk_size = 2 * (sizeof("\r\n") - 1) + NXT_OFF_T_HEXLEN; 797431Sigor@sysoev.ru static const char tail_chunk[] = "\r\n0\r\n\r\n"; 798431Sigor@sysoev.ru 799431Sigor@sysoev.ru size = 0; 800431Sigor@sysoev.ru prev = &out; 801431Sigor@sysoev.ru 802431Sigor@sysoev.ru for (b = out; b != NULL; b = b->next) { 803431Sigor@sysoev.ru 804431Sigor@sysoev.ru if (nxt_buf_is_last(b)) { 805431Sigor@sysoev.ru tail = nxt_buf_mem_alloc(r->mem_pool, chunk_size, 0); 806431Sigor@sysoev.ru if (nxt_slow_path(tail == NULL)) { 807431Sigor@sysoev.ru return NULL; 808431Sigor@sysoev.ru } 809431Sigor@sysoev.ru 810431Sigor@sysoev.ru *prev = tail; 811431Sigor@sysoev.ru tail->next = b; 812431Sigor@sysoev.ru /* 813431Sigor@sysoev.ru * The tail_chunk size with trailing zero is 8 bytes, so 814431Sigor@sysoev.ru * memcpy may be inlined with just single 8 byte move operation. 815431Sigor@sysoev.ru */ 816431Sigor@sysoev.ru nxt_memcpy(tail->mem.free, tail_chunk, sizeof(tail_chunk)); 817431Sigor@sysoev.ru tail->mem.free += sizeof(tail_chunk) - 1; 818431Sigor@sysoev.ru 819431Sigor@sysoev.ru break; 820431Sigor@sysoev.ru } 821431Sigor@sysoev.ru 822431Sigor@sysoev.ru size += nxt_buf_used_size(b); 823431Sigor@sysoev.ru prev = &b->next; 824431Sigor@sysoev.ru } 825431Sigor@sysoev.ru 826431Sigor@sysoev.ru if (size == 0) { 827431Sigor@sysoev.ru return out; 828431Sigor@sysoev.ru } 829431Sigor@sysoev.ru 830431Sigor@sysoev.ru header = nxt_buf_mem_alloc(r->mem_pool, chunk_size, 0); 831431Sigor@sysoev.ru if (nxt_slow_path(header == NULL)) { 832431Sigor@sysoev.ru return NULL; 833431Sigor@sysoev.ru } 834431Sigor@sysoev.ru 835431Sigor@sysoev.ru header->next = out; 836431Sigor@sysoev.ru header->mem.free = nxt_sprintf(header->mem.free, header->mem.end, 837431Sigor@sysoev.ru "\r\n%xO\r\n", size); 838431Sigor@sysoev.ru return header; 839431Sigor@sysoev.ru } 840431Sigor@sysoev.ru 841431Sigor@sysoev.ru 842431Sigor@sysoev.ru static void 843431Sigor@sysoev.ru nxt_h1p_sent(nxt_task_t *task, void *obj, void *data) 844431Sigor@sysoev.ru { 845431Sigor@sysoev.ru nxt_conn_t *c; 846431Sigor@sysoev.ru nxt_event_engine_t *engine; 847431Sigor@sysoev.ru 848431Sigor@sysoev.ru c = obj; 849431Sigor@sysoev.ru 850431Sigor@sysoev.ru nxt_debug(task, "h1p sent"); 851431Sigor@sysoev.ru 852431Sigor@sysoev.ru engine = task->thread->engine; 853431Sigor@sysoev.ru 854431Sigor@sysoev.ru c->write = nxt_sendbuf_completion0(task, &engine->fast_work_queue, 855431Sigor@sysoev.ru c->write); 856431Sigor@sysoev.ru if (c->write != NULL) { 857431Sigor@sysoev.ru nxt_conn_write(engine, c); 858431Sigor@sysoev.ru } 859431Sigor@sysoev.ru } 860431Sigor@sysoev.ru 861431Sigor@sysoev.ru 862431Sigor@sysoev.ru static void 863431Sigor@sysoev.ru nxt_h1p_request_close(nxt_task_t *task, nxt_http_proto_t proto) 864431Sigor@sysoev.ru { 865431Sigor@sysoev.ru nxt_conn_t *c; 866431Sigor@sysoev.ru nxt_h1proto_t *h1p; 867431Sigor@sysoev.ru 868431Sigor@sysoev.ru nxt_debug(task, "h1p request close"); 869431Sigor@sysoev.ru 870431Sigor@sysoev.ru h1p = proto.h1; 871431Sigor@sysoev.ru h1p->request = NULL; 872431Sigor@sysoev.ru 873431Sigor@sysoev.ru c = h1p->conn; 874431Sigor@sysoev.ru 875431Sigor@sysoev.ru if (h1p->keepalive) { 876431Sigor@sysoev.ru nxt_h1p_keepalive(task, h1p, c); 877431Sigor@sysoev.ru 878431Sigor@sysoev.ru } else { 879431Sigor@sysoev.ru nxt_h1p_close(task, c); 880431Sigor@sysoev.ru } 881431Sigor@sysoev.ru } 882431Sigor@sysoev.ru 883431Sigor@sysoev.ru 884431Sigor@sysoev.ru static void 885431Sigor@sysoev.ru nxt_h1p_keepalive(nxt_task_t *task, nxt_h1proto_t *h1p, nxt_conn_t *c) 886431Sigor@sysoev.ru { 887431Sigor@sysoev.ru size_t size; 888431Sigor@sysoev.ru nxt_buf_t *in, *b, *next; 889431Sigor@sysoev.ru 890431Sigor@sysoev.ru nxt_debug(task, "h1p keepalive"); 891431Sigor@sysoev.ru 892436Sigor@sysoev.ru if (!c->tcp_nodelay) { 893436Sigor@sysoev.ru nxt_conn_tcp_nodelay_on(task, c); 894436Sigor@sysoev.ru } 895436Sigor@sysoev.ru 896431Sigor@sysoev.ru b = h1p->buffers; 897431Sigor@sysoev.ru 898452Sigor@sysoev.ru nxt_memzero(h1p, offsetof(nxt_h1proto_t, conn)); 899431Sigor@sysoev.ru 900431Sigor@sysoev.ru in = c->read; 901431Sigor@sysoev.ru 902459Sigor@sysoev.ru if (in == NULL) { 903459Sigor@sysoev.ru /* A request with large body. */ 904459Sigor@sysoev.ru in = b; 905459Sigor@sysoev.ru c->read = in; 906459Sigor@sysoev.ru 907459Sigor@sysoev.ru b = in->next; 908459Sigor@sysoev.ru in->next = NULL; 909459Sigor@sysoev.ru } 910459Sigor@sysoev.ru 911459Sigor@sysoev.ru while (b != NULL) { 912459Sigor@sysoev.ru next = b->next; 913459Sigor@sysoev.ru nxt_mp_free(c->mem_pool, b); 914459Sigor@sysoev.ru b = next; 915459Sigor@sysoev.ru } 916459Sigor@sysoev.ru 917431Sigor@sysoev.ru size = nxt_buf_mem_used_size(&in->mem); 918431Sigor@sysoev.ru 919431Sigor@sysoev.ru if (size == 0) { 920431Sigor@sysoev.ru in->mem.pos = in->mem.start; 921431Sigor@sysoev.ru in->mem.free = in->mem.start; 922431Sigor@sysoev.ru 923431Sigor@sysoev.ru if (c->socket.read_ready) { 924431Sigor@sysoev.ru c->read_state = &nxt_h1p_read_header_state; 925431Sigor@sysoev.ru nxt_conn_read(task->thread->engine, c); 926431Sigor@sysoev.ru 927431Sigor@sysoev.ru } else { 928431Sigor@sysoev.ru c->read_state = &nxt_h1p_idle_state; 929431Sigor@sysoev.ru nxt_conn_wait(c); 930431Sigor@sysoev.ru } 931431Sigor@sysoev.ru 932431Sigor@sysoev.ru } else { 933431Sigor@sysoev.ru nxt_debug(task, "h1p pipelining"); 934431Sigor@sysoev.ru 935431Sigor@sysoev.ru nxt_memmove(in->mem.start, in->mem.pos, size); 936431Sigor@sysoev.ru 937431Sigor@sysoev.ru in->mem.pos = in->mem.start; 938431Sigor@sysoev.ru in->mem.free = in->mem.start + size; 939431Sigor@sysoev.ru 940431Sigor@sysoev.ru nxt_h1p_header_parse(task, c, c->socket.data); 941431Sigor@sysoev.ru } 942431Sigor@sysoev.ru } 943431Sigor@sysoev.ru 944431Sigor@sysoev.ru 945431Sigor@sysoev.ru static void 946431Sigor@sysoev.ru nxt_h1p_close(nxt_task_t *task, nxt_conn_t *c) 947431Sigor@sysoev.ru { 948431Sigor@sysoev.ru nxt_debug(task, "h1p close"); 949431Sigor@sysoev.ru 950431Sigor@sysoev.ru c->socket.data = NULL; 951431Sigor@sysoev.ru 952431Sigor@sysoev.ru if (c->socket.fd != -1) { 953431Sigor@sysoev.ru c->write_state = &nxt_router_conn_close_state; 954431Sigor@sysoev.ru 955431Sigor@sysoev.ru nxt_conn_close(task->thread->engine, c); 956431Sigor@sysoev.ru } 957431Sigor@sysoev.ru } 958431Sigor@sysoev.ru 959431Sigor@sysoev.ru 960431Sigor@sysoev.ru static void 961431Sigor@sysoev.ru nxt_h1p_conn_close(nxt_task_t *task, void *obj, void *data) 962431Sigor@sysoev.ru { 963431Sigor@sysoev.ru nxt_conn_t *c; 964431Sigor@sysoev.ru nxt_h1proto_t *h1p; 965431Sigor@sysoev.ru nxt_http_request_t *r; 966431Sigor@sysoev.ru 967431Sigor@sysoev.ru c = obj; 968431Sigor@sysoev.ru h1p = data; 969431Sigor@sysoev.ru 970431Sigor@sysoev.ru nxt_debug(task, "h1p conn close"); 971431Sigor@sysoev.ru 972431Sigor@sysoev.ru if (h1p != NULL) { 973431Sigor@sysoev.ru r = h1p->request; 974431Sigor@sysoev.ru 975431Sigor@sysoev.ru if (r != NULL) { 976431Sigor@sysoev.ru r->state->error_handler(task, r, r->proto.h1); 977431Sigor@sysoev.ru return; 978431Sigor@sysoev.ru } 979431Sigor@sysoev.ru } 980431Sigor@sysoev.ru 981431Sigor@sysoev.ru nxt_h1p_close(task, c); 982431Sigor@sysoev.ru } 983431Sigor@sysoev.ru 984431Sigor@sysoev.ru 985431Sigor@sysoev.ru static void 986431Sigor@sysoev.ru nxt_h1p_conn_error(nxt_task_t *task, void *obj, void *data) 987431Sigor@sysoev.ru { 988431Sigor@sysoev.ru nxt_conn_t *c; 989431Sigor@sysoev.ru nxt_h1proto_t *h1p; 990431Sigor@sysoev.ru 991431Sigor@sysoev.ru c = obj; 992431Sigor@sysoev.ru h1p = data; 993431Sigor@sysoev.ru 994431Sigor@sysoev.ru nxt_debug(task, "h1p conn error"); 995431Sigor@sysoev.ru 996431Sigor@sysoev.ru nxt_h1p_conn_close(task, c, h1p); 997431Sigor@sysoev.ru } 998431Sigor@sysoev.ru 999431Sigor@sysoev.ru 1000431Sigor@sysoev.ru static void 1001431Sigor@sysoev.ru nxt_h1p_conn_timeout(nxt_task_t *task, void *obj, void *data) 1002431Sigor@sysoev.ru { 1003431Sigor@sysoev.ru nxt_conn_t *c; 1004431Sigor@sysoev.ru nxt_timer_t *timer; 1005431Sigor@sysoev.ru 1006431Sigor@sysoev.ru timer = obj; 1007431Sigor@sysoev.ru 1008431Sigor@sysoev.ru nxt_debug(task, "h1p conn timeout"); 1009431Sigor@sysoev.ru 1010431Sigor@sysoev.ru c = nxt_read_timer_conn(timer); 1011431Sigor@sysoev.ru 1012431Sigor@sysoev.ru nxt_h1p_conn_close(task, c, c->socket.data); 1013431Sigor@sysoev.ru } 1014431Sigor@sysoev.ru 1015431Sigor@sysoev.ru 1016431Sigor@sysoev.ru static nxt_msec_t 1017431Sigor@sysoev.ru nxt_h1p_timeout_value(nxt_conn_t *c, uintptr_t data) 1018431Sigor@sysoev.ru { 1019431Sigor@sysoev.ru nxt_socket_conf_joint_t *joint; 1020431Sigor@sysoev.ru 1021431Sigor@sysoev.ru joint = c->joint; 1022431Sigor@sysoev.ru 1023431Sigor@sysoev.ru return nxt_value_at(nxt_msec_t, joint->socket_conf, data); 1024431Sigor@sysoev.ru } 1025