xref: /unit/src/nxt_http_request.c (revision 451)
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_http_request_start(nxt_task_t *task, void *obj, void *data);
12431Sigor@sysoev.ru static void nxt_http_app_request(nxt_task_t *task, void *obj, void *data);
13431Sigor@sysoev.ru static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data);
14431Sigor@sysoev.ru 
15431Sigor@sysoev.ru 
16431Sigor@sysoev.ru static const nxt_http_request_state_t  nxt_http_request_init_state;
17431Sigor@sysoev.ru static const nxt_http_request_state_t  nxt_http_request_body_state;
18431Sigor@sysoev.ru 
19431Sigor@sysoev.ru 
20431Sigor@sysoev.ru nxt_int_t
21431Sigor@sysoev.ru nxt_http_init(nxt_task_t *task, nxt_runtime_t *rt)
22431Sigor@sysoev.ru {
23431Sigor@sysoev.ru     nxt_int_t  ret;
24431Sigor@sysoev.ru 
25431Sigor@sysoev.ru     ret = nxt_h1p_init(task, rt);
26431Sigor@sysoev.ru 
27431Sigor@sysoev.ru     if (ret != NXT_OK) {
28431Sigor@sysoev.ru         return ret;
29431Sigor@sysoev.ru     }
30431Sigor@sysoev.ru 
31431Sigor@sysoev.ru     return nxt_http_response_hash_init(task, rt);
32431Sigor@sysoev.ru }
33431Sigor@sysoev.ru 
34431Sigor@sysoev.ru 
35431Sigor@sysoev.ru nxt_int_t
36431Sigor@sysoev.ru nxt_http_request_host(void *ctx, nxt_http_field_t *field, uintptr_t data)
37431Sigor@sysoev.ru {
38431Sigor@sysoev.ru     nxt_http_request_t  *r;
39431Sigor@sysoev.ru 
40431Sigor@sysoev.ru     r = ctx;
41431Sigor@sysoev.ru 
42431Sigor@sysoev.ru     /* TODO: validate host. */
43431Sigor@sysoev.ru 
44431Sigor@sysoev.ru     r->host = field;
45431Sigor@sysoev.ru 
46431Sigor@sysoev.ru     return NXT_OK;
47431Sigor@sysoev.ru }
48431Sigor@sysoev.ru 
49431Sigor@sysoev.ru 
50431Sigor@sysoev.ru nxt_int_t
51431Sigor@sysoev.ru nxt_http_request_field(void *ctx, nxt_http_field_t *field, uintptr_t offset)
52431Sigor@sysoev.ru {
53431Sigor@sysoev.ru     nxt_http_request_t  *r;
54431Sigor@sysoev.ru 
55431Sigor@sysoev.ru     r = ctx;
56431Sigor@sysoev.ru 
57431Sigor@sysoev.ru     nxt_value_at(nxt_http_field_t *, r, offset) = field;
58431Sigor@sysoev.ru 
59431Sigor@sysoev.ru     return NXT_OK;
60431Sigor@sysoev.ru }
61431Sigor@sysoev.ru 
62431Sigor@sysoev.ru 
63431Sigor@sysoev.ru nxt_int_t
64431Sigor@sysoev.ru nxt_http_request_content_length(void *ctx, nxt_http_field_t *field,
65431Sigor@sysoev.ru     uintptr_t data)
66431Sigor@sysoev.ru {
67431Sigor@sysoev.ru     nxt_http_request_t  *r;
68431Sigor@sysoev.ru 
69431Sigor@sysoev.ru     r = ctx;
70431Sigor@sysoev.ru 
71431Sigor@sysoev.ru     r->content_length = field;
72431Sigor@sysoev.ru     r->content_length_n = nxt_off_t_parse(field->value, field->value_length);
73431Sigor@sysoev.ru 
74431Sigor@sysoev.ru     return NXT_OK;
75431Sigor@sysoev.ru }
76431Sigor@sysoev.ru 
77431Sigor@sysoev.ru 
78431Sigor@sysoev.ru nxt_http_request_t *
79431Sigor@sysoev.ru nxt_http_request_create(nxt_task_t *task)
80431Sigor@sysoev.ru {
81431Sigor@sysoev.ru     nxt_mp_t            *mp;
82431Sigor@sysoev.ru     nxt_http_request_t  *r;
83431Sigor@sysoev.ru 
84431Sigor@sysoev.ru     mp = nxt_mp_create(1024, 128, 256, 32);
85431Sigor@sysoev.ru     if (nxt_slow_path(mp == NULL)) {
86431Sigor@sysoev.ru         return NULL;
87431Sigor@sysoev.ru     }
88431Sigor@sysoev.ru 
89431Sigor@sysoev.ru     r = nxt_mp_zget(mp, sizeof(nxt_http_request_t));
90431Sigor@sysoev.ru     if (nxt_slow_path(r == NULL)) {
91431Sigor@sysoev.ru         goto fail;
92431Sigor@sysoev.ru     }
93431Sigor@sysoev.ru 
94431Sigor@sysoev.ru     r->resp.fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t));
95*451Sigor@sysoev.ru     if (nxt_slow_path(r->resp.fields == NULL)) {
96431Sigor@sysoev.ru         goto fail;
97431Sigor@sysoev.ru     }
98431Sigor@sysoev.ru 
99431Sigor@sysoev.ru     r->mem_pool = mp;
100431Sigor@sysoev.ru     r->content_length_n = -1;
101431Sigor@sysoev.ru     r->resp.content_length_n = -1;
102431Sigor@sysoev.ru     r->state = &nxt_http_request_init_state;
103431Sigor@sysoev.ru 
104431Sigor@sysoev.ru     return r;
105431Sigor@sysoev.ru 
106431Sigor@sysoev.ru fail:
107431Sigor@sysoev.ru 
108431Sigor@sysoev.ru     nxt_mp_release(mp);
109431Sigor@sysoev.ru     return NULL;
110431Sigor@sysoev.ru }
111431Sigor@sysoev.ru 
112431Sigor@sysoev.ru 
113431Sigor@sysoev.ru static const nxt_http_request_state_t  nxt_http_request_init_state
114431Sigor@sysoev.ru     nxt_aligned(64) =
115431Sigor@sysoev.ru {
116431Sigor@sysoev.ru     .ready_handler = nxt_http_request_start,
117431Sigor@sysoev.ru     .error_handler = nxt_http_request_close_handler,
118431Sigor@sysoev.ru };
119431Sigor@sysoev.ru 
120431Sigor@sysoev.ru 
121431Sigor@sysoev.ru static void
122431Sigor@sysoev.ru nxt_http_request_start(nxt_task_t *task, void *obj, void *data)
123431Sigor@sysoev.ru {
124431Sigor@sysoev.ru     nxt_http_request_t  *r;
125431Sigor@sysoev.ru 
126431Sigor@sysoev.ru     r = obj;
127431Sigor@sysoev.ru 
128431Sigor@sysoev.ru     r->state = &nxt_http_request_body_state;
129431Sigor@sysoev.ru 
130431Sigor@sysoev.ru     nxt_http_request_read_body(task, r);
131431Sigor@sysoev.ru }
132431Sigor@sysoev.ru 
133431Sigor@sysoev.ru 
134431Sigor@sysoev.ru static const nxt_http_request_state_t  nxt_http_request_body_state
135431Sigor@sysoev.ru     nxt_aligned(64) =
136431Sigor@sysoev.ru {
137431Sigor@sysoev.ru     .ready_handler = nxt_http_app_request,
138431Sigor@sysoev.ru     .error_handler = nxt_http_request_close_handler,
139431Sigor@sysoev.ru };
140431Sigor@sysoev.ru 
141431Sigor@sysoev.ru 
142431Sigor@sysoev.ru static void
143431Sigor@sysoev.ru nxt_http_app_request(nxt_task_t *task, void *obj, void *data)
144431Sigor@sysoev.ru {
145431Sigor@sysoev.ru     nxt_int_t            ret;
146431Sigor@sysoev.ru     nxt_event_engine_t   *engine;
147431Sigor@sysoev.ru     nxt_http_request_t   *r;
148431Sigor@sysoev.ru     nxt_app_parse_ctx_t  *ar;
149431Sigor@sysoev.ru 
150431Sigor@sysoev.ru     r = obj;
151431Sigor@sysoev.ru 
152431Sigor@sysoev.ru     ar = nxt_mp_zget(r->mem_pool, sizeof(nxt_app_parse_ctx_t));
153431Sigor@sysoev.ru     if (nxt_slow_path(ar == NULL)) {
154431Sigor@sysoev.ru         nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
155431Sigor@sysoev.ru         return;
156431Sigor@sysoev.ru     }
157431Sigor@sysoev.ru 
158431Sigor@sysoev.ru     ar->request = r;
159431Sigor@sysoev.ru     ar->mem_pool = r->mem_pool;
160431Sigor@sysoev.ru     nxt_mp_retain(r->mem_pool);
161431Sigor@sysoev.ru 
162431Sigor@sysoev.ru     // STUB
163431Sigor@sysoev.ru     engine = task->thread->engine;
164431Sigor@sysoev.ru     ar->timer.task = &engine->task;
165431Sigor@sysoev.ru     ar->timer.work_queue = &engine->fast_work_queue;
166431Sigor@sysoev.ru     ar->timer.log = engine->task.log;
167431Sigor@sysoev.ru     ar->timer.precision = NXT_TIMER_DEFAULT_PRECISION;
168431Sigor@sysoev.ru 
169431Sigor@sysoev.ru     ar->r.remote.start = nxt_sockaddr_address(r->remote);
170431Sigor@sysoev.ru     ar->r.remote.length = r->remote->address_length;
171431Sigor@sysoev.ru 
172431Sigor@sysoev.ru     /*
173431Sigor@sysoev.ru      * TODO: need an application flag to get local address
174431Sigor@sysoev.ru      * required by "SERVER_ADDR" in Pyhton and PHP. Not used in Go.
175431Sigor@sysoev.ru      */
176431Sigor@sysoev.ru     nxt_http_request_local_addr(task, r);
177431Sigor@sysoev.ru 
178431Sigor@sysoev.ru     if (nxt_fast_path(r->local != NULL)) {
179431Sigor@sysoev.ru         ar->r.local.start = nxt_sockaddr_address(r->local);
180431Sigor@sysoev.ru         ar->r.local.length = r->local->address_length;
181431Sigor@sysoev.ru     }
182431Sigor@sysoev.ru 
183431Sigor@sysoev.ru     ar->r.header.fields = r->fields;
184431Sigor@sysoev.ru     ar->r.header.done = 1;
185431Sigor@sysoev.ru     ar->r.header.version = r->version;
186431Sigor@sysoev.ru 
187431Sigor@sysoev.ru     if (r->method != NULL) {
188431Sigor@sysoev.ru         ar->r.header.method = *r->method;
189431Sigor@sysoev.ru     }
190431Sigor@sysoev.ru 
191431Sigor@sysoev.ru     ar->r.header.target = r->target;
192431Sigor@sysoev.ru 
193431Sigor@sysoev.ru     if (r->path != NULL) {
194431Sigor@sysoev.ru         ar->r.header.path = *r->path;
195431Sigor@sysoev.ru     }
196431Sigor@sysoev.ru 
197431Sigor@sysoev.ru     if (r->args != NULL) {
198431Sigor@sysoev.ru         ar->r.header.query = *r->args;
199431Sigor@sysoev.ru     }
200431Sigor@sysoev.ru 
201431Sigor@sysoev.ru     if (r->host != NULL) {
202431Sigor@sysoev.ru         ar->r.header.host.length = r->host->value_length;
203431Sigor@sysoev.ru         ar->r.header.host.start = r->host->value;
204431Sigor@sysoev.ru     }
205431Sigor@sysoev.ru 
206431Sigor@sysoev.ru     if (r->content_type != NULL) {
207431Sigor@sysoev.ru         ar->r.header.content_type.length = r->content_type->value_length;
208431Sigor@sysoev.ru         ar->r.header.content_type.start = r->content_type->value;
209431Sigor@sysoev.ru     }
210431Sigor@sysoev.ru 
211431Sigor@sysoev.ru     if (r->content_length != NULL) {
212431Sigor@sysoev.ru         ar->r.header.content_length.length = r->content_length->value_length;
213431Sigor@sysoev.ru         ar->r.header.content_length.start = r->content_length->value;
214431Sigor@sysoev.ru     }
215431Sigor@sysoev.ru 
216431Sigor@sysoev.ru     if (r->cookie != NULL) {
217431Sigor@sysoev.ru         ar->r.header.cookie.length = r->cookie->value_length;
218431Sigor@sysoev.ru         ar->r.header.cookie.start = r->cookie->value;
219431Sigor@sysoev.ru     }
220431Sigor@sysoev.ru 
221431Sigor@sysoev.ru     ar->r.body.done = 1;
222431Sigor@sysoev.ru 
223431Sigor@sysoev.ru     ret = nxt_http_parse_request_init(&ar->resp_parser, r->mem_pool);
224431Sigor@sysoev.ru     if (nxt_slow_path(ret != NXT_OK)) {
225431Sigor@sysoev.ru         nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
226431Sigor@sysoev.ru         return;
227431Sigor@sysoev.ru     }
228431Sigor@sysoev.ru 
229431Sigor@sysoev.ru     nxt_router_process_http_request(task, ar);
230431Sigor@sysoev.ru }
231431Sigor@sysoev.ru 
232431Sigor@sysoev.ru 
233431Sigor@sysoev.ru void
234431Sigor@sysoev.ru nxt_http_request_read_body(nxt_task_t *task, nxt_http_request_t *r)
235431Sigor@sysoev.ru {
236431Sigor@sysoev.ru     if (r->proto.any != NULL) {
237431Sigor@sysoev.ru         nxt_http_proto_body_read[r->protocol](task, r);
238431Sigor@sysoev.ru     }
239431Sigor@sysoev.ru }
240431Sigor@sysoev.ru 
241431Sigor@sysoev.ru 
242431Sigor@sysoev.ru void
243431Sigor@sysoev.ru nxt_http_request_local_addr(nxt_task_t *task, nxt_http_request_t *r)
244431Sigor@sysoev.ru {
245431Sigor@sysoev.ru     if (r->proto.any != NULL) {
246431Sigor@sysoev.ru         nxt_http_proto_local_addr[r->protocol](task, r);
247431Sigor@sysoev.ru     }
248431Sigor@sysoev.ru }
249431Sigor@sysoev.ru 
250431Sigor@sysoev.ru 
251431Sigor@sysoev.ru void
252431Sigor@sysoev.ru nxt_http_request_header_send(nxt_task_t *task, nxt_http_request_t *r)
253431Sigor@sysoev.ru {
254431Sigor@sysoev.ru     u_char            *p, *end;
255431Sigor@sysoev.ru     nxt_http_field_t  *server, *content_length;
256431Sigor@sysoev.ru 
257431Sigor@sysoev.ru     /*
258431Sigor@sysoev.ru      * TODO: "Server" and "Content-Length" processing should be moved
259431Sigor@sysoev.ru      * to the last header filter.
260431Sigor@sysoev.ru      */
261431Sigor@sysoev.ru 
262431Sigor@sysoev.ru     server = nxt_list_zero_add(r->resp.fields);
263431Sigor@sysoev.ru     if (nxt_slow_path(server == NULL)) {
264431Sigor@sysoev.ru         goto fail;
265431Sigor@sysoev.ru     }
266431Sigor@sysoev.ru 
267431Sigor@sysoev.ru     nxt_http_field_set(server, "Server", "unit/" NXT_VERSION);
268431Sigor@sysoev.ru 
269431Sigor@sysoev.ru     if (r->resp.content_length_n != -1
270431Sigor@sysoev.ru         && (r->resp.content_length == NULL || r->resp.content_length->skip))
271431Sigor@sysoev.ru     {
272431Sigor@sysoev.ru         content_length = nxt_list_zero_add(r->resp.fields);
273431Sigor@sysoev.ru         if (nxt_slow_path(content_length == NULL)) {
274431Sigor@sysoev.ru             goto fail;
275431Sigor@sysoev.ru         }
276431Sigor@sysoev.ru 
277431Sigor@sysoev.ru         nxt_http_field_name_set(content_length, "Content-Length");
278431Sigor@sysoev.ru 
279431Sigor@sysoev.ru         p = nxt_mp_nget(r->mem_pool, NXT_OFF_T_LEN);
280431Sigor@sysoev.ru         if (nxt_slow_path(p == NULL)) {
281431Sigor@sysoev.ru             goto fail;
282431Sigor@sysoev.ru         }
283431Sigor@sysoev.ru 
284431Sigor@sysoev.ru         content_length->value = p;
285431Sigor@sysoev.ru         end = nxt_sprintf(p, p + NXT_OFF_T_LEN, "%O", r->resp.content_length_n);
286431Sigor@sysoev.ru         content_length->value_length = end - p;
287431Sigor@sysoev.ru 
288431Sigor@sysoev.ru         r->resp.content_length = content_length;
289431Sigor@sysoev.ru     }
290431Sigor@sysoev.ru 
291431Sigor@sysoev.ru     if (r->proto.any != NULL) {
292431Sigor@sysoev.ru         nxt_http_proto_header_send[r->protocol](task, r);
293431Sigor@sysoev.ru     }
294431Sigor@sysoev.ru 
295431Sigor@sysoev.ru     return;
296431Sigor@sysoev.ru 
297431Sigor@sysoev.ru fail:
298431Sigor@sysoev.ru 
299431Sigor@sysoev.ru     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
300431Sigor@sysoev.ru }
301431Sigor@sysoev.ru 
302431Sigor@sysoev.ru 
303431Sigor@sysoev.ru void
304431Sigor@sysoev.ru nxt_http_request_send(nxt_task_t *task, nxt_http_request_t *r, nxt_buf_t *out)
305431Sigor@sysoev.ru {
306431Sigor@sysoev.ru     if (r->proto.any != NULL) {
307431Sigor@sysoev.ru         nxt_http_proto_send[r->protocol](task, r, out);
308431Sigor@sysoev.ru     }
309431Sigor@sysoev.ru }
310431Sigor@sysoev.ru 
311431Sigor@sysoev.ru 
312431Sigor@sysoev.ru nxt_buf_t *
313431Sigor@sysoev.ru nxt_http_request_last_buffer(nxt_task_t *task, nxt_http_request_t *r)
314431Sigor@sysoev.ru {
315431Sigor@sysoev.ru     nxt_buf_t  *b;
316431Sigor@sysoev.ru 
317431Sigor@sysoev.ru     b = nxt_buf_mem_alloc(r->mem_pool, 0, 0);
318431Sigor@sysoev.ru 
319431Sigor@sysoev.ru     if (nxt_fast_path(b != NULL)) {
320431Sigor@sysoev.ru         nxt_buf_set_sync(b);
321431Sigor@sysoev.ru         nxt_buf_set_last(b);
322431Sigor@sysoev.ru         b->completion_handler = nxt_http_request_done;
323431Sigor@sysoev.ru         b->parent = r;
324431Sigor@sysoev.ru 
325431Sigor@sysoev.ru     } else {
326431Sigor@sysoev.ru         nxt_http_request_release(task, r);
327431Sigor@sysoev.ru     }
328431Sigor@sysoev.ru 
329431Sigor@sysoev.ru     return b;
330431Sigor@sysoev.ru }
331431Sigor@sysoev.ru 
332431Sigor@sysoev.ru 
333431Sigor@sysoev.ru static void
334431Sigor@sysoev.ru nxt_http_request_done(nxt_task_t *task, void *obj, void *data)
335431Sigor@sysoev.ru {
336431Sigor@sysoev.ru     nxt_http_request_t  *r;
337431Sigor@sysoev.ru 
338431Sigor@sysoev.ru     r = data;
339431Sigor@sysoev.ru 
340431Sigor@sysoev.ru     nxt_debug(task, "http request done");
341431Sigor@sysoev.ru 
342431Sigor@sysoev.ru     nxt_http_request_close_handler(task, r, r->proto.any);
343431Sigor@sysoev.ru }
344431Sigor@sysoev.ru 
345431Sigor@sysoev.ru 
346431Sigor@sysoev.ru void
347431Sigor@sysoev.ru nxt_http_request_release(nxt_task_t *task, nxt_http_request_t *r)
348431Sigor@sysoev.ru {
349431Sigor@sysoev.ru     nxt_debug(task, "http request release");
350431Sigor@sysoev.ru 
351431Sigor@sysoev.ru     nxt_work_queue_add(&task->thread->engine->fast_work_queue,
352431Sigor@sysoev.ru                        nxt_http_request_close_handler, task, r, r->proto.any);
353431Sigor@sysoev.ru }
354431Sigor@sysoev.ru 
355431Sigor@sysoev.ru 
356431Sigor@sysoev.ru void
357431Sigor@sysoev.ru nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data)
358431Sigor@sysoev.ru {
359431Sigor@sysoev.ru     nxt_http_proto_t        proto;
360431Sigor@sysoev.ru     nxt_http_request_t      *r;
361431Sigor@sysoev.ru     nxt_http_proto_close_t  handler;
362431Sigor@sysoev.ru 
363431Sigor@sysoev.ru     r = obj;
364431Sigor@sysoev.ru     proto.any = data;
365431Sigor@sysoev.ru 
366431Sigor@sysoev.ru     nxt_debug(task, "http request close handler");
367431Sigor@sysoev.ru 
368431Sigor@sysoev.ru     if (!r->logged) {
369431Sigor@sysoev.ru         r->logged = 1;
370431Sigor@sysoev.ru         // STUB
371431Sigor@sysoev.ru         nxt_debug(task, "http request log: \"%*s \"%V %V %V\" %d\"",
372431Sigor@sysoev.ru                   r->remote->address_length, nxt_sockaddr_address(r->remote),
373431Sigor@sysoev.ru                   r->method, &r->target, &r->version, r->status);
374431Sigor@sysoev.ru     }
375431Sigor@sysoev.ru 
376431Sigor@sysoev.ru     handler = nxt_http_proto_close[r->protocol];
377431Sigor@sysoev.ru 
378431Sigor@sysoev.ru     r->proto.any = NULL;
379431Sigor@sysoev.ru     nxt_mp_release(r->mem_pool);
380431Sigor@sysoev.ru 
381431Sigor@sysoev.ru     if (proto.any != NULL) {
382431Sigor@sysoev.ru         handler(task, proto);
383431Sigor@sysoev.ru     }
384431Sigor@sysoev.ru }
385