Annotation of embedaddon/lighttpd/src/mod_proxy.c, revision 1.1.1.3
1.1.1.3 ! misho 1: #include "first.h"
! 2:
1.1 misho 3: #include "buffer.h"
4: #include "server.h"
5: #include "keyvalue.h"
6: #include "log.h"
7:
8: #include "http_chunk.h"
9: #include "fdevent.h"
10: #include "connections.h"
11: #include "response.h"
12: #include "joblist.h"
13:
14: #include "plugin.h"
15:
16: #include "inet_ntop_cache.h"
17: #include "crc32.h"
18:
19: #include <sys/types.h>
20:
21: #include <unistd.h>
22: #include <errno.h>
23: #include <fcntl.h>
24: #include <string.h>
25: #include <stdlib.h>
26: #include <ctype.h>
27: #include <assert.h>
28:
29: #include <stdio.h>
30:
31: #include "sys-socket.h"
32:
33: #define data_proxy data_fastcgi
34: #define data_proxy_init data_fastcgi_init
35:
36: #define PROXY_RETRY_TIMEOUT 60
37:
38: /**
39: *
40: * the proxy module is based on the fastcgi module
41: *
42: * 28.06.2004 Jan Kneschke The first release
43: * 01.07.2004 Evgeny Rodichev Several bugfixes and cleanups
44: * - co-ordinate up- and downstream flows correctly (proxy_demux_response
45: * and proxy_handle_fdevent)
46: * - correctly transfer upstream http_response_status;
47: * - some unused structures removed.
48: *
49: * TODO: - delay upstream read if write_queue is too large
50: * (to prevent memory eating, like in apache). Shoud be
51: * configurable).
52: * - persistent connection with upstream servers
53: * - HTTP/1.1
54: */
55: typedef enum {
56: PROXY_BALANCE_UNSET,
57: PROXY_BALANCE_FAIR,
58: PROXY_BALANCE_HASH,
59: PROXY_BALANCE_RR
60: } proxy_balance_t;
61:
62: typedef struct {
63: array *extensions;
64: unsigned short debug;
65:
66: proxy_balance_t balance;
67: } plugin_config;
68:
69: typedef struct {
70: PLUGIN_DATA;
71:
72: buffer *parse_response;
73: buffer *balance_buf;
74:
75: plugin_config **config_storage;
76:
77: plugin_config conf;
78: } plugin_data;
79:
80: typedef enum {
81: PROXY_STATE_INIT,
82: PROXY_STATE_CONNECT,
83: PROXY_STATE_PREPARE_WRITE,
84: PROXY_STATE_WRITE,
1.1.1.3 ! misho 85: PROXY_STATE_READ
1.1 misho 86: } proxy_connection_state_t;
87:
88: enum { PROXY_STDOUT, PROXY_END_REQUEST };
89:
90: typedef struct {
91: proxy_connection_state_t state;
92: time_t state_timestamp;
93:
94: data_proxy *host;
95:
96: buffer *response;
97: buffer *response_header;
98:
99: chunkqueue *wb;
1.1.1.3 ! misho 100: off_t wb_reqlen;
1.1 misho 101:
102: int fd; /* fd to the proxy process */
103: int fde_ndx; /* index into the fd-event buffer */
104:
105: size_t path_info_offset; /* start of path_info in uri.path */
106:
107: connection *remote_conn; /* dump pointer */
108: plugin_data *plugin_data; /* dump pointer */
109: } handler_ctx;
110:
111:
112: /* ok, we need a prototype */
113: static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents);
114:
115: static handler_ctx * handler_ctx_init(void) {
116: handler_ctx * hctx;
117:
118:
119: hctx = calloc(1, sizeof(*hctx));
120:
121: hctx->state = PROXY_STATE_INIT;
122: hctx->host = NULL;
123:
124: hctx->response = buffer_init();
125: hctx->response_header = buffer_init();
126:
127: hctx->wb = chunkqueue_init();
1.1.1.3 ! misho 128: hctx->wb_reqlen = 0;
1.1 misho 129:
130: hctx->fd = -1;
131: hctx->fde_ndx = -1;
132:
133: return hctx;
134: }
135:
136: static void handler_ctx_free(handler_ctx *hctx) {
137: buffer_free(hctx->response);
138: buffer_free(hctx->response_header);
139: chunkqueue_free(hctx->wb);
140:
141: free(hctx);
142: }
143:
144: INIT_FUNC(mod_proxy_init) {
145: plugin_data *p;
146:
147: p = calloc(1, sizeof(*p));
148:
149: p->parse_response = buffer_init();
150: p->balance_buf = buffer_init();
151:
152: return p;
153: }
154:
155:
156: FREE_FUNC(mod_proxy_free) {
157: plugin_data *p = p_d;
158:
159: UNUSED(srv);
160:
161: buffer_free(p->parse_response);
162: buffer_free(p->balance_buf);
163:
164: if (p->config_storage) {
165: size_t i;
166: for (i = 0; i < srv->config_context->used; i++) {
167: plugin_config *s = p->config_storage[i];
168:
1.1.1.3 ! misho 169: if (NULL == s) continue;
1.1 misho 170:
1.1.1.3 ! misho 171: array_free(s->extensions);
1.1 misho 172:
1.1.1.3 ! misho 173: free(s);
1.1 misho 174: }
175: free(p->config_storage);
176: }
177:
178: free(p);
179:
180: return HANDLER_GO_ON;
181: }
182:
183: SETDEFAULTS_FUNC(mod_proxy_set_defaults) {
184: plugin_data *p = p_d;
185: data_unset *du;
186: size_t i = 0;
187:
188: config_values_t cv[] = {
189: { "proxy.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
190: { "proxy.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
191: { "proxy.balance", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
192: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
193: };
194:
1.1.1.2 misho 195: p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1 misho 196:
197: for (i = 0; i < srv->config_context->used; i++) {
1.1.1.3 ! misho 198: data_config const* config = (data_config const*)srv->config_context->data[i];
1.1 misho 199: plugin_config *s;
200:
201: s = malloc(sizeof(plugin_config));
202: s->extensions = array_init();
203: s->debug = 0;
204:
205: cv[0].destination = s->extensions;
206: cv[1].destination = &(s->debug);
207: cv[2].destination = p->balance_buf;
208:
209: buffer_reset(p->balance_buf);
210:
211: p->config_storage[i] = s;
212:
1.1.1.3 ! misho 213: if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1.1 misho 214: return HANDLER_ERROR;
215: }
216:
1.1.1.3 ! misho 217: if (buffer_string_is_empty(p->balance_buf)) {
1.1 misho 218: s->balance = PROXY_BALANCE_FAIR;
219: } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("fair"))) {
220: s->balance = PROXY_BALANCE_FAIR;
221: } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("round-robin"))) {
222: s->balance = PROXY_BALANCE_RR;
223: } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("hash"))) {
224: s->balance = PROXY_BALANCE_HASH;
225: } else {
226: log_error_write(srv, __FILE__, __LINE__, "sb",
227: "proxy.balance has to be one of: fair, round-robin, hash, but not:", p->balance_buf);
228: return HANDLER_ERROR;
229: }
230:
1.1.1.3 ! misho 231: if (NULL != (du = array_get_element(config->value, "proxy.server"))) {
1.1 misho 232: size_t j;
233: data_array *da = (data_array *)du;
234:
235: if (du->type != TYPE_ARRAY) {
236: log_error_write(srv, __FILE__, __LINE__, "sss",
1.1.1.3 ! misho 237: "unexpected type for key: ", "proxy.server", "expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1.1 misho 238:
239: return HANDLER_ERROR;
240: }
241:
242: /*
243: * proxy.server = ( "<ext>" => ...,
244: * "<ext>" => ... )
245: */
246:
247: for (j = 0; j < da->value->used; j++) {
248: data_array *da_ext = (data_array *)da->value->data[j];
249: size_t n;
250:
251: if (da_ext->type != TYPE_ARRAY) {
252: log_error_write(srv, __FILE__, __LINE__, "sssbs",
253: "unexpected type for key: ", "proxy.server",
1.1.1.3 ! misho 254: "[", da->value->data[j]->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1.1 misho 255:
256: return HANDLER_ERROR;
257: }
258:
259: /*
260: * proxy.server = ( "<ext>" =>
261: * ( "<host>" => ( ... ),
262: * "<host>" => ( ... )
263: * ),
264: * "<ext>" => ... )
265: */
266:
267: for (n = 0; n < da_ext->value->used; n++) {
268: data_array *da_host = (data_array *)da_ext->value->data[n];
269:
270: data_proxy *df;
271: data_array *dfa;
272:
273: config_values_t pcv[] = {
274: { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
275: { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
276: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
277: };
278:
279: if (da_host->type != TYPE_ARRAY) {
280: log_error_write(srv, __FILE__, __LINE__, "ssSBS",
281: "unexpected type for key:",
282: "proxy.server",
1.1.1.3 ! misho 283: "[", da_ext->value->data[n]->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1.1 misho 284:
285: return HANDLER_ERROR;
286: }
287:
288: df = data_proxy_init();
289:
290: df->port = 80;
291:
1.1.1.3 ! misho 292: buffer_copy_buffer(df->key, da_host->key);
1.1 misho 293:
294: pcv[0].destination = df->host;
295: pcv[1].destination = &(df->port);
296:
1.1.1.3 ! misho 297: if (0 != config_insert_values_internal(srv, da_host->value, pcv, T_CONFIG_SCOPE_CONNECTION)) {
1.1.1.2 misho 298: df->free((data_unset*) df);
1.1 misho 299: return HANDLER_ERROR;
300: }
301:
1.1.1.3 ! misho 302: if (buffer_string_is_empty(df->host)) {
1.1 misho 303: log_error_write(srv, __FILE__, __LINE__, "sbbbs",
304: "missing key (string):",
305: da->key,
306: da_ext->key,
307: da_host->key,
308: "host");
309:
1.1.1.2 misho 310: df->free((data_unset*) df);
1.1 misho 311: return HANDLER_ERROR;
312: }
313:
314: /* if extension already exists, take it */
315:
316: if (NULL == (dfa = (data_array *)array_get_element(s->extensions, da_ext->key->ptr))) {
317: dfa = data_array_init();
318:
1.1.1.3 ! misho 319: buffer_copy_buffer(dfa->key, da_ext->key);
1.1 misho 320:
321: array_insert_unique(dfa->value, (data_unset *)df);
322: array_insert_unique(s->extensions, (data_unset *)dfa);
323: } else {
324: array_insert_unique(dfa->value, (data_unset *)df);
325: }
326: }
327: }
328: }
329: }
330:
331: return HANDLER_GO_ON;
332: }
333:
334: static void proxy_connection_close(server *srv, handler_ctx *hctx) {
335: plugin_data *p;
336: connection *con;
337:
338: p = hctx->plugin_data;
339: con = hctx->remote_conn;
340:
341: if (hctx->fd != -1) {
342: fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
343: fdevent_unregister(srv->ev, hctx->fd);
344:
345: close(hctx->fd);
346: srv->cur_fds--;
347: }
348:
349: if (hctx->host) {
350: hctx->host->usage--;
351: }
352:
353: handler_ctx_free(hctx);
354: con->plugin_ctx[p->id] = NULL;
1.1.1.3 ! misho 355:
! 356: /* finish response (if not already con->file_started, con->file_finished) */
! 357: if (con->mode == p->id) {
! 358: http_response_backend_done(srv, con);
! 359: }
1.1 misho 360: }
361:
362: static int proxy_establish_connection(server *srv, handler_ctx *hctx) {
363: struct sockaddr *proxy_addr;
364: struct sockaddr_in proxy_addr_in;
1.1.1.3 ! misho 365: #if defined(HAVE_SYS_UN_H)
! 366: struct sockaddr_un proxy_addr_un;
! 367: #endif
1.1 misho 368: #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
369: struct sockaddr_in6 proxy_addr_in6;
370: #endif
371: socklen_t servlen;
372:
373: plugin_data *p = hctx->plugin_data;
374: data_proxy *host= hctx->host;
375: int proxy_fd = hctx->fd;
376:
377:
1.1.1.3 ! misho 378: #if defined(HAVE_SYS_UN_H)
! 379: if (strstr(host->host->ptr, "/")) {
! 380: if (buffer_string_length(host->host) + 1 > sizeof(proxy_addr_un.sun_path)) {
! 381: log_error_write(srv, __FILE__, __LINE__, "sB",
! 382: "ERROR: Unix Domain socket filename too long:",
! 383: host->host);
! 384: return -1;
! 385: }
! 386:
! 387: memset(&proxy_addr_un, 0, sizeof(proxy_addr_un));
! 388: proxy_addr_un.sun_family = AF_UNIX;
! 389: memcpy(proxy_addr_un.sun_path, host->host->ptr, buffer_string_length(host->host) + 1);
! 390: servlen = sizeof(proxy_addr_un);
! 391: proxy_addr = (struct sockaddr *) &proxy_addr_un;
! 392: } else
! 393: #endif
1.1 misho 394: #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
395: if (strstr(host->host->ptr, ":")) {
396: memset(&proxy_addr_in6, 0, sizeof(proxy_addr_in6));
397: proxy_addr_in6.sin6_family = AF_INET6;
398: inet_pton(AF_INET6, host->host->ptr, (char *) &proxy_addr_in6.sin6_addr);
399: proxy_addr_in6.sin6_port = htons(host->port);
400: servlen = sizeof(proxy_addr_in6);
401: proxy_addr = (struct sockaddr *) &proxy_addr_in6;
402: } else
403: #endif
404: {
405: memset(&proxy_addr_in, 0, sizeof(proxy_addr_in));
406: proxy_addr_in.sin_family = AF_INET;
407: proxy_addr_in.sin_addr.s_addr = inet_addr(host->host->ptr);
408: proxy_addr_in.sin_port = htons(host->port);
409: servlen = sizeof(proxy_addr_in);
410: proxy_addr = (struct sockaddr *) &proxy_addr_in;
411: }
412:
413:
414: if (-1 == connect(proxy_fd, proxy_addr, servlen)) {
415: if (errno == EINPROGRESS || errno == EALREADY) {
416: if (p->conf.debug) {
417: log_error_write(srv, __FILE__, __LINE__, "sd",
418: "connect delayed:", proxy_fd);
419: }
420:
421: return 1;
422: } else {
423:
424: log_error_write(srv, __FILE__, __LINE__, "sdsd",
425: "connect failed:", proxy_fd, strerror(errno), errno);
426:
427: return -1;
428: }
429: }
430: if (p->conf.debug) {
431: log_error_write(srv, __FILE__, __LINE__, "sd",
432: "connect succeeded: ", proxy_fd);
433: }
434:
435: return 0;
436: }
437:
438: static void proxy_set_header(connection *con, const char *key, const char *value) {
1.1.1.3 ! misho 439: data_string *ds_dst;
! 440:
! 441: if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
! 442: ds_dst = data_string_init();
! 443: }
1.1 misho 444:
1.1.1.3 ! misho 445: buffer_copy_string(ds_dst->key, key);
! 446: buffer_copy_string(ds_dst->value, value);
! 447: array_insert_unique(con->request.headers, (data_unset *)ds_dst);
1.1 misho 448: }
449:
450: static void proxy_append_header(connection *con, const char *key, const char *value) {
1.1.1.3 ! misho 451: data_string *ds_dst;
1.1 misho 452:
1.1.1.3 ! misho 453: if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
! 454: ds_dst = data_string_init();
! 455: }
! 456:
! 457: buffer_copy_string(ds_dst->key, key);
! 458: buffer_append_string(ds_dst->value, value);
! 459: array_insert_unique(con->request.headers, (data_unset *)ds_dst);
1.1 misho 460: }
461:
462:
463: static int proxy_create_env(server *srv, handler_ctx *hctx) {
464: size_t i;
465:
466: connection *con = hctx->remote_conn;
467: buffer *b;
468:
469: /* build header */
470:
1.1.1.3 ! misho 471: b = buffer_init();
1.1 misho 472:
473: /* request line */
474: buffer_copy_string(b, get_http_method_name(con->request.http_method));
475: buffer_append_string_len(b, CONST_STR_LEN(" "));
476:
477: buffer_append_string_buffer(b, con->request.uri);
478: buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n"));
479:
480: proxy_append_header(con, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
481: /* http_host is NOT is just a pointer to a buffer
482: * which is NULL if it is not set */
1.1.1.3 ! misho 483: if (!buffer_string_is_empty(con->request.http_host)) {
1.1 misho 484: proxy_set_header(con, "X-Host", con->request.http_host->ptr);
485: }
486: proxy_set_header(con, "X-Forwarded-Proto", con->uri.scheme->ptr);
487:
488: /* request header */
489: for (i = 0; i < con->request.headers->used; i++) {
490: data_string *ds;
491:
492: ds = (data_string *)con->request.headers->data[i];
493:
1.1.1.3 ! misho 494: if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
! 495: if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Connection"))) continue;
! 496: if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue;
! 497: /* Do not emit HTTP_PROXY in environment.
! 498: * Some executables use HTTP_PROXY to configure
! 499: * outgoing proxy. See also https://httpoxy.org/ */
! 500: if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) continue;
1.1 misho 501:
502: buffer_append_string_buffer(b, ds->key);
503: buffer_append_string_len(b, CONST_STR_LEN(": "));
504: buffer_append_string_buffer(b, ds->value);
505: buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
506: }
507: }
508:
1.1.1.3 ! misho 509: buffer_append_string_len(b, CONST_STR_LEN("Connection: close\r\n\r\n"));
! 510:
! 511: hctx->wb_reqlen = buffer_string_length(b);
! 512: chunkqueue_append_buffer(hctx->wb, b);
! 513: buffer_free(b);
1.1 misho 514:
515: /* body */
516:
517: if (con->request.content_length) {
1.1.1.3 ! misho 518: chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue);
! 519: hctx->wb_reqlen += con->request.content_length;/* (eventual) total request size */
1.1 misho 520: }
521:
522: return 0;
523: }
524:
525: static int proxy_set_state(server *srv, handler_ctx *hctx, proxy_connection_state_t state) {
526: hctx->state = state;
527: hctx->state_timestamp = srv->cur_ts;
528:
529: return 0;
530: }
531:
532:
533: static int proxy_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
534: char *s, *ns;
535: int http_response_status = -1;
536:
537: UNUSED(srv);
538:
1.1.1.3 ! misho 539: /* [\r]\n -> [\0]\0 */
1.1 misho 540:
1.1.1.3 ! misho 541: buffer_copy_buffer(p->parse_response, in);
1.1 misho 542:
1.1.1.3 ! misho 543: for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1) {
1.1 misho 544: char *key, *value;
545: int key_len;
546: data_string *ds;
547: int copy_header;
548:
549: ns[0] = '\0';
1.1.1.3 ! misho 550: if (s != ns && ns[-1] == '\r') ns[-1] = '\0';
1.1 misho 551:
552: if (-1 == http_response_status) {
553: /* The first line of a Response message is the Status-Line */
554:
555: for (key=s; *key && *key != ' '; key++);
556:
557: if (*key) {
558: http_response_status = (int) strtol(key, NULL, 10);
1.1.1.3 ! misho 559: if (http_response_status < 100 || http_response_status >= 1000) http_response_status = 502;
1.1 misho 560: } else {
561: http_response_status = 502;
562: }
563:
564: con->http_status = http_response_status;
565: con->parsed_response |= HTTP_STATUS;
566: continue;
567: }
568:
569: if (NULL == (value = strchr(s, ':'))) {
570: /* now we expect: "<key>: <value>\n" */
571:
572: continue;
573: }
574:
575: key = s;
576: key_len = value - key;
577:
578: value++;
579: /* strip WS */
580: while (*value == ' ' || *value == '\t') value++;
581:
582: copy_header = 1;
583:
584: switch(key_len) {
585: case 4:
586: if (0 == strncasecmp(key, "Date", key_len)) {
587: con->parsed_response |= HTTP_DATE;
588: }
589: break;
590: case 8:
591: if (0 == strncasecmp(key, "Location", key_len)) {
592: con->parsed_response |= HTTP_LOCATION;
593: }
594: break;
595: case 10:
596: if (0 == strncasecmp(key, "Connection", key_len)) {
597: copy_header = 0;
598: }
599: break;
600: case 14:
601: if (0 == strncasecmp(key, "Content-Length", key_len)) {
1.1.1.3 ! misho 602: con->response.content_length = strtoul(value, NULL, 10);
1.1 misho 603: con->parsed_response |= HTTP_CONTENT_LENGTH;
604: }
605: break;
606: default:
607: break;
608: }
609:
610: if (copy_header) {
611: if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
612: ds = data_response_init();
613: }
614: buffer_copy_string_len(ds->key, key, key_len);
615: buffer_copy_string(ds->value, value);
616:
617: array_insert_unique(con->response.headers, (data_unset *)ds);
618: }
619: }
620:
621: return 0;
622: }
623:
624:
625: static int proxy_demux_response(server *srv, handler_ctx *hctx) {
626: int fin = 0;
627: int b;
628: ssize_t r;
629:
630: plugin_data *p = hctx->plugin_data;
631: connection *con = hctx->remote_conn;
632: int proxy_fd = hctx->fd;
633:
634: /* check how much we have to read */
1.1.1.3 ! misho 635: #if !defined(_WIN32) && !defined(__CYGWIN__)
1.1 misho 636: if (ioctl(hctx->fd, FIONREAD, &b)) {
1.1.1.3 ! misho 637: if (errno == EAGAIN) {
! 638: fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
! 639: return 0;
! 640: }
1.1 misho 641: log_error_write(srv, __FILE__, __LINE__, "sd",
642: "ioctl failed: ",
643: proxy_fd);
644: return -1;
645: }
1.1.1.3 ! misho 646: #else
! 647: b = 4096;
! 648: #endif
1.1 misho 649:
650:
651: if (p->conf.debug) {
652: log_error_write(srv, __FILE__, __LINE__, "sd",
1.1.1.3 ! misho 653: "proxy - have to read:", b);
1.1 misho 654: }
655:
656: if (b > 0) {
1.1.1.3 ! misho 657: if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)) {
! 658: off_t cqlen = chunkqueue_length(con->write_queue);
! 659: if (cqlen + b > 65536 - 4096) {
! 660: if (!con->is_writable) {
! 661: /*(defer removal of FDEVENT_IN interest since
! 662: * connection_state_machine() might be able to send data
! 663: * immediately, unless !con->is_writable, where
! 664: * connection_state_machine() might not loop back to call
! 665: * mod_proxy_handle_subrequest())*/
! 666: fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
! 667: }
! 668: if (cqlen >= 65536-1) return 0;
! 669: b = 65536 - 1 - (int)cqlen;
! 670: }
1.1 misho 671: }
672:
1.1.1.3 ! misho 673: buffer_string_prepare_append(hctx->response, b);
! 674:
! 675: if (-1 == (r = read(hctx->fd, hctx->response->ptr + buffer_string_length(hctx->response), buffer_string_space(hctx->response)))) {
! 676: if (errno == EAGAIN) {
! 677: fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
! 678: return 0;
! 679: }
1.1 misho 680: log_error_write(srv, __FILE__, __LINE__, "sds",
681: "unexpected end-of-file (perhaps the proxy process died):",
682: proxy_fd, strerror(errno));
683: return -1;
684: }
685:
1.1.1.3 ! misho 686: #if defined(_WIN32) || defined(__CYGWIN__)
! 687: if (0 == r) return 1; /* fin */
! 688: #endif
! 689:
1.1 misho 690: /* this should be catched by the b > 0 above */
1.1.1.2 misho 691: force_assert(r);
1.1 misho 692:
1.1.1.3 ! misho 693: buffer_commit(hctx->response, r);
1.1 misho 694:
695: #if 0
696: log_error_write(srv, __FILE__, __LINE__, "sdsbs",
697: "demux: Response buffer len", hctx->response->used, ":", hctx->response, ":");
698: #endif
699:
700: if (0 == con->got_response) {
701: con->got_response = 1;
1.1.1.3 ! misho 702: buffer_string_prepare_copy(hctx->response_header, 1023);
1.1 misho 703: }
704:
705: if (0 == con->file_started) {
706: char *c;
707:
708: /* search for the \r\n\r\n in the string */
1.1.1.3 ! misho 709: if (NULL != (c = buffer_search_string_len(hctx->response, CONST_STR_LEN("\r\n\r\n")))) {
1.1 misho 710: size_t hlen = c - hctx->response->ptr + 4;
1.1.1.3 ! misho 711: size_t blen = buffer_string_length(hctx->response) - hlen;
1.1 misho 712: /* found */
713:
1.1.1.3 ! misho 714: buffer_append_string_len(hctx->response_header, hctx->response->ptr, hlen);
1.1 misho 715: #if 0
716: log_error_write(srv, __FILE__, __LINE__, "sb", "Header:", hctx->response_header);
717: #endif
718: /* parse the response header */
719: proxy_response_parse(srv, con, p, hctx->response_header);
720:
721: con->file_started = 1;
1.1.1.3 ! misho 722: if (blen > 0) {
! 723: if (0 != http_chunk_append_mem(srv, con, c + 4, blen)) {
! 724: /* error writing to tempfile;
! 725: * truncate response or send 500 if nothing sent yet */
! 726: fin = 1;
! 727: con->file_started = 0;
! 728: }
! 729: }
! 730: buffer_reset(hctx->response);
! 731: } else {
! 732: /* no luck, no header found */
! 733: /*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/
! 734: if (buffer_string_length(hctx->response) > MAX_HTTP_REQUEST_HEADER) {
! 735: log_error_write(srv, __FILE__, __LINE__, "sb", "response headers too large for", con->uri.path);
! 736: con->http_status = 502; /* Bad Gateway */
! 737: con->mode = DIRECT;
! 738: fin = 1;
1.1 misho 739: }
740: }
741: } else {
1.1.1.3 ! misho 742: if (0 != http_chunk_append_buffer(srv, con, hctx->response)) {
! 743: /* error writing to tempfile;
! 744: * truncate response or send 500 if nothing sent yet */
! 745: fin = 1;
! 746: }
! 747: buffer_reset(hctx->response);
1.1 misho 748: }
749: } else {
750: /* reading from upstream done */
751: fin = 1;
752: }
753:
754: return fin;
755: }
756:
757:
758: static handler_t proxy_write_request(server *srv, handler_ctx *hctx) {
759: data_proxy *host= hctx->host;
760: connection *con = hctx->remote_conn;
761:
762: int ret;
763:
1.1.1.3 ! misho 764: if (!host || buffer_string_is_empty(host->host) || !host->port) return HANDLER_ERROR;
1.1 misho 765:
766: switch(hctx->state) {
767: case PROXY_STATE_CONNECT:
768: /* wait for the connect() to finish */
769:
770: /* connect failed ? */
771: if (-1 == hctx->fde_ndx) return HANDLER_ERROR;
772:
773: /* wait */
774: return HANDLER_WAIT_FOR_EVENT;
775:
776: case PROXY_STATE_INIT:
1.1.1.3 ! misho 777: #if defined(HAVE_SYS_UN_H)
! 778: if (strstr(host->host->ptr,"/")) {
! 779: if (-1 == (hctx->fd = socket(AF_UNIX, SOCK_STREAM, 0))) {
! 780: log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno));
! 781: return HANDLER_ERROR;
! 782: }
! 783: } else
! 784: #endif
1.1 misho 785: #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
786: if (strstr(host->host->ptr,":")) {
1.1.1.3 ! misho 787: if (-1 == (hctx->fd = socket(AF_INET6, SOCK_STREAM, 0))) {
! 788: log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno));
! 789: return HANDLER_ERROR;
! 790: }
1.1 misho 791: } else
792: #endif
793: {
1.1.1.3 ! misho 794: if (-1 == (hctx->fd = socket(AF_INET, SOCK_STREAM, 0))) {
! 795: log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno));
! 796: return HANDLER_ERROR;
! 797: }
1.1 misho 798: }
799: hctx->fde_ndx = -1;
800:
801: srv->cur_fds++;
802:
803: fdevent_register(srv->ev, hctx->fd, proxy_handle_fdevent, hctx);
804:
805: if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
806: log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
807:
808: return HANDLER_ERROR;
809: }
810:
811: switch (proxy_establish_connection(srv, hctx)) {
812: case 1:
813: proxy_set_state(srv, hctx, PROXY_STATE_CONNECT);
814:
815: /* connection is in progress, wait for an event and call getsockopt() below */
816:
817: fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
818:
819: return HANDLER_WAIT_FOR_EVENT;
820: case -1:
821: /* if ECONNREFUSED choose another connection -> FIXME */
822: hctx->fde_ndx = -1;
823:
824: return HANDLER_ERROR;
825: default:
826: /* everything is ok, go on */
827: proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE);
828: break;
829: }
830:
831: /* fall through */
832:
833: case PROXY_STATE_PREPARE_WRITE:
834: proxy_create_env(srv, hctx);
835:
1.1.1.3 ! misho 836: fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1.1 misho 837: proxy_set_state(srv, hctx, PROXY_STATE_WRITE);
838:
839: /* fall through */
840: case PROXY_STATE_WRITE:;
841: ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT);
842:
843: chunkqueue_remove_finished_chunks(hctx->wb);
844:
845: if (-1 == ret) { /* error on our side */
846: log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno);
847:
848: return HANDLER_ERROR;
849: } else if (-2 == ret) { /* remote close */
850: log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed, remote connection close:", strerror(errno), errno);
851:
852: return HANDLER_ERROR;
853: }
854:
1.1.1.3 ! misho 855: if (hctx->wb->bytes_out == hctx->wb_reqlen) {
! 856: fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
! 857: #if (defined(__APPLE__) && defined(__MACH__)) \
! 858: || defined(__FreeBSD__) || defined(__NetBSD__) \
! 859: || defined(__OpenBSD__) || defined(__DragonflyBSD__)
! 860: /*(*BSD stack on remote might signal POLLHUP and remote
! 861: * might treat as socket error instead of half-close)*/
! 862: #else
! 863: /*(remote could be different machine running affected OS,
! 864: * so only issue shutdown for known local sockets)*/
! 865: if ( '/' == host->host->ptr[0]
! 866: || buffer_is_equal_string(host->host, CONST_STR_LEN("127.0.0.1"))
! 867: || buffer_is_equal_string(host->host, CONST_STR_LEN("::1"))) {
! 868: shutdown(hctx->fd, SHUT_WR);/* future: remove if HTTP/1.1 request */
! 869: }
! 870: #endif
1.1 misho 871: proxy_set_state(srv, hctx, PROXY_STATE_READ);
872: } else {
1.1.1.3 ! misho 873: off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out;
! 874: if (hctx->wb->bytes_in < hctx->wb_reqlen && wblen < 65536 - 16384) {
! 875: /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
! 876: if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
! 877: con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
! 878: con->is_readable = 1; /* trigger optimistic read from client */
! 879: }
! 880: }
! 881: if (0 == wblen) {
! 882: fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
! 883: } else {
! 884: fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
! 885: }
1.1 misho 886: }
887:
888: return HANDLER_WAIT_FOR_EVENT;
889: case PROXY_STATE_READ:
890: /* waiting for a response */
891: return HANDLER_WAIT_FOR_EVENT;
892: default:
893: log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
894: return HANDLER_ERROR;
895: }
896: }
897:
898: #define PATCH(x) \
899: p->conf.x = s->x;
900: static int mod_proxy_patch_connection(server *srv, connection *con, plugin_data *p) {
901: size_t i, j;
902: plugin_config *s = p->config_storage[0];
903:
904: PATCH(extensions);
905: PATCH(debug);
906: PATCH(balance);
907:
908: /* skip the first, the global context */
909: for (i = 1; i < srv->config_context->used; i++) {
910: data_config *dc = (data_config *)srv->config_context->data[i];
911: s = p->config_storage[i];
912:
913: /* condition didn't match */
914: if (!config_check_cond(srv, con, dc)) continue;
915:
916: /* merge config */
917: for (j = 0; j < dc->value->used; j++) {
918: data_unset *du = dc->value->data[j];
919:
920: if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.server"))) {
921: PATCH(extensions);
922: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.debug"))) {
923: PATCH(debug);
924: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.balance"))) {
925: PATCH(balance);
926: }
927: }
928: }
929:
930: return 0;
931: }
932: #undef PATCH
933:
1.1.1.3 ! misho 934: static handler_t proxy_send_request(server *srv, handler_ctx *hctx) {
1.1 misho 935: /* ok, create the request */
1.1.1.3 ! misho 936: handler_t rc = proxy_write_request(srv, hctx);
! 937: if (HANDLER_ERROR != rc) {
! 938: return rc;
! 939: } else {
! 940: data_proxy *host = hctx->host;
! 941: connection *con = hctx->remote_conn;
! 942: plugin_data *p = hctx->plugin_data;
1.1 misho 943: log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:",
944: host->host,
945: host->port,
946: hctx->fd);
947:
948: /* disable this server */
949: host->is_disabled = 1;
950: host->disable_ts = srv->cur_ts;
951:
1.1.1.3 ! misho 952: /* reset the enviroment and restart the sub-request */
! 953: con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
1.1 misho 954: proxy_connection_close(srv, hctx);
1.1.1.3 ! misho 955: con->mode = p->id;
1.1 misho 956:
1.1.1.3 ! misho 957: return HANDLER_COMEBACK;
! 958: }
! 959: }
1.1 misho 960:
961:
1.1.1.3 ! misho 962: static handler_t proxy_recv_response(server *srv, handler_ctx *hctx);
1.1 misho 963:
1.1.1.3 ! misho 964:
! 965: SUBREQUEST_FUNC(mod_proxy_handle_subrequest) {
! 966: plugin_data *p = p_d;
! 967:
! 968: handler_ctx *hctx = con->plugin_ctx[p->id];
! 969:
! 970: if (NULL == hctx) return HANDLER_GO_ON;
! 971:
! 972: /* not my job */
! 973: if (con->mode != p->id) return HANDLER_GO_ON;
! 974:
! 975: if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
! 976: && con->file_started) {
! 977: if (chunkqueue_length(con->write_queue) > 65536 - 4096) {
! 978: fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
! 979: } else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) {
! 980: /* optimistic read from backend, which might re-enable FDEVENT_IN */
! 981: handler_t rc = proxy_recv_response(srv, hctx); /*(might invalidate hctx)*/
! 982: if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
! 983: }
1.1 misho 984: }
985:
1.1.1.3 ! misho 986: if (0 == hctx->wb->bytes_in
! 987: ? con->state == CON_STATE_READ_POST
! 988: : hctx->wb->bytes_in < hctx->wb_reqlen) {
! 989: /*(64k - 4k to attempt to avoid temporary files
! 990: * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
! 991: if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096
! 992: && (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){
! 993: con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
! 994: if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT;
! 995: } else {
! 996: handler_t r = connection_handle_read_post_state(srv, con);
! 997: chunkqueue *req_cq = con->request_content_queue;
! 998: if (0 != hctx->wb->bytes_in && !chunkqueue_is_empty(req_cq)) {
! 999: chunkqueue_append_chunkqueue(hctx->wb, req_cq);
! 1000: if (fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_OUT) {
! 1001: return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r;
! 1002: }
! 1003: }
! 1004: if (r != HANDLER_GO_ON) return r;
! 1005: }
1.1 misho 1006: }
1.1.1.3 ! misho 1007:
! 1008: return ((0 == hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb))
! 1009: && hctx->state != PROXY_STATE_CONNECT)
! 1010: ? proxy_send_request(srv, hctx)
! 1011: : HANDLER_WAIT_FOR_EVENT;
1.1 misho 1012: }
1013:
1.1.1.3 ! misho 1014:
! 1015: static handler_t proxy_recv_response(server *srv, handler_ctx *hctx) {
! 1016:
! 1017: switch (proxy_demux_response(srv, hctx)) {
! 1018: case 0:
! 1019: break;
! 1020: case -1:
! 1021: http_response_backend_error(srv, hctx->remote_conn);
! 1022: /* fall through */
! 1023: case 1:
! 1024: /* we are done */
! 1025: proxy_connection_close(srv, hctx);
! 1026:
! 1027: return HANDLER_FINISHED;
! 1028: }
! 1029:
! 1030: return HANDLER_GO_ON;
! 1031: }
! 1032:
! 1033:
1.1 misho 1034: static handler_t proxy_handle_fdevent(server *srv, void *ctx, int revents) {
1035: handler_ctx *hctx = ctx;
1036: connection *con = hctx->remote_conn;
1037: plugin_data *p = hctx->plugin_data;
1038:
1.1.1.3 ! misho 1039: joblist_append(srv, con);
1.1 misho 1040:
1.1.1.3 ! misho 1041: if (revents & FDEVENT_IN) {
1.1 misho 1042:
1043: if (p->conf.debug) {
1044: log_error_write(srv, __FILE__, __LINE__, "sd",
1045: "proxy: fdevent-in", hctx->state);
1046: }
1047:
1.1.1.3 ! misho 1048: {
! 1049: handler_t rc = proxy_recv_response(srv,hctx);/*(might invalidate hctx)*/
! 1050: if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
1.1 misho 1051: }
1052: }
1053:
1054: if (revents & FDEVENT_OUT) {
1055: if (p->conf.debug) {
1056: log_error_write(srv, __FILE__, __LINE__, "sd",
1057: "proxy: fdevent-out", hctx->state);
1058: }
1059:
1060: if (hctx->state == PROXY_STATE_CONNECT) {
1061: int socket_error;
1062: socklen_t socket_error_len = sizeof(socket_error);
1063:
1064: /* try to finish the connect() */
1065: if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) {
1066: log_error_write(srv, __FILE__, __LINE__, "ss",
1067: "getsockopt failed:", strerror(errno));
1068:
1069: return HANDLER_FINISHED;
1070: }
1071: if (socket_error != 0) {
1072: log_error_write(srv, __FILE__, __LINE__, "ss",
1073: "establishing connection failed:", strerror(socket_error),
1074: "port:", hctx->host->port);
1075:
1076: return HANDLER_FINISHED;
1077: }
1078: if (p->conf.debug) {
1079: log_error_write(srv, __FILE__, __LINE__, "s", "proxy - connect - delayed success");
1080: }
1081:
1082: proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE);
1083: }
1084:
1.1.1.3 ! misho 1085: return proxy_send_request(srv, hctx); /*(might invalidate hctx)*/
1.1 misho 1086: }
1087:
1088: /* perhaps this issue is already handled */
1089: if (revents & FDEVENT_HUP) {
1090: if (p->conf.debug) {
1091: log_error_write(srv, __FILE__, __LINE__, "sd",
1092: "proxy: fdevent-hup", hctx->state);
1093: }
1094:
1095: if (hctx->state == PROXY_STATE_CONNECT) {
1096: /* connect() -> EINPROGRESS -> HUP */
1097:
1098: /**
1099: * what is proxy is doing if it can't reach the next hop ?
1100: *
1101: */
1102:
1103: if (hctx->host) {
1104: hctx->host->is_disabled = 1;
1105: hctx->host->disable_ts = srv->cur_ts;
1106: log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:",
1107: hctx->host->host,
1108: hctx->host->port,
1109: hctx->fd);
1110:
1111: /* disable this server */
1112: hctx->host->is_disabled = 1;
1113: hctx->host->disable_ts = srv->cur_ts;
1114:
1.1.1.3 ! misho 1115: /* reset the environment and restart the sub-request */
! 1116: con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
1.1 misho 1117: proxy_connection_close(srv, hctx);
1.1.1.3 ! misho 1118: con->mode = p->id;
1.1 misho 1119: } else {
1120: proxy_connection_close(srv, hctx);
1121: con->http_status = 503;
1122: }
1.1.1.3 ! misho 1123: } else if (con->file_started) {
! 1124: /* drain any remaining data from kernel pipe buffers
! 1125: * even if (con->conf.stream_response_body
! 1126: * & FDEVENT_STREAM_RESPONSE_BUFMIN)
! 1127: * since event loop will spin on fd FDEVENT_HUP event
! 1128: * until unregistered. */
! 1129: handler_t rc;
! 1130: do {
! 1131: rc = proxy_recv_response(srv,hctx);/*(might invalidate hctx)*/
! 1132: } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/
! 1133: return rc; /* HANDLER_FINISHED or HANDLER_ERROR */
! 1134: } else {
! 1135: proxy_connection_close(srv, hctx);
1.1 misho 1136: }
1137: } else if (revents & FDEVENT_ERR) {
1138: log_error_write(srv, __FILE__, __LINE__, "sd", "proxy-FDEVENT_ERR, but no HUP", revents);
1139:
1.1.1.3 ! misho 1140: http_response_backend_error(srv, con);
1.1 misho 1141: proxy_connection_close(srv, hctx);
1142: }
1143:
1144: return HANDLER_FINISHED;
1145: }
1146:
1147: static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p_d) {
1148: plugin_data *p = p_d;
1149: size_t s_len;
1150: unsigned long last_max = ULONG_MAX;
1151: int max_usage = INT_MAX;
1152: int ndx = -1;
1153: size_t k;
1154: buffer *fn;
1155: data_array *extension = NULL;
1156: size_t path_info_offset;
1157:
1158: if (con->mode != DIRECT) return HANDLER_GO_ON;
1159:
1160: /* Possibly, we processed already this request */
1161: if (con->file_started == 1) return HANDLER_GO_ON;
1162:
1163: mod_proxy_patch_connection(srv, con, p);
1164:
1165: fn = con->uri.path;
1.1.1.3 ! misho 1166: if (buffer_string_is_empty(fn)) return HANDLER_ERROR;
! 1167: s_len = buffer_string_length(fn);
1.1 misho 1168:
1169: path_info_offset = 0;
1170:
1171: if (p->conf.debug) {
1172: log_error_write(srv, __FILE__, __LINE__, "s", "proxy - start");
1173: }
1174:
1175: /* check if extension matches */
1176: for (k = 0; k < p->conf.extensions->used; k++) {
1177: data_array *ext = NULL;
1178: size_t ct_len;
1179:
1180: ext = (data_array *)p->conf.extensions->data[k];
1181:
1.1.1.3 ! misho 1182: if (buffer_is_empty(ext->key)) continue;
1.1 misho 1183:
1.1.1.3 ! misho 1184: ct_len = buffer_string_length(ext->key);
1.1 misho 1185:
1186: if (s_len < ct_len) continue;
1187:
1188: /* check extension in the form "/proxy_pattern" */
1189: if (*(ext->key->ptr) == '/') {
1190: if (strncmp(fn->ptr, ext->key->ptr, ct_len) == 0) {
1191: if (s_len > ct_len + 1) {
1192: char *pi_offset;
1193:
1194: if (NULL != (pi_offset = strchr(fn->ptr + ct_len + 1, '/'))) {
1195: path_info_offset = pi_offset - fn->ptr;
1196: }
1197: }
1198: extension = ext;
1199: break;
1200: }
1201: } else if (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len)) {
1202: /* check extension in the form ".fcg" */
1203: extension = ext;
1204: break;
1205: }
1206: }
1207:
1208: if (NULL == extension) {
1209: return HANDLER_GO_ON;
1210: }
1211:
1212: if (p->conf.debug) {
1213: log_error_write(srv, __FILE__, __LINE__, "s", "proxy - ext found");
1214: }
1215:
1216: if (extension->value->used == 1) {
1217: if ( ((data_proxy *)extension->value->data[0])->is_disabled ) {
1218: ndx = -1;
1219: } else {
1220: ndx = 0;
1221: }
1222: } else if (extension->value->used != 0) switch(p->conf.balance) {
1223: case PROXY_BALANCE_HASH:
1224: /* hash balancing */
1225:
1226: if (p->conf.debug) {
1227: log_error_write(srv, __FILE__, __LINE__, "sd",
1228: "proxy - used hash balancing, hosts:", extension->value->used);
1229: }
1230:
1231: for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->value->used; k++) {
1232: data_proxy *host = (data_proxy *)extension->value->data[k];
1233: unsigned long cur_max;
1234:
1235: if (host->is_disabled) continue;
1236:
1237: cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path)) +
1238: generate_crc32c(CONST_BUF_LEN(host->host)) + /* we can cache this */
1239: generate_crc32c(CONST_BUF_LEN(con->uri.authority));
1240:
1241: if (p->conf.debug) {
1242: log_error_write(srv, __FILE__, __LINE__, "sbbbd",
1243: "proxy - election:",
1244: con->uri.path,
1245: host->host,
1246: con->uri.authority,
1247: cur_max);
1248: }
1249:
1250: if ((last_max == ULONG_MAX) || /* first round */
1251: (cur_max > last_max)) {
1252: last_max = cur_max;
1253:
1254: ndx = k;
1255: }
1256: }
1257:
1258: break;
1259: case PROXY_BALANCE_FAIR:
1260: /* fair balancing */
1261: if (p->conf.debug) {
1262: log_error_write(srv, __FILE__, __LINE__, "s",
1263: "proxy - used fair balancing");
1264: }
1265:
1266: for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->value->used; k++) {
1267: data_proxy *host = (data_proxy *)extension->value->data[k];
1268:
1269: if (host->is_disabled) continue;
1270:
1271: if (host->usage < max_usage) {
1272: max_usage = host->usage;
1273:
1274: ndx = k;
1275: }
1276: }
1277:
1278: break;
1279: case PROXY_BALANCE_RR: {
1280: data_proxy *host;
1281:
1282: /* round robin */
1283: if (p->conf.debug) {
1284: log_error_write(srv, __FILE__, __LINE__, "s",
1285: "proxy - used round-robin balancing");
1286: }
1287:
1288: /* just to be sure */
1.1.1.2 misho 1289: force_assert(extension->value->used < INT_MAX);
1.1 misho 1290:
1291: host = (data_proxy *)extension->value->data[0];
1292:
1293: /* Use last_used_ndx from first host in list */
1294: k = host->last_used_ndx;
1295: ndx = k + 1; /* use next host after the last one */
1296: if (ndx < 0) ndx = 0;
1297:
1298: /* Search first active host after last_used_ndx */
1299: while ( ndx < (int) extension->value->used
1300: && (host = (data_proxy *)extension->value->data[ndx])->is_disabled ) ndx++;
1301:
1302: if (ndx >= (int) extension->value->used) {
1303: /* didn't found a higher id, wrap to the start */
1304: for (ndx = 0; ndx <= (int) k; ndx++) {
1305: host = (data_proxy *)extension->value->data[ndx];
1306: if (!host->is_disabled) break;
1307: }
1308:
1309: /* No active host found */
1310: if (host->is_disabled) ndx = -1;
1311: }
1312:
1313: /* Save new index for next round */
1314: ((data_proxy *)extension->value->data[0])->last_used_ndx = ndx;
1315:
1316: break;
1317: }
1318: default:
1319: break;
1320: }
1321:
1322: /* found a server */
1323: if (ndx != -1) {
1324: data_proxy *host = (data_proxy *)extension->value->data[ndx];
1325:
1326: /*
1327: * if check-local is disabled, use the uri.path handler
1328: *
1329: */
1330:
1331: /* init handler-context */
1332: handler_ctx *hctx;
1333: hctx = handler_ctx_init();
1334:
1335: hctx->path_info_offset = path_info_offset;
1336: hctx->remote_conn = con;
1337: hctx->plugin_data = p;
1338: hctx->host = host;
1339:
1340: con->plugin_ctx[p->id] = hctx;
1341:
1342: host->usage++;
1343:
1344: con->mode = p->id;
1345:
1346: if (p->conf.debug) {
1347: log_error_write(srv, __FILE__, __LINE__, "sbd",
1348: "proxy - found a host",
1349: host->host, host->port);
1350: }
1351:
1352: return HANDLER_GO_ON;
1353: } else {
1354: /* no handler found */
1355: con->http_status = 500;
1356:
1357: log_error_write(srv, __FILE__, __LINE__, "sb",
1358: "no proxy-handler found for:",
1359: fn);
1360:
1361: return HANDLER_FINISHED;
1362: }
1363: return HANDLER_GO_ON;
1364: }
1365:
1.1.1.3 ! misho 1366: static handler_t mod_proxy_connection_reset(server *srv, connection *con, void *p_d) {
1.1 misho 1367: plugin_data *p = p_d;
1.1.1.3 ! misho 1368: handler_ctx *hctx = con->plugin_ctx[p->id];
! 1369: if (hctx) proxy_connection_close(srv, hctx);
1.1 misho 1370:
1371: return HANDLER_GO_ON;
1372: }
1373:
1374: /**
1375: *
1376: * the trigger re-enables the disabled connections after the timeout is over
1377: *
1378: * */
1379:
1380: TRIGGER_FUNC(mod_proxy_trigger) {
1381: plugin_data *p = p_d;
1382:
1383: if (p->config_storage) {
1384: size_t i, n, k;
1385: for (i = 0; i < srv->config_context->used; i++) {
1386: plugin_config *s = p->config_storage[i];
1387:
1388: if (!s) continue;
1389:
1390: /* get the extensions for all configs */
1391:
1392: for (k = 0; k < s->extensions->used; k++) {
1393: data_array *extension = (data_array *)s->extensions->data[k];
1394:
1395: /* get all hosts */
1396: for (n = 0; n < extension->value->used; n++) {
1397: data_proxy *host = (data_proxy *)extension->value->data[n];
1398:
1399: if (!host->is_disabled ||
1400: srv->cur_ts - host->disable_ts < 5) continue;
1401:
1402: log_error_write(srv, __FILE__, __LINE__, "sbd",
1403: "proxy - re-enabled:",
1404: host->host, host->port);
1405:
1406: host->is_disabled = 0;
1407: }
1408: }
1409: }
1410: }
1411:
1412: return HANDLER_GO_ON;
1413: }
1414:
1415:
1416: int mod_proxy_plugin_init(plugin *p);
1417: int mod_proxy_plugin_init(plugin *p) {
1418: p->version = LIGHTTPD_VERSION_ID;
1419: p->name = buffer_init_string("proxy");
1420:
1421: p->init = mod_proxy_init;
1422: p->cleanup = mod_proxy_free;
1423: p->set_defaults = mod_proxy_set_defaults;
1.1.1.3 ! misho 1424: p->connection_reset = mod_proxy_connection_reset; /* end of req-resp cycle */
! 1425: p->handle_connection_close = mod_proxy_connection_reset; /* end of client connection */
1.1 misho 1426: p->handle_uri_clean = mod_proxy_check_extension;
1427: p->handle_subrequest = mod_proxy_handle_subrequest;
1428: p->handle_trigger = mod_proxy_trigger;
1429:
1430: p->data = NULL;
1431:
1432: return 0;
1433: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>