| CODENOTIFIER | HelpYou are not signed inSign in |
Project: lighttpd
Revision: 2261
Author: stbuehler
Date: 31 Jul 2008 12:45:03
Changes:Remove mod_proxy
Files:| ... | ...@@ -9,7 +9,6 @@ | |
| 9 | 9 | features.txt \ |
| 10 | 10 | performance.txt \ |
| 11 | 11 | plugins.txt \ |
| 12 | proxy.txt \ | |
| 13 | 12 | redirect.txt \ |
| 14 | 13 | rewrite.txt \ |
| 15 | 14 | secdownload.txt \ |
| ... | ...@@ -17,7 +17,6 @@ | |
| 17 | 17 | features.txt |
| 18 | 18 | performance.txt |
| 19 | 19 | plugins.txt |
| 20 | proxy.txt | |
| 21 | 20 | redirect.txt |
| 22 | 21 | rewrite.txt |
| 23 | 22 | rrdtool.txt |
| ... | ...@@ -1,104 +0,0 @@ | |
| 1 | =================== | |
| 2 | the Proxy Interface | |
| 3 | =================== | |
| 4 | ||
| 5 | ----------------- | |
| 6 | Module: mod_proxy | |
| 7 | ----------------- | |
| 8 | ||
| 9 | :Author: Jan Kneschke | |
| 10 | :Date: $Date: 2005-03-28T08:30:05.699628Z $ | |
| 11 | :Revision: $Revision: 227 $ | |
| 12 | ||
| 13 | :abstract: | |
| 14 | The proxy module a simplest way to connect lighttpd to | |
| 15 | java servers which have a HTTP-interface. | |
| 16 | ||
| 17 | .. meta:: | |
| 18 | :keywords: lighttpd, Proxy | |
| 19 | ||
| 20 | .. contents:: Table of Contents | |
| 21 | ||
| 22 | Description | |
| 23 | =========== | |
| 24 | ||
| 25 | ... | |
| 26 | ||
| 27 | Options | |
| 28 | ======= | |
| 29 | ||
| 30 | lighttpd provides the Proxy support via the proxy-module | |
| 31 | (mod_proxy) which provides 2 options in the config-file: | |
| 32 | ||
| 33 | :proxy.debug: | |
| 34 | a value between 0 and 65535 to set the debug-level in the | |
| 35 | Proxy module. Currently only 0 and 1 are used. Use 1 to | |
| 36 | enable some debug output, 0 to disable it. | |
| 37 | ||
| 38 | :proxy.balance: | |
| 39 | might be one of 'hash', 'round-robin' or 'fair' (default). | |
| 40 | ||
| 41 | 'round-robin' choses another host for each request, 'hash' | |
| 42 | is generating a hash over the request-uri and makes sure | |
| 43 | that the same request URI is sent to always the same host. | |
| 44 | That can increase the performance of the backend servers | |
| 45 | a lot due to higher cache-locality. 'fair' is the normal | |
| 46 | load-based, passive balancing. | |
| 47 | ||
| 48 | :proxy.server: | |
| 49 | tell the module where to send Proxy requests to. Every | |
| 50 | file-extension can have its own handler. Load-Balancing is | |
| 51 | done by specifying multiple handles for the same extension. | |
| 52 | ||
| 53 | structure of proxy.server section: :: | |
| 54 | ||
| 55 | ( <extension> => | |
| 56 | ( | |
| 57 | ( "host" => <string> , | |
| 58 | "port" => <integer> ), | |
| 59 | ( "host" => <string> , | |
| 60 | "port" => <integer> ) | |
| 61 | ), | |
| 62 | <extension> => ... | |
| 63 | ) | |
| 64 | ||
| 65 | :<extension>: is the file-extension or prefix (if started with "/") | |
| 66 | might empty to match all requests | |
| 67 | :"host": is ip of the proxy server | |
| 68 | :"port": is tcp-port on the "host" used by the proxy | |
| 69 | server (default: 80) | |
| 70 | ||
| 71 | e.g.: :: | |
| 72 | ||
| 73 | proxy.server = ( ".jsp" => | |
| 74 | ( ( | |
| 75 | "host" => "10.0.0.242", | |
| 76 | "port" => 81 | |
| 77 | ) ) | |
| 78 | ) | |
| 79 | ||
| 80 | Example: | |
| 81 | ======== | |
| 82 | ||
| 83 | Using lighttpd + mod_proxy in front of 8 Squids which handle the | |
| 84 | caching of dynamic content for you. All requests for the host | |
| 85 | www.example.org should be forwarded to the proxy. All proxies | |
| 86 | listen on port 80 for requests. :: | |
| 87 | ||
| 88 | $HTTP["host"] == "www.example.org" { | |
| 89 | proxy.balance = "hash" | |
| 90 | proxy.server = ( "" => ( ( "host" => "10.0.0.10" ), | |
| 91 | ( "host" => "10.0.0.11" ), | |
| 92 | ( "host" => "10.0.0.12" ), | |
| 93 | ( "host" => "10.0.0.13" ), | |
| 94 | ( "host" => "10.0.0.14" ), | |
| 95 | ( "host" => "10.0.0.15" ), | |
| 96 | ( "host" => "10.0.0.16" ), | |
| 97 | ( "host" => "10.0.0.17" ) ) ) | |
| 98 | } | |
| 99 | ||
| 100 | If one of the hosts goes down the all requests for this one server are | |
| 101 | moved equally to the other servers. If you want to know more about | |
| 102 | the algorithm used here google for 'Microsoft CARP'. | |
| 103 | ||
| 104 |
| ... | ...@@ -1,1304 +0,0 @@ | |
| 1 | #include <sys/types.h> | |
| 2 | ||
| 3 | #include <errno.h> | |
| 4 | #include <fcntl.h> | |
| 5 | #include <string.h> | |
| 6 | #include <stdlib.h> | |
| 7 | #include <ctype.h> | |
| 8 | #include <assert.h> | |
| 9 | ||
| 10 | #include "buffer.h" | |
| 11 | #include "server.h" | |
| 12 | #include "keyvalue.h" | |
| 13 | #include "log.h" | |
| 14 | ||
| 15 | #include "fdevent.h" | |
| 16 | #include "connections.h" | |
| 17 | #include "response.h" | |
| 18 | #include "joblist.h" | |
| 19 | ||
| 20 | #include "plugin.h" | |
| 21 | ||
| 22 | #include "inet_ntop_cache.h" | |
| 23 | #include "crc32.h" | |
| 24 | #include "network.h" | |
| 25 | ||
| 26 | #include "http_resp.h" | |
| 27 | ||
| 28 | #include <stdio.h> | |
| 29 | ||
| 30 | #ifdef HAVE_SYS_FILIO_H | |
| 31 | # include <sys/filio.h> | |
| 32 | #endif | |
| 33 | ||
| 34 | #include "sys-socket.h" | |
| 35 | #include "sys-files.h" | |
| 36 | #include "sys-strings.h" | |
| 37 | ||
| 38 | #define data_proxy data_fastcgi | |
| 39 | #define data_proxy_init data_fastcgi_init | |
| 40 | ||
| 41 | #define PROXY_RETRY_TIMEOUT 60 | |
| 42 | ||
| 43 | /** | |
| 44 | * | |
| 45 | * the proxy module is based on the fastcgi module | |
| 46 | * | |
| 47 | * 28.06.2004 Jan Kneschke The first release | |
| 48 | * 01.07.2004 Evgeny Rodichev Several bugfixes and cleanups | |
| 49 | * - co-ordinate up- and downstream flows correctly (proxy_demux_response | |
| 50 | * and proxy_handle_fdevent) | |
| 51 | * - correctly transfer upstream http_response_status; | |
| 52 | * - some unused structures removed. | |
| 53 | * | |
| 54 | * TODO: - delay upstream read if write_queue is too large | |
| 55 | * (to prevent memory eating, like in apache). Should be | |
| 56 | * configurable). | |
| 57 | * - persistent connection with upstream servers | |
| 58 | * - HTTP/1.1 | |
| 59 | */ | |
| 60 | ||
| 61 | ||
| 62 | ||
| 63 | typedef enum { | |
| 64 | PROXY_BALANCE_UNSET, | |
| 65 | PROXY_BALANCE_FAIR, | |
| 66 | PROXY_BALANCE_HASH, | |
| 67 | PROXY_BALANCE_RR | |
| 68 | } proxy_balance_t; | |
| 69 | ||
| 70 | typedef struct { | |
| 71 | array *extensions; | |
| 72 | int debug; | |
| 73 | ||
| 74 | proxy_balance_t balance; | |
| 75 | ||
| 76 | array *last_used_backends; /* "extension" : last_used_backend */ | |
| 77 | } plugin_config; | |
| 78 | ||
| 79 | typedef struct { | |
| 80 | PLUGIN_DATA; | |
| 81 | ||
| 82 | buffer *parse_response; | |
| 83 | buffer *balance_buf; | |
| 84 | ||
| 85 | http_resp *resp; | |
| 86 | ||
| 87 | array *ignore_headers; | |
| 88 | ||
| 89 | plugin_config **config_storage; | |
| 90 | ||
| 91 | plugin_config conf; | |
| 92 | } plugin_data; | |
| 93 | ||
| 94 | typedef enum { | |
| 95 | PROXY_STATE_INIT, | |
| 96 | PROXY_STATE_CONNECT, | |
| 97 | PROXY_STATE_PREPARE_WRITE, | |
| 98 | PROXY_STATE_WRITE, | |
| 99 | PROXY_STATE_RESPONSE_HEADER, | |
| 100 | PROXY_STATE_RESPONSE_CONTENT, | |
| 101 | PROXY_STATE_ERROR | |
| 102 | } proxy_connection_state_t; | |
| 103 | ||
| 104 | enum { PROXY_STDOUT, PROXY_END_REQUEST }; | |
| 105 | ||
| 106 | typedef struct { | |
| 107 | proxy_connection_state_t state; | |
| 108 | time_t state_timestamp; | |
| 109 | ||
| 110 | data_proxy *host; | |
| 111 | ||
| 112 | chunkqueue *wb; | |
| 113 | chunkqueue *rb; | |
| 114 | ||
| 115 | iosocket *sock; /* fd to the proxy process */ | |
| 116 | ||
| 117 | size_t path_info_offset; /* start of path_info in uri.path */ | |
| 118 | ||
| 119 | connection *remote_conn; /* dump pointer */ | |
| 120 | plugin_data *plugin_data; /* dump pointer */ | |
| 121 | } handler_ctx; | |
| 122 | ||
| 123 | ||
| 124 | /* ok, we need a prototype */ | |
| 125 | static handler_t proxy_handle_fdevent(void *s, void *ctx, int revents); | |
| 126 | ||
| 127 | static handler_ctx * handler_ctx_init() { | |
| 128 | handler_ctx * hctx; | |
| 129 | ||
| 130 | ||
| 131 | hctx = calloc(1, sizeof(*hctx)); | |
| 132 | ||
| 133 | hctx->state = PROXY_STATE_INIT; | |
| 134 | hctx->host = NULL; | |
| 135 | ||
| 136 | hctx->wb = chunkqueue_init(); | |
| 137 | hctx->rb = chunkqueue_init(); | |
| 138 | ||
| 139 | hctx->sock = iosocket_init(); | |
| 140 | ||
| 141 | return hctx; | |
| 142 | } | |
| 143 | ||
| 144 | static void handler_ctx_free(handler_ctx *hctx) { | |
| 145 | chunkqueue_free(hctx->wb); | |
| 146 | chunkqueue_free(hctx->rb); | |
| 147 | ||
| 148 | iosocket_free(hctx->sock); | |
| 149 | ||
| 150 | free(hctx); | |
| 151 | } | |
| 152 | ||
| 153 | INIT_FUNC(mod_proxy_init) { | |
| 154 | plugin_data *p; | |
| 155 | size_t i; | |
| 156 | ||
| 157 | char *hop2hop_headers[] = { | |
| 158 | "Connection", | |
| 159 | "Keep-Alive", | |
| 160 | "Host", | |
| 161 | NULL | |
| 162 | }; | |
| 163 | ||
| 164 | p = calloc(1, sizeof(*p)); | |
| 165 | ||
| 166 | p->balance_buf = buffer_init(); | |
| 167 | p->ignore_headers = array_init(); | |
| 168 | p->resp = http_response_init(); | |
| 169 | ||
| 170 | for (i = 0; hop2hop_headers[i]; i++) { | |
| 171 | data_string *ds; | |
| 172 | ||
| 173 | if (NULL == (ds = (data_string *)array_get_unused_element(p->ignore_headers, TYPE_STRING))) { | |
| 174 | ds = data_string_init(); | |
| 175 | } | |
| 176 | ||
| 177 | buffer_copy_string(ds->key, hop2hop_headers[i]); | |
| 178 | buffer_copy_string(ds->value, hop2hop_headers[i]); | |
| 179 | array_insert_unique(p->ignore_headers, (data_unset *)ds); | |
| 180 | } | |
| 181 | ||
| 182 | return p; | |
| 183 | } | |
| 184 | ||
| 185 | ||
| 186 | FREE_FUNC(mod_proxy_free) { | |
| 187 | plugin_data *p = p_d; | |
| 188 | ||
| 189 | UNUSED(srv); | |
| 190 | ||
| 191 | if (p->config_storage) { | |
| 192 | size_t i; | |
| 193 | for (i = 0; i < srv->config_context->used; i++) { | |
| 194 | plugin_config *s = p->config_storage[i]; | |
| 195 | ||
| 196 | if (s) { | |
| 197 | array_free(s->extensions); | |
| 198 | array_free(s->last_used_backends); | |
| 199 | ||
| 200 | free(s); | |
| 201 | } | |
| 202 | } | |
| 203 | free(p->config_storage); | |
| 204 | } | |
| 205 | ||
| 206 | array_free(p->ignore_headers); | |
| 207 | buffer_free(p->balance_buf); | |
| 208 | http_response_free(p->resp); | |
| 209 | ||
| 210 | free(p); | |
| 211 | ||
| 212 | return HANDLER_GO_ON; | |
| 213 | } | |
| 214 | ||
| 215 | SETDEFAULTS_FUNC(mod_proxy_set_defaults) { | |
| 216 | plugin_data *p = p_d; | |
| 217 | data_unset *du; | |
| 218 | size_t i = 0; | |
| 219 | ||
| 220 | config_values_t cv[] = { | |
| 221 | { "proxy.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ | |
| 222 | { "proxy.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ | |
| 223 | { "proxy.balance", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ | |
| 224 | { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } | |
| 225 | }; | |
| 226 | ||
| 227 | p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); | |
| 228 | ||
| 229 | for (i = 0; i < srv->config_context->used; i++) { | |
| 230 | plugin_config *s; | |
| 231 | array *ca; | |
| 232 | ||
| 233 | s = malloc(sizeof(plugin_config)); | |
| 234 | s->extensions = array_init(); | |
| 235 | s->last_used_backends = array_init(); | |
| 236 | s->debug = 0; | |
| 237 | ||
| 238 | cv[0].destination = s->extensions; | |
| 239 | cv[1].destination = &(s->debug); | |
| 240 | cv[2].destination = p->balance_buf; | |
| 241 | ||
| 242 | buffer_reset(p->balance_buf); | |
| 243 | ||
| 244 | p->config_storage[i] = s; | |
| 245 | ca = ((data_config *)srv->config_context->data[i])->value; | |
| 246 | ||
| 247 | if (0 != config_insert_values_global(srv, ca, cv)) { | |
| 248 | return HANDLER_ERROR; | |
| 249 | } | |
| 250 | ||
| 251 | if (buffer_is_empty(p->balance_buf)) { | |
| 252 | s->balance = PROXY_BALANCE_FAIR; | |
| 253 | } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("fair"))) { | |
| 254 | s->balance = PROXY_BALANCE_FAIR; | |
| 255 | } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("round-robin"))) { | |
| 256 | s->balance = PROXY_BALANCE_RR; | |
| 257 | } else if (buffer_is_equal_string(p->balance_buf, CONST_STR_LEN("hash"))) { | |
| 258 | s->balance = PROXY_BALANCE_HASH; | |
| 259 | } else { | |
| 260 | log_error_write(srv, __FILE__, __LINE__, "sb", | |
| 261 | "proxy.balance has to be one of: fair, round-robin, hash, but not:", p->balance_buf); | |
| 262 | return HANDLER_ERROR; | |
| 263 | } | |
| 264 | ||
| 265 | if (NULL != (du = array_get_element(ca, "proxy.server"))) { | |
| 266 | size_t j; | |
| 267 | data_array *da = (data_array *)du; | |
| 268 | ||
| 269 | if (du->type != TYPE_ARRAY) { | |
| 270 | log_error_write(srv, __FILE__, __LINE__, "sss", | |
| 271 | "unexpected type for key: ", "proxy.server", "array of strings"); | |
| 272 | ||
| 273 | return HANDLER_ERROR; | |
| 274 | } | |
| 275 | ||
| 276 | /* | |
| 277 | * proxy.server = ( "<ext>" => ..., | |
| 278 | * "<ext>" => ... ) | |
| 279 | */ | |
| 280 | ||
| 281 | for (j = 0; j < da->value->used; j++) { | |
| 282 | data_array *da_ext = (data_array *)da->value->data[j]; | |
| 283 | size_t n; | |
| 284 | ||
| 285 | if (da_ext->type != TYPE_ARRAY) { | |
| 286 | log_error_write(srv, __FILE__, __LINE__, "sssbs", | |
| 287 | "unexpected type for key: ", "proxy.server", | |
| 288 | "[", da->value->data[j]->key, "](string)"); | |
| 289 | ||
| 290 | return HANDLER_ERROR; | |
| 291 | } | |
| 292 | ||
| 293 | /* | |
| 294 | * proxy.server = ( "<ext>" => | |
| 295 | * ( "<host>" => ( ... ), | |
| 296 | * "<host>" => ( ... ) | |
| 297 | * ), | |
| 298 | * "<ext>" => ... ) | |
| 299 | */ | |
| 300 | ||
| 301 | for (n = 0; n < da_ext->value->used; n++) { | |
| 302 | data_array *da_host = (data_array *)da_ext->value->data[n]; | |
| 303 | ||
| 304 | data_proxy *df; | |
| 305 | data_array *dfa; | |
| 306 | ||
| 307 | config_values_t pcv[] = { | |
| 308 | { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ | |
| 309 | { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ | |
| 310 | { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } | |
| 311 | }; | |
| 312 | ||
| 313 | if (da_host->type != TYPE_ARRAY) { | |
| 314 | log_error_write(srv, __FILE__, __LINE__, "ssSBS", | |
| 315 | "unexpected type for key:", | |
| 316 | "proxy.server", | |
| 317 | "[", da_ext->value->data[n]->key, "](string)"); | |
| 318 | ||
| 319 | return HANDLER_ERROR; | |
| 320 | } | |
| 321 | ||
| 322 | df = data_proxy_init(); | |
| 323 | ||
| 324 | df->port = 80; | |
| 325 | ||
| 326 | buffer_copy_string_buffer(df->key, da_host->key); | |
| 327 | ||
| 328 | pcv[0].destination = df->host; | |
| 329 | pcv[1].destination = &(df->port); | |
| 330 | ||
| 331 | if (0 != config_insert_values_internal(srv, da_host->value, pcv)) { | |
| 332 | return HANDLER_ERROR; | |
| 333 | } | |
| 334 | ||
| 335 | if (buffer_is_empty(df->host)) { | |
| 336 | log_error_write(srv, __FILE__, __LINE__, "sbbbs", | |
| 337 | "missing key (string):", | |
| 338 | da->key, | |
| 339 | da_ext->key, | |
| 340 | da_host->key, | |
| 341 | "host"); | |
| 342 | ||
| 343 | return HANDLER_ERROR; | |
| 344 | } | |
| 345 | ||
| 346 | /* if extension already exists, take it */ | |
| 347 | ||
| 348 | if (NULL == (dfa = (data_array *)array_get_element(s->extensions, da_ext->key->ptr))) { | |
| 349 | dfa = data_array_init(); | |
| 350 | ||
| 351 | buffer_copy_string_buffer(dfa->key, da_ext->key); | |
| 352 | ||
| 353 | array_insert_unique(dfa->value, (data_unset *)df); | |
| 354 | array_insert_unique(s->extensions, (data_unset *)dfa); | |
| 355 | } else { | |
| 356 | array_insert_unique(dfa->value, (data_unset *)df); | |
| 357 | } | |
| 358 | } | |
| 359 | } | |
| 360 | } | |
| 361 | } | |
| 362 | ||
| 363 | return HANDLER_GO_ON; | |
| 364 | } | |
| 365 | ||
| 366 | void proxy_connection_close(server *srv, handler_ctx *hctx) { | |
| 367 | plugin_data *p; | |
| 368 | connection *con; | |
| 369 | ||
| 370 | if (NULL == hctx) return; | |
| 371 | ||
| 372 | p = hctx->plugin_data; | |
| 373 | con = hctx->remote_conn; | |
| 374 | ||
| 375 | if (hctx->sock->fd != -1) { | |
| 376 | fdevent_event_del(srv->ev, hctx->sock); | |
| 377 | fdevent_unregister(srv->ev, hctx->sock); | |
| 378 | ||
| 379 | close(hctx->sock->fd); | |
| 380 | srv->cur_fds--; | |
| 381 | } | |
| 382 | ||
| 383 | handler_ctx_free(hctx); | |
| 384 | con->plugin_ctx[p->id] = NULL; | |
| 385 | } | |
| 386 | ||
| 387 | static int proxy_establish_connection(server *srv, handler_ctx *hctx) { | |
| 388 | struct sockaddr *proxy_addr; | |
| 389 | struct sockaddr_in proxy_addr_in; | |
| 390 | socklen_t servlen; | |
| 391 | ||
| 392 | plugin_data *p = hctx->plugin_data; | |
| 393 | data_proxy *host= hctx->host; | |
| 394 | int proxy_fd = hctx->sock->fd; | |
| 395 | ||
| 396 | memset(&proxy_addr, 0, sizeof(proxy_addr)); | |
| 397 | ||
| 398 | proxy_addr_in.sin_family = AF_INET; | |
| 399 | proxy_addr_in.sin_addr.s_addr = inet_addr(host->host->ptr); | |
| 400 | proxy_addr_in.sin_port = htons(host->port); | |
| 401 | servlen = sizeof(proxy_addr_in); | |
| 402 | ||
| 403 | proxy_addr = (struct sockaddr *) &proxy_addr_in; | |
| 404 | ||
| 405 | if (-1 == connect(proxy_fd, proxy_addr, servlen)) { | |
| 406 | #ifdef _WIN32 | |
| 407 | errno = WSAGetLastError(); | |
| 408 | #endif | |
| 409 | switch(errno) { | |
| 410 | #ifdef _WIN32 | |
| 411 | case WSAEWOULDBLOCK: | |
| 412 | #endif | |
| 413 | case EINPROGRESS: | |
| 414 | case EALREADY: | |
| 415 | if (p->conf.debug) { | |
| 416 | log_error_write(srv, __FILE__, __LINE__, "sd", | |
| 417 | "connect delayed:", proxy_fd); | |
| 418 | } | |
| 419 | ||
| 420 | return 1; | |
| 421 | default: | |
| 422 | ||
| 423 | log_error_write(srv, __FILE__, __LINE__, "sdsd", | |
| 424 | "connect failed:", proxy_fd, strerror(errno), errno); | |
| 425 | ||
| 426 | return -1; | |
| 427 | } | |
| 428 | } | |
| 429 | fprintf(stderr, "%s.%d: connected fd = %d\r\n", __FILE__, __LINE__, proxy_fd); | |
| 430 | if (p->conf.debug) { | |
| 431 | log_error_write(srv, __FILE__, __LINE__, "sd", | |
| 432 | "connect succeeded: ", proxy_fd); | |
| 433 | } | |
| 434 | ||
| 435 | return 0; | |
| 436 | } | |
| 437 | ||
| 438 | void proxy_set_header(connection *con, const char *key, const char *value) { | |
| 439 | data_string *ds_dst; | |
| 440 | ||
| 441 | if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { | |
| 442 | ds_dst = data_string_init(); | |
| 443 | } | |
| 444 | ||
| 445 | buffer_copy_string(ds_dst->key, key); | |
| 446 | buffer_copy_string(ds_dst->value, value); | |
| 447 | array_insert_unique(con->request.headers, (data_unset *)ds_dst); | |
| 448 | } | |
| 449 | ||
| 450 | void proxy_append_header(connection *con, const char *key, const char *value) { | |
| 451 | data_string *ds_dst; | |
| 452 | ||
| 453 | if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { | |
| 454 | ds_dst = data_string_init(); | |
| 455 | } | |
| 456 | ||
| 457 | buffer_copy_string(ds_dst->key, key); | |
| 458 | buffer_append_string(ds_dst->value, value); | |
| 459 | array_insert_unique(con->request.headers, (data_unset *)ds_dst); | |
| 460 | } | |
| 461 | ||
| 462 | ||
| 463 | static int proxy_create_env(server *srv, handler_ctx *hctx) { | |
| 464 | size_t i; | |
| 465 | ||
| 466 | connection *con = hctx->remote_conn; | |
| 467 | plugin_data *p = hctx->plugin_data; | |
| 468 | buffer *b; | |
| 469 | ||
| 470 | /* build header */ | |
| 471 | ||
| 472 | b = chunkqueue_get_append_buffer(hctx->wb); | |
| 473 | ||
| 474 | /* request line */ | |
| 475 | buffer_copy_string(b, get_http_method_name(con->request.http_method)); | |
| 476 | BUFFER_APPEND_STRING_CONST(b, " "); | |
| 477 | ||
| 478 | buffer_append_string_buffer(b, con->request.uri); | |
| 479 | BUFFER_APPEND_STRING_CONST(b, " HTTP/1.0\r\n"); | |
| 480 | ||
| 481 | proxy_append_header(con, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv, &(con->dst_addr))); | |
| 482 | /* http_host is NOT is just a pointer to a buffer | |
| 483 | * which is NULL if it is not set */ | |
| 484 | if (con->request.http_host && | |
| 485 | !buffer_is_empty(con->request.http_host)) { | |
| 486 | proxy_set_header(con, "X-Host", con->request.http_host->ptr); | |
| 487 | } | |
| 488 | proxy_set_header(con, "X-Forwarded-Proto", con->conf.is_ssl ? "https" : "http"); | |
| 489 | ||
| 490 | /* request header */ | |
| 491 | for (i = 0; i < con->request.headers->used; i++) { | |
| 492 | data_string *ds; | |
| 493 | ||
| 494 | ds = (data_string *)con->request.headers->data[i]; | |
| 495 | ||
| 496 | if (buffer_is_empty(ds->value) || buffer_is_empty(ds->key)) continue; | |
| 497 | ||
| 498 | if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Connection"))) continue; | |
| 499 | if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Keep-Alive"))) continue; | |
| 500 | ||
| 501 | buffer_append_string_buffer(b, ds->key); | |
| 502 | BUFFER_APPEND_STRING_CONST(b, ": "); | |
| 503 | buffer_append_string_buffer(b, ds->value); | |
| 504 | BUFFER_APPEND_STRING_CONST(b, "\r\n"); | |
| 505 | } | |
| 506 | ||
| 507 | BUFFER_APPEND_STRING_CONST(b, "\r\n"); | |
| 508 | ||
| 509 | hctx->wb->bytes_in += b->used - 1; | |
| 510 | /* body */ | |
| 511 | ||
| 512 | if (con->request.content_length) { | |
| 513 | chunkqueue *req_cq = con->recv; | |
| 514 | chunk *req_c; | |
| 515 | off_t offset; | |
| 516 | ||
| 517 | /* something to send ? */ | |
| 518 | for (offset = 0, req_c = req_cq->first; offset != req_cq->bytes_in; req_c = req_c->next) { | |
| 519 | off_t weWant = req_cq->bytes_in - offset; | |
| 520 | off_t weHave = 0; | |
| 521 | ||
| 522 | /* we announce toWrite octects | |
| 523 | * now take all the request_content chunk that we need to fill this request | |
| 524 | * */ | |
| 525 | ||
| 526 | switch (req_c->type) { | |
| 527 | case FILE_CHUNK: | |
| 528 | weHave = req_c->file.length - req_c->offset; | |
| 529 | ||
| 530 | if (weHave > weWant) weHave = weWant; | |
| 531 | ||
| 532 | chunkqueue_append_file(hctx->wb, req_c->file.name, req_c->offset, weHave); | |
| 533 | ||
| 534 | req_c->offset += weHave; | |
| 535 | req_cq->bytes_out += weHave; | |
| 536 | ||
| 537 | hctx->wb->bytes_in += weHave; | |
| 538 | ||
| 539 | break; | |
| 540 | case MEM_CHUNK: | |
| 541 | /* append to the buffer */ | |
| 542 | weHave = req_c->mem->used - 1 - req_c->offset; | |
| 543 | ||
| 544 | if (weHave > weWant) weHave = weWant; | |
| 545 | ||
| 546 | b = chunkqueue_get_append_buffer(hctx->wb); | |
| 547 | buffer_append_memory(b, req_c->mem->ptr + req_c->offset, weHave); | |
| 548 | b->used++; /* add virtual \0 */ | |
| 549 | ||
| 550 | req_c->offset += weHave; | |
| 551 | req_cq->bytes_out += weHave; | |
| 552 | ||
| 553 | hctx->wb->bytes_in += weHave; | |
| 554 | ||
| 555 | break; | |
| 556 | default: | |
| 557 | break; | |
| 558 | } | |
| 559 | ||
| 560 | offset += weHave; | |
| 561 | } | |
| 562 | ||
| 563 | } | |
| 564 | ||
| 565 | return 0; | |
| 566 | } | |
| 567 | ||
| 568 | static int proxy_set_state(server *srv, handler_ctx *hctx, proxy_connection_state_t state) { | |
| 569 | hctx->state = state; | |
| 570 | hctx->state_timestamp = srv->cur_ts; | |
| 571 | ||
| 572 | return 0; | |
| 573 | } | |
| 574 | ||
| 575 | ||
| 576 | static int proxy_demux_response(server *srv, handler_ctx *hctx) { | |
| 577 | plugin_data *p = hctx->plugin_data; | |
| 578 | connection *con = hctx->remote_conn; | |
| 579 | chunkqueue *next_queue = NULL; | |
| 580 | chunk *c = NULL; | |
| 581 | ||
| 582 | switch(srv->network_backend_read(srv, con, hctx->sock, hctx->rb)) { | |
| 583 | case NETWORK_STATUS_SUCCESS: | |
| 584 | /* we got content */ | |
| 585 | break; | |
| 586 | case NETWORK_STATUS_WAIT_FOR_EVENT: | |
| 587 | return 0; | |
| 588 | case NETWORK_STATUS_CONNECTION_CLOSE: | |
| 589 | /* we are done, get out of here */ | |
| 590 | con->send->is_closed = 1; | |
| 591 | ||
| 592 | /* close the chunk-queue with a empty chunk */ | |
| 593 | ||
| 594 | return 1; | |
| 595 | default: | |
| 596 | /* oops */ | |
| 597 | return -1; | |
| 598 | } | |
| 599 | ||
| 600 | /* looks like we got some content | |
| 601 | * | |
| 602 | * split off the header from the incoming stream | |
| 603 | */ | |
| 604 | ||
| 605 | if (hctx->state == PROXY_STATE_RESPONSE_HEADER) { | |
| 606 | size_t i; | |
| 607 | int have_content_length = 0; | |
| 608 | ||
| 609 | http_response_reset(p->resp); | |
| 610 | ||
| 611 | /* the response header is not fully received yet, | |
| 612 | * | |
| 613 | * extract the http-response header from the rb-cq | |
| 614 | */ | |
| 615 | switch (http_response_parse_cq(hctx->rb, p->resp)) { | |
| 616 | case PARSE_ERROR: | |
| 617 | /* parsing failed */ | |
| 618 | ||
| 619 | con->http_status = 502; /* Bad Gateway */ | |
| 620 | return 1; | |
| 621 | case PARSE_NEED_MORE: | |
| 622 | return 0; | |
| 623 | case PARSE_SUCCESS: | |
| 624 | con->http_status = p->resp->status; | |
| 625 | ||
| 626 | chunkqueue_remove_finished_chunks(hctx->rb); | |
| 627 | ||
| 628 | /* copy the http-headers */ | |
| 629 | for (i = 0; i < p->resp->headers->used; i++) { | |
| 630 | const char *ign[] = { "Status", "Connection", NULL }; | |
| 631 | size_t j; | |
| 632 | data_string *ds; | |
| 633 | ||
| 634 | data_string *header = (data_string *)p->resp->headers->data[i]; | |
| 635 | ||
| 636 | /* some headers are ignored by default */ | |
| 637 | for (j = 0; ign[j]; j++) { | |
| 638 | if (0 == strcasecmp(ign[j], header->key->ptr)) break; | |
| 639 | } | |
| 640 | if (ign[j]) continue; | |
| 641 | ||
| 642 | if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Location"))) { | |
| 643 | /* CGI/1.1 rev 03 - 7.2.1.2 */ | |
| 644 | if (con->http_status == 0) con->http_status = 302; | |
| 645 | } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Content-Length"))) { | |
| 646 | have_content_length = 1; | |
| 647 | } | |
| 648 | ||
| 649 | if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { | |
| 650 | ds = data_response_init(); | |
| 651 | } | |
| 652 | buffer_copy_string_buffer(ds->key, header->key); | |
| 653 | buffer_copy_string_buffer(ds->value, header->value); | |
| 654 | ||
| 655 | array_insert_unique(con->response.headers, (data_unset *)ds); | |
| 656 | } | |
| 657 | ||
| 658 | con->file_started = 1; | |
| 659 | ||
| 660 | hctx->state = PROXY_STATE_RESPONSE_CONTENT; | |
| 661 | break; | |
| 662 | } | |
| 663 | } | |
| 664 | ||
| 665 | /* FIXME: pass the response-header to the other plugins to | |
| 666 | * setup the filter-queue | |
| 667 | * | |
| 668 | * - use next-queue instead of con->write_queue | |
| 669 | */ | |
| 670 | ||
| 671 | next_queue = con->send; | |
| 672 | ||
| 673 | assert(hctx->state == PROXY_STATE_RESPONSE_CONTENT); | |
| 674 | ||
| 675 | /* FIXME: if we have a content-length or chunked-encoding | |
| 676 | * handle it. | |
| 677 | * | |
| 678 | * for now we wait for EOF on the socket */ | |
| 679 | ||
| 680 | /* copy the content to the next cq */ | |
| 681 | chunkqueue_steal_all_chunks(con->send, hctx->rb); | |
| 682 | ||
| 683 | chunkqueue_remove_finished_chunks(hctx->rb); | |
| 684 | joblist_append(srv, con); | |
| 685 | ||
| 686 | return 0; | |
| 687 | } | |
| 688 | ||
| 689 | ||
| 690 | static handler_t proxy_write_request(server *srv, handler_ctx *hctx) { | |
| 691 | data_proxy *host= hctx->host; | |
| 692 | plugin_data *p = hctx->plugin_data; | |
| 693 | connection *con = hctx->remote_conn; | |
| 694 | ||
| 695 | int ret; | |
| 696 | ||
| 697 | if (!host || | |
| 698 | (!host->host->used || !host->port)) return -1; | |
| 699 | ||
| 700 | switch(hctx->state) { | |
| 701 | case PROXY_STATE_INIT: | |
| 702 | if (-1 == (hctx->sock->fd = socket(AF_INET, SOCK_STREAM, 0))) { | |
| 703 | log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); | |
| 704 | return HANDLER_ERROR; | |
| 705 | } | |
| 706 | hctx->sock->fde_ndx = -1; | |
| 707 | ||
| 708 | srv->cur_fds++; | |
| 709 | ||
| 710 | fdevent_register(srv->ev, hctx->sock, proxy_handle_fdevent, hctx); | |
| 711 | ||
| 712 | if (-1 == fdevent_fcntl_set(srv->ev, hctx->sock)) { | |
| 713 | log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); | |
| 714 | ||
| 715 | return HANDLER_ERROR; | |
| 716 | } | |
| 717 | ||
| 718 | /* fall through */ | |
| 719 | ||
| 720 | case PROXY_STATE_CONNECT: | |
| 721 | /* try to finish the connect() */ | |
| 722 | if (hctx->state == PROXY_STATE_INIT) { | |
| 723 | /* first round */ | |
| 724 | switch (proxy_establish_connection(srv, hctx)) { | |
| 725 | case 1: | |
| 726 | proxy_set_state(srv, hctx, PROXY_STATE_CONNECT); | |
| 727 | ||
| 728 | /* connection is in progress, wait for an event and call getsockopt() below */ | |
| 729 | ||
| 730 | fdevent_event_add(srv->ev, hctx->sock, FDEVENT_OUT); | |
| 731 | ||
| 732 | return HANDLER_WAIT_FOR_EVENT; | |
| 733 | case -1: | |
| 734 | /* if ECONNREFUSED choose another connection -> FIXME */ | |
| 735 | hctx->sock->fde_ndx = -1; | |
| 736 | ||
| 737 | return HANDLER_ERROR; | |
| 738 | default: | |
| 739 | /* everything is ok, go on */ | |
| 740 | break; | |
| 741 | } | |
| 742 | } else { | |
| 743 | int socket_error; | |
| 744 | socklen_t socket_error_len = sizeof(socket_error); | |
| 745 | ||
| 746 | /* we don't need it anymore */ | |
| 747 | fdevent_event_del(srv->ev, hctx->sock); | |
| 748 | ||
| 749 | /* try to finish the connect() */ | |
| 750 | if (0 != getsockopt(hctx->sock->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { | |
| 751 | log_error_write(srv, __FILE__, __LINE__, "ss", | |
| 752 | "getsockopt failed:", strerror(errno)); | |
| 753 | ||
| 754 | return HANDLER_ERROR; | |
| 755 | } | |
| 756 | if (socket_error != 0) { | |
| 757 | log_error_write(srv, __FILE__, __LINE__, "ss", | |
| 758 | "establishing connection failed:", strerror(socket_error), | |
| 759 | "port:", hctx->host->port); | |
| 760 | ||
| 761 | return HANDLER_ERROR; | |
| 762 | } | |
| 763 | if (p->conf.debug) { | |
| 764 | log_error_write(srv, __FILE__, __LINE__, "s", "proxy - connect - delayed success"); | |
| 765 | } | |
| 766 | } | |
| 767 | ||
| 768 | proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE); | |
| 769 | /* fall through */ | |
| 770 | case PROXY_STATE_PREPARE_WRITE: | |
| 771 | proxy_create_env(srv, hctx); | |
| 772 | ||
| 773 | proxy_set_state(srv, hctx, PROXY_STATE_WRITE); | |
| 774 | ||
| 775 | /* fall through */ | |
| 776 | case PROXY_STATE_WRITE:; | |
| 777 | ret = srv->network_backend_write(srv, con, hctx->sock, hctx->wb); | |
| 778 | ||
| 779 | chunkqueue_remove_finished_chunks(hctx->wb); | |
| 780 | ||
| 781 | switch(ret) { | |
| 782 | case NETWORK_STATUS_FATAL_ERROR: | |
| 783 | log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno); | |
| 784 | ||
| 785 | return HANDLER_ERROR; | |
| 786 | case NETWORK_STATUS_WAIT_FOR_EVENT: | |
| 787 | ||
| 788 | fdevent_event_add(srv->ev, hctx->sock, FDEVENT_OUT); | |
| 789 | ||
| 790 | return HANDLER_WAIT_FOR_EVENT; | |
| 791 | } | |
| 792 | ||
| 793 | if (hctx->wb->bytes_out == hctx->wb->bytes_in) { | |
| 794 | proxy_set_state(srv, hctx, PROXY_STATE_RESPONSE_HEADER); | |
| 795 | ||
| 796 | fdevent_event_del(srv->ev, hctx->sock); | |
| 797 | fdevent_event_add(srv->ev, hctx->sock, FDEVENT_IN); | |
| 798 | } else { | |
| 799 | fdevent_event_add(srv->ev, hctx->sock, FDEVENT_OUT); | |
| 800 | ||
| 801 | return HANDLER_WAIT_FOR_EVENT; | |
| 802 | } | |
| 803 | ||
| 804 | return HANDLER_WAIT_FOR_EVENT; | |
| 805 | case PROXY_STATE_RESPONSE_CONTENT: | |
| 806 | case PROXY_STATE_RESPONSE_HEADER: | |
| 807 | /* waiting for a response */ | |