8 #include "http_chunk.h"
10 #include "connections.h"
16 #include "inet_ntop_cache.h"
19 #include <sys/types.h>
31 #include "sys-socket.h"
33 #define data_proxy data_fastcgi
34 #define data_proxy_init data_fastcgi_init
36 #define PROXY_RETRY_TIMEOUT 60
40 * the proxy module is based on the fastcgi module
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.
49 * TODO: - delay upstream read if write_queue is too large
50 * (to prevent memory eating, like in apache). Shoud be
52 * - persistent connection with upstream servers
66 proxy_balance_t balance
;
72 buffer
*parse_response
;
75 plugin_config
**config_storage
;
83 PROXY_STATE_PREPARE_WRITE
,
86 } proxy_connection_state_t
;
88 enum { PROXY_STDOUT
, PROXY_END_REQUEST
};
91 proxy_connection_state_t state
;
92 time_t state_timestamp
;
97 buffer
*response_header
;
101 int fd
; /* fd to the proxy process */
102 int fde_ndx
; /* index into the fd-event buffer */
104 size_t path_info_offset
; /* start of path_info in uri.path */
106 connection
*remote_conn
; /* dump pointer */
107 plugin_data
*plugin_data
; /* dump pointer */
111 /* ok, we need a prototype */
112 static handler_t
proxy_handle_fdevent(server
*srv
, void *ctx
, int revents
);
114 static handler_ctx
* handler_ctx_init(void) {
118 hctx
= calloc(1, sizeof(*hctx
));
120 hctx
->state
= PROXY_STATE_INIT
;
123 hctx
->response
= buffer_init();
124 hctx
->response_header
= buffer_init();
126 hctx
->wb
= chunkqueue_init();
134 static void handler_ctx_free(handler_ctx
*hctx
) {
135 buffer_free(hctx
->response
);
136 buffer_free(hctx
->response_header
);
137 chunkqueue_free(hctx
->wb
);
142 INIT_FUNC(mod_proxy_init
) {
145 p
= calloc(1, sizeof(*p
));
147 p
->parse_response
= buffer_init();
148 p
->balance_buf
= buffer_init();
154 FREE_FUNC(mod_proxy_free
) {
155 plugin_data
*p
= p_d
;
159 buffer_free(p
->parse_response
);
160 buffer_free(p
->balance_buf
);
162 if (p
->config_storage
) {
164 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
165 plugin_config
*s
= p
->config_storage
[i
];
167 if (NULL
== s
) continue;
169 array_free(s
->extensions
);
173 free(p
->config_storage
);
178 return HANDLER_GO_ON
;
181 SETDEFAULTS_FUNC(mod_proxy_set_defaults
) {
182 plugin_data
*p
= p_d
;
186 config_values_t cv
[] = {
187 { "proxy.server", NULL
, T_CONFIG_LOCAL
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
188 { "proxy.debug", NULL
, T_CONFIG_SHORT
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
189 { "proxy.balance", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 2 */
190 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
193 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
195 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
196 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
199 s
= malloc(sizeof(plugin_config
));
200 s
->extensions
= array_init();
203 cv
[0].destination
= s
->extensions
;
204 cv
[1].destination
= &(s
->debug
);
205 cv
[2].destination
= p
->balance_buf
;
207 buffer_reset(p
->balance_buf
);
209 p
->config_storage
[i
] = s
;
211 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
212 return HANDLER_ERROR
;
215 if (buffer_string_is_empty(p
->balance_buf
)) {
216 s
->balance
= PROXY_BALANCE_FAIR
;
217 } else if (buffer_is_equal_string(p
->balance_buf
, CONST_STR_LEN("fair"))) {
218 s
->balance
= PROXY_BALANCE_FAIR
;
219 } else if (buffer_is_equal_string(p
->balance_buf
, CONST_STR_LEN("round-robin"))) {
220 s
->balance
= PROXY_BALANCE_RR
;
221 } else if (buffer_is_equal_string(p
->balance_buf
, CONST_STR_LEN("hash"))) {
222 s
->balance
= PROXY_BALANCE_HASH
;
224 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
225 "proxy.balance has to be one of: fair, round-robin, hash, but not:", p
->balance_buf
);
226 return HANDLER_ERROR
;
229 if (NULL
!= (du
= array_get_element(config
->value
, "proxy.server"))) {
231 data_array
*da
= (data_array
*)du
;
233 if (du
->type
!= TYPE_ARRAY
) {
234 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
235 "unexpected type for key: ", "proxy.server", "array of strings");
237 return HANDLER_ERROR
;
241 * proxy.server = ( "<ext>" => ...,
245 for (j
= 0; j
< da
->value
->used
; j
++) {
246 data_array
*da_ext
= (data_array
*)da
->value
->data
[j
];
249 if (da_ext
->type
!= TYPE_ARRAY
) {
250 log_error_write(srv
, __FILE__
, __LINE__
, "sssbs",
251 "unexpected type for key: ", "proxy.server",
252 "[", da
->value
->data
[j
]->key
, "](string)");
254 return HANDLER_ERROR
;
258 * proxy.server = ( "<ext>" =>
259 * ( "<host>" => ( ... ),
260 * "<host>" => ( ... )
265 for (n
= 0; n
< da_ext
->value
->used
; n
++) {
266 data_array
*da_host
= (data_array
*)da_ext
->value
->data
[n
];
271 config_values_t pcv
[] = {
272 { "host", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
273 { "port", NULL
, T_CONFIG_SHORT
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
274 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
277 if (da_host
->type
!= TYPE_ARRAY
) {
278 log_error_write(srv
, __FILE__
, __LINE__
, "ssSBS",
279 "unexpected type for key:",
281 "[", da_ext
->value
->data
[n
]->key
, "](string)");
283 return HANDLER_ERROR
;
286 df
= data_proxy_init();
290 buffer_copy_buffer(df
->key
, da_host
->key
);
292 pcv
[0].destination
= df
->host
;
293 pcv
[1].destination
= &(df
->port
);
295 if (0 != config_insert_values_internal(srv
, da_host
->value
, pcv
, T_CONFIG_SCOPE_CONNECTION
)) {
296 df
->free((data_unset
*) df
);
297 return HANDLER_ERROR
;
300 if (buffer_string_is_empty(df
->host
)) {
301 log_error_write(srv
, __FILE__
, __LINE__
, "sbbbs",
302 "missing key (string):",
308 df
->free((data_unset
*) df
);
309 return HANDLER_ERROR
;
312 /* if extension already exists, take it */
314 if (NULL
== (dfa
= (data_array
*)array_get_element(s
->extensions
, da_ext
->key
->ptr
))) {
315 dfa
= data_array_init();
317 buffer_copy_buffer(dfa
->key
, da_ext
->key
);
319 array_insert_unique(dfa
->value
, (data_unset
*)df
);
320 array_insert_unique(s
->extensions
, (data_unset
*)dfa
);
322 array_insert_unique(dfa
->value
, (data_unset
*)df
);
329 return HANDLER_GO_ON
;
332 static void proxy_connection_close(server
*srv
, handler_ctx
*hctx
) {
336 p
= hctx
->plugin_data
;
337 con
= hctx
->remote_conn
;
339 if (hctx
->fd
!= -1) {
340 fdevent_event_del(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
);
341 fdevent_unregister(srv
->ev
, hctx
->fd
);
351 handler_ctx_free(hctx
);
352 con
->plugin_ctx
[p
->id
] = NULL
;
354 /* finish response (if not already finished) */
355 if (con
->mode
== p
->id
356 && (con
->state
== CON_STATE_HANDLE_REQUEST
|| con
->state
== CON_STATE_READ_POST
)) {
357 /* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
358 * i.e. not called from proxy_connection_reset()) */
360 /* Send an error if we haven't sent any data yet */
361 if (0 == con
->file_started
) {
362 con
->http_status
= 500;
365 else if (!con
->file_finished
) {
366 http_chunk_close(srv
, con
);
367 con
->file_finished
= 1;
372 static int proxy_establish_connection(server
*srv
, handler_ctx
*hctx
) {
373 struct sockaddr
*proxy_addr
;
374 struct sockaddr_in proxy_addr_in
;
375 #if defined(HAVE_SYS_UN_H)
376 struct sockaddr_un proxy_addr_un
;
378 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
379 struct sockaddr_in6 proxy_addr_in6
;
383 plugin_data
*p
= hctx
->plugin_data
;
384 data_proxy
*host
= hctx
->host
;
385 int proxy_fd
= hctx
->fd
;
388 #if defined(HAVE_SYS_UN_H)
389 if (strstr(host
->host
->ptr
, "/")) {
390 if (buffer_string_length(host
->host
) + 1 > sizeof(proxy_addr_un
.sun_path
)) {
391 log_error_write(srv
, __FILE__
, __LINE__
, "sB",
392 "ERROR: Unix Domain socket filename too long:",
397 memset(&proxy_addr_un
, 0, sizeof(proxy_addr_un
));
398 proxy_addr_un
.sun_family
= AF_UNIX
;
399 strcpy(proxy_addr_un
.sun_path
, host
->host
->ptr
);
400 servlen
= sizeof(proxy_addr_un
);
401 proxy_addr
= (struct sockaddr
*) &proxy_addr_un
;
404 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
405 if (strstr(host
->host
->ptr
, ":")) {
406 memset(&proxy_addr_in6
, 0, sizeof(proxy_addr_in6
));
407 proxy_addr_in6
.sin6_family
= AF_INET6
;
408 inet_pton(AF_INET6
, host
->host
->ptr
, (char *) &proxy_addr_in6
.sin6_addr
);
409 proxy_addr_in6
.sin6_port
= htons(host
->port
);
410 servlen
= sizeof(proxy_addr_in6
);
411 proxy_addr
= (struct sockaddr
*) &proxy_addr_in6
;
415 memset(&proxy_addr_in
, 0, sizeof(proxy_addr_in
));
416 proxy_addr_in
.sin_family
= AF_INET
;
417 proxy_addr_in
.sin_addr
.s_addr
= inet_addr(host
->host
->ptr
);
418 proxy_addr_in
.sin_port
= htons(host
->port
);
419 servlen
= sizeof(proxy_addr_in
);
420 proxy_addr
= (struct sockaddr
*) &proxy_addr_in
;
424 if (-1 == connect(proxy_fd
, proxy_addr
, servlen
)) {
425 if (errno
== EINPROGRESS
|| errno
== EALREADY
) {
427 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
428 "connect delayed:", proxy_fd
);
434 log_error_write(srv
, __FILE__
, __LINE__
, "sdsd",
435 "connect failed:", proxy_fd
, strerror(errno
), errno
);
441 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
442 "connect succeeded: ", proxy_fd
);
448 static void proxy_set_header(connection
*con
, const char *key
, const char *value
) {
451 if (NULL
== (ds_dst
= (data_string
*)array_get_unused_element(con
->request
.headers
, TYPE_STRING
))) {
452 ds_dst
= data_string_init();
455 buffer_copy_string(ds_dst
->key
, key
);
456 buffer_copy_string(ds_dst
->value
, value
);
457 array_insert_unique(con
->request
.headers
, (data_unset
*)ds_dst
);
460 static void proxy_append_header(connection
*con
, const char *key
, const char *value
) {
463 if (NULL
== (ds_dst
= (data_string
*)array_get_unused_element(con
->request
.headers
, TYPE_STRING
))) {
464 ds_dst
= data_string_init();
467 buffer_copy_string(ds_dst
->key
, key
);
468 buffer_append_string(ds_dst
->value
, value
);
469 array_insert_unique(con
->request
.headers
, (data_unset
*)ds_dst
);
473 static int proxy_create_env(server
*srv
, handler_ctx
*hctx
) {
476 connection
*con
= hctx
->remote_conn
;
484 buffer_copy_string(b
, get_http_method_name(con
->request
.http_method
));
485 buffer_append_string_len(b
, CONST_STR_LEN(" "));
487 buffer_append_string_buffer(b
, con
->request
.uri
);
488 buffer_append_string_len(b
, CONST_STR_LEN(" HTTP/1.0\r\n"));
490 proxy_append_header(con
, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv
, &(con
->dst_addr
)));
491 /* http_host is NOT is just a pointer to a buffer
492 * which is NULL if it is not set */
493 if (!buffer_string_is_empty(con
->request
.http_host
)) {
494 proxy_set_header(con
, "X-Host", con
->request
.http_host
->ptr
);
496 proxy_set_header(con
, "X-Forwarded-Proto", con
->uri
.scheme
->ptr
);
499 for (i
= 0; i
< con
->request
.headers
->used
; i
++) {
502 ds
= (data_string
*)con
->request
.headers
->data
[i
];
504 if (!buffer_is_empty(ds
->value
) && !buffer_is_empty(ds
->key
)) {
505 if (buffer_is_equal_caseless_string(ds
->key
, CONST_STR_LEN("Connection"))) continue;
506 if (buffer_is_equal_caseless_string(ds
->key
, CONST_STR_LEN("Proxy-Connection"))) continue;
508 buffer_append_string_buffer(b
, ds
->key
);
509 buffer_append_string_len(b
, CONST_STR_LEN(": "));
510 buffer_append_string_buffer(b
, ds
->value
);
511 buffer_append_string_len(b
, CONST_STR_LEN("\r\n"));
515 buffer_append_string_len(b
, CONST_STR_LEN("Connection: close\r\n\r\n"));
517 chunkqueue_append_buffer(hctx
->wb
, b
);
522 if (con
->request
.content_length
) {
523 chunkqueue
*req_cq
= con
->request_content_queue
;
525 chunkqueue_steal(hctx
->wb
, req_cq
, req_cq
->bytes_in
);
531 static int proxy_set_state(server
*srv
, handler_ctx
*hctx
, proxy_connection_state_t state
) {
533 hctx
->state_timestamp
= srv
->cur_ts
;
539 static int proxy_response_parse(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*in
) {
541 int http_response_status
= -1;
545 /* [\r]\n -> [\0]\0 */
547 buffer_copy_buffer(p
->parse_response
, in
);
549 for (s
= p
->parse_response
->ptr
; NULL
!= (ns
= strchr(s
, '\n')); s
= ns
+ 1) {
556 if (s
!= ns
&& ns
[-1] == '\r') ns
[-1] = '\0';
558 if (-1 == http_response_status
) {
559 /* The first line of a Response message is the Status-Line */
561 for (key
=s
; *key
&& *key
!= ' '; key
++);
564 http_response_status
= (int) strtol(key
, NULL
, 10);
565 if (http_response_status
< 100 || http_response_status
>= 1000) http_response_status
= 502;
567 http_response_status
= 502;
570 con
->http_status
= http_response_status
;
571 con
->parsed_response
|= HTTP_STATUS
;
575 if (NULL
== (value
= strchr(s
, ':'))) {
576 /* now we expect: "<key>: <value>\n" */
582 key_len
= value
- key
;
586 while (*value
== ' ' || *value
== '\t') value
++;
592 if (0 == strncasecmp(key
, "Date", key_len
)) {
593 con
->parsed_response
|= HTTP_DATE
;
597 if (0 == strncasecmp(key
, "Location", key_len
)) {
598 con
->parsed_response
|= HTTP_LOCATION
;
602 if (0 == strncasecmp(key
, "Connection", key_len
)) {
607 if (0 == strncasecmp(key
, "Content-Length", key_len
)) {
608 con
->response
.content_length
= strtoul(value
, NULL
, 10);
609 con
->parsed_response
|= HTTP_CONTENT_LENGTH
;
617 if (NULL
== (ds
= (data_string
*)array_get_unused_element(con
->response
.headers
, TYPE_STRING
))) {
618 ds
= data_response_init();
620 buffer_copy_string_len(ds
->key
, key
, key_len
);
621 buffer_copy_string(ds
->value
, value
);
623 array_insert_unique(con
->response
.headers
, (data_unset
*)ds
);
631 static int proxy_demux_response(server
*srv
, handler_ctx
*hctx
) {
636 plugin_data
*p
= hctx
->plugin_data
;
637 connection
*con
= hctx
->remote_conn
;
638 int proxy_fd
= hctx
->fd
;
640 /* check how much we have to read */
641 if (ioctl(hctx
->fd
, FIONREAD
, &b
)) {
642 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
650 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
651 "proxy - have to read:", b
);
655 buffer_string_prepare_append(hctx
->response
, b
);
657 if (-1 == (r
= read(hctx
->fd
, hctx
->response
->ptr
+ buffer_string_length(hctx
->response
), buffer_string_space(hctx
->response
)))) {
658 if (errno
== EAGAIN
) return 0;
659 log_error_write(srv
, __FILE__
, __LINE__
, "sds",
660 "unexpected end-of-file (perhaps the proxy process died):",
661 proxy_fd
, strerror(errno
));
665 /* this should be catched by the b > 0 above */
668 buffer_commit(hctx
->response
, r
);
671 log_error_write(srv
, __FILE__
, __LINE__
, "sdsbs",
672 "demux: Response buffer len", hctx
->response
->used
, ":", hctx
->response
, ":");
675 if (0 == con
->got_response
) {
676 con
->got_response
= 1;
677 buffer_string_prepare_copy(hctx
->response_header
, 1023);
680 if (0 == con
->file_started
) {
683 /* search for the \r\n\r\n in the string */
684 if (NULL
!= (c
= buffer_search_string_len(hctx
->response
, CONST_STR_LEN("\r\n\r\n")))) {
685 size_t hlen
= c
- hctx
->response
->ptr
+ 4;
686 size_t blen
= buffer_string_length(hctx
->response
) - hlen
;
689 buffer_append_string_len(hctx
->response_header
, hctx
->response
->ptr
, hlen
);
691 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Header:", hctx
->response_header
);
693 /* parse the response header */
694 proxy_response_parse(srv
, con
, p
, hctx
->response_header
);
696 /* enable chunked-transfer-encoding */
697 if (con
->request
.http_version
== HTTP_VERSION_1_1
&&
698 !(con
->parsed_response
& HTTP_CONTENT_LENGTH
)) {
699 con
->response
.transfer_encoding
= HTTP_TRANSFER_ENCODING_CHUNKED
;
702 con
->file_started
= 1;
703 if (blen
> 0) http_chunk_append_mem(srv
, con
, c
+ 4, blen
);
704 buffer_reset(hctx
->response
);
707 http_chunk_append_buffer(srv
, con
, hctx
->response
);
708 buffer_reset(hctx
->response
);
712 /* reading from upstream done */
713 con
->file_finished
= 1;
715 http_chunk_close(srv
, con
);
724 static handler_t
proxy_write_request(server
*srv
, handler_ctx
*hctx
) {
725 data_proxy
*host
= hctx
->host
;
726 connection
*con
= hctx
->remote_conn
;
730 if (!host
|| buffer_string_is_empty(host
->host
) || !host
->port
) return HANDLER_ERROR
;
732 switch(hctx
->state
) {
733 case PROXY_STATE_CONNECT
:
734 /* wait for the connect() to finish */
736 /* connect failed ? */
737 if (-1 == hctx
->fde_ndx
) return HANDLER_ERROR
;
740 return HANDLER_WAIT_FOR_EVENT
;
742 case PROXY_STATE_INIT
:
743 #if defined(HAVE_SYS_UN_H)
744 if (strstr(host
->host
->ptr
,"/")) {
745 if (-1 == (hctx
->fd
= socket(AF_UNIX
, SOCK_STREAM
, 0))) {
746 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "socket failed: ", strerror(errno
));
747 return HANDLER_ERROR
;
751 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
752 if (strstr(host
->host
->ptr
,":")) {
753 if (-1 == (hctx
->fd
= socket(AF_INET6
, SOCK_STREAM
, 0))) {
754 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "socket failed: ", strerror(errno
));
755 return HANDLER_ERROR
;
760 if (-1 == (hctx
->fd
= socket(AF_INET
, SOCK_STREAM
, 0))) {
761 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "socket failed: ", strerror(errno
));
762 return HANDLER_ERROR
;
769 fdevent_register(srv
->ev
, hctx
->fd
, proxy_handle_fdevent
, hctx
);
771 if (-1 == fdevent_fcntl_set(srv
->ev
, hctx
->fd
)) {
772 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "fcntl failed: ", strerror(errno
));
774 return HANDLER_ERROR
;
777 switch (proxy_establish_connection(srv
, hctx
)) {
779 proxy_set_state(srv
, hctx
, PROXY_STATE_CONNECT
);
781 /* connection is in progress, wait for an event and call getsockopt() below */
783 fdevent_event_set(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_OUT
);
785 return HANDLER_WAIT_FOR_EVENT
;
787 /* if ECONNREFUSED choose another connection -> FIXME */
790 return HANDLER_ERROR
;
792 /* everything is ok, go on */
793 proxy_set_state(srv
, hctx
, PROXY_STATE_PREPARE_WRITE
);
799 case PROXY_STATE_PREPARE_WRITE
:
800 proxy_create_env(srv
, hctx
);
802 proxy_set_state(srv
, hctx
, PROXY_STATE_WRITE
);
805 case PROXY_STATE_WRITE
:;
806 ret
= srv
->network_backend_write(srv
, con
, hctx
->fd
, hctx
->wb
, MAX_WRITE_LIMIT
);
808 chunkqueue_remove_finished_chunks(hctx
->wb
);
810 if (-1 == ret
) { /* error on our side */
811 log_error_write(srv
, __FILE__
, __LINE__
, "ssd", "write failed:", strerror(errno
), errno
);
813 return HANDLER_ERROR
;
814 } else if (-2 == ret
) { /* remote close */
815 log_error_write(srv
, __FILE__
, __LINE__
, "ssd", "write failed, remote connection close:", strerror(errno
), errno
);
817 return HANDLER_ERROR
;
820 if (hctx
->wb
->bytes_out
== hctx
->wb
->bytes_in
) {
821 proxy_set_state(srv
, hctx
, PROXY_STATE_READ
);
822 fdevent_event_set(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_IN
);
824 fdevent_event_set(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_IN
|FDEVENT_OUT
);
827 return HANDLER_WAIT_FOR_EVENT
;
828 case PROXY_STATE_READ
:
829 /* waiting for a response */
830 return HANDLER_WAIT_FOR_EVENT
;
832 log_error_write(srv
, __FILE__
, __LINE__
, "s", "(debug) unknown state");
833 return HANDLER_ERROR
;
839 static int mod_proxy_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
841 plugin_config
*s
= p
->config_storage
[0];
847 /* skip the first, the global context */
848 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
849 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
850 s
= p
->config_storage
[i
];
852 /* condition didn't match */
853 if (!config_check_cond(srv
, con
, dc
)) continue;
856 for (j
= 0; j
< dc
->value
->used
; j
++) {
857 data_unset
*du
= dc
->value
->data
[j
];
859 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("proxy.server"))) {
861 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("proxy.debug"))) {
863 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("proxy.balance"))) {
873 static handler_t
proxy_send_request(server
*srv
, handler_ctx
*hctx
) {
874 /* ok, create the request */
875 handler_t rc
= proxy_write_request(srv
, hctx
);
876 if (HANDLER_ERROR
!= rc
) {
879 data_proxy
*host
= hctx
->host
;
880 connection
*con
= hctx
->remote_conn
;
881 log_error_write(srv
, __FILE__
, __LINE__
, "sbdd", "proxy-server disabled:",
886 /* disable this server */
887 host
->is_disabled
= 1;
888 host
->disable_ts
= srv
->cur_ts
;
890 /* reset the enviroment and restart the sub-request */
891 con
->mode
= DIRECT
;/*(avoid changing con->state, con->http_status)*/
892 proxy_connection_close(srv
, hctx
);
893 con
->mode
= hctx
->plugin_data
->id
; /* p->id */
895 return HANDLER_COMEBACK
;
899 SUBREQUEST_FUNC(mod_proxy_handle_subrequest
) {
900 plugin_data
*p
= p_d
;
902 handler_ctx
*hctx
= con
->plugin_ctx
[p
->id
];
904 if (NULL
== hctx
) return HANDLER_GO_ON
;
907 if (con
->mode
!= p
->id
) return HANDLER_GO_ON
;
909 if (con
->state
== CON_STATE_READ_POST
) {
910 handler_t r
= connection_handle_read_post_state(srv
, con
);
911 if (r
!= HANDLER_GO_ON
) return r
;
914 return (hctx
->state
!= PROXY_STATE_READ
)
915 ? proxy_send_request(srv
, hctx
)
916 : HANDLER_WAIT_FOR_EVENT
;
919 static handler_t
proxy_handle_fdevent(server
*srv
, void *ctx
, int revents
) {
920 handler_ctx
*hctx
= ctx
;
921 connection
*con
= hctx
->remote_conn
;
922 plugin_data
*p
= hctx
->plugin_data
;
924 joblist_append(srv
, con
);
926 if ((revents
& FDEVENT_IN
) &&
927 hctx
->state
== PROXY_STATE_READ
) {
930 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
931 "proxy: fdevent-in", hctx
->state
);
934 switch (proxy_demux_response(srv
, hctx
)) {
939 proxy_connection_close(srv
, hctx
);
941 return HANDLER_FINISHED
;
943 if (con
->file_started
== 0) {
944 /* reading response headers failed */
946 /* response might have been already started, kill the connection */
948 con
->file_finished
= 1;
949 con
->mode
= DIRECT
; /*(avoid sending final chunked block)*/
952 proxy_connection_close(srv
, hctx
);
954 return HANDLER_FINISHED
;
958 if (revents
& FDEVENT_OUT
) {
960 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
961 "proxy: fdevent-out", hctx
->state
);
964 if (hctx
->state
== PROXY_STATE_CONNECT
) {
966 socklen_t socket_error_len
= sizeof(socket_error
);
968 /* try to finish the connect() */
969 if (0 != getsockopt(hctx
->fd
, SOL_SOCKET
, SO_ERROR
, &socket_error
, &socket_error_len
)) {
970 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
971 "getsockopt failed:", strerror(errno
));
973 return HANDLER_FINISHED
;
975 if (socket_error
!= 0) {
976 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
977 "establishing connection failed:", strerror(socket_error
),
978 "port:", hctx
->host
->port
);
980 return HANDLER_FINISHED
;
983 log_error_write(srv
, __FILE__
, __LINE__
, "s", "proxy - connect - delayed success");
986 proxy_set_state(srv
, hctx
, PROXY_STATE_PREPARE_WRITE
);
989 if (hctx
->state
== PROXY_STATE_PREPARE_WRITE
||
990 hctx
->state
== PROXY_STATE_WRITE
) {
991 /* we are allowed to send something out
993 * 1. after a just finished connect() call
994 * 2. in a unfinished write() call (long POST request)
996 return proxy_send_request(srv
, hctx
); /*(might invalidate hctx)*/
998 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
999 "proxy: out", hctx
->state
);
1003 /* perhaps this issue is already handled */
1004 if (revents
& FDEVENT_HUP
) {
1005 if (p
->conf
.debug
) {
1006 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
1007 "proxy: fdevent-hup", hctx
->state
);
1010 if (hctx
->state
== PROXY_STATE_CONNECT
) {
1011 /* connect() -> EINPROGRESS -> HUP */
1014 * what is proxy is doing if it can't reach the next hop ?
1019 hctx
->host
->is_disabled
= 1;
1020 hctx
->host
->disable_ts
= srv
->cur_ts
;
1021 log_error_write(srv
, __FILE__
, __LINE__
, "sbdd", "proxy-server disabled:",
1026 /* disable this server */
1027 hctx
->host
->is_disabled
= 1;
1028 hctx
->host
->disable_ts
= srv
->cur_ts
;
1030 /* reset the environment and restart the sub-request */
1031 con
->mode
= DIRECT
;/*(avoid changing con->state, con->http_status)*/
1032 proxy_connection_close(srv
, hctx
);
1035 proxy_connection_close(srv
, hctx
);
1036 con
->http_status
= 503;
1039 proxy_connection_close(srv
, hctx
);
1041 } else if (revents
& FDEVENT_ERR
) {
1042 log_error_write(srv
, __FILE__
, __LINE__
, "sd", "proxy-FDEVENT_ERR, but no HUP", revents
);
1044 if (con
->file_started
) {
1045 con
->keep_alive
= 0;
1046 con
->file_finished
= 1;
1047 con
->mode
= DIRECT
; /*(avoid sending final chunked block)*/
1049 proxy_connection_close(srv
, hctx
);
1052 return HANDLER_FINISHED
;
1055 static handler_t
mod_proxy_check_extension(server
*srv
, connection
*con
, void *p_d
) {
1056 plugin_data
*p
= p_d
;
1058 unsigned long last_max
= ULONG_MAX
;
1059 int max_usage
= INT_MAX
;
1063 data_array
*extension
= NULL
;
1064 size_t path_info_offset
;
1066 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
1068 /* Possibly, we processed already this request */
1069 if (con
->file_started
== 1) return HANDLER_GO_ON
;
1071 mod_proxy_patch_connection(srv
, con
, p
);
1074 if (buffer_string_is_empty(fn
)) return HANDLER_ERROR
;
1075 s_len
= buffer_string_length(fn
);
1077 path_info_offset
= 0;
1079 if (p
->conf
.debug
) {
1080 log_error_write(srv
, __FILE__
, __LINE__
, "s", "proxy - start");
1083 /* check if extension matches */
1084 for (k
= 0; k
< p
->conf
.extensions
->used
; k
++) {
1085 data_array
*ext
= NULL
;
1088 ext
= (data_array
*)p
->conf
.extensions
->data
[k
];
1090 if (buffer_is_empty(ext
->key
)) continue;
1092 ct_len
= buffer_string_length(ext
->key
);
1094 if (s_len
< ct_len
) continue;
1096 /* check extension in the form "/proxy_pattern" */
1097 if (*(ext
->key
->ptr
) == '/') {
1098 if (strncmp(fn
->ptr
, ext
->key
->ptr
, ct_len
) == 0) {
1099 if (s_len
> ct_len
+ 1) {
1102 if (NULL
!= (pi_offset
= strchr(fn
->ptr
+ ct_len
+ 1, '/'))) {
1103 path_info_offset
= pi_offset
- fn
->ptr
;
1109 } else if (0 == strncmp(fn
->ptr
+ s_len
- ct_len
, ext
->key
->ptr
, ct_len
)) {
1110 /* check extension in the form ".fcg" */
1116 if (NULL
== extension
) {
1117 return HANDLER_GO_ON
;
1120 if (p
->conf
.debug
) {
1121 log_error_write(srv
, __FILE__
, __LINE__
, "s", "proxy - ext found");
1124 if (extension
->value
->used
== 1) {
1125 if ( ((data_proxy
*)extension
->value
->data
[0])->is_disabled
) {
1130 } else if (extension
->value
->used
!= 0) switch(p
->conf
.balance
) {
1131 case PROXY_BALANCE_HASH
:
1132 /* hash balancing */
1134 if (p
->conf
.debug
) {
1135 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
1136 "proxy - used hash balancing, hosts:", extension
->value
->used
);
1139 for (k
= 0, ndx
= -1, last_max
= ULONG_MAX
; k
< extension
->value
->used
; k
++) {
1140 data_proxy
*host
= (data_proxy
*)extension
->value
->data
[k
];
1141 unsigned long cur_max
;
1143 if (host
->is_disabled
) continue;
1145 cur_max
= generate_crc32c(CONST_BUF_LEN(con
->uri
.path
)) +
1146 generate_crc32c(CONST_BUF_LEN(host
->host
)) + /* we can cache this */
1147 generate_crc32c(CONST_BUF_LEN(con
->uri
.authority
));
1149 if (p
->conf
.debug
) {
1150 log_error_write(srv
, __FILE__
, __LINE__
, "sbbbd",
1151 "proxy - election:",
1158 if ((last_max
== ULONG_MAX
) || /* first round */
1159 (cur_max
> last_max
)) {
1167 case PROXY_BALANCE_FAIR
:
1168 /* fair balancing */
1169 if (p
->conf
.debug
) {
1170 log_error_write(srv
, __FILE__
, __LINE__
, "s",
1171 "proxy - used fair balancing");
1174 for (k
= 0, ndx
= -1, max_usage
= INT_MAX
; k
< extension
->value
->used
; k
++) {
1175 data_proxy
*host
= (data_proxy
*)extension
->value
->data
[k
];
1177 if (host
->is_disabled
) continue;
1179 if (host
->usage
< max_usage
) {
1180 max_usage
= host
->usage
;
1187 case PROXY_BALANCE_RR
: {
1191 if (p
->conf
.debug
) {
1192 log_error_write(srv
, __FILE__
, __LINE__
, "s",
1193 "proxy - used round-robin balancing");
1196 /* just to be sure */
1197 force_assert(extension
->value
->used
< INT_MAX
);
1199 host
= (data_proxy
*)extension
->value
->data
[0];
1201 /* Use last_used_ndx from first host in list */
1202 k
= host
->last_used_ndx
;
1203 ndx
= k
+ 1; /* use next host after the last one */
1204 if (ndx
< 0) ndx
= 0;
1206 /* Search first active host after last_used_ndx */
1207 while ( ndx
< (int) extension
->value
->used
1208 && (host
= (data_proxy
*)extension
->value
->data
[ndx
])->is_disabled
) ndx
++;
1210 if (ndx
>= (int) extension
->value
->used
) {
1211 /* didn't found a higher id, wrap to the start */
1212 for (ndx
= 0; ndx
<= (int) k
; ndx
++) {
1213 host
= (data_proxy
*)extension
->value
->data
[ndx
];
1214 if (!host
->is_disabled
) break;
1217 /* No active host found */
1218 if (host
->is_disabled
) ndx
= -1;
1221 /* Save new index for next round */
1222 ((data_proxy
*)extension
->value
->data
[0])->last_used_ndx
= ndx
;
1230 /* found a server */
1232 data_proxy
*host
= (data_proxy
*)extension
->value
->data
[ndx
];
1235 * if check-local is disabled, use the uri.path handler
1239 /* init handler-context */
1241 hctx
= handler_ctx_init();
1243 hctx
->path_info_offset
= path_info_offset
;
1244 hctx
->remote_conn
= con
;
1245 hctx
->plugin_data
= p
;
1248 con
->plugin_ctx
[p
->id
] = hctx
;
1254 if (p
->conf
.debug
) {
1255 log_error_write(srv
, __FILE__
, __LINE__
, "sbd",
1256 "proxy - found a host",
1257 host
->host
, host
->port
);
1260 return HANDLER_GO_ON
;
1262 /* no handler found */
1263 con
->http_status
= 500;
1265 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
1266 "no proxy-handler found for:",
1269 return HANDLER_FINISHED
;
1271 return HANDLER_GO_ON
;
1274 static handler_t
mod_proxy_connection_reset(server
*srv
, connection
*con
, void *p_d
) {
1275 plugin_data
*p
= p_d
;
1276 handler_ctx
*hctx
= con
->plugin_ctx
[p
->id
];
1277 if (hctx
) proxy_connection_close(srv
, hctx
);
1279 return HANDLER_GO_ON
;
1284 * the trigger re-enables the disabled connections after the timeout is over
1288 TRIGGER_FUNC(mod_proxy_trigger
) {
1289 plugin_data
*p
= p_d
;
1291 if (p
->config_storage
) {
1293 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
1294 plugin_config
*s
= p
->config_storage
[i
];
1298 /* get the extensions for all configs */
1300 for (k
= 0; k
< s
->extensions
->used
; k
++) {
1301 data_array
*extension
= (data_array
*)s
->extensions
->data
[k
];
1304 for (n
= 0; n
< extension
->value
->used
; n
++) {
1305 data_proxy
*host
= (data_proxy
*)extension
->value
->data
[n
];
1307 if (!host
->is_disabled
||
1308 srv
->cur_ts
- host
->disable_ts
< 5) continue;
1310 log_error_write(srv
, __FILE__
, __LINE__
, "sbd",
1311 "proxy - re-enabled:",
1312 host
->host
, host
->port
);
1314 host
->is_disabled
= 0;
1320 return HANDLER_GO_ON
;
1324 int mod_proxy_plugin_init(plugin
*p
);
1325 int mod_proxy_plugin_init(plugin
*p
) {
1326 p
->version
= LIGHTTPD_VERSION_ID
;
1327 p
->name
= buffer_init_string("proxy");
1329 p
->init
= mod_proxy_init
;
1330 p
->cleanup
= mod_proxy_free
;
1331 p
->set_defaults
= mod_proxy_set_defaults
;
1332 p
->connection_reset
= mod_proxy_connection_reset
; /* end of req-resp cycle */
1333 p
->handle_connection_close
= mod_proxy_connection_reset
; /* end of client connection */
1334 p
->handle_uri_clean
= mod_proxy_check_extension
;
1335 p
->handle_subrequest
= mod_proxy_handle_subrequest
;
1336 p
->handle_trigger
= mod_proxy_trigger
;