blob: 3bd93a3114253bc31cec707effd18e79f384e185 [file] [log] [blame]
Igor Sysoeve2a31542003-04-08 15:40:10 +00001
2#include <ngx_config.h>
3#include <ngx_core.h>
4#include <ngx_string.h>
5#include <ngx_file.h>
6#include <ngx_hunk.h>
7#include <ngx_inet.h>
8#include <ngx_conf_file.h>
9#include <ngx_event_write.h>
Igor Sysoev9e4920b2003-04-14 17:04:58 +000010#include <ngx_event_proxy.h>
Igor Sysoeve2a31542003-04-08 15:40:10 +000011
12#include <ngx_http.h>
13#include <ngx_http_config.h>
14#include <ngx_http_core_module.h>
15#include <ngx_http_output_filter.h>
16#include <ngx_http_event_proxy_handler.h>
17
18
Igor Sysoev9e4920b2003-04-14 17:04:58 +000019/* STUB */
20typedef struct {
Igor Sysoev0e18ebd2003-04-24 14:46:08 +000021 int type;
Igor Sysoev9e4920b2003-04-14 17:04:58 +000022} ngx_cache_header_t;
23
Igor Sysoev0e18ebd2003-04-24 14:46:08 +000024
Igor Sysoeve2a31542003-04-08 15:40:10 +000025static int ngx_http_proxy_handler(ngx_http_request_t *r);
26
Igor Sysoeva09f08d2003-04-25 14:43:13 +000027static int ngx_http_proxy_read_client_body(ngx_http_proxy_ctx_t *p);
Igor Sysoeve2a31542003-04-08 15:40:10 +000028static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p);
29static int ngx_http_proxy_process_upstream(ngx_http_proxy_ctx_t *p,
30 ngx_event_t *ev);
31static int ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p);
32static int ngx_http_proxy_process_upstream_event(ngx_event_t *ev);
33static int ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p);
34static int ngx_http_proxy_init_upstream(ngx_http_proxy_ctx_t *p);
35static int ngx_http_proxy_read_upstream_header(ngx_http_proxy_ctx_t *p);
Igor Sysoev183f9a62003-04-09 15:42:08 +000036
Igor Sysoeve2a31542003-04-08 15:40:10 +000037static int ngx_http_proxy_process_upstream_status_line(ngx_http_proxy_ctx_t *p);
Igor Sysoev183f9a62003-04-09 15:42:08 +000038static int ngx_http_proxy_process_upstream_headers(ngx_http_proxy_ctx_t *p);
39static int ngx_http_proxy_process_upstream_header_line(ngx_http_proxy_ctx_t *p);
Igor Sysoeve2a31542003-04-08 15:40:10 +000040
Igor Sysoev9e4920b2003-04-14 17:04:58 +000041static int ngx_http_proxy_read_upstream_body(ngx_http_proxy_ctx_t *p);
42static int ngx_http_proxy_write_upstream_body(ngx_http_proxy_ctx_t *p);
Igor Sysoevfd675862003-04-11 16:01:14 +000043
44
Igor Sysoeve2a31542003-04-08 15:40:10 +000045static int ngx_http_proxy_read_response_body(ngx_event_t *ev);
46static int ngx_http_proxy_write_to_client(ngx_event_t *ev);
47
48static int ngx_read_http_proxy_status_line(ngx_http_proxy_ctx_t *ctx);
49
50static int ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int error);
51static size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len);
52
Igor Sysoevfd675862003-04-11 16:01:14 +000053static int ngx_http_proxy_init(ngx_pool_t *pool);
Igor Sysoeve2a31542003-04-08 15:40:10 +000054static void *ngx_http_proxy_create_loc_conf(ngx_pool_t *pool);
55
56static char *ngx_http_proxy_set_pass(ngx_conf_t *cf, ngx_command_t *cmd,
57 char *conf);
58
59static char *ngx_http_proxy_parse_upstream(ngx_str_t *url,
60 ngx_http_proxy_upstream_url_t *uu);
61
62
63static ngx_command_t ngx_http_proxy_commands[] = {
64
65 {ngx_string("proxy_pass"),
66 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
67 ngx_http_proxy_set_pass,
68 NGX_HTTP_LOC_CONF_OFFSET,
69 0},
70
71 {ngx_string("proxy_large_header"),
72 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
73 ngx_conf_set_flag_slot,
74 NGX_HTTP_LOC_CONF_OFFSET,
75 offsetof(ngx_http_proxy_loc_conf_t, large_header)},
76
77 {ngx_null_string, 0, NULL, 0, 0}
78};
79
80
81static ngx_http_module_t ngx_http_proxy_module_ctx = {
82 NGX_HTTP_MODULE,
83
84 NULL, /* create server config */
85 NULL, /* init server config */
86
87 ngx_http_proxy_create_loc_conf, /* create location config */
88 NULL /* merge location config */
89};
90
91
92ngx_module_t ngx_http_proxy_module = {
93 0, /* module index */
94 &ngx_http_proxy_module_ctx, /* module context */
95 ngx_http_proxy_commands, /* module directives */
96 NGX_HTTP_MODULE_TYPE, /* module type */
Igor Sysoevfd675862003-04-11 16:01:14 +000097 ngx_http_proxy_init /* init module */
Igor Sysoeve2a31542003-04-08 15:40:10 +000098};
99
100
101static ngx_str_t http_methods[] = {
102 ngx_string("GET "),
103 ngx_string("HEAD "),
104 ngx_string("POST ")
105};
106
107
Igor Sysoev183f9a62003-04-09 15:42:08 +0000108static char *header_errors[] = {
109 "upstream sent too long status line",
110 "upstream sent invalid header",
111 "upstream sent too long header line"
112};
113
114
115static ngx_http_header_t headers_in[] = {
116 { ngx_string("Date"), offsetof(ngx_http_proxy_headers_in_t, date) },
117 { ngx_string("Server"), offsetof(ngx_http_proxy_headers_in_t, server) },
118 { ngx_string("Connection"),
119 offsetof(ngx_http_proxy_headers_in_t, connection) },
120 { ngx_string("Content-Type"),
121 offsetof(ngx_http_proxy_headers_in_t, content_type) },
122 { ngx_string("Content-Length"),
123 offsetof(ngx_http_proxy_headers_in_t, content_length) },
124 { ngx_string("Last-Modified"),
125 offsetof(ngx_http_proxy_headers_in_t, last_modified) },
126
127 { ngx_null_string, 0 }
128};
129
130
Igor Sysoeve2a31542003-04-08 15:40:10 +0000131static char http_version[] = " HTTP/1.0" CRLF;
132static char host_header[] = "Host: ";
133static char conn_close_header[] = "Connection: close" CRLF;
134
135
136/* AF_INET only */
137
138
139static int ngx_http_proxy_handler(ngx_http_request_t *r)
140{
141 int rc;
142 struct sockaddr_in addr;
143 ngx_chain_t *chain;
144 ngx_http_proxy_ctx_t *p;
145 ngx_http_log_ctx_t *hcx;
146 ngx_http_proxy_log_ctx_t *lcx;
147 ngx_http_proxy_loc_conf_t *lcf;
148
149 p = (ngx_http_proxy_ctx_t *)
150 ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
151
152 if (p == NULL) {
153 ngx_http_create_ctx(r, p, ngx_http_proxy_module_ctx,
154 sizeof(ngx_http_proxy_ctx_t),
155 NGX_HTTP_INTERNAL_SERVER_ERROR);
156 }
157
158 if (p->upstream_url == NULL) {
159 lcf = (ngx_http_proxy_loc_conf_t *)
160 ngx_http_get_module_loc_conf(r, ngx_http_proxy_module_ctx);
161
162 p->lcf = lcf;
163 p->request = r;
164 p->upstream_url = lcf->upstream_url;
165 p->upstreams = lcf->upstreams;
166 p->tries = lcf->upstreams->number;
167 }
168
169 ngx_test_null(p->log, ngx_palloc(r->pool, sizeof(ngx_log_t)),
170 NGX_HTTP_INTERNAL_SERVER_ERROR);
171 ngx_memcpy(p->log, r->connection->log, sizeof(ngx_log_t));
172 ngx_test_null(lcx, ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_log_ctx_t)),
173 NGX_HTTP_INTERNAL_SERVER_ERROR);
174
175 p->log->data = lcx;
176 hcx = r->connection->log->data;
177 lcx->client = hcx->client;
178 lcx->url = hcx->url;
179
Igor Sysoev183f9a62003-04-09 15:42:08 +0000180 r->proxy = 1;
181 p->accel = 1;
182
Igor Sysoeve2a31542003-04-08 15:40:10 +0000183 p->method = r->method;
Igor Sysoev183f9a62003-04-09 15:42:08 +0000184 p->headers_in.headers = ngx_create_table(r->pool, 10);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000185
Igor Sysoeva09f08d2003-04-25 14:43:13 +0000186 if (r->headers_in.content_length_n > 0) {
187 if (ngx_http_proxy_read_client_body(p) == NGX_ERROR) {
188 return NGX_HTTP_INTERNAL_SERVER_ERROR;
189 }
190 }
Igor Sysoeve2a31542003-04-08 15:40:10 +0000191
192 chain = ngx_http_proxy_create_request(p);
193 if (chain == NULL) {
194 return NGX_HTTP_INTERNAL_SERVER_ERROR;
195 }
196
197 /* TODO: duplicate the hunks and chain if there is backend farm */
Igor Sysoevfd675862003-04-11 16:01:14 +0000198 p->request_hunks = chain;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000199
200 p->last_error = NGX_HTTP_BAD_GATEWAY;
201 ngx_http_proxy_process_upstream(p, NULL);
202
203 /* On an error ngx_http_proxy_process_upstream() calls
204 ngx_http_proxy_finalize_request() so we return NGX_DONE to avoid
205 the additional NGX_HTTP_INTERNAL_SERVER_ERROR error
206 that would be generated by ngx_http_process_request() */
207
208 return NGX_DONE;
209}
210
211
Igor Sysoeva09f08d2003-04-25 14:43:13 +0000212static int ngx_http_proxy_read_client_body(ngx_http_proxy_ctx_t *p)
213{
214 int size, first_part;
215 ngx_hunk_t *h;
216 ngx_http_request_t *r;
217
218 r = p->request;
219
220 first_part = r->header_in->last - r->header_in->pos;
221
222 if (first_part > r->headers_in.content_length_n) {
223 first_part = r->headers_in.content_length_n;
224 size = 0;
225
226 } else {
227 size = r->headers_in.content_length_n - first_part;
228 if (size > p->lcf->client_request_buffer_size) {
229 size = p->lcf->client_request_buffer_size;
230
231 } else if (size > NGX_PAGE_SIZE) {
232 size = ((size + NGX_PAGE_SIZE) / NGX_PAGE_SIZE) * NGX_PAGE_SIZE;
233 }
234
235 if (size) {
236 ngx_test_null(p->client_request_hunk, ngx_palloc(r->pool, size),
237 NGX_ERROR);
238 }
239 }
240
241 if (first_part) {
242 ngx_test_null(h, ngx_alloc_hunk(r->pool), NGX_ERROR);
243
244 h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP;
245 h->pos = h->start = h->pre_start = r->header_in->pos;
246 h->last = h->end = h->post_end = r->header_in->pos + first_part;
247 h->file_pos = h->file_last = 0;
248 h->file = NULL;
249 h->shadow = NULL;
250 h->tag = 0;
251
252 p->client_first_part_hunk = h;
253 }
254
255 return NGX_OK;
256}
257
258
Igor Sysoeve2a31542003-04-08 15:40:10 +0000259static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p)
260{
261 int i;
262 size_t len;
263 ngx_hunk_t *hunk;
264 ngx_chain_t *chain;
265 ngx_table_elt_t *header;
266 ngx_http_request_t *r;
267
268 r = p->request;
269
270 len = http_methods[p->method - 1].len
271 + p->upstream_url->uri.len
272 + (r->uri.len - p->upstream_url->location->len)
273 + r->args.len + 1 /* 1 is for "?" */
274 + sizeof(http_version) - 1
275 + sizeof(host_header) - 1 + p->upstream_url->host.len + 2
276 + sizeof(conn_close_header) - 1
277 + 2; /* 2 is for "\r\n" at the header end */
278
279 header = (ngx_table_elt_t *) r->headers_in.headers->elts;
280 for (i = 0; i < r->headers_in.headers->nelts; i++) {
281
282 if (&header[i] == r->headers_in.host) {
283 continue;
284 }
285
286 if (&header[i] == r->headers_in.connection) {
287 continue;
288 }
289
290 /* 2 is for ": " and 2 is for "\r\n" */
291 len += header[i].key.len + 2 + header[i].value.len + 2;
292 }
293
294 /* STUB */ len++;
295
296 ngx_test_null(hunk, ngx_create_temp_hunk(r->pool, len, 0, 0), NULL);
297 ngx_add_hunk_to_chain(chain, hunk, r->pool, NULL);
298
299 /* the request line */
300
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000301 hunk->last = ngx_cpymem(hunk->last, http_methods[p->method - 1].data,
302 http_methods[p->method - 1].len);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000303
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000304 hunk->last = ngx_cpymem(hunk->last, p->upstream_url->uri.data,
305 p->upstream_url->uri.len);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000306
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000307 hunk->last = ngx_cpymem(hunk->last,
308 r->uri.data + p->upstream_url->location->len,
309 r->uri.len - p->upstream_url->location->len);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000310
311 if (r->args.len > 0) {
312 *(hunk->last++) = '?';
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000313 hunk->last = ngx_cpymem(hunk->last, r->args.data, r->args.len);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000314 }
315
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000316 hunk->last = ngx_cpymem(hunk->last, http_version, sizeof(http_version) - 1);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000317
318 /* the "Host" header */
319
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000320 hunk->last = ngx_cpymem(hunk->last, host_header, sizeof(host_header) - 1);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000321
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000322 hunk->last = ngx_cpymem(hunk->last, p->upstream_url->host.data,
323 p->upstream_url->host.len);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000324
325 *(hunk->last++) = CR; *(hunk->last++) = LF;
326
327 /* the "Connection: close" header */
328
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000329 hunk->last = ngx_cpymem(hunk->last, conn_close_header,
330 sizeof(conn_close_header) - 1);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000331
332 for (i = 0; i < r->headers_in.headers->nelts; i++) {
333
334 if (&header[i] == r->headers_in.host) {
335 continue;
336 }
337
338 if (&header[i] == r->headers_in.connection) {
339 continue;
340 }
341
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000342 hunk->last = ngx_cpymem(hunk->last, header[i].key.data,
343 header[i].key.len);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000344
345 *(hunk->last++) = ':'; *(hunk->last++) = ' ';
346
Igor Sysoev3d09c8d2003-05-06 17:03:16 +0000347 hunk->last = ngx_cpymem(hunk->last, header[i].value.data,
348 header[i].value.len);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000349
350 *(hunk->last++) = CR; *(hunk->last++) = LF;
351
352 ngx_log_debug(r->connection->log, "proxy: '%s: %s'" _
353 header[i].key.data _ header[i].value.data);
354 }
355
356 /* add "\r\n" at the header end */
357 *(hunk->last++) = CR; *(hunk->last++) = LF;
358
359 /* STUB */ *(hunk->last++) = '\0';
360 ngx_log_debug(r->connection->log, "PROXY:\n'%s'" _ hunk->pos);
361
362 return chain;
363}
364
365
366static int ngx_http_proxy_process_upstream(ngx_http_proxy_ctx_t *p,
367 ngx_event_t *ev)
368{
369 int rc;
370 time_t now;
371 ngx_connection_t *c;
372 ngx_http_proxy_upstream_t *u;
373
374 for ( ;; ) {
375
376 if (ev == NULL) {
377 /* STUB: look up cached connection */
378 c = NULL;
379
380 if (c) {
381 p->cached_connection = 1;
382 p->connection = c;
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000383 c->write->event_handler = c->read->event_handler =
384 ngx_http_proxy_process_upstream_event;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000385 rc = ngx_http_proxy_send_request(p);
386
387 } else {
388 p->cached_connection = 0;
389 p->connection = NULL;
390 rc = ngx_http_proxy_connect(p);
391 }
392
393 if (p->connection) {
394 ev = p->connection->write;
395 }
396
397 } else {
398
399 if (ev->timedout) {
400 rc = NGX_HTTP_GATEWAY_TIME_OUT;
401
402 } else if (ev->write) {
403
404 rc = p->state_write_upstream_handler(p);
405
406 } else { /* ev->read */
407
408 rc = p->state_read_upstream_handler(p);
409 }
410 }
411
412 if (rc == NGX_DONE || rc == NGX_AGAIN) {
413 return rc;
414 }
415
416 if (rc == NGX_ERROR) {
417 return ngx_http_proxy_finalize_request(p,
418 NGX_HTTP_INTERNAL_SERVER_ERROR);
419 return NGX_DONE;
420 }
421
Igor Sysoevfb970512003-04-23 14:34:42 +0000422 if (p->tries > 1
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000423 && (rc == NGX_HTTP_BAD_GATEWAY
424 || rc == NGX_HTTP_GATEWAY_TIME_OUT
425 || (rc == NGX_OK
426 && p->status == NGX_HTTP_INTERNAL_SERVER_ERROR
427 && p->lcf->retry_500_error)))
Igor Sysoeve2a31542003-04-08 15:40:10 +0000428 {
429 if (ev) {
430 ngx_event_close_connection(ev);
431 ev = NULL;
432 }
433
434 if (!p->cached_connection) {
435 if (p->upstreams->number > 1) {
436 now = ngx_time();
437 u = &p->upstreams->u[p->cur_upstream];
438
439 /* Here is the race condition when the upstreams are shared
440 between threads or processes but it's not serious */
441
442 u->fails++;
443 u->accessed = now;
444
445 /* */
446 }
447
448 p->tries--;
449 p->last_error = rc;
450 }
451
452 if (p->tries == 0) {
453 ngx_http_proxy_finalize_request(p, p->last_error);
454 return NGX_ERROR;
455 }
Igor Sysoev183f9a62003-04-09 15:42:08 +0000456
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000457 /* reinitialize the proxy context for the next upstream */
458
Igor Sysoev183f9a62003-04-09 15:42:08 +0000459 p->headers_in.server->key.len = 0;
460 p->headers_in.connection->key.len = 0;
461 p->headers_in.content_type->key.len = 0;
462 p->headers_in.content_length->key.len = 0;
463 p->headers_in.last_modified->key.len = 0;
464
465 p->headers_in.headers->nelts = 0;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000466 }
467
468 if (rc == NGX_OK) {
469 ngx_http_proxy_finalize_request(p, p->status);
470 return NGX_DONE;
471 }
472
473 if (rc > NGX_OK) {
474 ngx_http_proxy_finalize_request(p, rc);
475 }
476
477 return NGX_DONE;
478 }
479}
480
481
482static int ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p)
483{
Igor Sysoev6b863e32003-05-12 15:52:24 +0000484 int rc, event, instance;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000485 struct sockaddr_in *addr;
486 ngx_err_t err;
487 ngx_socket_t s;
488 ngx_event_t *rev, *wev;
489 ngx_connection_t *c;
490 ngx_http_proxy_log_ctx_t *lcx;
491 ngx_http_proxy_upstream_t *u;
492
493 if (p->upstreams->number > 1) {
494 if (p->tries == p->upstreams->number) {
495
496 /* Here is the race condition
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000497 when the upstreams are shared between
498 the threads or the processes but it should not be serious */
Igor Sysoeve2a31542003-04-08 15:40:10 +0000499
500 p->cur_upstream = p->upstreams->current++;
501
502 if (p->upstreams->current >= p->upstreams->number) {
503 p->upstreams->current = 0;
504 }
505
506 /* */
507
508#if (NGX_MULTITHREADED || NGX_MULTIPROCESSED)
509 /* eliminate the sequences of the race condition */
510 if (p->cur_upstream >= p->upstreams->number) {
511 p->cur_upstream = 0;
512 }
513#endif
514 }
515
516 if (p->upstreams->max_fails > 0) {
517
518 for ( ;; ) {
519 u = &p->upstreams->u[p->cur_upstream];
520
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000521
Igor Sysoeve2a31542003-04-08 15:40:10 +0000522 /* Here is the race condition
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000523 when the upstreams are shared between
524 the threads or the processes but it should not be serious */
Igor Sysoeve2a31542003-04-08 15:40:10 +0000525
526 if (u->fails > p->upstreams->max_fails
527 || u->accessed < p->upstreams->fail_timeout)
528 {
529 break;
530 }
531
532 /* */
533
534 p->cur_upstream++;
535
536 if (p->cur_upstream >= p->upstreams->number) {
537 p->cur_upstream = 0;
538 }
539
540 p->tries--;
541
542 if (p->tries == 0) {
543 return p->last_error;
544 }
545 }
546 }
547 }
548
549 lcx = p->log->data;
550 lcx->action = "connecting to an upstream";
551 lcx->upstream = p->upstreams->u[p->cur_upstream].addr_port_name.data;
552 p->log->handler = ngx_http_proxy_log_error;
553
554 s = ngx_socket(AF_INET, SOCK_STREAM, IPPROTO_IP, 0);
555
556 if (s == -1) {
557 ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
558 ngx_socket_n " failed");
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000559 return NGX_HTTP_INTERNAL_SERVER_ERROR;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000560 }
561
562 if (p->lcf->rcvbuf) {
563 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
564 (const void *) &p->lcf->rcvbuf, sizeof(int)) == -1) {
565 ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
566 "setsockopt(SO_RCVBUF) failed");
567
568 if (ngx_close_socket(s) == -1) {
569 ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
570 ngx_close_socket_n " failed");
571 }
572
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000573 return NGX_HTTP_INTERNAL_SERVER_ERROR;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000574 }
575 }
576
577 if (ngx_nonblocking(s) == -1) {
578 ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
579 ngx_nonblocking_n " failed");
580
581 if (ngx_close_socket(s) == -1) {
582 ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
583 ngx_close_socket_n " failed");
584 }
585
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000586 return NGX_HTTP_INTERNAL_SERVER_ERROR;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000587 }
588
589 c = &ngx_connections[s];
590 rev = &ngx_read_events[s];
591 wev = &ngx_write_events[s];
592
Igor Sysoev6b863e32003-05-12 15:52:24 +0000593 instance = rev->instance;
594
Igor Sysoeve2a31542003-04-08 15:40:10 +0000595 ngx_memzero(c, sizeof(ngx_connection_t));
596 ngx_memzero(rev, sizeof(ngx_event_t));
597 ngx_memzero(wev, sizeof(ngx_event_t));
598
599 rev->index = wev->index = NGX_INVALID_INDEX;
600 rev->data = wev->data = c;
601 c->read = rev;
602 c->write = wev;
Igor Sysoev6b863e32003-05-12 15:52:24 +0000603
604 rev->instance = wev->instance = !instance;
605
Igor Sysoeve2a31542003-04-08 15:40:10 +0000606 rev->log = wev->log = c->log = p->log;
607 c->fd = s;
608 wev->close_handler = rev->close_handler = ngx_event_close_connection;
609
Igor Sysoeve2a31542003-04-08 15:40:10 +0000610 if (ngx_event_flags & NGX_HAVE_EDGE_EVENT) {
611 if (ngx_edge_add_event(wev) != NGX_OK) {
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000612 return NGX_HTTP_INTERNAL_SERVER_ERROR;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000613 }
614 }
615
Igor Sysoeve2a31542003-04-08 15:40:10 +0000616 ngx_test_null(c->pool, ngx_create_pool(p->lcf->conn_pool_size, p->log),
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000617 NGX_HTTP_INTERNAL_SERVER_ERROR);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000618
619 ngx_test_null(p->sockaddr,
620 ngx_pcalloc(c->pool, sizeof(struct sockaddr_in)),
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000621 NGX_HTTP_INTERNAL_SERVER_ERROR);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000622
623 addr = (struct sockaddr_in *) p->sockaddr;
624
625 addr->sin_family = AF_INET;
626 addr->sin_addr.s_addr = p->upstreams->u[p->cur_upstream].addr;
627 addr->sin_port = htons(p->upstreams->u[p->cur_upstream].port);
628
629 rc = connect(s, p->sockaddr, sizeof(struct sockaddr_in));
630
631 if (rc == -1) {
632 err = ngx_socket_errno;
633 if (err != NGX_EINPROGRESS) {
634 ngx_log_error(NGX_LOG_CRIT, p->log, err, "connect() failed");
635
636 if (ngx_close_socket(s) == -1) {
637 ngx_log_error(NGX_LOG_ALERT, p->log, ngx_socket_errno,
638 ngx_close_socket_n " failed");
639 }
640
Igor Sysoeve2a31542003-04-08 15:40:10 +0000641 return NGX_HTTP_BAD_GATEWAY;
642 }
643 }
644
645 c->data = p->request;
646 p->connection = c;
647
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000648 if ((ngx_event_flags & NGX_HAVE_EDGE_EVENT) == 0) { /* not epoll */
Igor Sysoeve2a31542003-04-08 15:40:10 +0000649
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000650 if (ngx_event_flags & NGX_HAVE_CLEAR_EVENT) { /* kqueue */
Igor Sysoeve2a31542003-04-08 15:40:10 +0000651 event = NGX_CLEAR_EVENT;
652
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000653 } else { /* select, poll, /dev/poll */
Igor Sysoeve2a31542003-04-08 15:40:10 +0000654 event = NGX_LEVEL_EVENT;
655 }
656
657 /* TODO: aio, iocp */
658
659 if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000660 return NGX_HTTP_INTERNAL_SERVER_ERROR;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000661 }
662 }
663
Igor Sysoev0e18ebd2003-04-24 14:46:08 +0000664 rev->event_handler = ngx_http_proxy_process_upstream_event;
665 p->request->connection->write->event_handler = NULL;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000666
667 p->state_write_upstream_handler = ngx_http_proxy_send_request;
668 p->state_read_upstream_handler = ngx_http_proxy_init_upstream;
669
670 /* The connection has been established */
671 if (rc == 0) {
672 wev->write = 1;
673 wev->ready = 1;
674
675 return ngx_http_proxy_send_request(p);
676 }
677
678 /* The connection is in a progress */
679
680 wev->timer_set = 1;
681 ngx_add_timer(wev, p->lcf->connect_timeout);
682
Igor Sysoeve2a31542003-04-08 15:40:10 +0000683 /* TODO: aio, iocp */
684
685 if (ngx_event_flags & NGX_HAVE_EDGE_EVENT) {
686 return NGX_DONE;
687 }
688
689 if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {
Igor Sysoev8dcd23e2003-04-22 15:02:58 +0000690 return NGX_HTTP_INTERNAL_SERVER_ERROR;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000691 }
692
Igor Sysoeve2a31542003-04-08 15:40:10 +0000693 return NGX_DONE;
694}
695
696
697static int ngx_http_proxy_process_upstream_event(ngx_event_t *ev)
698{
699 ngx_connection_t *c;
700 ngx_http_request_t *r;
701 ngx_http_proxy_ctx_t *p;
702
703 c = (ngx_connection_t *) ev->data;
704 r = (ngx_http_request_t *) c->data;
705 p = (ngx_http_proxy_ctx_t *)
706 ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
707
708 return ngx_http_proxy_process_upstream(p, ev);
709}
710
711
712static int ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p)
713{
714 ngx_chain_t *chain;
715 ngx_event_t *wev;
716
Igor Sysoevfd675862003-04-11 16:01:14 +0000717 chain = ngx_write_chain(p->connection, p->request_hunks, 0);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000718 if (chain == (ngx_chain_t *) -1) {
719 return NGX_ERROR;
720 }
721
Igor Sysoevfd675862003-04-11 16:01:14 +0000722 p->request_hunks = chain;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000723
724 wev = p->connection->write;
725
726 ngx_del_timer(wev);
727
728 if (chain) {
729 ngx_add_timer(wev, p->lcf->send_timeout);
730 wev->timer_set = 1;
731
732 } else {
733 wev->timer_set = 0;
734 }
735
736 return NGX_DONE;
737}
738
739
740static int ngx_http_proxy_init_upstream(ngx_http_proxy_ctx_t *p)
741{
742 int n;
743 ngx_hunk_t **ph;
744 ngx_http_request_t *r;
745
746 r = p->request;
747
748 ngx_test_null(p->header_in,
Igor Sysoev6a7fd112003-04-17 17:59:35 +0000749 ngx_create_temp_hunk(r->pool, p->lcf->header_size, 0, 0),
750 NGX_ERROR);
751
752#if 0
753 ngx_test_null(p->header_in,
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000754 ngx_create_temp_hunk(r->pool,
755 p->lcf->header_size
756 - sizeof(ngx_cache_header_t),
757 sizeof(ngx_cache_header_t),
758 0),
Igor Sysoeve2a31542003-04-08 15:40:10 +0000759 NGX_ERROR);
760
761 p->header_in->type = NGX_HUNK_MEMORY|NGX_HUNK_IN_MEMORY;
Igor Sysoev6a7fd112003-04-17 17:59:35 +0000762#endif
Igor Sysoeve2a31542003-04-08 15:40:10 +0000763
Igor Sysoev183f9a62003-04-09 15:42:08 +0000764#if 0
Igor Sysoeve2a31542003-04-08 15:40:10 +0000765 ngx_test_null(p->headers_in,
766 ngx_palloc(r->pool, sizeof(ngx_http_proxy_headers_in_t)),
767 NGX_ERROR);
Igor Sysoev183f9a62003-04-09 15:42:08 +0000768#endif
Igor Sysoeve2a31542003-04-08 15:40:10 +0000769
Igor Sysoev183f9a62003-04-09 15:42:08 +0000770 p->nhunks = p->lcf->max_block_size / p->lcf->block_size;
771 if (p->nhunks * p->lcf->block_size < p->lcf->max_block_size) {
772 p->nhunks++;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000773 }
774
Igor Sysoev183f9a62003-04-09 15:42:08 +0000775 ngx_init_array(p->hunks, r->pool, p->nhunks, sizeof(ngx_hunk_t *),
Igor Sysoeve2a31542003-04-08 15:40:10 +0000776 NGX_ERROR);
777
778 ngx_test_null(ph, ngx_push_array(&p->hunks), NGX_ERROR);
779 *ph = p->header_in;
780
781 p->state_handler = ngx_http_proxy_process_upstream_status_line;
782
783 return ngx_http_proxy_read_upstream_header(p);
784}
785
786
787static int ngx_http_proxy_read_upstream_header(ngx_http_proxy_ctx_t *p)
788{
Igor Sysoev183f9a62003-04-09 15:42:08 +0000789 int i, n, rc;
790 ngx_event_t *rev;
Igor Sysoev6a7fd112003-04-17 17:59:35 +0000791 ngx_chain_t *temp;
Igor Sysoev183f9a62003-04-09 15:42:08 +0000792 ngx_table_elt_t *ch, *ph;
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000793 ngx_event_proxy_t *ep;
Igor Sysoev183f9a62003-04-09 15:42:08 +0000794 ngx_http_request_t *r;
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000795 ngx_http_proxy_log_ctx_t *lcx;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000796
797 rev = p->connection->read;
798
799 do {
800 n = ngx_event_recv(p->connection, p->header_in->last,
801 p->header_in->end - p->header_in->last);
802
803 if (n == NGX_AGAIN) {
804 if (rev->timer_set) {
805 ngx_del_timer(rev);
806 } else {
807 rev->timer_set = 1;
808 }
809
810 ngx_add_timer(rev, p->lcf->read_timeout);
811 return NGX_AGAIN;
812 }
813
814 if (n == NGX_ERROR) {
815 return NGX_HTTP_BAD_GATEWAY;
816 }
817
818 ngx_log_debug(p->log, "http proxy read %d" _ n);
819
820 if (n == 0) {
821 ngx_log_error(NGX_LOG_INFO, p->log, 0,
822 "upstream closed prematurely connection");
823 return NGX_HTTP_BAD_GATEWAY;
824 }
825
826 p->header_in->last += n;
827
828 /* the state handlers are called in the following order:
Igor Sysoev183f9a62003-04-09 15:42:08 +0000829 ngx_http_proxy_process_upstream_status_line(p)
830 ngx_http_proxy_process_upstream_headers(p) */
Igor Sysoeve2a31542003-04-08 15:40:10 +0000831
832 do {
833 rc = p->state_handler(p);
Igor Sysoev183f9a62003-04-09 15:42:08 +0000834 } while (rc == NGX_AGAIN && p->header_in->pos < p->header_in->last);
Igor Sysoeve2a31542003-04-08 15:40:10 +0000835
836 } while (rc == NGX_AGAIN
837 && (rev->ready || ngx_event_flags & NGX_HAVE_AIO_EVENT));
838
Igor Sysoev183f9a62003-04-09 15:42:08 +0000839 if (rc == NGX_OK) {
840
841 r = p->request;
842
843 /* copy an upstream header to r->headers_out */
844
845 ph = (ngx_table_elt_t *) p->headers_in.headers->elts;
846 for (i = 0; i < p->headers_in.headers->nelts; i++) {
847
848 if (&ph[i] == p->headers_in.connection) {
849 continue;
850 }
851
852 if (p->accel && &ph[i] == p->headers_in.date) {
853 continue;
854 }
855
856 ngx_test_null(ch, ngx_push_table(r->headers_out.headers),
857 NGX_HTTP_INTERNAL_SERVER_ERROR);
858
859 ch->key.len = ph[i].key.len;
860 ch->key.data = ph[i].key.data;
861 ch->value.len = ph[i].value.len;
862 ch->value.data = ph[i].value.data;
863 }
864
865 if (p->headers_in.server) {
866 r->headers_out.server = p->headers_in.server;
867 }
868
869 if (!p->accel && p->headers_in.date) {
870 r->headers_out.date = p->headers_in.date;
871 }
872
Igor Sysoevcde24782003-04-10 15:08:54 +0000873 /* TODO: look "Content-Length" */
874 p->block_size = p->lcf->block_size;
875
Igor Sysoev183f9a62003-04-09 15:42:08 +0000876 r->headers_out.status = p->status;
877
Igor Sysoev183f9a62003-04-09 15:42:08 +0000878 rc = ngx_http_send_header(r);
879
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000880#if 1
Igor Sysoev6a7fd112003-04-17 17:59:35 +0000881#if 0
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000882 rc = ngx_http_output_filter(r, p->header_in);
Igor Sysoev6a7fd112003-04-17 17:59:35 +0000883#endif
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000884
885 ngx_test_null(ep, ngx_pcalloc(r->pool, sizeof(ngx_event_proxy_t)),
886 NGX_ERROR);
887
888 ep->output_filter = (ngx_event_proxy_output_filter_pt)
889 ngx_http_output_filter;
890 ep->output_data = r;
891 ep->block_size = p->lcf->block_size;
892 ep->max_block_size = p->lcf->max_block_size;
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000893 ep->upstream = p->connection;
Igor Sysoev6a7fd112003-04-17 17:59:35 +0000894 ep->downstream = r->connection;
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000895 ep->pool = r->pool;
896 ep->log = p->log;
897 ep->temp_path = p->lcf->temp_path;
898
899 ngx_test_null(ep->temp_file, ngx_palloc(r->pool, sizeof(ngx_file_t)),
900 NGX_ERROR);
901 ep->temp_file->fd = NGX_INVALID_FILE;
902 ep->temp_file->log = p->log;
903
Igor Sysoev6a7fd112003-04-17 17:59:35 +0000904 ep->max_temp_file_size = p->lcf->max_temp_file_size;
905 ep->temp_file_write_size = p->lcf->temp_file_write_size;
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000906 ep->temp_file_warn = "an upstream response is buffered "
907 "to a temporary file";
908
Igor Sysoev6a7fd112003-04-17 17:59:35 +0000909 ngx_test_null(ep->preread_hunks, ngx_alloc_chain_entry(r->pool),
910 NGX_ERROR);
911 ep->preread_hunks->hunk = p->header_in;
912 ep->preread_hunks->next = NULL;
913#if 0
914 ep->last_preread_hunk = ep->preread_hunks;
915#endif
916
917 ep->preread_size = p->header_in->last - p->header_in->pos;
918
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000919 p->event_proxy = ep;
920
921 lcx = p->log->data;
922 lcx->action = "reading an upstream";
923
924 p->state_read_upstream_handler = ngx_http_proxy_read_upstream_body;
925 p->state_write_upstream_handler = ngx_http_proxy_write_upstream_body;
926
927 ngx_http_proxy_read_upstream_body(p);
928#endif
929
930#if 0
Igor Sysoevcde24782003-04-10 15:08:54 +0000931 /* STUB */
932 p->header_in->type |= NGX_HUNK_LAST;
933 rc = ngx_http_output_filter(r, p->header_in);
934 ngx_http_proxy_finalize_request(p, NGX_OK);
Igor Sysoev9e4920b2003-04-14 17:04:58 +0000935#endif
Igor Sysoevcde24782003-04-10 15:08:54 +0000936
Igor Sysoev183f9a62003-04-09 15:42:08 +0000937 /* STUB */ return NGX_DONE;
938 }
939
Igor Sysoeve2a31542003-04-08 15:40:10 +0000940 if (rc > NGX_OK) {
941 return rc;
942 }
943
Igor Sysoev183f9a62003-04-09 15:42:08 +0000944 /* STUB */ return NGX_DONE;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000945}
946
947
948static int ngx_http_proxy_process_upstream_status_line(ngx_http_proxy_ctx_t *p)
949{
950 int rc;
951
Igor Sysoeve2a31542003-04-08 15:40:10 +0000952 rc = ngx_read_http_proxy_status_line(p);
953
954 if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) {
955 p->status = 200;
956 p->status_line.len = 0;
957 p->full_status_line.len = 0;
Igor Sysoev183f9a62003-04-09 15:42:08 +0000958 p->state_handler = ngx_http_proxy_process_upstream_headers;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000959
Igor Sysoev183f9a62003-04-09 15:42:08 +0000960 } else if (rc == NGX_OK) {
Igor Sysoeve2a31542003-04-08 15:40:10 +0000961 p->status_line.len = p->status_end - p->status_start;
962 p->full_status_line.len = p->status_end - p->header_in->start;
963
964 if (p->lcf->large_header) {
965 ngx_test_null(p->full_status_line.data,
966 ngx_palloc(p->request->pool,
967 p->full_status_line.len + 1),
968 NGX_HTTP_INTERNAL_SERVER_ERROR);
969
970 ngx_cpystrn(p->full_status_line.data, p->header_in->start,
971 p->full_status_line.len + 1);
972
973 if (p->header_in->pos == p->header_in->end) {
974 p->header_in->pos = p->header_in->last = p->header_in->start;
975 }
976
977 } else {
978 p->status_line.data = p->status_start;
979 p->full_status_line.data = p->header_in->start;
980 *p->status_end = '\0';
Igor Sysoev183f9a62003-04-09 15:42:08 +0000981
982 if (p->header_in->pos == p->header_in->end) {
983 ngx_log_error(NGX_LOG_ERR, p->log, 0,
984 "upstream sent too long status line");
985 return NGX_HTTP_BAD_GATEWAY;
986 }
Igor Sysoeve2a31542003-04-08 15:40:10 +0000987 }
988
989 ngx_log_debug(p->log, "upstream status: %d, '%s'" _
990 p->status _ p->full_status_line.data);
991
Igor Sysoev183f9a62003-04-09 15:42:08 +0000992 p->state_handler = ngx_http_proxy_process_upstream_headers;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000993 }
994
Igor Sysoev183f9a62003-04-09 15:42:08 +0000995 /* rc == NGX_AGAIN */
Igor Sysoeve2a31542003-04-08 15:40:10 +0000996
Igor Sysoev183f9a62003-04-09 15:42:08 +0000997 return NGX_AGAIN;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000998}
999
1000
Igor Sysoev183f9a62003-04-09 15:42:08 +00001001static int ngx_http_proxy_process_upstream_headers(ngx_http_proxy_ctx_t *p)
1002{
1003 int rc, offset;
1004 ngx_http_request_t *r;
1005
1006 r = p->request;
1007
1008 for ( ;; ) {
Igor Sysoev6b863e32003-05-12 15:52:24 +00001009 rc = ngx_parse_http_header_line(r, p->header_in);
Igor Sysoev183f9a62003-04-09 15:42:08 +00001010
1011 /* a header line has been parsed successfully */
1012
1013 if (rc == NGX_OK) {
1014 if (ngx_http_proxy_process_upstream_header_line(p) == NGX_ERROR) {
1015 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1016 }
1017
1018 if (p->lcf->large_header
1019 && p->header_in->pos == p->header_in->last)
1020 {
1021 p->header_in->pos = p->header_in->last = p->header_in->start;
1022 }
1023
1024 return NGX_AGAIN;
1025
1026 /* a whole header has been parsed successfully */
1027
1028 } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
1029 ngx_log_debug(p->log, "proxy HTTP header done");
1030
1031 p->state_handler = NULL;
1032 return NGX_OK;
1033
1034 /* there was error while a header line parsing */
1035
1036 } else if (rc != NGX_AGAIN) {
1037 ngx_log_error(NGX_LOG_ERR, p->log, 0,
1038 "upstream sent ERROR %d", rc);
1039 return NGX_HTTP_BAD_GATEWAY;
1040 }
1041
1042 /* NGX_AGAIN: a header line parsing is still not complete */
1043
1044 if (p->header_in->pos == p->header_in->last) {
1045 /* if the large upstream headers are enabled then
1046 we need to compact p->header_in hunk */
1047
1048 if (p->lcf->large_header) {
1049 offset = r->header_name_start - p->header_in->start;
1050
1051 if (offset == 0) {
1052 ngx_log_error(NGX_LOG_ERR, p->log, 0,
1053 "upstream sent too long header line");
1054 return NGX_HTTP_BAD_GATEWAY;
1055 }
1056
1057 ngx_memcpy(p->header_in->start, r->header_name_start,
1058 p->header_in->last - r->header_name_start);
1059
1060 p->header_in->last -= offset;
1061 p->header_in->pos -= offset;
1062 r->header_name_start = p->header_in->start;
1063 r->header_name_end -= offset;
1064 r->header_start -= offset;
1065 r->header_end -= offset;
1066
1067 } else {
1068 ngx_log_error(NGX_LOG_ERR, p->log, 0,
1069 "upstream sent too long header line");
1070 /* NGX_HTTP_PARSE_TOO_LONG_HEADER */
1071 return NGX_HTTP_BAD_GATEWAY;
1072 }
1073 }
1074
1075 return NGX_AGAIN;
1076 }
1077}
Igor Sysoeve2a31542003-04-08 15:40:10 +00001078
1079
Igor Sysoev183f9a62003-04-09 15:42:08 +00001080static int ngx_http_proxy_process_upstream_header_line(ngx_http_proxy_ctx_t *p)
1081{
1082 int i;
1083 ngx_table_elt_t *h;
1084 ngx_http_request_t *r;
Igor Sysoeve2a31542003-04-08 15:40:10 +00001085
Igor Sysoev183f9a62003-04-09 15:42:08 +00001086 r = p->request;
Igor Sysoeve2a31542003-04-08 15:40:10 +00001087
Igor Sysoev183f9a62003-04-09 15:42:08 +00001088 ngx_test_null(h, ngx_push_table(p->headers_in.headers), NGX_ERROR);
Igor Sysoeve2a31542003-04-08 15:40:10 +00001089
Igor Sysoev183f9a62003-04-09 15:42:08 +00001090 h->key.len = r->header_name_end - r->header_name_start;
1091 h->value.len = r->header_end - r->header_start;
Igor Sysoeve2a31542003-04-08 15:40:10 +00001092
Igor Sysoev183f9a62003-04-09 15:42:08 +00001093 /* if the large upstream headers are enabled then
1094 we need to copy the header name and value */
1095
1096 if (p->lcf->large_header) {
1097 ngx_test_null(h->key.data, ngx_palloc(r->pool, h->key.len + 1),
1098 NGX_ERROR);
1099 ngx_test_null(h->value.data, ngx_palloc(r->pool, h->value.len + 1),
1100 NGX_ERROR);
1101 ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
1102 ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);
1103
1104 } else {
1105 h->key.data = r->header_name_start;
1106 h->key.data[h->key.len] = '\0';
1107 h->value.data = r->header_start;
1108 h->value.data[h->value.len] = '\0';
1109 }
1110
1111 for (i = 0; headers_in[i].name.len != 0; i++) {
1112 if (headers_in[i].name.len != h->key.len) {
1113 continue;
1114 }
1115
1116 if (ngx_strcasecmp(headers_in[i].name.data, h->key.data) == 0) {
1117 *((ngx_table_elt_t **)
1118 ((char *) &p->headers_in + headers_in[i].offset)) = h;
1119 }
1120 }
1121
1122 ngx_log_debug(p->log, "proxy HTTP header: '%s: %s'" _
1123 h->key.data _ h->value.data);
1124
1125 return NGX_OK;
1126}
Igor Sysoeve2a31542003-04-08 15:40:10 +00001127
1128
Igor Sysoev9e4920b2003-04-14 17:04:58 +00001129static int ngx_http_proxy_read_upstream_body(ngx_http_proxy_ctx_t *p)
1130{
1131 int rc;
1132
1133 rc = ngx_event_proxy_read_upstream(p->event_proxy);
Igor Sysoevfb970512003-04-23 14:34:42 +00001134
1135 if (p->event_proxy->fatal_error) {
1136 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1137 }
1138
Igor Sysoev0e18ebd2003-04-24 14:46:08 +00001139 if (p->event_proxy->upstream_eof || p->event_proxy->upstream_error) {
Igor Sysoev9e4920b2003-04-14 17:04:58 +00001140 rc = ngx_event_close_connection(p->connection->read);
1141 }
1142
1143 return rc;
1144}
1145
1146
Igor Sysoev0e18ebd2003-04-24 14:46:08 +00001147static int ngx_http_proxy_process_client_event(ngx_event_t *ev)
1148{
1149 ngx_connection_t *c;
1150 ngx_http_request_t *r;
1151 ngx_http_proxy_ctx_t *p;
1152
1153 c = (ngx_connection_t *) ev->data;
1154 r = (ngx_http_request_t *) c->data;
1155 p = (ngx_http_proxy_ctx_t *)
1156 ngx_http_get_module_ctx(r, ngx_http_proxy_module_ctx);
1157
1158 return ngx_http_proxy_process_upstream(p, ev);
1159}
1160
1161
Igor Sysoev9e4920b2003-04-14 17:04:58 +00001162static int ngx_http_proxy_write_upstream_body(ngx_http_proxy_ctx_t *p)
1163{
Igor Sysoev6a7fd112003-04-17 17:59:35 +00001164 return ngx_event_proxy_write_to_downstream(p->event_proxy);
Igor Sysoev9e4920b2003-04-14 17:04:58 +00001165}
1166
1167
Igor Sysoeve2a31542003-04-08 15:40:10 +00001168
1169
1170static int ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int error)
1171{
Igor Sysoev0e18ebd2003-04-24 14:46:08 +00001172#if 0
1173
1174 if (p->event_proxy->upstream_eof) {
1175 rc = ngx_event_close_connection(p->connection->read);
1176 link cache;
1177 }
1178
1179 if (p->event_proxy->upstream_error) {
1180 rc = ngx_event_close_connection(p->connection->read);
1181 }
1182
1183 if (p->event_proxy->downstream_error) {
1184 rc = ngx_event_close_connection(p->request->connection->write);
1185 }
1186
1187#endif
1188
Igor Sysoeve2a31542003-04-08 15:40:10 +00001189 return ngx_http_finalize_request(p->request, error);
1190}
1191
1192
1193static int ngx_http_proxy_error(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p,
1194 int error)
1195{
1196 ngx_event_close_connection(p->connection->read);
1197
1198 return ngx_http_error(r, error);
1199}
1200
1201
1202static size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len)
1203{
1204 ngx_http_proxy_log_ctx_t *lcx = (ngx_http_proxy_log_ctx_t *) data;
1205
1206 return ngx_snprintf(buf, len,
1207 " while %s, upstream: %s, client: %s, URL: %s",
1208 lcx->action, lcx->upstream, lcx->client, lcx->url);
1209}
1210
1211
1212
1213static int ngx_read_http_proxy_status_line(ngx_http_proxy_ctx_t *ctx)
1214{
1215 char ch;
1216 char *p;
1217 enum {
1218 sw_start = 0,
1219 sw_first_major_digit,
1220 sw_major_digit,
1221 sw_first_minor_digit,
1222 sw_minor_digit,
1223 sw_status,
1224 sw_space_after_status,
1225 sw_status_text,
1226 sw_almost_done,
1227 sw_done
1228 } state;
1229
1230 state = ctx->state;
1231 p = ctx->header_in->pos;
1232
1233 while (p < ctx->header_in->last && state < sw_done) {
1234 ch = *p++;
1235
1236 switch (state) {
1237
1238 /* "HTTP/" */
1239 case sw_start:
1240 if (p + 3 >= ctx->header_in->last) {
1241 return NGX_AGAIN;
1242 }
1243
1244 if (ch != 'H' || *p != 'T' || *(p + 1) != 'T' || *(p + 2) != 'P'
1245 || *(p + 3) != '/')
1246 {
1247 return NGX_HTTP_PROXY_PARSE_NO_HEADER;
1248 }
1249
1250 p += 4;
1251 state = sw_first_major_digit;
1252 break;
1253
1254 /* first digit of major HTTP version */
1255 case sw_first_major_digit:
1256 if (ch < '1' || ch > '9') {
1257 return NGX_HTTP_PROXY_PARSE_NO_HEADER;
1258 }
1259
1260 state = sw_major_digit;
1261 break;
1262
1263 /* major HTTP version or dot */
1264 case sw_major_digit:
1265 if (ch == '.') {
1266 state = sw_first_minor_digit;
1267 break;
1268 }
1269
1270 if (ch < '0' || ch > '9') {
1271 return NGX_HTTP_PROXY_PARSE_NO_HEADER;
1272 }
1273
1274 break;
1275
1276 /* first digit of minor HTTP version */
1277 case sw_first_minor_digit:
1278 if (ch < '0' || ch > '9') {
1279 return NGX_HTTP_PROXY_PARSE_NO_HEADER;
1280 }
1281
1282 state = sw_minor_digit;
1283 break;
1284
1285 /* minor HTTP version or end of request line */
1286 case sw_minor_digit:
1287 if (ch == ' ') {
1288 state = sw_status;
1289 break;
1290 }
1291
1292 if (ch < '0' || ch > '9') {
1293 return NGX_HTTP_PROXY_PARSE_NO_HEADER;
1294 }
1295
1296 break;
1297
1298 /* HTTP status code */
1299 case sw_status:
1300 if (ch < '0' || ch > '9') {
1301 return NGX_HTTP_PROXY_PARSE_NO_HEADER;
1302 }
1303
1304 ctx->status = ctx->status * 10 + ch - '0';
1305
1306 if (++ctx->status_count == 3) {
1307 state = sw_space_after_status;
1308 ctx->status_start = p - 3;
1309 }
1310
1311 break;
1312
1313 /* space or end of line */
1314 case sw_space_after_status:
1315 switch (ch) {
1316 case ' ':
1317 state = sw_status_text;
1318 break;
1319 case CR:
1320 state = sw_almost_done;
1321 break;
1322 case LF:
1323 state = sw_done;
1324 break;
1325 default:
1326 return NGX_HTTP_PROXY_PARSE_NO_HEADER;
1327 }
1328 break;
1329
1330 /* any text until end of line */
1331 case sw_status_text:
1332 switch (ch) {
1333 case CR:
1334 state = sw_almost_done;
1335 break;
1336 case LF:
1337 state = sw_done;
1338 break;
1339 }
1340 break;
1341
1342 /* end of request line */
1343 case sw_almost_done:
1344 ctx->status_end = p - 2;
1345 switch (ch) {
1346 case LF:
1347 state = sw_done;
1348 break;
1349 default:
1350 return NGX_HTTP_PROXY_PARSE_NO_HEADER;
1351 }
1352 break;
1353 }
1354 }
1355
1356 ctx->header_in->pos = p;
1357
1358 if (state == sw_done) {
1359 if (ctx->status_end == NULL) {
1360 ctx->status_end = p - 1;
1361 }
1362
1363 ctx->state = sw_start;
1364 return NGX_OK;
1365
1366 } else {
1367 ctx->state = state;
1368 return NGX_AGAIN;
1369 }
1370}
1371
1372
Igor Sysoevfd675862003-04-11 16:01:14 +00001373static int ngx_http_proxy_init(ngx_pool_t *pool)
1374{
1375 int i;
1376 ngx_file_t file;
1377 ngx_path_t path;
1378
1379 file.log = pool->log;
1380
1381 path.name.data = "temp";
1382 path.name.len = 4;
1383 path.level[0] = 1;
1384 path.level[1] = 2;
1385 path.level[2] = 3;
1386 path.len = 0;
1387
1388 for (i = 0; i < 3; i++) {
1389 if (path.level[i] == 0) {
1390 break;
1391 }
1392 path.len += path.level[i] + 1;
1393 }
1394
Igor Sysoev1d8d9ee2003-04-28 15:06:39 +00001395 return ngx_create_temp_file(&file, &path, pool, 0);
Igor Sysoevfd675862003-04-11 16:01:14 +00001396}
1397
1398
Igor Sysoeve2a31542003-04-08 15:40:10 +00001399static void *ngx_http_proxy_create_loc_conf(ngx_pool_t *pool)
1400{
Igor Sysoevfd675862003-04-11 16:01:14 +00001401 int i;
1402 ngx_http_proxy_loc_conf_t *conf;
Igor Sysoeve2a31542003-04-08 15:40:10 +00001403
1404 ngx_test_null(conf,
1405 ngx_pcalloc(pool, sizeof(ngx_http_proxy_loc_conf_t)),
1406 NULL);
1407
1408 /* STUB */
1409 conf->conn_pool_size = 16384;
1410 conf->connect_timeout = 10000;
1411 conf->send_timeout = 10000;
1412 conf->read_timeout = 10000;
Igor Sysoev6a7fd112003-04-17 17:59:35 +00001413 conf->header_size = 2048;
Igor Sysoev153d7432003-04-15 15:06:52 +00001414
Igor Sysoev6a7fd112003-04-17 17:59:35 +00001415#if 1
Igor Sysoeve2a31542003-04-08 15:40:10 +00001416 conf->block_size = 4096;
Igor Sysoev9e4920b2003-04-14 17:04:58 +00001417 conf->max_block_size = 4096 * 3;
Igor Sysoev153d7432003-04-15 15:06:52 +00001418 conf->max_temp_file_size = 4096 * 5;
Igor Sysoev6a7fd112003-04-17 17:59:35 +00001419 conf->temp_file_write_size = 4096 * 2;
Igor Sysoev153d7432003-04-15 15:06:52 +00001420#else
1421 conf->block_size = 2048;
1422 conf->max_block_size = 4096 * 6;
1423 conf->max_temp_file_size = 4096 * 5;
Igor Sysoev6a7fd112003-04-17 17:59:35 +00001424 conf->temp_file_write_size = 4096 * 5;
Igor Sysoev153d7432003-04-15 15:06:52 +00001425#endif
Igor Sysoevfd675862003-04-11 16:01:14 +00001426
1427 ngx_test_null(conf->temp_path, ngx_pcalloc(pool, sizeof(ngx_path_t)), NULL);
1428
1429 conf->temp_path->name.data = "temp";
1430 conf->temp_path->name.len = 4;
1431 conf->temp_path->level[0] = 1;
1432 conf->temp_path->level[1] = 2;
1433 conf->temp_path->level[2] = 3;
1434 conf->temp_path->len = 0;
1435
1436 for (i = 0; i < 3; i++) {
1437 if (conf->temp_path->level[i] == 0) {
1438 break;
1439 }
1440 conf->temp_path->len += conf->temp_path->level[i] + 1;
1441 }
Igor Sysoeve2a31542003-04-08 15:40:10 +00001442 /**/
1443
1444 return conf;
1445}
1446
1447
1448static char *ngx_http_proxy_set_pass(ngx_conf_t *cf, ngx_command_t *cmd,
1449 char *conf)
1450{
1451 ngx_http_proxy_loc_conf_t *lcf = (ngx_http_proxy_loc_conf_t *) conf;
1452
1453 int i, s, len;
1454 char *err, *host;
1455 struct hostent *h;
1456 u_int32_t addr;
1457 ngx_str_t *value;
1458 ngx_http_conf_ctx_t *ctx;
1459 ngx_http_core_loc_conf_t *core_lcf;
1460
1461 value = (ngx_str_t *) cf->args->elts;
1462
1463 if (ngx_strncasecmp(value[1].data, "http://", 7) != 0) {
1464 return "invalid URL prefix";
1465 }
1466
1467 ngx_test_null(lcf->upstream_url,
1468 ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_upstream_url_t)),
1469 NGX_CONF_ERROR);
1470
1471 value[1].data += 7;
1472 value[1].len -= 7;
1473
1474 err = ngx_http_proxy_parse_upstream(&value[1], lcf->upstream_url);
1475
1476 if (err) {
1477 return err;
1478 }
1479
1480 if (lcf->upstream_url->port == 0) {
1481 lcf->upstream_url->port = 80;
1482 }
1483
1484 ngx_test_null(host, ngx_palloc(cf->pool, lcf->upstream_url->host.len + 1),
1485 NGX_CONF_ERROR);
1486 ngx_cpystrn(host, lcf->upstream_url->host.data,
1487 lcf->upstream_url->host.len + 1);
1488
1489 /* TODO: look up upstreams */
1490
1491 addr = inet_addr(host);
1492 if (addr == INADDR_NONE) {
1493 h = gethostbyname(host);
1494
1495 if (h == NULL || h->h_addr_list[0] == NULL) {
1496 /* STUB: "host %s not found", host */
1497 return "host not found";
1498 }
1499
1500 for (i = 0; h->h_addr_list[i] != NULL; i++) {
1501 /* void */
1502 }
1503
1504 /* MP: ngx_shared_palloc() */
1505
1506 ngx_test_null(lcf->upstreams,
1507 ngx_pcalloc(cf->pool,
1508 sizeof(ngx_http_proxy_upstreams_t)
1509 + sizeof(ngx_http_proxy_upstream_t) * (i - 1)),
1510 NGX_CONF_ERROR);
1511
1512 lcf->upstreams->number = i;
1513
1514 for (i = 0; h->h_addr_list[i] != NULL; i++) {
1515 lcf->upstreams->u[i].host.data = host;
1516 lcf->upstreams->u[i].host.len = lcf->upstream_url->host.len;
1517 lcf->upstreams->u[i].addr = *(u_int32_t *)(h->h_addr_list[i]);
1518 lcf->upstreams->u[i].port = lcf->upstream_url->port;
1519
1520 len = INET_ADDRSTRLEN + lcf->upstream_url->port_name.len + 1;
1521 ngx_test_null(lcf->upstreams->u[i].addr_port_name.data,
1522 ngx_palloc(cf->pool, len),
1523 NGX_CONF_ERROR);
1524
1525 s = ngx_inet_ntop(AF_INET,
1526 (char *) &lcf->upstreams->u[i].addr,
1527 lcf->upstreams->u[i].addr_port_name.data,
1528 len);
1529
1530 lcf->upstreams->u[i].addr_port_name.data[s++] = ':';
1531
1532 ngx_cpystrn(lcf->upstreams->u[i].addr_port_name.data + s,
1533 lcf->upstream_url->port_name.data,
1534 lcf->upstream_url->port_name.len + 1);
1535
1536 lcf->upstreams->u[i].addr_port_name.len =
1537 s + lcf->upstream_url->port_name.len + 1;
1538 }
1539
1540 } else {
1541
1542 /* MP: ngx_shared_palloc() */
1543
1544 ngx_test_null(lcf->upstreams,
1545 ngx_palloc(cf->pool, sizeof(ngx_http_proxy_upstreams_t)),
1546 NGX_CONF_ERROR);
1547
1548 lcf->upstreams->number = 1;
1549
1550 lcf->upstreams->u[0].host.data = host;
1551 lcf->upstreams->u[0].host.len = lcf->upstream_url->host.len;
1552 lcf->upstreams->u[0].addr = addr;
1553 lcf->upstreams->u[0].port = lcf->upstream_url->port;
1554
1555 len = lcf->upstream_url->host.len
1556 + lcf->upstream_url->port_name.len + 1;
1557
1558 ngx_test_null(lcf->upstreams->u[0].addr_port_name.data,
1559 ngx_palloc(cf->pool, len + 1),
1560 NGX_CONF_ERROR);
1561
1562 ngx_memcpy(lcf->upstreams->u[0].addr_port_name.data,
1563 lcf->upstream_url->host.data,
1564 lcf->upstream_url->host.len);
1565
1566 s = lcf->upstream_url->host.len;
1567
1568 lcf->upstreams->u[0].addr_port_name.data[s++] = ':';
1569
1570 ngx_cpystrn(lcf->upstreams->u[0].addr_port_name.data + s,
1571 lcf->upstream_url->port_name.data,
1572 lcf->upstream_url->port_name.len + 1);
1573
1574 lcf->upstreams->u[0].addr_port_name.len = len;
1575 }
1576
1577 ctx = cf->ctx;
1578 core_lcf = ctx->loc_conf[ngx_http_core_module_ctx.index];
1579 core_lcf->handler = ngx_http_proxy_handler;
1580 lcf->upstream_url->location = &core_lcf->name;
1581
1582 return NULL;
1583}
1584
1585
1586static char *ngx_http_proxy_parse_upstream(ngx_str_t *url,
1587 ngx_http_proxy_upstream_url_t *uu)
1588{
1589 size_t i;
1590
1591 if (url->data[0] == ':' || url->data[0] == '/') {
1592 return "invalid upstream URL";
1593 }
1594
1595 uu->host.data = url->data;
1596 uu->host_header.data = url->data;
1597
1598 for (i = 1; i < url->len; i++) {
1599 if (url->data[i] == ':') {
1600 uu->port_name.data = &url->data[i] + 1;
1601 uu->host.len = i;
1602 }
1603
1604 if (url->data[i] == '/') {
1605 uu->uri.data = &url->data[i];
1606 uu->uri.len = url->len - i;
1607 uu->host_header.len = i;
1608
1609 if (uu->host.len == 0) {
1610 uu->host.len = i;
1611 }
1612
1613 if (uu->port_name.data == NULL) {
1614 return NULL;
1615 }
1616
1617 uu->port_name.len = &url->data[i] - uu->port_name.data;
1618
1619 if (uu->port_name.len > 0) {
1620 uu->port = ngx_atoi(uu->port_name.data, uu->port_name.len);
1621 if (uu->port > 0) {
1622 return NULL;
1623 }
1624 }
1625
1626 return "invalid port in upstream URL";
1627 }
1628 }
1629
1630 if (uu->host.len == 0) {
1631 uu->host.len = i;
1632 }
1633
1634 uu->host_header.len = i;
1635
1636 uu->uri.data = "/";
1637 uu->uri.len = 1;
1638
1639 if (uu->port_name.data == NULL) {
1640 return NULL;
1641 }
1642
1643 uu->port_name.len = &url->data[i] - uu->port_name.data;
1644
1645 if (uu->port_name.len > 0) {
1646 uu->port = ngx_atoi(uu->port_name.data, uu->port_name.len);
1647 if (uu->port > 0) {
1648 return NULL;
1649 }
1650 }
1651
1652 return "invalid port in upstream URL";
1653}