1 #include "first.h" 2 3 #include <string.h> 4 #include <stdlib.h> 5 6 #include "gw_backend.h" 7 #include "base.h" 8 #include "array.h" 9 #include "buffer.h" 10 #include "http_kv.h" 11 #include "http_header.h" 12 #include "log.h" 13 #include "sock_addr.h" 14 #include "status_counter.h" 15 16 /** 17 * 18 * HTTP reverse proxy 19 * 20 * TODO: - HTTP/1.1 21 * - HTTP/1.1 persistent connection with upstream servers 22 */ 23 24 /* (future: might split struct and move part to http-header-glue.c) */ 25 typedef struct http_header_remap_opts { 26 const array *urlpaths; 27 const array *hosts_request; 28 const array *hosts_response; 29 int https_remap; 30 int upgrade; 31 int connect_method; 32 /*(not used in plugin_config, but used in handler_ctx)*/ 33 const buffer *http_host; 34 const buffer *forwarded_host; 35 const data_string *forwarded_urlpath; 36 } http_header_remap_opts; 37 38 typedef enum { 39 PROXY_FORWARDED_NONE = 0x00, 40 PROXY_FORWARDED_FOR = 0x01, 41 PROXY_FORWARDED_PROTO = 0x02, 42 PROXY_FORWARDED_HOST = 0x04, 43 PROXY_FORWARDED_BY = 0x08, 44 PROXY_FORWARDED_REMOTE_USER = 0x10 45 } proxy_forwarded_t; 46 47 typedef struct { 48 gw_plugin_config gw; /* start must match layout of gw_plugin_config */ 49 unsigned int replace_http_host; 50 unsigned int forwarded; 51 http_header_remap_opts header; 52 } plugin_config; 53 54 typedef struct { 55 PLUGIN_DATA; 56 pid_t srv_pid; /* must match layout of gw_plugin_data through conf member */ 57 plugin_config conf; 58 plugin_config defaults; 59 } plugin_data; 60 61 static int proxy_check_extforward; 62 63 typedef struct { 64 gw_handler_ctx gw; 65 http_response_opts opts; 66 plugin_config conf; 67 } handler_ctx; 68 69 70 INIT_FUNC(mod_proxy_init) { 71 return calloc(1, sizeof(plugin_data)); 72 } 73 74 75 static void mod_proxy_free_config(plugin_data * const p) 76 { 77 if (NULL == p->cvlist) return; 78 /* (init i to 0 if global context; to 1 to skip empty global context) */ 79 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 80 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 81 for (; -1 != cpv->k_id; ++cpv) { 82 switch (cpv->k_id) { 83 case 5: /* proxy.header */ 84 if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v); 85 break; 86 default: 87 break; 88 } 89 } 90 } 91 } 92 93 94 FREE_FUNC(mod_proxy_free) { 95 plugin_data * const p = p_d; 96 mod_proxy_free_config(p); 97 gw_free(p); 98 } 99 100 static void mod_proxy_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) 101 { 102 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 103 case 0: /* proxy.server */ 104 if (cpv->vtype == T_CONFIG_LOCAL) { 105 gw_plugin_config * const gw = cpv->v.v; 106 pconf->gw.exts = gw->exts; 107 pconf->gw.exts_auth = gw->exts_auth; 108 pconf->gw.exts_resp = gw->exts_resp; 109 } 110 break; 111 case 1: /* proxy.balance */ 112 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ 113 pconf->gw.balance = (int)cpv->v.u; 114 break; 115 case 2: /* proxy.debug */ 116 pconf->gw.debug = (int)cpv->v.u; 117 break; 118 case 3: /* proxy.map-extensions */ 119 pconf->gw.ext_mapping = cpv->v.a; 120 break; 121 case 4: /* proxy.forwarded */ 122 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ 123 pconf->forwarded = cpv->v.u; 124 break; 125 case 5: /* proxy.header */ 126 /*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ 127 pconf->header = *(http_header_remap_opts *)cpv->v.v; /*(copies struct)*/ 128 break; 129 case 6: /* proxy.replace-http-host */ 130 pconf->replace_http_host = cpv->v.u; 131 break; 132 default:/* should not happen */ 133 return; 134 } 135 } 136 137 138 static void mod_proxy_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) 139 { 140 do { 141 mod_proxy_merge_config_cpv(pconf, cpv); 142 } while ((++cpv)->k_id != -1); 143 } 144 145 146 static void mod_proxy_patch_config(request_st * const r, plugin_data * const p) 147 { 148 memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); 149 for (int i = 1, used = p->nconfig; i < used; ++i) { 150 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 151 mod_proxy_merge_config(&p->conf, p->cvlist+p->cvlist[i].v.u2[0]); 152 } 153 } 154 155 156 static unsigned int mod_proxy_parse_forwarded(server *srv, const array *a) 157 { 158 unsigned int forwarded = 0; 159 for (uint32_t j = 0, used = a->used; j < used; ++j) { 160 proxy_forwarded_t param; 161 data_unset *du = a->data[j]; 162 if (buffer_eq_slen(&du->key, CONST_STR_LEN("by"))) 163 param = PROXY_FORWARDED_BY; 164 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for"))) 165 param = PROXY_FORWARDED_FOR; 166 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("host"))) 167 param = PROXY_FORWARDED_HOST; 168 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto"))) 169 param = PROXY_FORWARDED_PROTO; 170 else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user"))) 171 param = PROXY_FORWARDED_REMOTE_USER; 172 else { 173 log_error(srv->errh, __FILE__, __LINE__, 174 "proxy.forwarded keys must be one of: " 175 "by, for, host, proto, remote_user, but not: %s", du->key.ptr); 176 return UINT_MAX; 177 } 178 if (du->type == TYPE_STRING) { 179 data_string *ds = (data_string *)du; 180 if (buffer_eq_slen(&ds->value, CONST_STR_LEN("enable"))) 181 forwarded |= param; 182 else if (!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))) { 183 log_error(srv->errh, __FILE__, __LINE__, 184 "proxy.forwarded values must be one of: " 185 "0, 1, enable, disable; error for key: %s", du->key.ptr); 186 return UINT_MAX; 187 } 188 } 189 else if (du->type == TYPE_INTEGER) { 190 data_integer *di = (data_integer *)du; 191 if (di->value) forwarded |= param; 192 } 193 else { 194 log_error(srv->errh, __FILE__, __LINE__, 195 "proxy.forwarded values must be one of: " 196 "0, 1, enable, disable; error for key: %s", du->key.ptr); 197 return UINT_MAX; 198 } 199 } 200 return forwarded; 201 } 202 203 204 static http_header_remap_opts * mod_proxy_parse_header_opts(server *srv, const array *a) 205 { 206 http_header_remap_opts header; 207 memset(&header, 0, sizeof(header)); 208 for (uint32_t j = 0, used = a->used; j < used; ++j) { 209 data_array *da = (data_array *)a->data[j]; 210 if (buffer_eq_slen(&da->key, CONST_STR_LEN("https-remap"))) { 211 data_string *ds = (data_string *)da; 212 if (ds->type != TYPE_STRING) { 213 log_error(srv->errh, __FILE__, __LINE__, 214 "unexpected value for proxy.header; " 215 "expected \"enable\" or \"disable\" for https-remap"); 216 return NULL; 217 } 218 header.https_remap = 219 !buffer_eq_slen(&ds->value, CONST_STR_LEN("disable")) 220 && !buffer_eq_slen(&ds->value, CONST_STR_LEN("0")); 221 continue; 222 } 223 else if (buffer_eq_slen(&da->key, CONST_STR_LEN("upgrade"))) { 224 data_string *ds = (data_string *)da; 225 if (ds->type != TYPE_STRING) { 226 log_error(srv->errh, __FILE__, __LINE__, 227 "unexpected value for proxy.header; " 228 "expected \"upgrade\" => \"enable\" or \"disable\""); 229 return NULL; 230 } 231 header.upgrade = 232 !buffer_eq_slen(&ds->value, CONST_STR_LEN("disable")) 233 && !buffer_eq_slen(&ds->value, CONST_STR_LEN("0")); 234 continue; 235 } 236 else if (buffer_eq_slen(&da->key, CONST_STR_LEN("connect"))) { 237 data_string *ds = (data_string *)da; 238 if (ds->type != TYPE_STRING) { 239 log_error(srv->errh, __FILE__, __LINE__, 240 "unexpected value for proxy.header; " 241 "expected \"connect\" => \"enable\" or \"disable\""); 242 return NULL; 243 } 244 header.connect_method = 245 !buffer_eq_slen(&ds->value, CONST_STR_LEN("disable")) 246 && !buffer_eq_slen(&ds->value, CONST_STR_LEN("0")); 247 continue; 248 } 249 if (da->type != TYPE_ARRAY || !array_is_kvstring(&da->value)) { 250 log_error(srv->errh, __FILE__, __LINE__, 251 "unexpected value for proxy.header; " 252 "expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s", 253 da->key.ptr); 254 return NULL; 255 } 256 if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-urlpath"))) { 257 header.urlpaths = &da->value; 258 } 259 else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-request"))) { 260 header.hosts_request = &da->value; 261 } 262 else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-response"))) { 263 header.hosts_response = &da->value; 264 } 265 else { 266 log_error(srv->errh, __FILE__, __LINE__, 267 "unexpected key for proxy.header; " 268 "expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s", 269 da->key.ptr); 270 return NULL; 271 } 272 } 273 274 http_header_remap_opts *opts = malloc(sizeof(header)); 275 force_assert(opts); 276 memcpy(opts, &header, sizeof(header)); 277 return opts; 278 } 279 280 281 SETDEFAULTS_FUNC(mod_proxy_set_defaults) 282 { 283 static const config_plugin_keys_t cpk[] = { 284 { CONST_STR_LEN("proxy.server"), 285 T_CONFIG_ARRAY_KVARRAY, 286 T_CONFIG_SCOPE_CONNECTION } 287 ,{ CONST_STR_LEN("proxy.balance"), 288 T_CONFIG_STRING, 289 T_CONFIG_SCOPE_CONNECTION } 290 ,{ CONST_STR_LEN("proxy.debug"), 291 T_CONFIG_INT, 292 T_CONFIG_SCOPE_CONNECTION } 293 ,{ CONST_STR_LEN("proxy.map-extensions"), 294 T_CONFIG_ARRAY_KVSTRING, 295 T_CONFIG_SCOPE_CONNECTION } 296 ,{ CONST_STR_LEN("proxy.forwarded"), 297 T_CONFIG_ARRAY_KVANY, 298 T_CONFIG_SCOPE_CONNECTION } 299 ,{ CONST_STR_LEN("proxy.header"), 300 T_CONFIG_ARRAY_KVANY, 301 T_CONFIG_SCOPE_CONNECTION } 302 ,{ CONST_STR_LEN("proxy.replace-http-host"), 303 T_CONFIG_BOOL, 304 T_CONFIG_SCOPE_CONNECTION } 305 ,{ NULL, 0, 306 T_CONFIG_UNSET, 307 T_CONFIG_SCOPE_UNSET } 308 }; 309 310 plugin_data * const p = p_d; 311 if (!config_plugin_values_init(srv, p, cpk, "mod_proxy")) 312 return HANDLER_ERROR; 313 314 /* process and validate config directives 315 * (init i to 0 if global context; to 1 to skip empty global context) */ 316 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 317 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 318 gw_plugin_config *gw = NULL; 319 for (; -1 != cpv->k_id; ++cpv) { 320 switch (cpv->k_id) { 321 case 0: /* proxy.server */ 322 gw = calloc(1, sizeof(gw_plugin_config)); 323 force_assert(gw); 324 if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, cpv->v.a, 325 gw, 0, cpk[cpv->k_id].k)) { 326 gw_plugin_config_free(gw); 327 return HANDLER_ERROR; 328 } 329 /* error if "mode" = "authorizer"; 330 * proxy can not act as authorizer */ 331 /*(check after gw_set_defaults_backend())*/ 332 if (gw->exts_auth && gw->exts_auth->used) { 333 log_error(srv->errh, __FILE__, __LINE__, 334 "%s must not define any hosts with " 335 "attribute \"mode\" = \"authorizer\"", cpk[cpv->k_id].k); 336 gw_plugin_config_free(gw); 337 return HANDLER_ERROR; 338 } 339 cpv->v.v = gw; 340 cpv->vtype = T_CONFIG_LOCAL; 341 break; 342 case 1: /* proxy.balance */ 343 cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b); 344 break; 345 case 2: /* proxy.debug */ 346 case 3: /* proxy.map-extensions */ 347 break; 348 case 4: /* proxy.forwarded */ 349 cpv->v.u = mod_proxy_parse_forwarded(srv, cpv->v.a); 350 if (UINT_MAX == cpv->v.u) return HANDLER_ERROR; 351 cpv->vtype = T_CONFIG_LOCAL; 352 break; 353 case 5: /* proxy.header */ 354 cpv->v.v = mod_proxy_parse_header_opts(srv, cpv->v.a); 355 if (NULL == cpv->v.v) return HANDLER_ERROR; 356 cpv->vtype = T_CONFIG_LOCAL; 357 break; 358 case 6: /* proxy.replace-http-host */ 359 break; 360 default:/* should not happen */ 361 break; 362 } 363 } 364 365 /* disable check-local for all exts (default enabled) */ 366 if (gw && gw->exts) { /*(check after gw_set_defaults_backend())*/ 367 gw_exts_clear_check_local(gw->exts); 368 } 369 } 370 371 /* default is 0 */ 372 /*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/ 373 374 /* initialize p->defaults from global config context */ 375 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 376 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 377 if (-1 != cpv->k_id) 378 mod_proxy_merge_config(&p->defaults, cpv); 379 } 380 381 /* special-case behavior if mod_extforward is loaded */ 382 for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { 383 buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value; 384 if (buffer_eq_slen(m, CONST_STR_LEN("mod_extforward"))) { 385 proxy_check_extforward = 1; 386 break; 387 } 388 } 389 390 return HANDLER_GO_ON; 391 } 392 393 394 /* (future: might move to http-header-glue.c) */ 395 static const buffer * http_header_remap_host_match (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen) 396 { 397 const array *hosts = is_req 398 ? remap_hdrs->hosts_request 399 : remap_hdrs->hosts_response; 400 if (hosts) { 401 const char * const s = b->ptr+off; 402 for (size_t i = 0, used = hosts->used; i < used; ++i) { 403 const data_string * const ds = (data_string *)hosts->data[i]; 404 const buffer *k = &ds->key; 405 size_t mlen = buffer_string_length(k); 406 if (1 == mlen && k->ptr[0] == '-') { 407 /* match with authority provided in Host (if is_req) 408 * (If no Host in client request, then matching against empty 409 * string will probably not match, and no remap will be 410 * performed) */ 411 k = is_req 412 ? remap_hdrs->http_host 413 : remap_hdrs->forwarded_host; 414 if (NULL == k) continue; 415 mlen = buffer_string_length(k); 416 } 417 if (buffer_eq_icase_ss(s, alen, k->ptr, mlen)) { 418 if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("-"))) { 419 return remap_hdrs->http_host; 420 } 421 else if (!buffer_string_is_empty(&ds->value)) { 422 /*(save first matched request host for response match)*/ 423 if (is_req && NULL == remap_hdrs->forwarded_host) 424 remap_hdrs->forwarded_host = &ds->value; 425 return &ds->value; 426 } /*(else leave authority as-is and stop matching)*/ 427 break; 428 } 429 } 430 } 431 return NULL; 432 } 433 434 435 /* (future: might move to http-header-glue.c) */ 436 static size_t http_header_remap_host (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen) 437 { 438 const buffer * const m = 439 http_header_remap_host_match(b, off, remap_hdrs, is_req, alen); 440 if (NULL == m) return alen; /*(no match; return original authority length)*/ 441 442 buffer_substr_replace(b, off, alen, m); 443 return buffer_string_length(m); /*(length of replacement authority)*/ 444 } 445 446 447 /* (future: might move to http-header-glue.c) */ 448 static size_t http_header_remap_urlpath (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req) 449 { 450 const array *urlpaths = remap_hdrs->urlpaths; 451 if (urlpaths) { 452 const char * const s = b->ptr+off; 453 const size_t plen = buffer_string_length(b) - off; /*(urlpath len)*/ 454 if (is_req) { /* request */ 455 for (size_t i = 0, used = urlpaths->used; i < used; ++i) { 456 const data_string * const ds = (data_string *)urlpaths->data[i]; 457 const size_t mlen = buffer_string_length(&ds->key); 458 if (mlen <= plen && 0 == memcmp(s, ds->key.ptr, mlen)) { 459 if (NULL == remap_hdrs->forwarded_urlpath) 460 remap_hdrs->forwarded_urlpath = ds; 461 buffer_substr_replace(b, off, mlen, &ds->value); 462 return buffer_string_length(&ds->value);/*(replacement len)*/ 463 } 464 } 465 } 466 else { /* response; perform reverse map */ 467 if (NULL != remap_hdrs->forwarded_urlpath) { 468 const data_string * const ds = remap_hdrs->forwarded_urlpath; 469 const size_t mlen = buffer_string_length(&ds->value); 470 if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) { 471 buffer_substr_replace(b, off, mlen, &ds->key); 472 return buffer_string_length(&ds->key); /*(replacement len)*/ 473 } 474 } 475 for (size_t i = 0, used = urlpaths->used; i < used; ++i) { 476 const data_string * const ds = (data_string *)urlpaths->data[i]; 477 const size_t mlen = buffer_string_length(&ds->value); 478 if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) { 479 buffer_substr_replace(b, off, mlen, &ds->key); 480 return buffer_string_length(&ds->key); /*(replacement len)*/ 481 } 482 } 483 } 484 } 485 return 0; 486 } 487 488 489 /* (future: might move to http-header-glue.c) */ 490 static void http_header_remap_uri (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req) 491 { 492 /* find beginning of URL-path (might be preceded by scheme://authority 493 * (caller should make sure any leading whitespace is prior to offset) */ 494 if (b->ptr[off] != '/') { 495 char *s = b->ptr+off; 496 size_t alen; /*(authority len (host len))*/ 497 size_t slen; /*(scheme len)*/ 498 const buffer *m; 499 /* skip over scheme and authority of URI to find beginning of URL-path 500 * (value might conceivably be relative URL-path instead of URI) */ 501 if (NULL == (s = strchr(s, ':')) || s[1] != '/' || s[2] != '/') return; 502 slen = s - (b->ptr+off); 503 s += 3; 504 off = (size_t)(s - b->ptr); 505 if (NULL != (s = strchr(s, '/'))) { 506 alen = (size_t)(s - b->ptr) - off; 507 if (0 == alen) return; /*(empty authority, e.g. "http:///")*/ 508 } 509 else { 510 alen = buffer_string_length(b) - off; 511 if (0 == alen) return; /*(empty authority, e.g. "http:///")*/ 512 buffer_append_string_len(b, CONST_STR_LEN("/")); 513 } 514 515 /* remap authority (if configured) and set offset to url-path */ 516 m = http_header_remap_host_match(b, off, remap_hdrs, is_req, alen); 517 if (NULL != m) { 518 if (remap_hdrs->https_remap 519 && (is_req ? 5==slen && 0==memcmp(b->ptr+off-slen-3,"https",5) 520 : 4==slen && 0==memcmp(b->ptr+off-slen-3,"http",4))){ 521 if (is_req) { 522 memcpy(b->ptr+off-slen-3+4,"://",3); /*("https"=>"http")*/ 523 --off; 524 ++alen; 525 } 526 else {/*(!is_req)*/ 527 memcpy(b->ptr+off-slen-3+4,"s://",4); /*("http" =>"https")*/ 528 ++off; 529 --alen; 530 } 531 } 532 buffer_substr_replace(b, off, alen, m); 533 alen = buffer_string_length(m);/*(length of replacement authority)*/ 534 } 535 off += alen; 536 } 537 538 /* remap URLs (if configured) */ 539 http_header_remap_urlpath(b, off, remap_hdrs, is_req); 540 } 541 542 543 /* (future: might move to http-header-glue.c) */ 544 static void http_header_remap_setcookie (buffer *b, size_t off, http_header_remap_opts *remap_hdrs) 545 { 546 /* Given the special-case of Set-Cookie and the (too) loosely restricted 547 * characters allowed, for best results, the Set-Cookie value should be the 548 * entire string in b from offset to end of string. In response headers, 549 * lighttpd may concatenate multiple Set-Cookie headers into single entry 550 * in r->resp_headers, separated by "\r\nSet-Cookie: " */ 551 for (char *s = b->ptr+off, *e; *s; s = e) { 552 size_t len; 553 { 554 while (*s != ';' && *s != '\n' && *s != '\0') ++s; 555 if (*s == '\n') { 556 /*(include +1 for '\n', but leave ' ' for ++s below)*/ 557 s += sizeof("Set-Cookie:"); 558 } 559 if ('\0' == *s) return; 560 do { ++s; } while (*s == ' ' || *s == '\t'); 561 if ('\0' == *s) return; 562 e = s+1; 563 if ('=' == *s) continue; 564 /*(interested only in Domain and Path attributes)*/ 565 while (*e != '=' && *e != '\0') ++e; 566 if ('\0' == *e) return; 567 ++e; 568 switch ((int)(e - s - 1)) { 569 case 4: 570 if (buffer_eq_icase_ssn(s, "path", 4)) { 571 if (*e == '"') ++e; 572 if (*e != '/') continue; 573 off = (size_t)(e - b->ptr); 574 len = http_header_remap_urlpath(b, off, remap_hdrs, 0); 575 e = b->ptr+off+len; /*(b may have been reallocated)*/ 576 continue; 577 } 578 break; 579 case 6: 580 if (buffer_eq_icase_ssn(s, "domain", 6)) { 581 size_t alen = 0; 582 if (*e == '"') ++e; 583 if (*e == '.') ++e; 584 if (*e == ';') continue; 585 off = (size_t)(e - b->ptr); 586 for (char c; (c = e[alen]) != ';' && c != ' ' && c != '\t' 587 && c != '\r' && c != '\0'; ++alen); 588 len = http_header_remap_host(b, off, remap_hdrs, 0, alen); 589 e = b->ptr+off+len; /*(b may have been reallocated)*/ 590 continue; 591 } 592 break; 593 default: 594 break; 595 } 596 } 597 } 598 } 599 600 601 static void buffer_append_string_backslash_escaped(buffer *b, const char *s, size_t len) { 602 /* (future: might move to buffer.c) */ 603 size_t j = 0; 604 char *p; 605 606 buffer_string_prepare_append(b, len*2 + 4); 607 p = b->ptr + buffer_string_length(b); 608 609 for (size_t i = 0; i < len; ++i) { 610 int c = s[i]; 611 if (c == '"' || c == '\\' || c == 0x7F || (c < 0x20 && c != '\t')) 612 p[j++] = '\\'; 613 p[j++] = c; 614 } 615 616 buffer_commit(b, j); 617 } 618 619 static void proxy_set_Forwarded(connection * const con, request_st * const r, const unsigned int flags) { 620 buffer *b = NULL, *efor = NULL, *eproto = NULL, *ehost = NULL; 621 int semicolon = 0; 622 623 if (proxy_check_extforward) { 624 efor = 625 http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR")); 626 eproto = 627 http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO")); 628 ehost = 629 http_header_env_get(r, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST")); 630 } 631 632 /* note: set "Forwarded" prior to updating X-Forwarded-For (below) */ 633 634 if (flags) 635 b = http_header_request_get(r, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded")); 636 637 if (flags && NULL == b) { 638 const buffer *xff = 639 http_header_request_get(r, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For")); 640 http_header_request_set(r, HTTP_HEADER_FORWARDED, 641 CONST_STR_LEN("Forwarded"), 642 CONST_STR_LEN("x")); /*(must not be blank for _get below)*/ 643 #ifdef __COVERITY__ 644 force_assert(NULL != b); /*(not NULL because created directly above)*/ 645 #endif 646 b = http_header_request_get(r, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded")); 647 buffer_clear(b); 648 if (NULL != xff) { 649 /* use X-Forwarded-For contents to seed Forwarded */ 650 char *s = xff->ptr; 651 size_t used = buffer_string_length(xff); 652 for (size_t i=0, j, ipv6; i < used; ++i) { 653 while (s[i] == ' ' || s[i] == '\t' || s[i] == ',') ++i; 654 if (s[i] == '\0') break; 655 j = i; 656 do { 657 ++i; 658 } while (s[i]!=' ' && s[i]!='\t' && s[i]!=',' && s[i]!='\0'); 659 buffer_append_string_len(b, CONST_STR_LEN("for=")); 660 /* over-simplified test expecting only IPv4 or IPv6 addresses, 661 * (not expecting :port, so treat existence of colon as IPv6, 662 * and not expecting unix paths, especially not containing ':') 663 * quote all strings, backslash-escape since IPs not validated*/ 664 ipv6 = (NULL != memchr(s+j, ':', i-j)); /*(over-simplified) */ 665 buffer_append_string_len(b, CONST_STR_LEN("\"")); 666 if (ipv6) 667 buffer_append_string_len(b, CONST_STR_LEN("[")); 668 buffer_append_string_backslash_escaped(b, s+j, i-j); 669 if (ipv6) 670 buffer_append_string_len(b, CONST_STR_LEN("]")); 671 buffer_append_string_len(b, CONST_STR_LEN("\"")); 672 buffer_append_string_len(b, CONST_STR_LEN(", ")); 673 } 674 } 675 } else if (flags) { /*(NULL != b)*/ 676 buffer_append_string_len(b, CONST_STR_LEN(", ")); 677 } 678 679 if (flags & PROXY_FORWARDED_FOR) { 680 int family = sock_addr_get_family(&con->dst_addr); 681 buffer_append_string_len(b, CONST_STR_LEN("for=")); 682 if (NULL != efor) { 683 /* over-simplified test expecting only IPv4 or IPv6 addresses, 684 * (not expecting :port, so treat existence of colon as IPv6, 685 * and not expecting unix paths, especially not containing ':') 686 * quote all strings and backslash-escape since IPs not validated 687 * (should be IP from original con->dst_addr_buf, 688 * so trustable and without :port) */ 689 int ipv6 = (NULL != strchr(efor->ptr, ':')); 690 buffer_append_string_len(b, CONST_STR_LEN("\"")); 691 if (ipv6) buffer_append_string_len(b, CONST_STR_LEN("[")); 692 buffer_append_string_backslash_escaped( 693 b, CONST_BUF_LEN(efor)); 694 if (ipv6) buffer_append_string_len(b, CONST_STR_LEN("]")); 695 buffer_append_string_len(b, CONST_STR_LEN("\"")); 696 } else if (family == AF_INET) { 697 /*(Note: if :port is added, then must be quoted-string: 698 * e.g. for="...:port")*/ 699 buffer_append_string_buffer(b, con->dst_addr_buf); 700 } else if (family == AF_INET6) { 701 buffer_append_string_len(b, CONST_STR_LEN("\"[")); 702 buffer_append_string_buffer(b, con->dst_addr_buf); 703 buffer_append_string_len(b, CONST_STR_LEN("]\"")); 704 } else { 705 buffer_append_string_len(b, CONST_STR_LEN("\"")); 706 buffer_append_string_backslash_escaped( 707 b, CONST_BUF_LEN(con->dst_addr_buf)); 708 buffer_append_string_len(b, CONST_STR_LEN("\"")); 709 } 710 semicolon = 1; 711 } 712 713 if (flags & PROXY_FORWARDED_BY) { 714 int family = sock_addr_get_family(&con->srv_socket->addr); 715 /* Note: getsockname() and inet_ntop() are expensive operations. 716 * (recommendation: do not to enable by=... unless required) 717 * future: might use con->srv_socket->srv_token if addr is not 718 * INADDR_ANY or in6addr_any, but must omit optional :port 719 * from con->srv_socket->srv_token for consistency */ 720 721 if (semicolon) buffer_append_string_len(b, CONST_STR_LEN(";")); 722 buffer_append_string_len(b, CONST_STR_LEN("by=")); 723 buffer_append_string_len(b, CONST_STR_LEN("\"")); 724 #ifdef HAVE_SYS_UN_H 725 /* special-case: might need to encode unix domain socket path */ 726 if (family == AF_UNIX) { 727 buffer_append_string_backslash_escaped( 728 b, CONST_BUF_LEN(con->srv_socket->srv_token)); 729 } 730 else 731 #endif 732 { 733 sock_addr addr; 734 socklen_t addrlen = sizeof(addr); 735 if (0 == getsockname(con->fd,(struct sockaddr *)&addr, &addrlen)) { 736 sock_addr_stringify_append_buffer(b, &addr); 737 } 738 } 739 buffer_append_string_len(b, CONST_STR_LEN("\"")); 740 semicolon = 1; 741 } 742 743 if (flags & PROXY_FORWARDED_PROTO) { 744 /* expecting "http" or "https" 745 * (not checking if quoted-string and encoding needed) */ 746 if (semicolon) buffer_append_string_len(b, CONST_STR_LEN(";")); 747 buffer_append_string_len(b, CONST_STR_LEN("proto=")); 748 if (NULL != eproto) { 749 buffer_append_string_buffer(b, eproto); 750 } else if (con->srv_socket->is_ssl) { 751 buffer_append_string_len(b, CONST_STR_LEN("https")); 752 } else { 753 buffer_append_string_len(b, CONST_STR_LEN("http")); 754 } 755 semicolon = 1; 756 } 757 758 if (flags & PROXY_FORWARDED_HOST) { 759 if (NULL != ehost) { 760 if (semicolon) 761 buffer_append_string_len(b, CONST_STR_LEN(";")); 762 buffer_append_string_len(b, CONST_STR_LEN("host=\"")); 763 buffer_append_string_backslash_escaped( 764 b, CONST_BUF_LEN(ehost)); 765 buffer_append_string_len(b, CONST_STR_LEN("\"")); 766 semicolon = 1; 767 } else if (!buffer_string_is_empty(r->http_host)) { 768 if (semicolon) 769 buffer_append_string_len(b, CONST_STR_LEN(";")); 770 buffer_append_string_len(b, CONST_STR_LEN("host=\"")); 771 buffer_append_string_backslash_escaped( 772 b, CONST_BUF_LEN(r->http_host)); 773 buffer_append_string_len(b, CONST_STR_LEN("\"")); 774 semicolon = 1; 775 } 776 } 777 778 if (flags & PROXY_FORWARDED_REMOTE_USER) { 779 const buffer *remote_user = 780 http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")); 781 if (NULL != remote_user) { 782 if (semicolon) 783 buffer_append_string_len(b, CONST_STR_LEN(";")); 784 buffer_append_string_len(b, CONST_STR_LEN("remote_user=\"")); 785 buffer_append_string_backslash_escaped( 786 b, CONST_BUF_LEN(remote_user)); 787 buffer_append_string_len(b, CONST_STR_LEN("\"")); 788 semicolon = 1; 789 } 790 } 791 792 /* legacy X-* headers, including X-Forwarded-For */ 793 794 b = (NULL != efor) ? efor : con->dst_addr_buf; 795 http_header_request_set(r, HTTP_HEADER_X_FORWARDED_FOR, 796 CONST_STR_LEN("X-Forwarded-For"), 797 CONST_BUF_LEN(b)); 798 799 b = (NULL != ehost) ? ehost : r->http_host; 800 if (!buffer_string_is_empty(b)) { 801 http_header_request_set(r, HTTP_HEADER_OTHER, 802 CONST_STR_LEN("X-Host"), 803 CONST_BUF_LEN(b)); 804 http_header_request_set(r, HTTP_HEADER_OTHER, 805 CONST_STR_LEN("X-Forwarded-Host"), 806 CONST_BUF_LEN(b)); 807 } 808 809 b = (NULL != eproto) ? eproto : &r->uri.scheme; 810 http_header_request_set(r, HTTP_HEADER_X_FORWARDED_PROTO, 811 CONST_STR_LEN("X-Forwarded-Proto"), 812 CONST_BUF_LEN(b)); 813 } 814 815 816 static handler_t proxy_stdin_append(gw_handler_ctx *hctx) { 817 /*handler_ctx *hctx = (handler_ctx *)gwhctx;*/ 818 chunkqueue * const req_cq = hctx->r->reqbody_queue; 819 const off_t req_cqlen = req_cq->bytes_in - req_cq->bytes_out; 820 if (req_cqlen) { 821 /* XXX: future: use http_chunk_len_append() */ 822 buffer * const tb = hctx->r->tmp_buf; 823 buffer_clear(tb); 824 buffer_append_uint_hex_lc(tb, (uintmax_t)req_cqlen); 825 buffer_append_string_len(tb, CONST_STR_LEN("\r\n")); 826 827 const off_t len = (off_t)buffer_string_length(tb) 828 + 2 /*(+2 end chunk "\r\n")*/ 829 + req_cqlen; 830 if (-1 != hctx->wb_reqlen) 831 hctx->wb_reqlen += (hctx->wb_reqlen >= 0) ? len : -len; 832 833 (chunkqueue_is_empty(hctx->wb) || hctx->wb->first->type == MEM_CHUNK) 834 /* else FILE_CHUNK for temp file */ 835 ? chunkqueue_append_mem(hctx->wb, CONST_BUF_LEN(tb)) 836 : chunkqueue_append_mem_min(hctx->wb, CONST_BUF_LEN(tb)); 837 chunkqueue_steal(hctx->wb, req_cq, req_cqlen); 838 839 chunkqueue_append_mem_min(hctx->wb, CONST_STR_LEN("\r\n")); 840 } 841 842 if (hctx->wb->bytes_in == hctx->wb_reqlen) {/*hctx->r->reqbody_length >= 0*/ 843 /* terminate STDIN */ 844 chunkqueue_append_mem(hctx->wb, CONST_STR_LEN("0\r\n\r\n")); 845 hctx->wb_reqlen += (int)sizeof("0\r\n\r\n"); 846 } 847 848 return HANDLER_GO_ON; 849 } 850 851 852 static handler_t proxy_create_env(gw_handler_ctx *gwhctx) { 853 handler_ctx *hctx = (handler_ctx *)gwhctx; 854 request_st * const r = hctx->gw.r; 855 const int remap_headers = (NULL != hctx->conf.header.urlpaths 856 || NULL != hctx->conf.header.hosts_request); 857 const int upgrade = hctx->conf.header.upgrade 858 && (NULL != http_header_request_get(r, HTTP_HEADER_UPGRADE, CONST_STR_LEN("Upgrade"))); 859 size_t rsz = (size_t)(r->read_queue->bytes_out - hctx->gw.wb->bytes_in); 860 buffer * const b = chunkqueue_prepend_buffer_open_sz(hctx->gw.wb, rsz < 65536 ? rsz : r->rqst_header_len); 861 862 /* build header */ 863 864 /* request line */ 865 http_method_append(b, r->http_method); 866 buffer_append_string_len(b, CONST_STR_LEN(" ")); 867 buffer_append_string_buffer(b, &r->target); 868 if (remap_headers) 869 http_header_remap_uri(b, buffer_string_length(b) - buffer_string_length(&r->target), &hctx->conf.header, 1); 870 871 int stream_chunked = 0; 872 if (-1 == r->reqbody_length && r->conf.stream_request_body) { 873 stream_chunked = 1; 874 hctx->gw.stdin_append = proxy_stdin_append; 875 } 876 if (!stream_chunked && !upgrade) 877 buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n")); 878 else 879 buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.1\r\n")); 880 881 if (hctx->conf.replace_http_host && !buffer_string_is_empty(hctx->gw.host->id)) { 882 if (hctx->gw.conf.debug > 1) { 883 log_error(r->conf.errh, __FILE__, __LINE__, 884 "proxy - using \"%s\" as HTTP Host", hctx->gw.host->id->ptr); 885 } 886 buffer_append_string_len(b, CONST_STR_LEN("Host: ")); 887 buffer_append_string_buffer(b, hctx->gw.host->id); 888 buffer_append_string_len(b, CONST_STR_LEN("\r\n")); 889 } else if (!buffer_string_is_empty(r->http_host)) { 890 buffer_append_string_len(b, CONST_STR_LEN("Host: ")); 891 buffer_append_string_buffer(b, r->http_host); 892 if (remap_headers) { 893 size_t alen = buffer_string_length(r->http_host); 894 http_header_remap_host(b, buffer_string_length(b) - alen, &hctx->conf.header, 1, alen); 895 } 896 buffer_append_string_len(b, CONST_STR_LEN("\r\n")); 897 } 898 899 /* "Forwarded" and legacy X- headers */ 900 proxy_set_Forwarded(r->con, r, hctx->conf.forwarded); 901 902 if (r->reqbody_length > 0 903 || (0 == r->reqbody_length 904 && !http_method_get_or_head(r->http_method))) { 905 /* set Content-Length if client sent Transfer-Encoding: chunked 906 * and not streaming to backend (request body has been fully received) */ 907 const buffer *vb = http_header_request_get(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); 908 if (NULL == vb) { 909 char buf[LI_ITOSTRING_LENGTH]; 910 http_header_request_set(r, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"), 911 buf, li_itostrn(buf, sizeof(buf), r->reqbody_length)); 912 } 913 } 914 else if (stream_chunked) 915 buffer_append_string_len(b, CONST_STR_LEN("Transfer-Encoding: chunked\r\n")); 916 917 /* request header */ 918 for (size_t i = 0, used = r->rqst_headers.used; i < used; ++i) { 919 data_string *ds = (data_string *)r->rqst_headers.data[i]; 920 const size_t klen = buffer_string_length(&ds->key); 921 size_t vlen; 922 switch (klen) { 923 default: 924 break; 925 case 4: 926 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Host"))) continue; /*(handled further above)*/ 927 break; 928 case 10: 929 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Connection"))) continue; 930 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Set-Cookie"))) continue; /*(response header only; avoid accidental reflection)*/ 931 break; 932 case 16: 933 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Proxy-Connection"))) continue; 934 break; 935 case 5: 936 /* Do not emit HTTP_PROXY in environment. 937 * Some executables use HTTP_PROXY to configure 938 * outgoing proxy. See also https://httpoxy.org/ */ 939 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Proxy"))) continue; 940 break; 941 case 0: 942 continue; 943 } 944 945 vlen = buffer_string_length(&ds->value); 946 if (0 == vlen) continue; 947 948 if (buffer_string_space(b) < klen + vlen + 4) { 949 size_t extend = b->size * 2 - buffer_string_length(b); 950 extend = extend > klen + vlen + 4 ? extend : klen + vlen + 4 + 4095; 951 buffer_string_prepare_append(b, extend); 952 } 953 954 buffer_append_string_len(b, ds->key.ptr, klen); 955 buffer_append_string_len(b, CONST_STR_LEN(": ")); 956 buffer_append_string_len(b, ds->value.ptr, vlen); 957 buffer_append_string_len(b, CONST_STR_LEN("\r\n")); 958 959 if (!remap_headers) continue; 960 961 /* check for hdrs for which to remap URIs in-place after append to b */ 962 963 switch (klen) { 964 default: 965 continue; 966 #if 0 /* "URI" is HTTP response header (non-standard; historical in Apache) */ 967 case 3: 968 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("URI"))) break; 969 continue; 970 #endif 971 #if 0 /* "Location" is HTTP response header */ 972 case 8: 973 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Location"))) break; 974 continue; 975 #endif 976 case 11: /* "Destination" is WebDAV request header */ 977 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Destination"))) break; 978 continue; 979 case 16: /* "Content-Location" may be HTTP request or response header */ 980 if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Content-Location"))) break; 981 continue; 982 } 983 984 http_header_remap_uri(b, buffer_string_length(b) - vlen - 2, &hctx->conf.header, 1); 985 } 986 987 if (!upgrade) 988 buffer_append_string_len(b, CONST_STR_LEN("Connection: close\r\n\r\n")); 989 else 990 buffer_append_string_len(b, CONST_STR_LEN("Connection: close, upgrade\r\n\r\n")); 991 992 hctx->gw.wb_reqlen = buffer_string_length(b); 993 chunkqueue_prepend_buffer_commit(hctx->gw.wb); 994 995 if (r->reqbody_length) { 996 chunkqueue_append_chunkqueue(hctx->gw.wb, r->reqbody_queue); 997 if (r->reqbody_length > 0) 998 hctx->gw.wb_reqlen += r->reqbody_length; /* total req size */ 999 else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/ 1000 hctx->gw.wb_reqlen = -hctx->gw.wb_reqlen; 1001 } 1002 1003 status_counter_inc(CONST_STR_LEN("proxy.requests")); 1004 return HANDLER_GO_ON; 1005 } 1006 1007 1008 static handler_t proxy_create_env_connect(gw_handler_ctx *gwhctx) { 1009 handler_ctx *hctx = (handler_ctx *)gwhctx; 1010 request_st * const r = hctx->gw.r; 1011 r->http_status = 200; /* OK */ 1012 r->resp_body_started = 1; 1013 gw_set_transparent(&hctx->gw); 1014 http_response_upgrade_read_body_unknown(r); 1015 1016 status_counter_inc(CONST_STR_LEN("proxy.requests")); 1017 return HANDLER_GO_ON; 1018 } 1019 1020 1021 static handler_t proxy_response_headers(request_st * const r, struct http_response_opts_t *opts) { 1022 /* response headers just completed */ 1023 handler_ctx *hctx = (handler_ctx *)opts->pdata; 1024 1025 if (r->resp_htags & HTTP_HEADER_UPGRADE) { 1026 if (hctx->conf.header.upgrade && r->http_status == 101) { 1027 /* 101 Switching Protocols; transition to transparent proxy */ 1028 gw_set_transparent(&hctx->gw); 1029 http_response_upgrade_read_body_unknown(r); 1030 } 1031 else { 1032 r->resp_htags &= ~HTTP_HEADER_UPGRADE; 1033 #if 0 1034 /* preserve prior questionable behavior; likely broken behavior 1035 * anyway if backend thinks connection is being upgraded but client 1036 * does not receive Connection: upgrade */ 1037 http_header_response_unset(r, HTTP_HEADER_UPGRADE, 1038 CONST_STR_LEN("Upgrade")) 1039 #endif 1040 } 1041 } 1042 1043 /* rewrite paths, if needed */ 1044 1045 if (NULL == hctx->conf.header.urlpaths 1046 && NULL == hctx->conf.header.hosts_response) 1047 return HANDLER_GO_ON; 1048 1049 if (r->resp_htags & HTTP_HEADER_LOCATION) { 1050 buffer *vb = http_header_response_get(r, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location")); 1051 if (vb) http_header_remap_uri(vb, 0, &hctx->conf.header, 0); 1052 } 1053 if (r->resp_htags & HTTP_HEADER_CONTENT_LOCATION) { 1054 buffer *vb = http_header_response_get(r, HTTP_HEADER_CONTENT_LOCATION, CONST_STR_LEN("Content-Location")); 1055 if (vb) http_header_remap_uri(vb, 0, &hctx->conf.header, 0); 1056 } 1057 if (r->resp_htags & HTTP_HEADER_SET_COOKIE) { 1058 buffer *vb = http_header_response_get(r, HTTP_HEADER_SET_COOKIE, CONST_STR_LEN("Set-Cookie")); 1059 if (vb) http_header_remap_setcookie(vb, 0, &hctx->conf.header); 1060 } 1061 1062 return HANDLER_GO_ON; 1063 } 1064 1065 static handler_t mod_proxy_check_extension(request_st * const r, void *p_d) { 1066 plugin_data *p = p_d; 1067 handler_t rc; 1068 1069 if (NULL != r->handler_module) return HANDLER_GO_ON; 1070 1071 mod_proxy_patch_config(r, p); 1072 if (NULL == p->conf.gw.exts) return HANDLER_GO_ON; 1073 1074 rc = gw_check_extension(r, (gw_plugin_data *)p, 1, sizeof(handler_ctx)); 1075 if (HANDLER_GO_ON != rc) return rc; 1076 1077 if (r->handler_module == p->self) { 1078 handler_ctx *hctx = r->plugin_ctx[p->id]; 1079 hctx->gw.create_env = proxy_create_env; 1080 hctx->gw.response = chunk_buffer_acquire(); 1081 hctx->gw.opts.backend = BACKEND_PROXY; 1082 hctx->gw.opts.pdata = hctx; 1083 hctx->gw.opts.headers = proxy_response_headers; 1084 1085 hctx->conf = p->conf; /*(copies struct)*/ 1086 hctx->conf.header.http_host = r->http_host; 1087 hctx->conf.header.upgrade &= (r->http_version == HTTP_VERSION_1_1); 1088 /* mod_proxy currently sends all backend requests as http. 1089 * https-remap is a flag since it might not be needed if backend 1090 * honors Forwarded or X-Forwarded-Proto headers, e.g. by using 1091 * lighttpd mod_extforward or similar functionality in backend*/ 1092 if (hctx->conf.header.https_remap) { 1093 hctx->conf.header.https_remap = 1094 buffer_is_equal_string(&r->uri.scheme, CONST_STR_LEN("https")); 1095 } 1096 1097 if (r->http_method == HTTP_METHOD_CONNECT) { 1098 /*(note: not requiring HTTP/1.1 due to too many non-compliant 1099 * clients such as 'openssl s_client')*/ 1100 if (hctx->conf.header.connect_method) { 1101 hctx->gw.create_env = proxy_create_env_connect; 1102 } 1103 else { 1104 r->http_status = 405; /* Method Not Allowed */ 1105 r->handler_module = NULL; 1106 return HANDLER_FINISHED; 1107 } 1108 } 1109 } 1110 1111 return HANDLER_GO_ON; 1112 } 1113 1114 1115 int mod_proxy_plugin_init(plugin *p); 1116 int mod_proxy_plugin_init(plugin *p) { 1117 p->version = LIGHTTPD_VERSION_ID; 1118 p->name = "proxy"; 1119 1120 p->init = mod_proxy_init; 1121 p->cleanup = mod_proxy_free; 1122 p->set_defaults = mod_proxy_set_defaults; 1123 p->connection_reset = gw_connection_reset; 1124 p->handle_uri_clean = mod_proxy_check_extension; 1125 p->handle_subrequest = gw_handle_subrequest; 1126 p->handle_trigger = gw_handle_trigger; 1127 p->handle_waitpid = gw_handle_waitpid_cb; 1128 1129 return 0; 1130 } 1131