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