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