1: #include "first.h"
2:
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,
85: PROXY_STATE_READ
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;
100: off_t wb_reqlen;
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();
128: hctx->wb_reqlen = 0;
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:
169: if (NULL == s) continue;
170:
171: array_free(s->extensions);
172:
173: free(s);
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:
195: p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
196:
197: for (i = 0; i < srv->config_context->used; i++) {
198: data_config const* config = (data_config const*)srv->config_context->data[i];
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:
213: if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
214: return HANDLER_ERROR;
215: }
216:
217: if (buffer_string_is_empty(p->balance_buf)) {
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:
231: if (NULL != (du = array_get_element(config->value, "proxy.server"))) {
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",
237: "unexpected type for key: ", "proxy.server", "expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
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",
254: "[", da->value->data[j]->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
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",
283: "[", da_ext->value->data[n]->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
284:
285: return HANDLER_ERROR;
286: }
287:
288: df = data_proxy_init();
289:
290: df->port = 80;
291:
292: buffer_copy_buffer(df->key, da_host->key);
293:
294: pcv[0].destination = df->host;
295: pcv[1].destination = &(df->port);
296:
297: if (0 != config_insert_values_internal(srv, da_host->value, pcv, T_CONFIG_SCOPE_CONNECTION)) {
298: df->free((data_unset*) df);
299: return HANDLER_ERROR;
300: }
301:
302: if (buffer_string_is_empty(df->host)) {
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:
310: df->free((data_unset*) df);
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:
319: buffer_copy_buffer(dfa->key, da_ext->key);
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;
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: }
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;
365: #if defined(HAVE_SYS_UN_H)
366: struct sockaddr_un proxy_addr_un;
367: #endif
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:
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
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) {
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: }
444:
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);
448: }
449:
450: static void proxy_append_header(connection *con, const char *key, const char *value) {
451: data_string *ds_dst;
452:
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);
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:
471: b = buffer_init();
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 */
483: if (!buffer_string_is_empty(con->request.http_host)) {
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:
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;
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:
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);
514:
515: /* body */
516:
517: if (con->request.content_length) {
518: chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue);
519: hctx->wb_reqlen += con->request.content_length;/* (eventual) total request size */
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:
539: /* [\r]\n -> [\0]\0 */
540:
541: buffer_copy_buffer(p->parse_response, in);
542:
543: for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1) {
544: char *key, *value;
545: int key_len;
546: data_string *ds;
547: int copy_header;
548:
549: ns[0] = '\0';
550: if (s != ns && ns[-1] == '\r') ns[-1] = '\0';
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);
559: if (http_response_status < 100 || http_response_status >= 1000) http_response_status = 502;
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)) {
602: con->response.content_length = strtoul(value, NULL, 10);
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 */
635: #if !defined(_WIN32) && !defined(__CYGWIN__)
636: if (ioctl(hctx->fd, FIONREAD, &b)) {
637: if (errno == EAGAIN) {
638: fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
639: return 0;
640: }
641: log_error_write(srv, __FILE__, __LINE__, "sd",
642: "ioctl failed: ",
643: proxy_fd);
644: return -1;
645: }
646: #else
647: b = 4096;
648: #endif
649:
650:
651: if (p->conf.debug) {
652: log_error_write(srv, __FILE__, __LINE__, "sd",
653: "proxy - have to read:", b);
654: }
655:
656: if (b > 0) {
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: }
671: }
672:
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: }
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:
686: #if defined(_WIN32) || defined(__CYGWIN__)
687: if (0 == r) return 1; /* fin */
688: #endif
689:
690: /* this should be catched by the b > 0 above */
691: force_assert(r);
692:
693: buffer_commit(hctx->response, r);
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;
702: buffer_string_prepare_copy(hctx->response_header, 1023);
703: }
704:
705: if (0 == con->file_started) {
706: char *c;
707:
708: /* search for the \r\n\r\n in the string */
709: if (NULL != (c = buffer_search_string_len(hctx->response, CONST_STR_LEN("\r\n\r\n")))) {
710: size_t hlen = c - hctx->response->ptr + 4;
711: size_t blen = buffer_string_length(hctx->response) - hlen;
712: /* found */
713:
714: buffer_append_string_len(hctx->response_header, hctx->response->ptr, hlen);
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;
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;
739: }
740: }
741: } else {
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);
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:
764: if (!host || buffer_string_is_empty(host->host) || !host->port) return HANDLER_ERROR;
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:
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
785: #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
786: if (strstr(host->host->ptr,":")) {
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: }
791: } else
792: #endif
793: {
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: }
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:
836: fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
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:
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
871: proxy_set_state(srv, hctx, PROXY_STATE_READ);
872: } else {
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: }
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:
934: static handler_t proxy_send_request(server *srv, handler_ctx *hctx) {
935: /* ok, create the request */
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;
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:
952: /* reset the enviroment and restart the sub-request */
953: con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
954: proxy_connection_close(srv, hctx);
955: con->mode = p->id;
956:
957: return HANDLER_COMEBACK;
958: }
959: }
960:
961:
962: static handler_t proxy_recv_response(server *srv, handler_ctx *hctx);
963:
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: }
984: }
985:
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: }
1006: }
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;
1012: }
1013:
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:
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:
1039: joblist_append(srv, con);
1040:
1041: if (revents & FDEVENT_IN) {
1042:
1043: if (p->conf.debug) {
1044: log_error_write(srv, __FILE__, __LINE__, "sd",
1045: "proxy: fdevent-in", hctx->state);
1046: }
1047:
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)*/
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:
1085: return proxy_send_request(srv, hctx); /*(might invalidate hctx)*/
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:
1115: /* reset the environment and restart the sub-request */
1116: con->mode = DIRECT;/*(avoid changing con->state, con->http_status)*/
1117: proxy_connection_close(srv, hctx);
1118: con->mode = p->id;
1119: } else {
1120: proxy_connection_close(srv, hctx);
1121: con->http_status = 503;
1122: }
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);
1136: }
1137: } else if (revents & FDEVENT_ERR) {
1138: log_error_write(srv, __FILE__, __LINE__, "sd", "proxy-FDEVENT_ERR, but no HUP", revents);
1139:
1140: http_response_backend_error(srv, con);
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;
1166: if (buffer_string_is_empty(fn)) return HANDLER_ERROR;
1167: s_len = buffer_string_length(fn);
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:
1182: if (buffer_is_empty(ext->key)) continue;
1183:
1184: ct_len = buffer_string_length(ext->key);
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 */
1289: force_assert(extension->value->used < INT_MAX);
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:
1366: static handler_t mod_proxy_connection_reset(server *srv, connection *con, void *p_d) {
1367: plugin_data *p = p_d;
1368: handler_ctx *hctx = con->plugin_ctx[p->id];
1369: if (hctx) proxy_connection_close(srv, hctx);
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;
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 */
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>