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 unsigned short replace_http_host
;
68 proxy_balance_t balance
;
74 buffer
*parse_response
;
77 plugin_config
**config_storage
;
85 PROXY_STATE_PREPARE_WRITE
,
88 } proxy_connection_state_t
;
90 enum { PROXY_STDOUT
, PROXY_END_REQUEST
};
93 proxy_connection_state_t state
;
94 time_t state_timestamp
;
99 buffer
*response_header
;
104 int fd
; /* fd to the proxy process */
105 int fde_ndx
; /* index into the fd-event buffer */
109 connection
*remote_conn
; /* dumb pointer */
110 plugin_data
*plugin_data
; /* dumb pointer */
115 /* ok, we need a prototype */
116 static handler_t
proxy_handle_fdevent(server
*srv
, void *ctx
, int revents
);
118 static handler_ctx
* handler_ctx_init(void) {
122 hctx
= calloc(1, sizeof(*hctx
));
124 hctx
->state
= PROXY_STATE_INIT
;
127 hctx
->response
= buffer_init();
128 hctx
->response_header
= buffer_init();
130 hctx
->wb
= chunkqueue_init();
139 static void handler_ctx_free(handler_ctx
*hctx
) {
140 buffer_free(hctx
->response
);
141 buffer_free(hctx
->response_header
);
142 chunkqueue_free(hctx
->wb
);
147 INIT_FUNC(mod_proxy_init
) {
150 p
= calloc(1, sizeof(*p
));
152 p
->parse_response
= buffer_init();
153 p
->balance_buf
= buffer_init();
159 FREE_FUNC(mod_proxy_free
) {
160 plugin_data
*p
= p_d
;
164 buffer_free(p
->parse_response
);
165 buffer_free(p
->balance_buf
);
167 if (p
->config_storage
) {
169 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
170 plugin_config
*s
= p
->config_storage
[i
];
172 if (NULL
== s
) continue;
174 array_free(s
->extensions
);
178 free(p
->config_storage
);
183 return HANDLER_GO_ON
;
186 SETDEFAULTS_FUNC(mod_proxy_set_defaults
) {
187 plugin_data
*p
= p_d
;
191 config_values_t cv
[] = {
192 { "proxy.server", NULL
, T_CONFIG_LOCAL
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
193 { "proxy.debug", NULL
, T_CONFIG_SHORT
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
194 { "proxy.balance", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 2 */
195 { "proxy.replace-http-host", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
}, /* 3 */
196 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
199 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
201 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
202 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
205 s
= malloc(sizeof(plugin_config
));
206 s
->extensions
= array_init();
208 s
->replace_http_host
= 0;
210 cv
[0].destination
= s
->extensions
;
211 cv
[1].destination
= &(s
->debug
);
212 cv
[2].destination
= p
->balance_buf
;
213 cv
[3].destination
= &(s
->replace_http_host
);
215 buffer_reset(p
->balance_buf
);
217 p
->config_storage
[i
] = s
;
219 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
220 return HANDLER_ERROR
;
223 if (buffer_string_is_empty(p
->balance_buf
)) {
224 s
->balance
= PROXY_BALANCE_FAIR
;
225 } else if (buffer_is_equal_string(p
->balance_buf
, CONST_STR_LEN("fair"))) {
226 s
->balance
= PROXY_BALANCE_FAIR
;
227 } else if (buffer_is_equal_string(p
->balance_buf
, CONST_STR_LEN("round-robin"))) {
228 s
->balance
= PROXY_BALANCE_RR
;
229 } else if (buffer_is_equal_string(p
->balance_buf
, CONST_STR_LEN("hash"))) {
230 s
->balance
= PROXY_BALANCE_HASH
;
231 } else if (buffer_is_equal_string(p
->balance_buf
, CONST_STR_LEN("sticky"))) {
232 s
->balance
= PROXY_BALANCE_STICKY
;
234 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
235 "proxy.balance has to be one of: fair, round-robin, hash, sticky, but not:", p
->balance_buf
);
236 return HANDLER_ERROR
;
239 if (NULL
!= (du
= array_get_element(config
->value
, "proxy.server"))) {
241 data_array
*da
= (data_array
*)du
;
243 if (du
->type
!= TYPE_ARRAY
) {
244 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
245 "unexpected type for key: ", "proxy.server", "expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
247 return HANDLER_ERROR
;
251 * proxy.server = ( "<ext>" => ...,
255 for (j
= 0; j
< da
->value
->used
; j
++) {
256 data_array
*da_ext
= (data_array
*)da
->value
->data
[j
];
259 if (da_ext
->type
!= TYPE_ARRAY
) {
260 log_error_write(srv
, __FILE__
, __LINE__
, "sssbs",
261 "unexpected type for key: ", "proxy.server",
262 "[", da
->value
->data
[j
]->key
, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
264 return HANDLER_ERROR
;
268 * proxy.server = ( "<ext>" =>
269 * ( "<host>" => ( ... ),
270 * "<host>" => ( ... )
275 for (n
= 0; n
< da_ext
->value
->used
; n
++) {
276 data_array
*da_host
= (data_array
*)da_ext
->value
->data
[n
];
281 config_values_t pcv
[] = {
282 { "host", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
283 { "port", NULL
, T_CONFIG_SHORT
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
284 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
287 if (da_host
->type
!= TYPE_ARRAY
) {
288 log_error_write(srv
, __FILE__
, __LINE__
, "ssSBS",
289 "unexpected type for key:",
291 "[", da_ext
->value
->data
[n
]->key
, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
293 return HANDLER_ERROR
;
296 df
= data_proxy_init();
300 buffer_copy_buffer(df
->key
, da_host
->key
);
302 pcv
[0].destination
= df
->host
;
303 pcv
[1].destination
= &(df
->port
);
305 if (0 != config_insert_values_internal(srv
, da_host
->value
, pcv
, T_CONFIG_SCOPE_CONNECTION
)) {
306 df
->free((data_unset
*) df
);
307 return HANDLER_ERROR
;
310 if (buffer_string_is_empty(df
->host
)) {
311 log_error_write(srv
, __FILE__
, __LINE__
, "sbbbs",
312 "missing key (string):",
318 df
->free((data_unset
*) df
);
319 return HANDLER_ERROR
;
322 /* if extension already exists, take it */
324 if (NULL
== (dfa
= (data_array
*)array_get_element(s
->extensions
, da_ext
->key
->ptr
))) {
325 dfa
= data_array_init();
327 buffer_copy_buffer(dfa
->key
, da_ext
->key
);
329 array_insert_unique(dfa
->value
, (data_unset
*)df
);
330 array_insert_unique(s
->extensions
, (data_unset
*)dfa
);
332 array_insert_unique(dfa
->value
, (data_unset
*)df
);
339 return HANDLER_GO_ON
;
343 static void proxy_backend_close(server
*srv
, handler_ctx
*hctx
) {
344 if (hctx
->fd
!= -1) {
345 fdevent_event_del(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
);
346 fdevent_unregister(srv
->ev
, hctx
->fd
);
347 fdevent_sched_close(srv
->ev
, hctx
->fd
, 1);
358 static data_proxy
* mod_proxy_extension_host_get(server
*srv
, connection
*con
, data_array
*extension
, proxy_balance_t balance
, int debug
) {
359 unsigned long last_max
= ULONG_MAX
;
360 int max_usage
= INT_MAX
;
364 if (extension
->value
->used
== 1) {
365 if ( ((data_proxy
*)extension
->value
->data
[0])->is_disabled
) {
370 } else if (extension
->value
->used
!= 0) switch(balance
) {
371 case PROXY_BALANCE_HASH
:
375 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
376 "proxy - used hash balancing, hosts:", extension
->value
->used
);
379 for (k
= 0, ndx
= -1, last_max
= ULONG_MAX
; k
< extension
->value
->used
; k
++) {
380 data_proxy
*host
= (data_proxy
*)extension
->value
->data
[k
];
381 unsigned long cur_max
;
383 if (host
->is_disabled
) continue;
385 cur_max
= generate_crc32c(CONST_BUF_LEN(con
->uri
.path
)) +
386 generate_crc32c(CONST_BUF_LEN(host
->host
)) + /* we can cache this */
387 generate_crc32c(CONST_BUF_LEN(con
->uri
.authority
));
390 log_error_write(srv
, __FILE__
, __LINE__
, "sbbbd",
398 if ((last_max
== ULONG_MAX
) || /* first round */
399 (cur_max
> last_max
)) {
407 case PROXY_BALANCE_FAIR
:
410 log_error_write(srv
, __FILE__
, __LINE__
, "s",
411 "proxy - used fair balancing");
414 for (k
= 0, ndx
= -1, max_usage
= INT_MAX
; k
< extension
->value
->used
; k
++) {
415 data_proxy
*host
= (data_proxy
*)extension
->value
->data
[k
];
417 if (host
->is_disabled
) continue;
419 if (host
->usage
< max_usage
) {
420 max_usage
= host
->usage
;
427 case PROXY_BALANCE_RR
: {
432 log_error_write(srv
, __FILE__
, __LINE__
, "s",
433 "proxy - used round-robin balancing");
436 /* just to be sure */
437 force_assert(extension
->value
->used
< INT_MAX
);
439 host
= (data_proxy
*)extension
->value
->data
[0];
441 /* Use last_used_ndx from first host in list */
442 k
= host
->last_used_ndx
;
443 ndx
= k
+ 1; /* use next host after the last one */
444 if (ndx
< 0) ndx
= 0;
446 /* Search first active host after last_used_ndx */
447 while ( ndx
< (int) extension
->value
->used
448 && (host
= (data_proxy
*)extension
->value
->data
[ndx
])->is_disabled
) ndx
++;
450 if (ndx
>= (int) extension
->value
->used
) {
451 /* didn't found a higher id, wrap to the start */
452 for (ndx
= 0; ndx
<= (int) k
; ndx
++) {
453 host
= (data_proxy
*)extension
->value
->data
[ndx
];
454 if (!host
->is_disabled
) break;
457 /* No active host found */
458 if (host
->is_disabled
) ndx
= -1;
461 /* Save new index for next round */
462 ((data_proxy
*)extension
->value
->data
[0])->last_used_ndx
= ndx
;
466 case PROXY_BALANCE_STICKY
:
467 /* source sticky balancing */
470 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
471 "proxy - used sticky balancing, hosts:", extension
->value
->used
);
474 for (k
= 0, ndx
= -1, last_max
= ULONG_MAX
; k
< extension
->value
->used
; k
++) {
475 data_proxy
*host
= (data_proxy
*)extension
->value
->data
[k
];
476 unsigned long cur_max
;
478 if (host
->is_disabled
) continue;
480 cur_max
= generate_crc32c(CONST_BUF_LEN(con
->dst_addr_buf
)) +
481 generate_crc32c(CONST_BUF_LEN(host
->host
)) +
485 log_error_write(srv
, __FILE__
, __LINE__
, "sbbdd",
493 if ((last_max
== ULONG_MAX
) || /* first round */
494 (cur_max
> last_max
)) {
508 data_proxy
*host
= (data_proxy
*)extension
->value
->data
[ndx
];
511 log_error_write(srv
, __FILE__
, __LINE__
, "sbd",
512 "proxy - found a host",
513 host
->host
, host
->port
);
519 /* no handler found */
520 con
->http_status
= 503; /* Service Unavailable */
523 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
524 "no proxy-handler found for:",
531 static void proxy_connection_close(server
*srv
, handler_ctx
*hctx
) {
535 p
= hctx
->plugin_data
;
536 con
= hctx
->remote_conn
;
538 proxy_backend_close(srv
, hctx
);
539 handler_ctx_free(hctx
);
540 con
->plugin_ctx
[p
->id
] = NULL
;
542 /* finish response (if not already con->file_started, con->file_finished) */
543 if (con
->mode
== p
->id
) {
544 http_response_backend_done(srv
, con
);
548 static handler_t
proxy_reconnect(server
*srv
, handler_ctx
*hctx
) {
549 proxy_backend_close(srv
, hctx
);
551 hctx
->host
= mod_proxy_extension_host_get(srv
, hctx
->remote_conn
, hctx
->ext
, hctx
->conf
.balance
, (int)hctx
->conf
.debug
);
552 if (NULL
== hctx
->host
) return HANDLER_FINISHED
;
554 hctx
->state
= PROXY_STATE_INIT
;
555 return HANDLER_COMEBACK
;
558 static int proxy_establish_connection(server
*srv
, handler_ctx
*hctx
) {
559 struct sockaddr
*proxy_addr
;
560 struct sockaddr_in proxy_addr_in
;
561 #if defined(HAVE_SYS_UN_H)
562 struct sockaddr_un proxy_addr_un
;
564 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
565 struct sockaddr_in6 proxy_addr_in6
;
569 data_proxy
*host
= hctx
->host
;
570 int proxy_fd
= hctx
->fd
;
573 #if defined(HAVE_SYS_UN_H)
574 if (strstr(host
->host
->ptr
, "/")) {
575 if (buffer_string_length(host
->host
) + 1 > sizeof(proxy_addr_un
.sun_path
)) {
576 log_error_write(srv
, __FILE__
, __LINE__
, "sB",
577 "ERROR: Unix Domain socket filename too long:",
582 memset(&proxy_addr_un
, 0, sizeof(proxy_addr_un
));
583 proxy_addr_un
.sun_family
= AF_UNIX
;
584 memcpy(proxy_addr_un
.sun_path
, host
->host
->ptr
, buffer_string_length(host
->host
) + 1);
585 servlen
= sizeof(proxy_addr_un
);
586 proxy_addr
= (struct sockaddr
*) &proxy_addr_un
;
589 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
590 if (strstr(host
->host
->ptr
, ":")) {
591 memset(&proxy_addr_in6
, 0, sizeof(proxy_addr_in6
));
592 proxy_addr_in6
.sin6_family
= AF_INET6
;
593 inet_pton(AF_INET6
, host
->host
->ptr
, (char *) &proxy_addr_in6
.sin6_addr
);
594 proxy_addr_in6
.sin6_port
= htons(host
->port
);
595 servlen
= sizeof(proxy_addr_in6
);
596 proxy_addr
= (struct sockaddr
*) &proxy_addr_in6
;
600 memset(&proxy_addr_in
, 0, sizeof(proxy_addr_in
));
601 proxy_addr_in
.sin_family
= AF_INET
;
602 proxy_addr_in
.sin_addr
.s_addr
= inet_addr(host
->host
->ptr
);
603 proxy_addr_in
.sin_port
= htons(host
->port
);
604 servlen
= sizeof(proxy_addr_in
);
605 proxy_addr
= (struct sockaddr
*) &proxy_addr_in
;
609 if (-1 == connect(proxy_fd
, proxy_addr
, servlen
)) {
610 if (errno
== EINPROGRESS
|| errno
== EALREADY
) {
611 if (hctx
->conf
.debug
) {
612 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
613 "connect delayed:", proxy_fd
);
619 log_error_write(srv
, __FILE__
, __LINE__
, "sdsd",
620 "connect failed:", proxy_fd
, strerror(errno
), errno
);
625 if (hctx
->conf
.debug
) {
626 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
627 "connect succeeded: ", proxy_fd
);
633 static void proxy_set_header(connection
*con
, const char *key
, const char *value
) {
636 if (NULL
== (ds_dst
= (data_string
*)array_get_unused_element(con
->request
.headers
, TYPE_STRING
))) {
637 ds_dst
= data_string_init();
640 buffer_copy_string(ds_dst
->key
, key
);
641 buffer_copy_string(ds_dst
->value
, value
);
642 array_insert_unique(con
->request
.headers
, (data_unset
*)ds_dst
);
645 static void proxy_append_header(connection
*con
, const char *key
, const char *value
) {
648 if (NULL
== (ds_dst
= (data_string
*)array_get_unused_element(con
->request
.headers
, TYPE_STRING
))) {
649 ds_dst
= data_string_init();
652 buffer_copy_string(ds_dst
->key
, key
);
653 buffer_append_string(ds_dst
->value
, value
);
654 array_insert_unique(con
->request
.headers
, (data_unset
*)ds_dst
);
658 static int proxy_create_env(server
*srv
, handler_ctx
*hctx
) {
661 connection
*con
= hctx
->remote_conn
;
663 int replace_http_host
= 0;
670 buffer_copy_string(b
, get_http_method_name(con
->request
.http_method
));
671 buffer_append_string_len(b
, CONST_STR_LEN(" "));
673 buffer_append_string_buffer(b
, con
->request
.uri
);
674 buffer_append_string_len(b
, CONST_STR_LEN(" HTTP/1.0\r\n"));
675 if (hctx
->conf
.replace_http_host
&& !buffer_string_is_empty(hctx
->host
->key
)) {
676 replace_http_host
= 1;
677 if (hctx
->conf
.debug
> 1) {
678 log_error_write(srv
, __FILE__
, __LINE__
, "SBS",
679 "proxy - using \"", hctx
->host
->key
, "\" as HTTP Host");
681 buffer_append_string_len(b
, CONST_STR_LEN("Host: "));
682 buffer_append_string_buffer(b
, hctx
->host
->key
);
683 buffer_append_string_len(b
, CONST_STR_LEN("\r\n"));
686 proxy_append_header(con
, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv
, &(con
->dst_addr
)));
687 /* http_host is NOT is just a pointer to a buffer
688 * which is NULL if it is not set */
689 if (!buffer_string_is_empty(con
->request
.http_host
)) {
690 proxy_set_header(con
, "X-Host", con
->request
.http_host
->ptr
);
692 proxy_set_header(con
, "X-Forwarded-Proto", con
->uri
.scheme
->ptr
);
695 for (i
= 0; i
< con
->request
.headers
->used
; i
++) {
698 ds
= (data_string
*)con
->request
.headers
->data
[i
];
700 if (!buffer_is_empty(ds
->value
) && !buffer_is_empty(ds
->key
)) {
701 if (replace_http_host
&&
702 buffer_is_equal_caseless_string(ds
->key
, CONST_STR_LEN("Host"))) continue;
703 if (buffer_is_equal_caseless_string(ds
->key
, CONST_STR_LEN("Connection"))) continue;
704 if (buffer_is_equal_caseless_string(ds
->key
, CONST_STR_LEN("Proxy-Connection"))) continue;
705 /* Do not emit HTTP_PROXY in environment.
706 * Some executables use HTTP_PROXY to configure
707 * outgoing proxy. See also https://httpoxy.org/ */
708 if (buffer_is_equal_caseless_string(ds
->key
, CONST_STR_LEN("Proxy"))) continue;
710 buffer_append_string_buffer(b
, ds
->key
);
711 buffer_append_string_len(b
, CONST_STR_LEN(": "));
712 buffer_append_string_buffer(b
, ds
->value
);
713 buffer_append_string_len(b
, CONST_STR_LEN("\r\n"));
717 buffer_append_string_len(b
, CONST_STR_LEN("Connection: close\r\n\r\n"));
719 hctx
->wb_reqlen
= buffer_string_length(b
);
720 chunkqueue_append_buffer(hctx
->wb
, b
);
725 if (con
->request
.content_length
) {
726 chunkqueue_append_chunkqueue(hctx
->wb
, con
->request_content_queue
);
727 hctx
->wb_reqlen
+= con
->request
.content_length
;/* (eventual) total request size */
733 static int proxy_set_state(server
*srv
, handler_ctx
*hctx
, proxy_connection_state_t state
) {
735 hctx
->state_timestamp
= srv
->cur_ts
;
741 static int proxy_response_parse(server
*srv
, connection
*con
, plugin_data
*p
, buffer
*in
) {
743 int http_response_status
= -1;
747 /* [\r]\n -> [\0]\0 */
749 buffer_copy_buffer(p
->parse_response
, in
);
751 for (s
= p
->parse_response
->ptr
; NULL
!= (ns
= strchr(s
, '\n')); s
= ns
+ 1) {
758 if (s
!= ns
&& ns
[-1] == '\r') ns
[-1] = '\0';
760 if (-1 == http_response_status
) {
761 /* The first line of a Response message is the Status-Line */
763 for (key
=s
; *key
&& *key
!= ' '; key
++);
766 http_response_status
= (int) strtol(key
, NULL
, 10);
767 if (http_response_status
< 100 || http_response_status
>= 1000) http_response_status
= 502;
769 http_response_status
= 502;
772 con
->http_status
= http_response_status
;
773 con
->parsed_response
|= HTTP_STATUS
;
777 if (NULL
== (value
= strchr(s
, ':'))) {
778 /* now we expect: "<key>: <value>\n" */
784 key_len
= value
- key
;
788 while (*value
== ' ' || *value
== '\t') value
++;
794 if (0 == strncasecmp(key
, "Date", key_len
)) {
795 con
->parsed_response
|= HTTP_DATE
;
799 if (0 == strncasecmp(key
, "Location", key_len
)) {
800 con
->parsed_response
|= HTTP_LOCATION
;
804 if (0 == strncasecmp(key
, "Connection", key_len
)) {
809 if (0 == strncasecmp(key
, "Content-Length", key_len
)) {
810 con
->response
.content_length
= strtoul(value
, NULL
, 10);
811 con
->parsed_response
|= HTTP_CONTENT_LENGTH
;
819 if (NULL
== (ds
= (data_string
*)array_get_unused_element(con
->response
.headers
, TYPE_STRING
))) {
820 ds
= data_response_init();
822 buffer_copy_string_len(ds
->key
, key
, key_len
);
823 buffer_copy_string(ds
->value
, value
);
825 array_insert_unique(con
->response
.headers
, (data_unset
*)ds
);
833 static int proxy_demux_response(server
*srv
, handler_ctx
*hctx
) {
838 plugin_data
*p
= hctx
->plugin_data
;
839 connection
*con
= hctx
->remote_conn
;
840 int proxy_fd
= hctx
->fd
;
842 /* check how much we have to read */
843 #if !defined(_WIN32) && !defined(__CYGWIN__)
844 if (ioctl(hctx
->fd
, FIONREAD
, &b
)) {
845 if (errno
== EAGAIN
) {
846 fdevent_event_add(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_IN
);
849 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
859 if (hctx
->conf
.debug
) {
860 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
861 "proxy - have to read:", b
);
865 if ((con
->conf
.stream_response_body
& FDEVENT_STREAM_RESPONSE_BUFMIN
)) {
866 off_t cqlen
= chunkqueue_length(con
->write_queue
);
867 if (cqlen
+ b
> 65536 - 4096) {
868 if (!con
->is_writable
) {
869 /*(defer removal of FDEVENT_IN interest since
870 * connection_state_machine() might be able to send data
871 * immediately, unless !con->is_writable, where
872 * connection_state_machine() might not loop back to call
873 * mod_proxy_handle_subrequest())*/
874 fdevent_event_clr(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_IN
);
876 if (cqlen
>= 65536-1) return 0;
877 b
= 65536 - 1 - (int)cqlen
;
881 buffer_string_prepare_append(hctx
->response
, b
);
883 if (-1 == (r
= read(hctx
->fd
, hctx
->response
->ptr
+ buffer_string_length(hctx
->response
), buffer_string_space(hctx
->response
)))) {
884 if (errno
== EAGAIN
) {
885 fdevent_event_add(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_IN
);
888 log_error_write(srv
, __FILE__
, __LINE__
, "sds",
889 "unexpected end-of-file (perhaps the proxy process died):",
890 proxy_fd
, strerror(errno
));
894 #if defined(_WIN32) || defined(__CYGWIN__)
895 if (0 == r
) return 1; /* fin */
898 /* this should be catched by the b > 0 above */
901 buffer_commit(hctx
->response
, r
);
904 log_error_write(srv
, __FILE__
, __LINE__
, "sdsbs",
905 "demux: Response buffer len", hctx
->response
->used
, ":", hctx
->response
, ":");
908 if (0 == con
->got_response
) {
909 con
->got_response
= 1;
910 buffer_string_prepare_copy(hctx
->response_header
, 1023);
913 if (0 == con
->file_started
) {
916 /* search for the \r\n\r\n in the string */
917 if (NULL
!= (c
= buffer_search_string_len(hctx
->response
, CONST_STR_LEN("\r\n\r\n")))) {
918 size_t hlen
= c
- hctx
->response
->ptr
+ 4;
919 size_t blen
= buffer_string_length(hctx
->response
) - hlen
;
922 buffer_append_string_len(hctx
->response_header
, hctx
->response
->ptr
, hlen
);
924 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "Header:", hctx
->response_header
);
926 /* parse the response header */
927 proxy_response_parse(srv
, con
, p
, hctx
->response_header
);
929 con
->file_started
= 1;
931 if (0 != http_chunk_append_mem(srv
, con
, c
+ 4, blen
)) {
932 /* error writing to tempfile;
933 * truncate response or send 500 if nothing sent yet */
935 con
->file_started
= 0;
938 buffer_reset(hctx
->response
);
940 /* no luck, no header found */
941 /*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/
942 if (buffer_string_length(hctx
->response
) > MAX_HTTP_REQUEST_HEADER
) {
943 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "response headers too large for", con
->uri
.path
);
944 con
->http_status
= 502; /* Bad Gateway */
950 if (0 != http_chunk_append_buffer(srv
, con
, hctx
->response
)) {
951 /* error writing to tempfile;
952 * truncate response or send 500 if nothing sent yet */
955 buffer_reset(hctx
->response
);
958 /* reading from upstream done */
966 static handler_t
proxy_write_request(server
*srv
, handler_ctx
*hctx
) {
967 data_proxy
*host
= hctx
->host
;
968 connection
*con
= hctx
->remote_conn
;
972 switch(hctx
->state
) {
973 case PROXY_STATE_INIT
:
974 #if defined(HAVE_SYS_UN_H)
975 if (strstr(host
->host
->ptr
,"/")) {
976 if (-1 == (hctx
->fd
= fdevent_socket_nb_cloexec(AF_UNIX
, SOCK_STREAM
, 0))) {
977 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "socket failed: ", strerror(errno
));
978 return HANDLER_ERROR
;
982 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
983 if (strstr(host
->host
->ptr
,":")) {
984 if (-1 == (hctx
->fd
= fdevent_socket_nb_cloexec(AF_INET6
, SOCK_STREAM
, 0))) {
985 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "socket failed: ", strerror(errno
));
986 return HANDLER_ERROR
;
991 if (-1 == (hctx
->fd
= fdevent_socket_nb_cloexec(AF_INET
, SOCK_STREAM
, 0))) {
992 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "socket failed: ", strerror(errno
));
993 return HANDLER_ERROR
;
1000 fdevent_register(srv
->ev
, hctx
->fd
, proxy_handle_fdevent
, hctx
);
1002 if (-1 == fdevent_fcntl_set(srv
->ev
, hctx
->fd
)) {
1003 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "fcntl failed: ", strerror(errno
));
1005 return HANDLER_ERROR
;
1009 case PROXY_STATE_CONNECT
:
1010 if (hctx
->state
== PROXY_STATE_INIT
) {
1011 switch (proxy_establish_connection(srv
, hctx
)) {
1013 proxy_set_state(srv
, hctx
, PROXY_STATE_CONNECT
);
1015 /* connection is in progress, wait for an event and call getsockopt() below */
1017 fdevent_event_set(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_OUT
);
1019 return HANDLER_WAIT_FOR_EVENT
;
1021 /* if ECONNREFUSED choose another connection */
1024 return HANDLER_ERROR
;
1026 /* everything is ok, go on */
1031 socklen_t socket_error_len
= sizeof(socket_error
);
1033 /* try to finish the connect() */
1034 if (0 != getsockopt(hctx
->fd
, SOL_SOCKET
, SO_ERROR
, &socket_error
, &socket_error_len
)) {
1035 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
1036 "getsockopt failed:", strerror(errno
));
1038 return HANDLER_ERROR
;
1040 if (socket_error
!= 0) {
1041 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
1042 "establishing connection failed:", strerror(socket_error
),
1043 "port:", hctx
->host
->port
);
1045 return HANDLER_ERROR
;
1047 if (hctx
->conf
.debug
) {
1048 log_error_write(srv
, __FILE__
, __LINE__
, "s", "proxy - connect - delayed success");
1052 /* ok, we have the connection */
1054 proxy_set_state(srv
, hctx
, PROXY_STATE_PREPARE_WRITE
);
1056 case PROXY_STATE_PREPARE_WRITE
:
1057 proxy_create_env(srv
, hctx
);
1059 fdevent_event_add(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_IN
);
1060 proxy_set_state(srv
, hctx
, PROXY_STATE_WRITE
);
1063 case PROXY_STATE_WRITE
:;
1064 ret
= srv
->network_backend_write(srv
, con
, hctx
->fd
, hctx
->wb
, MAX_WRITE_LIMIT
);
1066 chunkqueue_remove_finished_chunks(hctx
->wb
);
1068 if (-1 == ret
) { /* error on our side */
1069 log_error_write(srv
, __FILE__
, __LINE__
, "ssd", "write failed:", strerror(errno
), errno
);
1071 return HANDLER_ERROR
;
1072 } else if (-2 == ret
) { /* remote close */
1073 log_error_write(srv
, __FILE__
, __LINE__
, "ssd", "write failed, remote connection close:", strerror(errno
), errno
);
1075 return HANDLER_ERROR
;
1078 if (hctx
->wb
->bytes_out
== hctx
->wb_reqlen
) {
1079 fdevent_event_clr(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_OUT
);
1080 proxy_set_state(srv
, hctx
, PROXY_STATE_READ
);
1082 off_t wblen
= hctx
->wb
->bytes_in
- hctx
->wb
->bytes_out
;
1083 if (hctx
->wb
->bytes_in
< hctx
->wb_reqlen
&& wblen
< 65536 - 16384) {
1084 /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
1085 if (!(con
->conf
.stream_request_body
& FDEVENT_STREAM_REQUEST_POLLIN
)) {
1086 con
->conf
.stream_request_body
|= FDEVENT_STREAM_REQUEST_POLLIN
;
1087 con
->is_readable
= 1; /* trigger optimistic read from client */
1091 fdevent_event_clr(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_OUT
);
1093 fdevent_event_add(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_OUT
);
1097 return HANDLER_WAIT_FOR_EVENT
;
1098 case PROXY_STATE_READ
:
1099 /* waiting for a response */
1100 return HANDLER_WAIT_FOR_EVENT
;
1102 log_error_write(srv
, __FILE__
, __LINE__
, "s", "(debug) unknown state");
1103 return HANDLER_ERROR
;
1109 static int mod_proxy_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
1111 plugin_config
*s
= p
->config_storage
[0];
1116 PATCH(replace_http_host
);
1118 /* skip the first, the global context */
1119 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
1120 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
1121 s
= p
->config_storage
[i
];
1123 /* condition didn't match */
1124 if (!config_check_cond(srv
, con
, dc
)) continue;
1127 for (j
= 0; j
< dc
->value
->used
; j
++) {
1128 data_unset
*du
= dc
->value
->data
[j
];
1130 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("proxy.server"))) {
1132 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("proxy.debug"))) {
1134 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("proxy.balance"))) {
1136 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("proxy.replace-http-host"))) {
1137 PATCH(replace_http_host
);
1146 static handler_t
proxy_send_request(server
*srv
, handler_ctx
*hctx
) {
1147 /* ok, create the request */
1148 handler_t rc
= proxy_write_request(srv
, hctx
);
1149 if (HANDLER_ERROR
!= rc
) {
1152 data_proxy
*host
= hctx
->host
;
1153 log_error_write(srv
, __FILE__
, __LINE__
, "sbdd", "proxy-server disabled:",
1158 /* disable this server */
1159 host
->is_disabled
= 1;
1160 host
->disable_ts
= srv
->cur_ts
;
1162 /* reset the environment and restart the sub-request */
1163 return proxy_reconnect(srv
, hctx
);
1168 static handler_t
proxy_recv_response(server
*srv
, handler_ctx
*hctx
);
1171 SUBREQUEST_FUNC(mod_proxy_handle_subrequest
) {
1172 plugin_data
*p
= p_d
;
1174 handler_ctx
*hctx
= con
->plugin_ctx
[p
->id
];
1176 if (NULL
== hctx
) return HANDLER_GO_ON
;
1179 if (con
->mode
!= p
->id
) return HANDLER_GO_ON
;
1181 if ((con
->conf
.stream_response_body
& FDEVENT_STREAM_RESPONSE_BUFMIN
)
1182 && con
->file_started
) {
1183 if (chunkqueue_length(con
->write_queue
) > 65536 - 4096) {
1184 fdevent_event_clr(srv
->ev
, &(hctx
->fde_ndx
), hctx
->fd
, FDEVENT_IN
);
1185 } else if (!(fdevent_event_get_interest(srv
->ev
, hctx
->fd
) & FDEVENT_IN
)) {
1186 /* optimistic read from backend, which might re-enable FDEVENT_IN */
1187 handler_t rc
= proxy_recv_response(srv
, hctx
); /*(might invalidate hctx)*/
1188 if (rc
!= HANDLER_GO_ON
) return rc
; /*(unless HANDLER_GO_ON)*/
1192 if (0 == hctx
->wb
->bytes_in
1193 ? con
->state
== CON_STATE_READ_POST
1194 : hctx
->wb
->bytes_in
< hctx
->wb_reqlen
) {
1195 /*(64k - 4k to attempt to avoid temporary files
1196 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
1197 if (hctx
->wb
->bytes_in
- hctx
->wb
->bytes_out
> 65536 - 4096
1198 && (con
->conf
.stream_request_body
& FDEVENT_STREAM_REQUEST_BUFMIN
)){
1199 con
->conf
.stream_request_body
&= ~FDEVENT_STREAM_REQUEST_POLLIN
;
1200 if (0 != hctx
->wb
->bytes_in
) return HANDLER_WAIT_FOR_EVENT
;
1202 handler_t r
= connection_handle_read_post_state(srv
, con
);
1203 chunkqueue
*req_cq
= con
->request_content_queue
;
1204 if (0 != hctx
->wb
->bytes_in
&& !chunkqueue_is_empty(req_cq
)) {
1205 chunkqueue_append_chunkqueue(hctx
->wb
, req_cq
);
1206 if (fdevent_event_get_interest(srv
->ev
, hctx
->fd
) & FDEVENT_OUT
) {
1207 return (r
== HANDLER_GO_ON
) ? HANDLER_WAIT_FOR_EVENT
: r
;
1210 if (r
!= HANDLER_GO_ON
) return r
;
1212 /* mod_proxy sends HTTP/1.0 request and ideally should send
1213 * Content-Length with request if request body is present, so
1214 * send 411 Length Required if Content-Length missing.
1215 * (occurs here if client sends Transfer-Encoding: chunked
1216 * and module is flagged to stream request body to backend) */
1217 if (-1 == con
->request
.content_length
) {
1218 return connection_handle_read_post_error(srv
, con
, 411);
1223 return ((0 == hctx
->wb
->bytes_in
|| !chunkqueue_is_empty(hctx
->wb
))
1224 && hctx
->state
!= PROXY_STATE_CONNECT
)
1225 ? proxy_send_request(srv
, hctx
)
1226 : HANDLER_WAIT_FOR_EVENT
;
1230 static handler_t
proxy_recv_response(server
*srv
, handler_ctx
*hctx
) {
1232 switch (proxy_demux_response(srv
, hctx
)) {
1236 http_response_backend_error(srv
, hctx
->remote_conn
);
1240 proxy_connection_close(srv
, hctx
);
1242 return HANDLER_FINISHED
;
1245 return HANDLER_GO_ON
;
1249 static handler_t
proxy_handle_fdevent(server
*srv
, void *ctx
, int revents
) {
1250 handler_ctx
*hctx
= ctx
;
1251 connection
*con
= hctx
->remote_conn
;
1253 joblist_append(srv
, con
);
1255 if (revents
& FDEVENT_IN
) {
1256 handler_t rc
= proxy_recv_response(srv
,hctx
);/*(might invalidate hctx)*/
1257 if (rc
!= HANDLER_GO_ON
) return rc
; /*(unless HANDLER_GO_ON)*/
1260 if (revents
& FDEVENT_OUT
) {
1261 return proxy_send_request(srv
, hctx
); /*(might invalidate hctx)*/
1264 /* perhaps this issue is already handled */
1265 if (revents
& FDEVENT_HUP
) {
1266 if (hctx
->state
== PROXY_STATE_CONNECT
) {
1267 /* connect() -> EINPROGRESS -> HUP */
1268 proxy_send_request(srv
, hctx
); /*(might invalidate hctx)*/
1269 } else if (con
->file_started
) {
1270 /* drain any remaining data from kernel pipe buffers
1271 * even if (con->conf.stream_response_body
1272 * & FDEVENT_STREAM_RESPONSE_BUFMIN)
1273 * since event loop will spin on fd FDEVENT_HUP event
1274 * until unregistered. */
1277 rc
= proxy_recv_response(srv
,hctx
);/*(might invalidate hctx)*/
1278 } while (rc
== HANDLER_GO_ON
); /*(unless HANDLER_GO_ON)*/
1279 return rc
; /* HANDLER_FINISHED or HANDLER_ERROR */
1281 proxy_connection_close(srv
, hctx
);
1283 } else if (revents
& FDEVENT_ERR
) {
1284 log_error_write(srv
, __FILE__
, __LINE__
, "sd", "proxy-FDEVENT_ERR, but no HUP", revents
);
1286 http_response_backend_error(srv
, con
);
1287 proxy_connection_close(srv
, hctx
);
1290 return HANDLER_FINISHED
;
1293 static handler_t
mod_proxy_check_extension(server
*srv
, connection
*con
, void *p_d
) {
1294 plugin_data
*p
= p_d
;
1298 data_array
*extension
= NULL
;
1301 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
1303 /* Possibly, we processed already this request */
1304 if (con
->file_started
== 1) return HANDLER_GO_ON
;
1306 mod_proxy_patch_connection(srv
, con
, p
);
1309 if (buffer_string_is_empty(fn
)) return HANDLER_ERROR
;
1310 s_len
= buffer_string_length(fn
);
1312 /* check if extension matches */
1313 for (k
= 0; k
< p
->conf
.extensions
->used
; k
++) {
1314 data_array
*ext
= NULL
;
1317 ext
= (data_array
*)p
->conf
.extensions
->data
[k
];
1319 if (buffer_is_empty(ext
->key
)) continue;
1321 ct_len
= buffer_string_length(ext
->key
);
1323 if (s_len
< ct_len
) continue;
1325 /* check extension in the form "/proxy_pattern" */
1326 if (*(ext
->key
->ptr
) == '/') {
1327 if (strncmp(fn
->ptr
, ext
->key
->ptr
, ct_len
) == 0) {
1331 } else if (0 == strncmp(fn
->ptr
+ s_len
- ct_len
, ext
->key
->ptr
, ct_len
)) {
1332 /* check extension in the form ".fcg" */
1338 if (NULL
== extension
) {
1339 return HANDLER_GO_ON
;
1342 host
= mod_proxy_extension_host_get(srv
, con
, extension
, p
->conf
.balance
, (int)p
->conf
.debug
);
1344 return HANDLER_FINISHED
;
1347 /* found a server */
1351 * if check-local is disabled, use the uri.path handler
1355 /* init handler-context */
1357 hctx
= handler_ctx_init();
1359 hctx
->remote_conn
= con
;
1360 hctx
->plugin_data
= p
;
1362 hctx
->ext
= extension
;
1364 hctx
->conf
.balance
= p
->conf
.balance
;
1365 hctx
->conf
.debug
= p
->conf
.debug
;
1366 hctx
->conf
.replace_http_host
= p
->conf
.replace_http_host
;
1368 con
->plugin_ctx
[p
->id
] = hctx
;
1371 if (p
->conf
.debug
) {
1372 log_error_write(srv
, __FILE__
, __LINE__
, "sbd",
1373 "proxy - found a host",
1374 host
->host
, host
->port
);
1377 return HANDLER_GO_ON
;
1381 static handler_t
mod_proxy_connection_reset(server
*srv
, connection
*con
, void *p_d
) {
1382 plugin_data
*p
= p_d
;
1383 handler_ctx
*hctx
= con
->plugin_ctx
[p
->id
];
1384 if (hctx
) proxy_connection_close(srv
, hctx
);
1386 return HANDLER_GO_ON
;
1391 * the trigger re-enables the disabled connections after the timeout is over
1395 TRIGGER_FUNC(mod_proxy_trigger
) {
1396 plugin_data
*p
= p_d
;
1398 if (p
->config_storage
) {
1400 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
1401 plugin_config
*s
= p
->config_storage
[i
];
1405 /* get the extensions for all configs */
1407 for (k
= 0; k
< s
->extensions
->used
; k
++) {
1408 data_array
*extension
= (data_array
*)s
->extensions
->data
[k
];
1411 for (n
= 0; n
< extension
->value
->used
; n
++) {
1412 data_proxy
*host
= (data_proxy
*)extension
->value
->data
[n
];
1414 if (!host
->is_disabled
||
1415 srv
->cur_ts
- host
->disable_ts
< 5) continue;
1417 log_error_write(srv
, __FILE__
, __LINE__
, "sbd",
1418 "proxy - re-enabled:",
1419 host
->host
, host
->port
);
1421 host
->is_disabled
= 0;
1427 return HANDLER_GO_ON
;
1431 int mod_proxy_plugin_init(plugin
*p
);
1432 int mod_proxy_plugin_init(plugin
*p
) {
1433 p
->version
= LIGHTTPD_VERSION_ID
;
1434 p
->name
= buffer_init_string("proxy");
1436 p
->init
= mod_proxy_init
;
1437 p
->cleanup
= mod_proxy_free
;
1438 p
->set_defaults
= mod_proxy_set_defaults
;
1439 p
->connection_reset
= mod_proxy_connection_reset
; /* end of req-resp cycle */
1440 p
->handle_uri_clean
= mod_proxy_check_extension
;
1441 p
->handle_subrequest
= mod_proxy_handle_subrequest
;
1442 p
->handle_trigger
= mod_proxy_trigger
;