Coverage Report

Created: 2026-04-01 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}