/src/unit/src/nxt_http_proxy.c
Line | Count | Source |
1 | | |
2 | | /* |
3 | | * Copyright (C) Igor Sysoev |
4 | | * Copyright (C) NGINX, Inc. |
5 | | */ |
6 | | |
7 | | #include <nxt_router.h> |
8 | | #include <nxt_http.h> |
9 | | #include <nxt_upstream.h> |
10 | | |
11 | | |
12 | | struct nxt_upstream_proxy_s { |
13 | | nxt_sockaddr_t *sockaddr; |
14 | | uint8_t protocol; |
15 | | }; |
16 | | |
17 | | |
18 | | static void nxt_http_proxy_server_get(nxt_task_t *task, |
19 | | nxt_upstream_server_t *us); |
20 | | static void nxt_http_proxy_upstream_ready(nxt_task_t *task, |
21 | | nxt_upstream_server_t *us); |
22 | | static void nxt_http_proxy_upstream_error(nxt_task_t *task, |
23 | | nxt_upstream_server_t *us); |
24 | | static nxt_http_action_t *nxt_http_proxy(nxt_task_t *task, |
25 | | nxt_http_request_t *r, nxt_http_action_t *action); |
26 | | static void nxt_http_proxy_header_send(nxt_task_t *task, void *obj, void *data); |
27 | | static void nxt_http_proxy_header_sent(nxt_task_t *task, void *obj, void *data); |
28 | | static void nxt_http_proxy_header_read(nxt_task_t *task, void *obj, void *data); |
29 | | static void nxt_http_proxy_send_body(nxt_task_t *task, void *obj, void *data); |
30 | | static void nxt_http_proxy_buf_mem_completion(nxt_task_t *task, void *obj, |
31 | | void *data); |
32 | | static void nxt_http_proxy_error(nxt_task_t *task, void *obj, void *data); |
33 | | |
34 | | |
35 | | static const nxt_http_request_state_t nxt_http_proxy_header_send_state; |
36 | | static const nxt_http_request_state_t nxt_http_proxy_header_sent_state; |
37 | | static const nxt_http_request_state_t nxt_http_proxy_header_read_state; |
38 | | static const nxt_http_request_state_t nxt_http_proxy_read_state; |
39 | | |
40 | | |
41 | | static const nxt_upstream_server_proto_t nxt_upstream_simple_proto = { |
42 | | .get = nxt_http_proxy_server_get, |
43 | | }; |
44 | | |
45 | | |
46 | | static const nxt_upstream_peer_state_t nxt_upstream_proxy_state = { |
47 | | .ready = nxt_http_proxy_upstream_ready, |
48 | | .error = nxt_http_proxy_upstream_error, |
49 | | }; |
50 | | |
51 | | |
52 | | nxt_int_t |
53 | | nxt_http_proxy_init(nxt_mp_t *mp, nxt_http_action_t *action, |
54 | | nxt_http_action_conf_t *acf) |
55 | 0 | { |
56 | 0 | nxt_str_t name; |
57 | 0 | nxt_sockaddr_t *sa; |
58 | 0 | nxt_upstream_t *up; |
59 | 0 | nxt_upstream_proxy_t *proxy; |
60 | |
|
61 | 0 | sa = NULL; |
62 | 0 | nxt_conf_get_string(acf->proxy, &name); |
63 | |
|
64 | 0 | if (nxt_str_start(&name, "http://", 7)) { |
65 | 0 | name.length -= 7; |
66 | 0 | name.start += 7; |
67 | |
|
68 | 0 | sa = nxt_sockaddr_parse(mp, &name); |
69 | 0 | if (nxt_slow_path(sa == NULL)) { |
70 | 0 | return NXT_ERROR; |
71 | 0 | } |
72 | | |
73 | 0 | sa->type = SOCK_STREAM; |
74 | 0 | } |
75 | | |
76 | 0 | if (sa != NULL) { |
77 | 0 | up = nxt_mp_alloc(mp, sizeof(nxt_upstream_t)); |
78 | 0 | if (nxt_slow_path(up == NULL)) { |
79 | 0 | return NXT_ERROR; |
80 | 0 | } |
81 | | |
82 | 0 | up->name.length = sa->length; |
83 | 0 | up->name.start = nxt_sockaddr_start(sa); |
84 | 0 | up->proto = &nxt_upstream_simple_proto; |
85 | |
|
86 | 0 | proxy = nxt_mp_alloc(mp, sizeof(nxt_upstream_proxy_t)); |
87 | 0 | if (nxt_slow_path(proxy == NULL)) { |
88 | 0 | return NXT_ERROR; |
89 | 0 | } |
90 | | |
91 | 0 | proxy->sockaddr = sa; |
92 | 0 | proxy->protocol = NXT_HTTP_PROTO_H1; |
93 | 0 | up->type.proxy = proxy; |
94 | |
|
95 | 0 | action->u.upstream = up; |
96 | 0 | action->handler = nxt_http_proxy; |
97 | 0 | } |
98 | | |
99 | 0 | return NXT_OK; |
100 | 0 | } |
101 | | |
102 | | |
103 | | static nxt_http_action_t * |
104 | | nxt_http_proxy(nxt_task_t *task, nxt_http_request_t *r, |
105 | | nxt_http_action_t *action) |
106 | 0 | { |
107 | 0 | nxt_upstream_t *u; |
108 | |
|
109 | 0 | u = action->u.upstream; |
110 | |
|
111 | 0 | nxt_debug(task, "http proxy: \"%V\"", &u->name); |
112 | |
|
113 | 0 | return nxt_upstream_proxy_handler(task, r, u); |
114 | 0 | } |
115 | | |
116 | | |
117 | | nxt_http_action_t * |
118 | | nxt_upstream_proxy_handler(nxt_task_t *task, nxt_http_request_t *r, |
119 | | nxt_upstream_t *upstream) |
120 | 0 | { |
121 | 0 | nxt_http_peer_t *peer; |
122 | 0 | nxt_upstream_server_t *us; |
123 | |
|
124 | 0 | us = nxt_mp_zalloc(r->mem_pool, sizeof(nxt_upstream_server_t)); |
125 | 0 | if (nxt_slow_path(us == NULL)) { |
126 | 0 | nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); |
127 | 0 | return NULL; |
128 | 0 | } |
129 | | |
130 | 0 | peer = nxt_mp_zalloc(r->mem_pool, sizeof(nxt_http_peer_t)); |
131 | 0 | if (nxt_slow_path(peer == NULL)) { |
132 | 0 | nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); |
133 | 0 | return NULL; |
134 | 0 | } |
135 | | |
136 | 0 | peer->request = r; |
137 | 0 | r->peer = peer; |
138 | |
|
139 | 0 | nxt_mp_retain(r->mem_pool); |
140 | |
|
141 | 0 | us->state = &nxt_upstream_proxy_state; |
142 | 0 | us->peer.http = peer; |
143 | 0 | peer->server = us; |
144 | |
|
145 | 0 | us->upstream = upstream; |
146 | 0 | upstream->proto->get(task, us); |
147 | |
|
148 | 0 | return NULL; |
149 | 0 | } |
150 | | |
151 | | |
152 | | static void |
153 | | nxt_http_proxy_server_get(nxt_task_t *task, nxt_upstream_server_t *us) |
154 | 0 | { |
155 | 0 | nxt_upstream_proxy_t *proxy; |
156 | |
|
157 | 0 | proxy = us->upstream->type.proxy; |
158 | |
|
159 | 0 | us->sockaddr = proxy->sockaddr; |
160 | 0 | us->protocol = proxy->protocol; |
161 | |
|
162 | 0 | us->state->ready(task, us); |
163 | 0 | } |
164 | | |
165 | | |
166 | | static void |
167 | | nxt_http_proxy_upstream_ready(nxt_task_t *task, nxt_upstream_server_t *us) |
168 | 0 | { |
169 | 0 | nxt_http_peer_t *peer; |
170 | |
|
171 | 0 | peer = us->peer.http; |
172 | |
|
173 | 0 | peer->protocol = us->protocol; |
174 | |
|
175 | 0 | peer->request->state = &nxt_http_proxy_header_send_state; |
176 | |
|
177 | 0 | nxt_http_proto[peer->protocol].peer_connect(task, peer); |
178 | 0 | } |
179 | | |
180 | | |
181 | | static void |
182 | | nxt_http_proxy_upstream_error(nxt_task_t *task, nxt_upstream_server_t *us) |
183 | 0 | { |
184 | 0 | nxt_http_request_t *r; |
185 | |
|
186 | 0 | r = us->peer.http->request; |
187 | |
|
188 | 0 | nxt_mp_release(r->mem_pool); |
189 | |
|
190 | 0 | nxt_http_request_error(task, r, NXT_HTTP_BAD_GATEWAY); |
191 | 0 | } |
192 | | |
193 | | |
194 | | static const nxt_http_request_state_t nxt_http_proxy_header_send_state |
195 | | nxt_aligned(64) = |
196 | | { |
197 | | .ready_handler = nxt_http_proxy_header_send, |
198 | | .error_handler = nxt_http_proxy_error, |
199 | | }; |
200 | | |
201 | | |
202 | | static void |
203 | | nxt_http_proxy_header_send(nxt_task_t *task, void *obj, void *data) |
204 | 0 | { |
205 | 0 | nxt_http_peer_t *peer; |
206 | 0 | nxt_http_request_t *r; |
207 | |
|
208 | 0 | r = obj; |
209 | 0 | peer = data; |
210 | 0 | r->state = &nxt_http_proxy_header_sent_state; |
211 | |
|
212 | 0 | nxt_http_proto[peer->protocol].peer_header_send(task, peer); |
213 | 0 | } |
214 | | |
215 | | |
216 | | static const nxt_http_request_state_t nxt_http_proxy_header_sent_state |
217 | | nxt_aligned(64) = |
218 | | { |
219 | | .ready_handler = nxt_http_proxy_header_sent, |
220 | | .error_handler = nxt_http_proxy_error, |
221 | | }; |
222 | | |
223 | | |
224 | | static void |
225 | | nxt_http_proxy_header_sent(nxt_task_t *task, void *obj, void *data) |
226 | 0 | { |
227 | 0 | nxt_http_peer_t *peer; |
228 | 0 | nxt_http_request_t *r; |
229 | |
|
230 | 0 | r = obj; |
231 | 0 | peer = data; |
232 | 0 | r->state = &nxt_http_proxy_header_read_state; |
233 | |
|
234 | 0 | nxt_http_proto[peer->protocol].peer_header_read(task, peer); |
235 | 0 | } |
236 | | |
237 | | |
238 | | static const nxt_http_request_state_t nxt_http_proxy_header_read_state |
239 | | nxt_aligned(64) = |
240 | | { |
241 | | .ready_handler = nxt_http_proxy_header_read, |
242 | | .error_handler = nxt_http_proxy_error, |
243 | | }; |
244 | | |
245 | | |
246 | | static void |
247 | | nxt_http_proxy_header_read(nxt_task_t *task, void *obj, void *data) |
248 | 0 | { |
249 | 0 | nxt_http_peer_t *peer; |
250 | 0 | nxt_http_field_t *f, *field; |
251 | 0 | nxt_http_request_t *r; |
252 | |
|
253 | 0 | r = obj; |
254 | 0 | peer = data; |
255 | |
|
256 | 0 | r->status = peer->status; |
257 | |
|
258 | 0 | nxt_debug(task, "http proxy status: %d", peer->status); |
259 | |
|
260 | 0 | nxt_list_each(field, peer->fields) { |
261 | |
|
262 | 0 | nxt_debug(task, "http proxy header: \"%*s: %*s\"", |
263 | 0 | (size_t) field->name_length, field->name, |
264 | 0 | (size_t) field->value_length, field->value); |
265 | |
|
266 | 0 | if (!field->skip) { |
267 | 0 | f = nxt_list_add(r->resp.fields); |
268 | 0 | if (nxt_slow_path(f == NULL)) { |
269 | 0 | nxt_http_proxy_error(task, r, peer); |
270 | 0 | return; |
271 | 0 | } |
272 | | |
273 | 0 | *f = *field; |
274 | 0 | } |
275 | |
|
276 | 0 | } nxt_list_loop; |
277 | | |
278 | 0 | r->state = &nxt_http_proxy_read_state; |
279 | |
|
280 | 0 | nxt_http_request_header_send(task, r, nxt_http_proxy_send_body, peer); |
281 | 0 | } |
282 | | |
283 | | |
284 | | static const nxt_http_request_state_t nxt_http_proxy_read_state |
285 | | nxt_aligned(64) = |
286 | | { |
287 | | .ready_handler = nxt_http_proxy_send_body, |
288 | | .error_handler = nxt_http_proxy_error, |
289 | | }; |
290 | | |
291 | | |
292 | | static void |
293 | | nxt_http_proxy_send_body(nxt_task_t *task, void *obj, void *data) |
294 | 0 | { |
295 | 0 | nxt_buf_t *out; |
296 | 0 | nxt_http_peer_t *peer; |
297 | 0 | nxt_http_request_t *r; |
298 | |
|
299 | 0 | r = obj; |
300 | 0 | peer = data; |
301 | 0 | out = peer->body; |
302 | |
|
303 | 0 | if (out != NULL) { |
304 | 0 | peer->body = NULL; |
305 | 0 | nxt_http_request_send(task, r, out); |
306 | 0 | } |
307 | |
|
308 | 0 | if (!peer->closed) { |
309 | 0 | nxt_http_proto[peer->protocol].peer_read(task, peer); |
310 | |
|
311 | 0 | } else { |
312 | 0 | nxt_http_proto[peer->protocol].peer_close(task, peer); |
313 | |
|
314 | 0 | nxt_mp_release(r->mem_pool); |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | | |
319 | | nxt_buf_t * |
320 | | nxt_http_proxy_buf_mem_alloc(nxt_task_t *task, nxt_http_request_t *r, |
321 | | size_t size) |
322 | 0 | { |
323 | 0 | nxt_buf_t *b; |
324 | |
|
325 | 0 | b = nxt_event_engine_buf_mem_alloc(task->thread->engine, size); |
326 | 0 | if (nxt_fast_path(b != NULL)) { |
327 | 0 | b->completion_handler = nxt_http_proxy_buf_mem_completion; |
328 | 0 | b->parent = r; |
329 | 0 | nxt_mp_retain(r->mem_pool); |
330 | |
|
331 | 0 | } else { |
332 | 0 | nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); |
333 | 0 | } |
334 | |
|
335 | 0 | return b; |
336 | 0 | } |
337 | | |
338 | | |
339 | | static void |
340 | | nxt_http_proxy_buf_mem_completion(nxt_task_t *task, void *obj, void *data) |
341 | 0 | { |
342 | 0 | nxt_buf_t *b, *next; |
343 | 0 | nxt_http_peer_t *peer; |
344 | 0 | nxt_http_request_t *r; |
345 | |
|
346 | 0 | b = obj; |
347 | 0 | r = data; |
348 | |
|
349 | 0 | peer = r->peer; |
350 | |
|
351 | 0 | do { |
352 | 0 | next = b->next; |
353 | |
|
354 | 0 | nxt_http_proxy_buf_mem_free(task, r, b); |
355 | |
|
356 | 0 | b = next; |
357 | 0 | } while (b != NULL); |
358 | |
|
359 | 0 | if (!peer->closed) { |
360 | 0 | nxt_http_proto[peer->protocol].peer_read(task, peer); |
361 | 0 | } |
362 | 0 | } |
363 | | |
364 | | |
365 | | void |
366 | | nxt_http_proxy_buf_mem_free(nxt_task_t *task, nxt_http_request_t *r, |
367 | | nxt_buf_t *b) |
368 | 0 | { |
369 | 0 | nxt_event_engine_buf_mem_free(task->thread->engine, b); |
370 | |
|
371 | 0 | nxt_mp_release(r->mem_pool); |
372 | 0 | } |
373 | | |
374 | | |
375 | | static void |
376 | | nxt_http_proxy_error(nxt_task_t *task, void *obj, void *data) |
377 | 0 | { |
378 | 0 | nxt_http_peer_t *peer; |
379 | 0 | nxt_http_request_t *r; |
380 | |
|
381 | 0 | r = obj; |
382 | 0 | peer = r->peer; |
383 | |
|
384 | 0 | if (!peer->closed) { |
385 | 0 | nxt_http_proto[peer->protocol].peer_close(task, peer); |
386 | 0 | nxt_mp_release(r->mem_pool); |
387 | 0 | } |
388 | |
|
389 | 0 | nxt_http_request_error(&r->task, r, peer->status); |
390 | 0 | } |
391 | | |
392 | | |
393 | | nxt_int_t |
394 | | nxt_http_proxy_date(void *ctx, nxt_http_field_t *field, uintptr_t data) |
395 | 0 | { |
396 | 0 | nxt_http_request_t *r; |
397 | |
|
398 | 0 | r = ctx; |
399 | |
|
400 | 0 | r->resp.date = field; |
401 | |
|
402 | 0 | return NXT_OK; |
403 | 0 | } |
404 | | |
405 | | |
406 | | nxt_int_t |
407 | | nxt_http_proxy_content_length(void *ctx, nxt_http_field_t *field, |
408 | | uintptr_t data) |
409 | 0 | { |
410 | 0 | nxt_off_t n; |
411 | 0 | nxt_http_request_t *r; |
412 | |
|
413 | 0 | r = ctx; |
414 | |
|
415 | 0 | r->resp.content_length = field; |
416 | |
|
417 | 0 | n = nxt_off_t_parse(field->value, field->value_length); |
418 | |
|
419 | 0 | if (nxt_fast_path(n >= 0)) { |
420 | 0 | r->resp.content_length_n = n; |
421 | 0 | } |
422 | |
|
423 | 0 | return NXT_OK; |
424 | 0 | } |
425 | | |
426 | | |
427 | | nxt_int_t |
428 | | nxt_http_proxy_skip(void *ctx, nxt_http_field_t *field, uintptr_t data) |
429 | 0 | { |
430 | 0 | field->skip = 1; |
431 | |
|
432 | 0 | return NXT_OK; |
433 | 0 | } |