lighttpd 1.4.x
https://www.lighttpd.net/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1086 lines
41 KiB
1086 lines
41 KiB
#include "first.h" |
|
|
|
#include <string.h> |
|
#include <stdlib.h> |
|
|
|
#include "gw_backend.h" |
|
#include "base.h" |
|
#include "array.h" |
|
#include "buffer.h" |
|
#include "http_kv.h" |
|
#include "http_header.h" |
|
#include "log.h" |
|
#include "sock_addr.h" |
|
#include "status_counter.h" |
|
|
|
/** |
|
* |
|
* HTTP reverse proxy |
|
* |
|
* TODO: - HTTP/1.1 |
|
* - HTTP/1.1 persistent connection with upstream servers |
|
*/ |
|
|
|
/* (future: might split struct and move part to http-header-glue.c) */ |
|
typedef struct http_header_remap_opts { |
|
const array *urlpaths; |
|
const array *hosts_request; |
|
const array *hosts_response; |
|
int https_remap; |
|
int upgrade; |
|
int connect_method; |
|
/*(not used in plugin_config, but used in handler_ctx)*/ |
|
const buffer *http_host; |
|
const buffer *forwarded_host; |
|
const data_string *forwarded_urlpath; |
|
} http_header_remap_opts; |
|
|
|
typedef enum { |
|
PROXY_FORWARDED_NONE = 0x00, |
|
PROXY_FORWARDED_FOR = 0x01, |
|
PROXY_FORWARDED_PROTO = 0x02, |
|
PROXY_FORWARDED_HOST = 0x04, |
|
PROXY_FORWARDED_BY = 0x08, |
|
PROXY_FORWARDED_REMOTE_USER = 0x10 |
|
} proxy_forwarded_t; |
|
|
|
typedef struct { |
|
gw_plugin_config gw; /* start must match layout of gw_plugin_config */ |
|
unsigned int replace_http_host; |
|
unsigned int forwarded; |
|
http_header_remap_opts header; |
|
} plugin_config; |
|
|
|
typedef struct { |
|
PLUGIN_DATA; |
|
pid_t srv_pid; /* must match layout of gw_plugin_data through conf member */ |
|
plugin_config conf; |
|
plugin_config defaults; |
|
} plugin_data; |
|
|
|
static int proxy_check_extforward; |
|
|
|
typedef struct { |
|
gw_handler_ctx gw; |
|
http_response_opts opts; |
|
plugin_config conf; |
|
} handler_ctx; |
|
|
|
|
|
INIT_FUNC(mod_proxy_init) { |
|
return calloc(1, sizeof(plugin_data)); |
|
} |
|
|
|
|
|
static void mod_proxy_free_config(plugin_data * const p) |
|
{ |
|
if (NULL == p->cvlist) return; |
|
/* (init i to 0 if global context; to 1 to skip empty global context) */ |
|
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { |
|
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; |
|
for (; -1 != cpv->k_id; ++cpv) { |
|
switch (cpv->k_id) { |
|
case 5: /* proxy.header */ |
|
if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
FREE_FUNC(mod_proxy_free) { |
|
plugin_data * const p = p_d; |
|
mod_proxy_free_config(p); |
|
gw_free(p); |
|
} |
|
|
|
static void mod_proxy_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) |
|
{ |
|
switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ |
|
case 0: /* proxy.server */ |
|
if (cpv->vtype == T_CONFIG_LOCAL) { |
|
gw_plugin_config * const gw = cpv->v.v; |
|
pconf->gw.exts = gw->exts; |
|
pconf->gw.exts_auth = gw->exts_auth; |
|
pconf->gw.exts_resp = gw->exts_resp; |
|
} |
|
break; |
|
case 1: /* proxy.balance */ |
|
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ |
|
pconf->gw.balance = (int)cpv->v.u; |
|
break; |
|
case 2: /* proxy.debug */ |
|
pconf->gw.debug = (int)cpv->v.u; |
|
break; |
|
case 3: /* proxy.map-extensions */ |
|
pconf->gw.ext_mapping = cpv->v.a; |
|
break; |
|
case 4: /* proxy.forwarded */ |
|
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ |
|
pconf->forwarded = cpv->v.u; |
|
break; |
|
case 5: /* proxy.header */ |
|
/*if (cpv->vtype == T_CONFIG_LOCAL)*//*always true here for this param*/ |
|
pconf->header = *(http_header_remap_opts *)cpv->v.v; /*(copies struct)*/ |
|
break; |
|
case 6: /* proxy.replace-http-host */ |
|
pconf->replace_http_host = cpv->v.u; |
|
break; |
|
default:/* should not happen */ |
|
return; |
|
} |
|
} |
|
|
|
|
|
static void mod_proxy_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) |
|
{ |
|
do { |
|
mod_proxy_merge_config_cpv(pconf, cpv); |
|
} while ((++cpv)->k_id != -1); |
|
} |
|
|
|
|
|
static void mod_proxy_patch_config(connection * const con, plugin_data * const p) |
|
{ |
|
memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); |
|
for (int i = 1, used = p->nconfig; i < used; ++i) { |
|
if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id)) |
|
mod_proxy_merge_config(&p->conf, p->cvlist+p->cvlist[i].v.u2[0]); |
|
} |
|
} |
|
|
|
|
|
static unsigned int mod_proxy_parse_forwarded(server *srv, const array *a) |
|
{ |
|
unsigned int forwarded = 0; |
|
for (uint32_t j = 0, used = a->used; j < used; ++j) { |
|
proxy_forwarded_t param; |
|
data_unset *du = a->data[j]; |
|
if (buffer_eq_slen(&du->key, CONST_STR_LEN("by"))) |
|
param = PROXY_FORWARDED_BY; |
|
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("for"))) |
|
param = PROXY_FORWARDED_FOR; |
|
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("host"))) |
|
param = PROXY_FORWARDED_HOST; |
|
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("proto"))) |
|
param = PROXY_FORWARDED_PROTO; |
|
else if (buffer_eq_slen(&du->key, CONST_STR_LEN("remote_user"))) |
|
param = PROXY_FORWARDED_REMOTE_USER; |
|
else { |
|
log_error(srv->errh, __FILE__, __LINE__, |
|
"proxy.forwarded keys must be one of: " |
|
"by, for, host, proto, remote_user, but not: %s", du->key.ptr); |
|
return UINT_MAX; |
|
} |
|
if (du->type == TYPE_STRING) { |
|
data_string *ds = (data_string *)du; |
|
if (buffer_eq_slen(&ds->value, CONST_STR_LEN("enable"))) |
|
forwarded |= param; |
|
else if (!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable"))) { |
|
log_error(srv->errh, __FILE__, __LINE__, |
|
"proxy.forwarded values must be one of: " |
|
"0, 1, enable, disable; error for key: %s", du->key.ptr); |
|
return UINT_MAX; |
|
} |
|
} |
|
else if (du->type == TYPE_INTEGER) { |
|
data_integer *di = (data_integer *)du; |
|
if (di->value) forwarded |= param; |
|
} |
|
else { |
|
log_error(srv->errh, __FILE__, __LINE__, |
|
"proxy.forwarded values must be one of: " |
|
"0, 1, enable, disable; error for key: %s", du->key.ptr); |
|
return UINT_MAX; |
|
} |
|
} |
|
return forwarded; |
|
} |
|
|
|
|
|
static http_header_remap_opts * mod_proxy_parse_header_opts(server *srv, const array *a) |
|
{ |
|
http_header_remap_opts header; |
|
memset(&header, 0, sizeof(header)); |
|
for (uint32_t j = 0, used = a->used; j < used; ++j) { |
|
data_array *da = (data_array *)a->data[j]; |
|
if (buffer_eq_slen(&da->key, CONST_STR_LEN("https-remap"))) { |
|
data_string *ds = (data_string *)da; |
|
if (ds->type != TYPE_STRING) { |
|
log_error(srv->errh, __FILE__, __LINE__, |
|
"unexpected value for proxy.header; " |
|
"expected \"enable\" or \"disable\" for https-remap"); |
|
return NULL; |
|
} |
|
header.https_remap = |
|
!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable")) |
|
&& !buffer_eq_slen(&ds->value, CONST_STR_LEN("0")); |
|
continue; |
|
} |
|
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("upgrade"))) { |
|
data_string *ds = (data_string *)da; |
|
if (ds->type != TYPE_STRING) { |
|
log_error(srv->errh, __FILE__, __LINE__, |
|
"unexpected value for proxy.header; " |
|
"expected \"upgrade\" => \"enable\" or \"disable\""); |
|
return NULL; |
|
} |
|
header.upgrade = |
|
!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable")) |
|
&& !buffer_eq_slen(&ds->value, CONST_STR_LEN("0")); |
|
continue; |
|
} |
|
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("connect"))) { |
|
data_string *ds = (data_string *)da; |
|
if (ds->type != TYPE_STRING) { |
|
log_error(srv->errh, __FILE__, __LINE__, |
|
"unexpected value for proxy.header; " |
|
"expected \"connect\" => \"enable\" or \"disable\""); |
|
return NULL; |
|
} |
|
header.connect_method = |
|
!buffer_eq_slen(&ds->value, CONST_STR_LEN("disable")) |
|
&& !buffer_eq_slen(&ds->value, CONST_STR_LEN("0")); |
|
continue; |
|
} |
|
if (da->type != TYPE_ARRAY || !array_is_kvstring(&da->value)) { |
|
log_error(srv->errh, __FILE__, __LINE__, |
|
"unexpected value for proxy.header; " |
|
"expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s", |
|
da->key.ptr); |
|
return NULL; |
|
} |
|
if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-urlpath"))) { |
|
header.urlpaths = &da->value; |
|
} |
|
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-request"))) { |
|
header.hosts_request = &da->value; |
|
} |
|
else if (buffer_eq_slen(&da->key, CONST_STR_LEN("map-host-response"))) { |
|
header.hosts_response = &da->value; |
|
} |
|
else { |
|
log_error(srv->errh, __FILE__, __LINE__, |
|
"unexpected key for proxy.header; " |
|
"expected ( \"param\" => ( \"key\" => \"value\" ) ) near key %s", |
|
da->key.ptr); |
|
return NULL; |
|
} |
|
} |
|
|
|
http_header_remap_opts *opts = malloc(sizeof(header)); |
|
force_assert(opts); |
|
memcpy(opts, &header, sizeof(header)); |
|
return opts; |
|
} |
|
|
|
|
|
SETDEFAULTS_FUNC(mod_proxy_set_defaults) |
|
{ |
|
static const config_plugin_keys_t cpk[] = { |
|
{ CONST_STR_LEN("proxy.server"), |
|
T_CONFIG_ARRAY_KVARRAY, |
|
T_CONFIG_SCOPE_CONNECTION } |
|
,{ CONST_STR_LEN("proxy.balance"), |
|
T_CONFIG_STRING, |
|
T_CONFIG_SCOPE_CONNECTION } |
|
,{ CONST_STR_LEN("proxy.debug"), |
|
T_CONFIG_INT, |
|
T_CONFIG_SCOPE_CONNECTION } |
|
,{ CONST_STR_LEN("proxy.map-extensions"), |
|
T_CONFIG_ARRAY_KVSTRING, |
|
T_CONFIG_SCOPE_CONNECTION } |
|
,{ CONST_STR_LEN("proxy.forwarded"), |
|
T_CONFIG_ARRAY_KVANY, |
|
T_CONFIG_SCOPE_CONNECTION } |
|
,{ CONST_STR_LEN("proxy.header"), |
|
T_CONFIG_ARRAY_KVANY, |
|
T_CONFIG_SCOPE_CONNECTION } |
|
,{ CONST_STR_LEN("proxy.replace-http-host"), |
|
T_CONFIG_BOOL, |
|
T_CONFIG_SCOPE_CONNECTION } |
|
,{ NULL, 0, |
|
T_CONFIG_UNSET, |
|
T_CONFIG_SCOPE_UNSET } |
|
}; |
|
|
|
plugin_data * const p = p_d; |
|
if (!config_plugin_values_init(srv, p, cpk, "mod_proxy")) |
|
return HANDLER_ERROR; |
|
|
|
/* process and validate config directives |
|
* (init i to 0 if global context; to 1 to skip empty global context) */ |
|
for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { |
|
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; |
|
gw_plugin_config *gw = NULL; |
|
for (; -1 != cpv->k_id; ++cpv) { |
|
switch (cpv->k_id) { |
|
case 0: /* proxy.server */ |
|
gw = calloc(1, sizeof(gw_plugin_config)); |
|
force_assert(gw); |
|
if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, cpv->v.a, |
|
gw, 0, cpk[cpv->k_id].k)) { |
|
gw_plugin_config_free(gw); |
|
return HANDLER_ERROR; |
|
} |
|
/* error if "mode" = "authorizer"; |
|
* proxy can not act as authorizer */ |
|
/*(check after gw_set_defaults_backend())*/ |
|
if (gw->exts_auth && gw->exts_auth->used) { |
|
log_error(srv->errh, __FILE__, __LINE__, |
|
"%s must not define any hosts with " |
|
"attribute \"mode\" = \"authorizer\"", cpk[cpv->k_id].k); |
|
gw_plugin_config_free(gw); |
|
return HANDLER_ERROR; |
|
} |
|
cpv->v.v = gw; |
|
cpv->vtype = T_CONFIG_LOCAL; |
|
break; |
|
case 1: /* proxy.balance */ |
|
cpv->v.u = (unsigned int)gw_get_defaults_balance(srv, cpv->v.b); |
|
break; |
|
case 2: /* proxy.debug */ |
|
case 3: /* proxy.map-extensions */ |
|
break; |
|
case 4: /* proxy.forwarded */ |
|
cpv->v.u = mod_proxy_parse_forwarded(srv, cpv->v.a); |
|
if (UINT_MAX == cpv->v.u) return HANDLER_ERROR; |
|
cpv->vtype = T_CONFIG_LOCAL; |
|
break; |
|
case 5: /* proxy.header */ |
|
cpv->v.v = mod_proxy_parse_header_opts(srv, cpv->v.a); |
|
if (NULL == cpv->v.v) return HANDLER_ERROR; |
|
cpv->vtype = T_CONFIG_LOCAL; |
|
break; |
|
case 6: /* proxy.replace-http-host */ |
|
break; |
|
default:/* should not happen */ |
|
break; |
|
} |
|
} |
|
|
|
/* disable check-local for all exts (default enabled) */ |
|
if (gw && gw->exts) { /*(check after gw_set_defaults_backend())*/ |
|
gw_exts_clear_check_local(gw->exts); |
|
} |
|
} |
|
|
|
/* default is 0 */ |
|
/*p->defaults.balance = (unsigned int)gw_get_defaults_balance(srv, NULL);*/ |
|
|
|
/* initialize p->defaults from global config context */ |
|
if (p->nconfig > 0 && p->cvlist->v.u2[1]) { |
|
const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; |
|
if (-1 != cpv->k_id) |
|
mod_proxy_merge_config(&p->defaults, cpv); |
|
} |
|
|
|
/* special-case behavior if mod_extforward is loaded */ |
|
for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) { |
|
buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value; |
|
if (buffer_eq_slen(m, CONST_STR_LEN("mod_extforward"))) { |
|
proxy_check_extforward = 1; |
|
break; |
|
} |
|
} |
|
|
|
return HANDLER_GO_ON; |
|
} |
|
|
|
|
|
/* (future: might move to http-header-glue.c) */ |
|
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) |
|
{ |
|
const array *hosts = is_req |
|
? remap_hdrs->hosts_request |
|
: remap_hdrs->hosts_response; |
|
if (hosts) { |
|
const char * const s = b->ptr+off; |
|
for (size_t i = 0, used = hosts->used; i < used; ++i) { |
|
const data_string * const ds = (data_string *)hosts->data[i]; |
|
const buffer *k = &ds->key; |
|
size_t mlen = buffer_string_length(k); |
|
if (1 == mlen && k->ptr[0] == '-') { |
|
/* match with authority provided in Host (if is_req) |
|
* (If no Host in client request, then matching against empty |
|
* string will probably not match, and no remap will be |
|
* performed) */ |
|
k = is_req |
|
? remap_hdrs->http_host |
|
: remap_hdrs->forwarded_host; |
|
if (NULL == k) continue; |
|
mlen = buffer_string_length(k); |
|
} |
|
if (buffer_eq_icase_ss(s, alen, k->ptr, mlen)) { |
|
if (buffer_is_equal_string(&ds->value, CONST_STR_LEN("-"))) { |
|
return remap_hdrs->http_host; |
|
} |
|
else if (!buffer_string_is_empty(&ds->value)) { |
|
/*(save first matched request host for response match)*/ |
|
if (is_req && NULL == remap_hdrs->forwarded_host) |
|
remap_hdrs->forwarded_host = &ds->value; |
|
return &ds->value; |
|
} /*(else leave authority as-is and stop matching)*/ |
|
break; |
|
} |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
/* (future: might move to http-header-glue.c) */ |
|
static size_t http_header_remap_host (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen) |
|
{ |
|
const buffer * const m = |
|
http_header_remap_host_match(b, off, remap_hdrs, is_req, alen); |
|
if (NULL == m) return alen; /*(no match; return original authority length)*/ |
|
|
|
buffer_substr_replace(b, off, alen, m); |
|
return buffer_string_length(m); /*(length of replacement authority)*/ |
|
} |
|
|
|
|
|
/* (future: might move to http-header-glue.c) */ |
|
static size_t http_header_remap_urlpath (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req) |
|
{ |
|
const array *urlpaths = remap_hdrs->urlpaths; |
|
if (urlpaths) { |
|
const char * const s = b->ptr+off; |
|
const size_t plen = buffer_string_length(b) - off; /*(urlpath len)*/ |
|
if (is_req) { /* request */ |
|
for (size_t i = 0, used = urlpaths->used; i < used; ++i) { |
|
const data_string * const ds = (data_string *)urlpaths->data[i]; |
|
const size_t mlen = buffer_string_length(&ds->key); |
|
if (mlen <= plen && 0 == memcmp(s, ds->key.ptr, mlen)) { |
|
if (NULL == remap_hdrs->forwarded_urlpath) |
|
remap_hdrs->forwarded_urlpath = ds; |
|
buffer_substr_replace(b, off, mlen, &ds->value); |
|
return buffer_string_length(&ds->value);/*(replacement len)*/ |
|
} |
|
} |
|
} |
|
else { /* response; perform reverse map */ |
|
if (NULL != remap_hdrs->forwarded_urlpath) { |
|
const data_string * const ds = remap_hdrs->forwarded_urlpath; |
|
const size_t mlen = buffer_string_length(&ds->value); |
|
if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) { |
|
buffer_substr_replace(b, off, mlen, &ds->key); |
|
return buffer_string_length(&ds->key); /*(replacement len)*/ |
|
} |
|
} |
|
for (size_t i = 0, used = urlpaths->used; i < used; ++i) { |
|
const data_string * const ds = (data_string *)urlpaths->data[i]; |
|
const size_t mlen = buffer_string_length(&ds->value); |
|
if (mlen <= plen && 0 == memcmp(s, ds->value.ptr, mlen)) { |
|
buffer_substr_replace(b, off, mlen, &ds->key); |
|
return buffer_string_length(&ds->key); /*(replacement len)*/ |
|
} |
|
} |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
/* (future: might move to http-header-glue.c) */ |
|
static void http_header_remap_uri (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req) |
|
{ |
|
/* find beginning of URL-path (might be preceded by scheme://authority |
|
* (caller should make sure any leading whitespace is prior to offset) */ |
|
if (b->ptr[off] != '/') { |
|
char *s = b->ptr+off; |
|
size_t alen; /*(authority len (host len))*/ |
|
size_t slen; /*(scheme len)*/ |
|
const buffer *m; |
|
/* skip over scheme and authority of URI to find beginning of URL-path |
|
* (value might conceivably be relative URL-path instead of URI) */ |
|
if (NULL == (s = strchr(s, ':')) || s[1] != '/' || s[2] != '/') return; |
|
slen = s - (b->ptr+off); |
|
s += 3; |
|
off = (size_t)(s - b->ptr); |
|
if (NULL != (s = strchr(s, '/'))) { |
|
alen = (size_t)(s - b->ptr) - off; |
|
if (0 == alen) return; /*(empty authority, e.g. "http:///")*/ |
|
} |
|
else { |
|
alen = buffer_string_length(b) - off; |
|
if (0 == alen) return; /*(empty authority, e.g. "http:///")*/ |
|
buffer_append_string_len(b, CONST_STR_LEN("/")); |
|
} |
|
|
|
/* remap authority (if configured) and set offset to url-path */ |
|
m = http_header_remap_host_match(b, off, remap_hdrs, is_req, alen); |
|
if (NULL != m) { |
|
if (remap_hdrs->https_remap |
|
&& (is_req ? 5==slen && 0==memcmp(b->ptr+off-slen-3,"https",5) |
|
: 4==slen && 0==memcmp(b->ptr+off-slen-3,"http",4))){ |
|
if (is_req) { |
|
memcpy(b->ptr+off-slen-3+4,"://",3); /*("https"=>"http")*/ |
|
--off; |
|
++alen; |
|
} |
|
else {/*(!is_req)*/ |
|
memcpy(b->ptr+off-slen-3+4,"s://",4); /*("http" =>"https")*/ |
|
++off; |
|
--alen; |
|
} |
|
} |
|
buffer_substr_replace(b, off, alen, m); |
|
alen = buffer_string_length(m);/*(length of replacement authority)*/ |
|
} |
|
off += alen; |
|
} |
|
|
|
/* remap URLs (if configured) */ |
|
http_header_remap_urlpath(b, off, remap_hdrs, is_req); |
|
} |
|
|
|
|
|
/* (future: might move to http-header-glue.c) */ |
|
static void http_header_remap_setcookie (buffer *b, size_t off, http_header_remap_opts *remap_hdrs) |
|
{ |
|
/* Given the special-case of Set-Cookie and the (too) loosely restricted |
|
* characters allowed, for best results, the Set-Cookie value should be the |
|
* entire string in b from offset to end of string. In response headers, |
|
* lighttpd may concatenate multiple Set-Cookie headers into single entry |
|
* in con->response.headers, separated by "\r\nSet-Cookie: " */ |
|
for (char *s = b->ptr+off, *e; *s; s = e) { |
|
size_t len; |
|
{ |
|
while (*s != ';' && *s != '\n' && *s != '\0') ++s; |
|
if (*s == '\n') { |
|
/*(include +1 for '\n', but leave ' ' for ++s below)*/ |
|
s += sizeof("Set-Cookie:"); |
|
} |
|
if ('\0' == *s) return; |
|
do { ++s; } while (*s == ' ' || *s == '\t'); |
|
if ('\0' == *s) return; |
|
e = s+1; |
|
if ('=' == *s) continue; |
|
/*(interested only in Domain and Path attributes)*/ |
|
while (*e != '=' && *e != '\0') ++e; |
|
if ('\0' == *e) return; |
|
++e; |
|
switch ((int)(e - s - 1)) { |
|
case 4: |
|
if (buffer_eq_icase_ssn(s, "path", 4)) { |
|
if (*e == '"') ++e; |
|
if (*e != '/') continue; |
|
off = (size_t)(e - b->ptr); |
|
len = http_header_remap_urlpath(b, off, remap_hdrs, 0); |
|
e = b->ptr+off+len; /*(b may have been reallocated)*/ |
|
continue; |
|
} |
|
break; |
|
case 6: |
|
if (buffer_eq_icase_ssn(s, "domain", 6)) { |
|
size_t alen = 0; |
|
if (*e == '"') ++e; |
|
if (*e == '.') ++e; |
|
if (*e == ';') continue; |
|
off = (size_t)(e - b->ptr); |
|
for (char c; (c = e[alen]) != ';' && c != ' ' && c != '\t' |
|
&& c != '\r' && c != '\0'; ++alen); |
|
len = http_header_remap_host(b, off, remap_hdrs, 0, alen); |
|
e = b->ptr+off+len; /*(b may have been reallocated)*/ |
|
continue; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
static void buffer_append_string_backslash_escaped(buffer *b, const char *s, size_t len) { |
|
/* (future: might move to buffer.c) */ |
|
size_t j = 0; |
|
char *p; |
|
|
|
buffer_string_prepare_append(b, len*2 + 4); |
|
p = b->ptr + buffer_string_length(b); |
|
|
|
for (size_t i = 0; i < len; ++i) { |
|
int c = s[i]; |
|
if (c == '"' || c == '\\' || c == 0x7F || (c < 0x20 && c != '\t')) |
|
p[j++] = '\\'; |
|
p[j++] = c; |
|
} |
|
|
|
buffer_commit(b, j); |
|
} |
|
|
|
static void proxy_set_Forwarded(connection *con, const unsigned int flags) { |
|
buffer *b = NULL, *efor = NULL, *eproto = NULL, *ehost = NULL; |
|
int semicolon = 0; |
|
|
|
if (proxy_check_extforward) { |
|
efor = |
|
http_header_env_get(con, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR")); |
|
eproto = |
|
http_header_env_get(con, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO")); |
|
ehost = |
|
http_header_env_get(con, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST")); |
|
} |
|
|
|
/* note: set "Forwarded" prior to updating X-Forwarded-For (below) */ |
|
|
|
if (flags) |
|
b = http_header_request_get(con, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded")); |
|
|
|
if (flags && NULL == b) { |
|
const buffer *xff = |
|
http_header_request_get(con, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For")); |
|
http_header_request_set(con, HTTP_HEADER_FORWARDED, |
|
CONST_STR_LEN("Forwarded"), |
|
CONST_STR_LEN("x")); /*(must not be blank for _get below)*/ |
|
#ifdef __COVERITY__ |
|
force_assert(NULL != b); /*(not NULL because created directly above)*/ |
|
#endif |
|
b = http_header_request_get(con, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded")); |
|
buffer_clear(b); |
|
if (NULL != xff) { |
|
/* use X-Forwarded-For contents to seed Forwarded */ |
|
char *s = xff->ptr; |
|
size_t used = buffer_string_length(xff); |
|
for (size_t i=0, j, ipv6; i < used; ++i) { |
|
while (s[i] == ' ' || s[i] == '\t' || s[i] == ',') ++i; |
|
if (s[i] == '\0') break; |
|
j = i; |
|
do { |
|
++i; |
|
} while (s[i]!=' ' && s[i]!='\t' && s[i]!=',' && s[i]!='\0'); |
|
buffer_append_string_len(b, CONST_STR_LEN("for=")); |
|
/* over-simplified test expecting only IPv4 or IPv6 addresses, |
|
* (not expecting :port, so treat existence of colon as IPv6, |
|
* and not expecting unix paths, especially not containing ':') |
|
* quote all strings, backslash-escape since IPs not validated*/ |
|
ipv6 = (NULL != memchr(s+j, ':', i-j)); /*(over-simplified) */ |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
if (ipv6) |
|
buffer_append_string_len(b, CONST_STR_LEN("[")); |
|
buffer_append_string_backslash_escaped(b, s+j, i-j); |
|
if (ipv6) |
|
buffer_append_string_len(b, CONST_STR_LEN("]")); |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
buffer_append_string_len(b, CONST_STR_LEN(", ")); |
|
} |
|
} |
|
} else if (flags) { /*(NULL != b)*/ |
|
buffer_append_string_len(b, CONST_STR_LEN(", ")); |
|
} |
|
|
|
if (flags & PROXY_FORWARDED_FOR) { |
|
int family = sock_addr_get_family(&con->dst_addr); |
|
buffer_append_string_len(b, CONST_STR_LEN("for=")); |
|
if (NULL != efor) { |
|
/* over-simplified test expecting only IPv4 or IPv6 addresses, |
|
* (not expecting :port, so treat existence of colon as IPv6, |
|
* and not expecting unix paths, especially not containing ':') |
|
* quote all strings and backslash-escape since IPs not validated |
|
* (should be IP from original con->dst_addr_buf, |
|
* so trustable and without :port) */ |
|
int ipv6 = (NULL != strchr(efor->ptr, ':')); |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
if (ipv6) buffer_append_string_len(b, CONST_STR_LEN("[")); |
|
buffer_append_string_backslash_escaped( |
|
b, CONST_BUF_LEN(efor)); |
|
if (ipv6) buffer_append_string_len(b, CONST_STR_LEN("]")); |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
} else if (family == AF_INET) { |
|
/*(Note: if :port is added, then must be quoted-string: |
|
* e.g. for="...:port")*/ |
|
buffer_append_string_buffer(b, con->dst_addr_buf); |
|
} else if (family == AF_INET6) { |
|
buffer_append_string_len(b, CONST_STR_LEN("\"[")); |
|
buffer_append_string_buffer(b, con->dst_addr_buf); |
|
buffer_append_string_len(b, CONST_STR_LEN("]\"")); |
|
} else { |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
buffer_append_string_backslash_escaped( |
|
b, CONST_BUF_LEN(con->dst_addr_buf)); |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
} |
|
semicolon = 1; |
|
} |
|
|
|
if (flags & PROXY_FORWARDED_BY) { |
|
int family = sock_addr_get_family(&con->srv_socket->addr); |
|
/* Note: getsockname() and inet_ntop() are expensive operations. |
|
* (recommendation: do not to enable by=... unless required) |
|
* future: might use con->srv_socket->srv_token if addr is not |
|
* INADDR_ANY or in6addr_any, but must omit optional :port |
|
* from con->srv_socket->srv_token for consistency */ |
|
|
|
if (semicolon) buffer_append_string_len(b, CONST_STR_LEN(";")); |
|
buffer_append_string_len(b, CONST_STR_LEN("by=")); |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
#ifdef HAVE_SYS_UN_H |
|
/* special-case: might need to encode unix domain socket path */ |
|
if (family == AF_UNIX) { |
|
buffer_append_string_backslash_escaped( |
|
b, CONST_BUF_LEN(con->srv_socket->srv_token)); |
|
} |
|
else |
|
#endif |
|
{ |
|
sock_addr addr; |
|
socklen_t addrlen = sizeof(addr); |
|
if (0 == getsockname(con->fd,(struct sockaddr *)&addr, &addrlen)) { |
|
sock_addr_stringify_append_buffer(b, &addr); |
|
} |
|
} |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
semicolon = 1; |
|
} |
|
|
|
if (flags & PROXY_FORWARDED_PROTO) { |
|
/* expecting "http" or "https" |
|
* (not checking if quoted-string and encoding needed) */ |
|
if (semicolon) buffer_append_string_len(b, CONST_STR_LEN(";")); |
|
buffer_append_string_len(b, CONST_STR_LEN("proto=")); |
|
if (NULL != eproto) { |
|
buffer_append_string_buffer(b, eproto); |
|
} else if (con->srv_socket->is_ssl) { |
|
buffer_append_string_len(b, CONST_STR_LEN("https")); |
|
} else { |
|
buffer_append_string_len(b, CONST_STR_LEN("http")); |
|
} |
|
semicolon = 1; |
|
} |
|
|
|
if (flags & PROXY_FORWARDED_HOST) { |
|
if (NULL != ehost) { |
|
if (semicolon) |
|
buffer_append_string_len(b, CONST_STR_LEN(";")); |
|
buffer_append_string_len(b, CONST_STR_LEN("host=\"")); |
|
buffer_append_string_backslash_escaped( |
|
b, CONST_BUF_LEN(ehost)); |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
semicolon = 1; |
|
} else if (!buffer_string_is_empty(con->request.http_host)) { |
|
if (semicolon) |
|
buffer_append_string_len(b, CONST_STR_LEN(";")); |
|
buffer_append_string_len(b, CONST_STR_LEN("host=\"")); |
|
buffer_append_string_backslash_escaped( |
|
b, CONST_BUF_LEN(con->request.http_host)); |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
semicolon = 1; |
|
} |
|
} |
|
|
|
if (flags & PROXY_FORWARDED_REMOTE_USER) { |
|
const buffer *remote_user = |
|
http_header_env_get(con, CONST_STR_LEN("REMOTE_USER")); |
|
if (NULL != remote_user) { |
|
if (semicolon) |
|
buffer_append_string_len(b, CONST_STR_LEN(";")); |
|
buffer_append_string_len(b, CONST_STR_LEN("remote_user=\"")); |
|
buffer_append_string_backslash_escaped( |
|
b, CONST_BUF_LEN(remote_user)); |
|
buffer_append_string_len(b, CONST_STR_LEN("\"")); |
|
semicolon = 1; |
|
} |
|
} |
|
|
|
/* legacy X-* headers, including X-Forwarded-For */ |
|
|
|
b = (NULL != efor) ? efor : con->dst_addr_buf; |
|
http_header_request_set(con, HTTP_HEADER_X_FORWARDED_FOR, |
|
CONST_STR_LEN("X-Forwarded-For"), |
|
CONST_BUF_LEN(b)); |
|
|
|
b = (NULL != ehost) ? ehost : con->request.http_host; |
|
if (!buffer_string_is_empty(b)) { |
|
http_header_request_set(con, HTTP_HEADER_OTHER, |
|
CONST_STR_LEN("X-Host"), |
|
CONST_BUF_LEN(b)); |
|
http_header_request_set(con, HTTP_HEADER_OTHER, |
|
CONST_STR_LEN("X-Forwarded-Host"), |
|
CONST_BUF_LEN(b)); |
|
} |
|
|
|
b = (NULL != eproto) ? eproto : con->uri.scheme; |
|
http_header_request_set(con, HTTP_HEADER_X_FORWARDED_PROTO, |
|
CONST_STR_LEN("X-Forwarded-Proto"), |
|
CONST_BUF_LEN(b)); |
|
} |
|
|
|
|
|
static handler_t proxy_create_env(gw_handler_ctx *gwhctx) { |
|
handler_ctx *hctx = (handler_ctx *)gwhctx; |
|
connection *con = hctx->gw.remote_conn; |
|
const int remap_headers = (NULL != hctx->conf.header.urlpaths |
|
|| NULL != hctx->conf.header.hosts_request); |
|
const int upgrade = hctx->conf.header.upgrade |
|
&& (NULL != http_header_request_get(con, HTTP_HEADER_UPGRADE, CONST_STR_LEN("Upgrade"))); |
|
size_t rsz = (size_t)(con->read_queue->bytes_out - hctx->gw.wb->bytes_in); |
|
buffer * const b = chunkqueue_prepend_buffer_open_sz(hctx->gw.wb, rsz < 65536 ? rsz : con->request.rqst_header_len); |
|
|
|
/* build header */ |
|
|
|
/* request line */ |
|
http_method_append(b, con->request.http_method); |
|
buffer_append_string_len(b, CONST_STR_LEN(" ")); |
|
buffer_append_string_buffer(b, con->request.target); |
|
if (remap_headers) |
|
http_header_remap_uri(b, buffer_string_length(b) - buffer_string_length(con->request.target), &hctx->conf.header, 1); |
|
if (!upgrade) |
|
buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n")); |
|
else |
|
buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.1\r\n")); |
|
|
|
if (hctx->conf.replace_http_host && !buffer_string_is_empty(hctx->gw.host->id)) { |
|
if (hctx->gw.conf.debug > 1) { |
|
log_error(con->conf.errh, __FILE__, __LINE__, |
|
"proxy - using \"%s\" as HTTP Host", hctx->gw.host->id->ptr); |
|
} |
|
buffer_append_string_len(b, CONST_STR_LEN("Host: ")); |
|
buffer_append_string_buffer(b, hctx->gw.host->id); |
|
buffer_append_string_len(b, CONST_STR_LEN("\r\n")); |
|
} else if (!buffer_string_is_empty(con->request.http_host)) { |
|
buffer_append_string_len(b, CONST_STR_LEN("Host: ")); |
|
buffer_append_string_buffer(b, con->request.http_host); |
|
if (remap_headers) { |
|
size_t alen = buffer_string_length(con->request.http_host); |
|
http_header_remap_host(b, buffer_string_length(b) - alen, &hctx->conf.header, 1, alen); |
|
} |
|
buffer_append_string_len(b, CONST_STR_LEN("\r\n")); |
|
} |
|
|
|
/* "Forwarded" and legacy X- headers */ |
|
proxy_set_Forwarded(con, hctx->conf.forwarded); |
|
|
|
if (con->request.reqbody_length > 0 |
|
|| (0 == con->request.reqbody_length |
|
&& !http_method_get_or_head(con->request.http_method))) { |
|
/* set Content-Length if client sent Transfer-Encoding: chunked |
|
* and not streaming to backend (request body has been fully received) */ |
|
const buffer *vb = http_header_request_get(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); |
|
if (NULL == vb) { |
|
char buf[LI_ITOSTRING_LENGTH]; |
|
http_header_request_set(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"), |
|
buf, li_itostrn(buf, sizeof(buf), con->request.reqbody_length)); |
|
} |
|
} |
|
|
|
/* request header */ |
|
for (size_t i = 0, used = con->request.headers.used; i < used; ++i) { |
|
data_string *ds = (data_string *)con->request.headers.data[i]; |
|
const size_t klen = buffer_string_length(&ds->key); |
|
size_t vlen; |
|
switch (klen) { |
|
default: |
|
break; |
|
case 4: |
|
if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Host"))) continue; /*(handled further above)*/ |
|
break; |
|
case 10: |
|
if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Connection"))) continue; |
|
if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Set-Cookie"))) continue; /*(response header only; avoid accidental reflection)*/ |
|
break; |
|
case 16: |
|
if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Proxy-Connection"))) continue; |
|
break; |
|
case 5: |
|
/* Do not emit HTTP_PROXY in environment. |
|
* Some executables use HTTP_PROXY to configure |
|
* outgoing proxy. See also https://httpoxy.org/ */ |
|
if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Proxy"))) continue; |
|
break; |
|
case 0: |
|
continue; |
|
} |
|
|
|
vlen = buffer_string_length(&ds->value); |
|
if (0 == vlen) continue; |
|
|
|
if (buffer_string_space(b) < klen + vlen + 4) { |
|
size_t extend = b->size * 2 - buffer_string_length(b); |
|
extend = extend > klen + vlen + 4 ? extend : klen + vlen + 4 + 4095; |
|
buffer_string_prepare_append(b, extend); |
|
} |
|
|
|
buffer_append_string_len(b, ds->key.ptr, klen); |
|
buffer_append_string_len(b, CONST_STR_LEN(": ")); |
|
buffer_append_string_len(b, ds->value.ptr, vlen); |
|
buffer_append_string_len(b, CONST_STR_LEN("\r\n")); |
|
|
|
if (!remap_headers) continue; |
|
|
|
/* check for hdrs for which to remap URIs in-place after append to b */ |
|
|
|
switch (klen) { |
|
default: |
|
continue; |
|
#if 0 /* "URI" is HTTP response header (non-standard; historical in Apache) */ |
|
case 3: |
|
if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("URI"))) break; |
|
continue; |
|
#endif |
|
#if 0 /* "Location" is HTTP response header */ |
|
case 8: |
|
if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Location"))) break; |
|
continue; |
|
#endif |
|
case 11: /* "Destination" is WebDAV request header */ |
|
if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Destination"))) break; |
|
continue; |
|
case 16: /* "Content-Location" may be HTTP request or response header */ |
|
if (buffer_is_equal_caseless_string(&ds->key, CONST_STR_LEN("Content-Location"))) break; |
|
continue; |
|
} |
|
|
|
http_header_remap_uri(b, buffer_string_length(b) - vlen - 2, &hctx->conf.header, 1); |
|
} |
|
|
|
if (!upgrade) |
|
buffer_append_string_len(b, CONST_STR_LEN("Connection: close\r\n\r\n")); |
|
else |
|
buffer_append_string_len(b, CONST_STR_LEN("Connection: close, upgrade\r\n\r\n")); |
|
|
|
hctx->gw.wb_reqlen = buffer_string_length(b); |
|
chunkqueue_prepend_buffer_commit(hctx->gw.wb); |
|
|
|
if (con->request.reqbody_length) { |
|
chunkqueue_append_chunkqueue(hctx->gw.wb, con->request.reqbody_queue); |
|
if (con->request.reqbody_length > 0) |
|
hctx->gw.wb_reqlen += con->request.reqbody_length; /* total req size */ |
|
else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/ |
|
hctx->gw.wb_reqlen = -hctx->gw.wb_reqlen; |
|
} |
|
|
|
status_counter_inc(CONST_STR_LEN("proxy.requests")); |
|
return HANDLER_GO_ON; |
|
} |
|
|
|
|
|
static handler_t proxy_create_env_connect(gw_handler_ctx *gwhctx) { |
|
handler_ctx *hctx = (handler_ctx *)gwhctx; |
|
connection *con = hctx->gw.remote_conn; |
|
con->http_status = 200; /* OK */ |
|
con->response.resp_body_started = 1; |
|
gw_set_transparent(&hctx->gw); |
|
http_response_upgrade_read_body_unknown(con); |
|
|
|
status_counter_inc(CONST_STR_LEN("proxy.requests")); |
|
return HANDLER_GO_ON; |
|
} |
|
|
|
|
|
static handler_t proxy_response_headers(connection *con, struct http_response_opts_t *opts) { |
|
/* response headers just completed */ |
|
handler_ctx *hctx = (handler_ctx *)opts->pdata; |
|
|
|
if (con->response.htags & HTTP_HEADER_UPGRADE) { |
|
if (hctx->conf.header.upgrade && con->http_status == 101) { |
|
/* 101 Switching Protocols; transition to transparent proxy */ |
|
gw_set_transparent(&hctx->gw); |
|
http_response_upgrade_read_body_unknown(con); |
|
} |
|
else { |
|
con->response.htags &= ~HTTP_HEADER_UPGRADE; |
|
#if 0 |
|
/* preserve prior questionable behavior; likely broken behavior |
|
* anyway if backend thinks connection is being upgraded but client |
|
* does not receive Connection: upgrade */ |
|
http_header_response_unset(con, HTTP_HEADER_UPGRADE, |
|
CONST_STR_LEN("Upgrade")) |
|
#endif |
|
} |
|
} |
|
|
|
/* rewrite paths, if needed */ |
|
|
|
if (NULL == hctx->conf.header.urlpaths |
|
&& NULL == hctx->conf.header.hosts_response) |
|
return HANDLER_GO_ON; |
|
|
|
if (con->response.htags & HTTP_HEADER_LOCATION) { |
|
buffer *vb = http_header_response_get(con, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location")); |
|
if (vb) http_header_remap_uri(vb, 0, &hctx->conf.header, 0); |
|
} |
|
if (con->response.htags & HTTP_HEADER_CONTENT_LOCATION) { |
|
buffer *vb = http_header_response_get(con, HTTP_HEADER_CONTENT_LOCATION, CONST_STR_LEN("Content-Location")); |
|
if (vb) http_header_remap_uri(vb, 0, &hctx->conf.header, 0); |
|
} |
|
if (con->response.htags & HTTP_HEADER_SET_COOKIE) { |
|
buffer *vb = http_header_response_get(con, HTTP_HEADER_SET_COOKIE, CONST_STR_LEN("Set-Cookie")); |
|
if (vb) http_header_remap_setcookie(vb, 0, &hctx->conf.header); |
|
} |
|
|
|
return HANDLER_GO_ON; |
|
} |
|
|
|
static handler_t mod_proxy_check_extension(connection *con, void *p_d) { |
|
plugin_data *p = p_d; |
|
handler_t rc; |
|
|
|
if (con->mode != DIRECT) return HANDLER_GO_ON; |
|
|
|
mod_proxy_patch_config(con, p); |
|
if (NULL == p->conf.gw.exts) return HANDLER_GO_ON; |
|
|
|
rc = gw_check_extension(con, (gw_plugin_data *)p, 1, sizeof(handler_ctx)); |
|
if (HANDLER_GO_ON != rc) return rc; |
|
|
|
if (con->mode == p->id) { |
|
handler_ctx *hctx = con->request.plugin_ctx[p->id]; |
|
hctx->gw.create_env = proxy_create_env; |
|
hctx->gw.response = chunk_buffer_acquire(); |
|
hctx->gw.opts.backend = BACKEND_PROXY; |
|
hctx->gw.opts.pdata = hctx; |
|
hctx->gw.opts.headers = proxy_response_headers; |
|
|
|
hctx->conf = p->conf; /*(copies struct)*/ |
|
hctx->conf.header.http_host = con->request.http_host; |
|
hctx->conf.header.upgrade &= (con->request.http_version == HTTP_VERSION_1_1); |
|
/* mod_proxy currently sends all backend requests as http. |
|
* https-remap is a flag since it might not be needed if backend |
|
* honors Forwarded or X-Forwarded-Proto headers, e.g. by using |
|
* lighttpd mod_extforward or similar functionality in backend*/ |
|
if (hctx->conf.header.https_remap) { |
|
hctx->conf.header.https_remap = |
|
buffer_is_equal_string(con->uri.scheme, CONST_STR_LEN("https")); |
|
} |
|
|
|
if (con->request.http_method == HTTP_METHOD_CONNECT) { |
|
/*(note: not requiring HTTP/1.1 due to too many non-compliant |
|
* clients such as 'openssl s_client')*/ |
|
if (hctx->conf.header.connect_method) { |
|
hctx->gw.create_env = proxy_create_env_connect; |
|
} |
|
else { |
|
con->http_status = 405; /* Method Not Allowed */ |
|
con->mode = DIRECT; |
|
return HANDLER_FINISHED; |
|
} |
|
} |
|
} |
|
|
|
return HANDLER_GO_ON; |
|
} |
|
|
|
|
|
int mod_proxy_plugin_init(plugin *p); |
|
int mod_proxy_plugin_init(plugin *p) { |
|
p->version = LIGHTTPD_VERSION_ID; |
|
p->name = "proxy"; |
|
|
|
p->init = mod_proxy_init; |
|
p->cleanup = mod_proxy_free; |
|
p->set_defaults = mod_proxy_set_defaults; |
|
p->connection_reset = gw_connection_reset; |
|
p->handle_uri_clean = mod_proxy_check_extension; |
|
p->handle_subrequest = gw_handle_subrequest; |
|
p->handle_trigger = gw_handle_trigger; |
|
p->handle_waitpid = gw_handle_waitpid_cb; |
|
|
|
return 0; |
|
}
|
|
|