[multiple] split con, request (very large change)
NB: r->tmp_buf == srv->tmp_buf (pointer is copied for quicker access) NB: request read and write chunkqueues currently point to connection chunkqueues; per-request and per-connection chunkqueues are not distinct from one another con->read_queue == r->read_queue con->write_queue == r->write_queue NB: in the future, a separate connection config may be needed for connection-level module hooks. Similarly, might need to have per-request chunkqueues separate from per-connection chunkqueues. Should probably also have a request_reset() which is distinct from connection_reset().personal/stbuehler/ci-build
parent
cc2134c88b
commit
7c7f8c467c
106
src/base.h
106
src/base.h
|
@ -16,40 +16,38 @@
|
|||
struct fdevents; /* declaration */
|
||||
|
||||
|
||||
typedef struct {
|
||||
off_t content_length;
|
||||
unsigned int htags; /* bitfield of flagged headers present in response */
|
||||
array headers;
|
||||
char send_chunked;
|
||||
char resp_body_started;
|
||||
char resp_body_finished;
|
||||
uint32_t resp_header_len;
|
||||
plugin *handler_module;
|
||||
} response;
|
||||
|
||||
typedef struct {
|
||||
buffer *scheme; /* scheme without colon or slashes ( "http" or "https" ) */
|
||||
|
||||
/* authority with optional portnumber ("site.name" or "site.name:8080" ) NOTE: without "username:password@" */
|
||||
buffer *authority;
|
||||
|
||||
/* path including leading slash ("/" or "/index.html") - urldecoded, and sanitized ( buffer_path_simplify() && buffer_urldecode_path() ) */
|
||||
buffer *path;
|
||||
buffer *path_raw; /* raw path, as sent from client. no urldecoding or path simplifying */
|
||||
buffer *query; /* querystring ( everything after "?", ie: in "/index.php?foo=1", query is "foo=1" ) */
|
||||
} request_uri;
|
||||
|
||||
typedef struct {
|
||||
buffer *path;
|
||||
buffer *basedir; /* path = "(basedir)(.*)" */
|
||||
|
||||
buffer *doc_root; /* path = doc_root + rel_path */
|
||||
buffer *rel_path;
|
||||
|
||||
buffer *etag;
|
||||
} physical;
|
||||
|
||||
struct connection {
|
||||
|
||||
request_st request;
|
||||
|
||||
int fd; /* the FD for this connection */
|
||||
int ndx; /* reverse mapping to server->connection[ndx] */
|
||||
fdnode *fdn; /* fdevent (fdnode *) object */
|
||||
|
||||
/* fd states */
|
||||
signed char is_readable;
|
||||
signed char is_writable;
|
||||
char is_ssl_sock;
|
||||
char traffic_limit_reached;
|
||||
|
||||
chunkqueue *write_queue; /* a large queue for low-level write ( HTTP response ) [ file, mem ] */
|
||||
chunkqueue *read_queue; /* a small queue for low-level read ( HTTP request ) [ mem ] */
|
||||
|
||||
off_t bytes_written; /* used by mod_accesslog, mod_rrd */
|
||||
off_t bytes_written_cur_second; /* used by mod_accesslog, mod_rrd */
|
||||
off_t bytes_read; /* used by mod_accesslog, mod_rrd */
|
||||
|
||||
int (* network_write)(struct connection *con, chunkqueue *cq, off_t max_bytes);
|
||||
int (* network_read)(struct connection *con, chunkqueue *cq, off_t max_bytes);
|
||||
|
||||
server *srv;
|
||||
void *plugin_slots;
|
||||
void *config_data_base;
|
||||
|
||||
sock_addr dst_addr;
|
||||
buffer *dst_addr_buf;
|
||||
struct server_socket *srv_socket; /* reference to the server-socket */
|
||||
|
||||
/* timestamps */
|
||||
time_t read_idle_ts;
|
||||
time_t close_timeout_ts;
|
||||
|
@ -59,47 +57,7 @@ struct connection {
|
|||
uint32_t request_count; /* number of requests handled in this connection */
|
||||
int keep_alive_idle; /* remember max_keep_alive_idle from config */
|
||||
|
||||
fdnode *fdn; /* fdevent (fdnode *) object */
|
||||
int fd; /* the FD for this connection */
|
||||
int ndx; /* reverse mapping to server->connection[ndx] */
|
||||
|
||||
/* fd states */
|
||||
int is_readable;
|
||||
int is_writable;
|
||||
int is_ssl_sock;
|
||||
|
||||
chunkqueue *write_queue; /* a large queue for low-level write ( HTTP response ) [ file, mem ] */
|
||||
chunkqueue *read_queue; /* a small queue for low-level read ( HTTP request ) [ mem ] */
|
||||
|
||||
int traffic_limit_reached;
|
||||
|
||||
off_t bytes_written; /* used by mod_accesslog, mod_rrd */
|
||||
off_t bytes_written_cur_second; /* used by mod_accesslog, mod_rrd */
|
||||
off_t bytes_read; /* used by mod_accesslog, mod_rrd */
|
||||
|
||||
sock_addr dst_addr;
|
||||
buffer *dst_addr_buf;
|
||||
|
||||
/* request */
|
||||
int http_status;
|
||||
|
||||
request_st request;
|
||||
request_uri uri;
|
||||
physical physical;
|
||||
response response;
|
||||
|
||||
server *srv;
|
||||
|
||||
void *plugin_slots;
|
||||
|
||||
request_config conf;
|
||||
void *config_data_base;
|
||||
|
||||
uint16_t proto_default_port;
|
||||
|
||||
struct server_socket *srv_socket; /* reference to the server-socket */
|
||||
int (* network_write)(struct connection *con, chunkqueue *cq, off_t max_bytes);
|
||||
int (* network_read)(struct connection *con, chunkqueue *cq, off_t max_bytes);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
@ -182,7 +140,7 @@ struct server {
|
|||
|
||||
struct fdevents *ev;
|
||||
int (* network_backend_write)(int fd, chunkqueue *cq, off_t max_bytes, log_error_st *errh);
|
||||
handler_t (* request_env)(connection *con);
|
||||
handler_t (* request_env)(request_st *r);
|
||||
|
||||
/* buffers */
|
||||
buffer *tmp_buf;
|
||||
|
|
|
@ -266,13 +266,6 @@ void buffer_append_int(buffer *b, intmax_t val) {
|
|||
buffer_append_string_len(b, str, buf_end - str);
|
||||
}
|
||||
|
||||
void buffer_copy_int(buffer *b, intmax_t val) {
|
||||
force_assert(NULL != b);
|
||||
|
||||
b->used = 0;
|
||||
buffer_append_int(b, val);
|
||||
}
|
||||
|
||||
void buffer_append_strftime(buffer *b, const char *format, const struct tm *tm) {
|
||||
size_t rv;
|
||||
char* buf;
|
||||
|
|
|
@ -90,7 +90,6 @@ static inline void buffer_append_string_buffer(buffer *b, const buffer *src);
|
|||
#define buffer_append_uint_hex(b,len) buffer_append_uint_hex_lc((b),(len))
|
||||
void buffer_append_uint_hex_lc(buffer *b, uintmax_t len);
|
||||
void buffer_append_int(buffer *b, intmax_t val);
|
||||
void buffer_copy_int(buffer *b, intmax_t val);
|
||||
|
||||
void buffer_append_strftime(buffer *b, const char *format, const struct tm *tm);
|
||||
|
||||
|
|
|
@ -24,9 +24,15 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* internal reference to srv->config_context array of (data_config *) */
|
||||
static struct {
|
||||
const data_config * const *data; /* (srv->config_context->data) */
|
||||
uint32_t used; /* (srv->config_context->used) */
|
||||
} config_reference;
|
||||
|
||||
void config_get_config_cond_info(server *srv, uint32_t idx, config_cond_info *cfginfo) {
|
||||
data_config *dc = (data_config *)srv->config_context->data[idx];
|
||||
|
||||
void config_get_config_cond_info(config_cond_info * const cfginfo, uint32_t idx) {
|
||||
const data_config * const dc = (data_config *)config_reference.data[idx];
|
||||
cfginfo->comp = dc->comp;
|
||||
cfginfo->cond = dc->cond;
|
||||
cfginfo->string = &dc->string;
|
||||
|
@ -246,6 +252,11 @@ int config_plugin_values_init(server * const srv, void *p_d, const config_plugin
|
|||
int rc = 1; /* default is success */
|
||||
force_assert(sizeof(matches) >= srv->config_context->used);
|
||||
|
||||
/* save config reference data for later internal use
|
||||
* (config_plugin_values_init() is called with same srv->config_context) */
|
||||
config_reference.data = (const data_config * const *)srv->config_context->data;
|
||||
config_reference.used = srv->config_context->used;
|
||||
|
||||
/* traverse config contexts twice: once to count, once to store matches */
|
||||
|
||||
for (uint32_t u = 0; u < srv->config_context->used; ++u) {
|
||||
|
@ -306,8 +317,8 @@ int config_plugin_values_init(server * const srv, void *p_d, const config_plugin
|
|||
|
||||
__attribute_cold__
|
||||
__attribute_noinline__
|
||||
static void config_cond_result_trace(connection *con, const data_config *dc, int cached) {
|
||||
cond_cache_t * const cache = &con->request.cond_cache[dc->context_ndx];
|
||||
static void config_cond_result_trace(request_st * const r, const data_config * const dc, const int cached) {
|
||||
cond_cache_t * const cache = &r->cond_cache[dc->context_ndx];
|
||||
const char *msg;
|
||||
switch (cache->result) {
|
||||
case COND_RESULT_UNSET: msg = "unset"; break;
|
||||
|
@ -316,14 +327,14 @@ static void config_cond_result_trace(connection *con, const data_config *dc, int
|
|||
case COND_RESULT_TRUE: msg = "true"; break;
|
||||
default: msg = "invalid cond_result_t"; break;
|
||||
}
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "%d (%s) result: %s",
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "%d (%s) result: %s",
|
||||
dc->context_ndx, &"uncached"[cached ? 2 : 0], msg);
|
||||
}
|
||||
|
||||
static cond_result_t config_check_cond_nocache(connection *con, const data_config *dc, int debug_cond, cond_cache_t *cache);
|
||||
static cond_result_t config_check_cond_nocache(request_st *r, const data_config *dc, int debug_cond, cond_cache_t *cache);
|
||||
|
||||
static cond_result_t config_check_cond_nocache_calc(connection *con, const data_config *dc, int debug_cond, cond_cache_t *cache) {
|
||||
cache->result = config_check_cond_nocache(con, dc, debug_cond, cache);
|
||||
static cond_result_t config_check_cond_nocache_calc(request_st * const r, const data_config * const dc, const int debug_cond, cond_cache_t * const cache) {
|
||||
cache->result = config_check_cond_nocache(r, dc, debug_cond, cache);
|
||||
switch (cache->result) {
|
||||
case COND_RESULT_FALSE:
|
||||
case COND_RESULT_TRUE:
|
||||
|
@ -333,73 +344,73 @@ static cond_result_t config_check_cond_nocache_calc(connection *con, const data_
|
|||
default:
|
||||
break;
|
||||
}
|
||||
if (debug_cond) config_cond_result_trace(con, dc, 0);
|
||||
if (debug_cond) config_cond_result_trace(r, dc, 0);
|
||||
return cache->result;
|
||||
}
|
||||
|
||||
static cond_result_t config_check_cond_cached(connection *con, const data_config *dc, const int debug_cond) {
|
||||
cond_cache_t * const cache = &con->request.cond_cache[dc->context_ndx];
|
||||
static cond_result_t config_check_cond_cached(request_st * const r, const data_config * const dc, const int debug_cond) {
|
||||
cond_cache_t * const cache = &r->cond_cache[dc->context_ndx];
|
||||
if (COND_RESULT_UNSET != cache->result) {
|
||||
if (debug_cond) config_cond_result_trace(con, dc, 1);
|
||||
if (debug_cond) config_cond_result_trace(r, dc, 1);
|
||||
return cache->result;
|
||||
}
|
||||
return config_check_cond_nocache_calc(con, dc, debug_cond, cache);
|
||||
return config_check_cond_nocache_calc(r, dc, debug_cond, cache);
|
||||
}
|
||||
|
||||
static int config_addrstr_eq_remote_ip_mask(connection *con, const char *addrstr, int nm_bits, sock_addr *rmt) {
|
||||
static int config_addrstr_eq_remote_ip_mask(request_st * const r, const char * const addrstr, const int nm_bits, sock_addr * const rmt) {
|
||||
/* special-case 0 == nm_bits to mean "all bits of the address" in addrstr */
|
||||
sock_addr addr;
|
||||
if (1 == sock_addr_inet_pton(&addr, addrstr, AF_INET, 0)) {
|
||||
if (nm_bits > 32) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "ERROR: ipv4 netmask too large: %d", nm_bits);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: ipv4 netmask too large: %d", nm_bits);
|
||||
return -1;
|
||||
}
|
||||
} else if (1 == sock_addr_inet_pton(&addr, addrstr, AF_INET6, 0)) {
|
||||
if (nm_bits > 128) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "ERROR: ipv6 netmask too large: %d", nm_bits);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: ipv6 netmask too large: %d", nm_bits);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "ERROR: ip addr is invalid: %s", addrstr);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: ip addr is invalid: %s", addrstr);
|
||||
return -1;
|
||||
}
|
||||
return sock_addr_is_addr_eq_bits(&addr, rmt, nm_bits);
|
||||
}
|
||||
|
||||
static int config_addrbuf_eq_remote_ip_mask(connection *con, const buffer *string, char *nm_slash, sock_addr *rmt) {
|
||||
static int config_addrbuf_eq_remote_ip_mask(request_st * const r, const buffer * const string, char * const nm_slash, sock_addr * const rmt) {
|
||||
char *err;
|
||||
int nm_bits = strtol(nm_slash + 1, &err, 10);
|
||||
size_t addrstrlen = (size_t)(nm_slash - string->ptr);
|
||||
char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/
|
||||
|
||||
if (*err) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "ERROR: non-digit found in netmask: %s %s", string->ptr, err);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: non-digit found in netmask: %s %s", string->ptr, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nm_bits <= 0) {
|
||||
if (*(nm_slash+1) == '\0') {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "ERROR: no number after / %s", string->ptr);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: no number after / %s", string->ptr);
|
||||
} else {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "ERROR: invalid netmask <= 0: %s %s", string->ptr, err);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: invalid netmask <= 0: %s %s", string->ptr, err);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addrstrlen >= sizeof(addrstr)) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "ERROR: address string too long: %s", string->ptr);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "ERROR: address string too long: %s", string->ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(addrstr, string->ptr, addrstrlen);
|
||||
addrstr[addrstrlen] = '\0';
|
||||
|
||||
return config_addrstr_eq_remote_ip_mask(con, addrstr, nm_bits, rmt);
|
||||
return config_addrstr_eq_remote_ip_mask(r, addrstr, nm_bits, rmt);
|
||||
}
|
||||
|
||||
static int data_config_pcre_exec(const data_config *dc, cond_cache_t *cache, const buffer *b, cond_match_t *cond_match);
|
||||
|
||||
static cond_result_t config_check_cond_nocache(connection *con, const data_config *dc, const int debug_cond, cond_cache_t * const cache) {
|
||||
static cond_result_t config_check_cond_nocache(request_st * const r, const data_config * const dc, const int debug_cond, cond_cache_t * const cache) {
|
||||
static struct const_char_buffer {
|
||||
const char *ptr;
|
||||
uint32_t used;
|
||||
|
@ -414,10 +425,10 @@ static cond_result_t config_check_cond_nocache(connection *con, const data_confi
|
|||
* if the parent is not decided yet or false, we can't be true either
|
||||
*/
|
||||
if (debug_cond) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "go parent %s", dc->parent->key.ptr);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "go parent %s", dc->parent->key.ptr);
|
||||
}
|
||||
|
||||
switch (config_check_cond_cached(con, dc->parent, debug_cond)) {
|
||||
switch (config_check_cond_cached(r, dc->parent, debug_cond)) {
|
||||
case COND_RESULT_UNSET:
|
||||
/* decide later */
|
||||
return COND_RESULT_UNSET;
|
||||
|
@ -437,11 +448,11 @@ static cond_result_t config_check_cond_nocache(connection *con, const data_confi
|
|||
* was evaluated as "false" (not unset/skipped/true)
|
||||
*/
|
||||
if (debug_cond) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "go prev %s", dc->prev->key.ptr);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__, "go prev %s", dc->prev->key.ptr);
|
||||
}
|
||||
|
||||
/* make sure prev is checked first */
|
||||
switch (config_check_cond_cached(con, dc->prev, debug_cond)) {
|
||||
switch (config_check_cond_cached(r, dc->prev, debug_cond)) {
|
||||
case COND_RESULT_UNSET:
|
||||
/* decide later */
|
||||
return COND_RESULT_UNSET;
|
||||
|
@ -455,11 +466,10 @@ static cond_result_t config_check_cond_nocache(connection *con, const data_confi
|
|||
}
|
||||
}
|
||||
|
||||
if (!(con->request.conditional_is_valid & (1 << dc->comp))) {
|
||||
if (!(r->conditional_is_valid & (1 << dc->comp))) {
|
||||
if (debug_cond) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__, "%d %s not available yet",
|
||||
dc->comp,
|
||||
dc->key.ptr);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"%d %s not available yet", dc->comp, dc->key.ptr);
|
||||
}
|
||||
|
||||
return COND_RESULT_UNSET;
|
||||
|
@ -482,7 +492,7 @@ static cond_result_t config_check_cond_nocache(connection *con, const data_confi
|
|||
switch (dc->comp) {
|
||||
case COMP_HTTP_HOST:
|
||||
|
||||
l = con->uri.authority;
|
||||
l = &r->uri.authority;
|
||||
|
||||
if (buffer_string_is_empty(l)) {
|
||||
l = (buffer *)&empty_string;
|
||||
|
@ -492,7 +502,7 @@ static cond_result_t config_check_cond_nocache(connection *con, const data_confi
|
|||
switch(dc->cond) {
|
||||
case CONFIG_COND_NE:
|
||||
case CONFIG_COND_EQ: {
|
||||
unsigned short port = sock_addr_get_port(&con->srv_socket->addr);
|
||||
unsigned short port = sock_addr_get_port(&r->con->srv_socket->addr);
|
||||
if (0 == port) break;
|
||||
const char *ck_colon = strchr(dc->string.ptr, ':');
|
||||
const char *val_colon = strchr(l->ptr, ':');
|
||||
|
@ -500,14 +510,14 @@ static cond_result_t config_check_cond_nocache(connection *con, const data_confi
|
|||
/* append server-port if necessary */
|
||||
if (NULL != ck_colon && NULL == val_colon) {
|
||||
/* condition "host:port" but client send "host" */
|
||||
buffer *tb = con->srv->tmp_buf;
|
||||
buffer *tb = r->tmp_buf;
|
||||
buffer_copy_buffer(tb, l);
|
||||
buffer_append_string_len(tb, CONST_STR_LEN(":"));
|
||||
buffer_append_int(tb, port);
|
||||
l = tb;
|
||||
} else if (NULL != val_colon && NULL == ck_colon) {
|
||||
/* condition "host" but client send "host:port" */
|
||||
buffer *tb = con->srv->tmp_buf;
|
||||
buffer *tb = r->tmp_buf;
|
||||
buffer_copy_string_len(tb, l->ptr, val_colon - l->ptr);
|
||||
l = tb;
|
||||
}
|
||||
|
@ -532,52 +542,52 @@ static cond_result_t config_check_cond_nocache(connection *con, const data_confi
|
|||
if ((dc->cond == CONFIG_COND_EQ ||
|
||||
dc->cond == CONFIG_COND_NE) &&
|
||||
(NULL != (nm_slash = strchr(dc->string.ptr, '/')))) {
|
||||
switch (config_addrbuf_eq_remote_ip_mask(con, &dc->string, nm_slash, &con->dst_addr)) {
|
||||
switch (config_addrbuf_eq_remote_ip_mask(r, &dc->string, nm_slash, &r->con->dst_addr)) {
|
||||
case 1: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
|
||||
case 0: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
|
||||
case -1: return COND_RESULT_FALSE; /*(error parsing configfile entry)*/
|
||||
}
|
||||
}
|
||||
l = con->dst_addr_buf;
|
||||
l = r->con->dst_addr_buf;
|
||||
break;
|
||||
}
|
||||
case COMP_HTTP_SCHEME:
|
||||
l = con->uri.scheme;
|
||||
l = &r->uri.scheme;
|
||||
break;
|
||||
|
||||
case COMP_HTTP_URL:
|
||||
l = con->uri.path;
|
||||
l = &r->uri.path;
|
||||
break;
|
||||
|
||||
case COMP_HTTP_QUERY_STRING:
|
||||
l = con->uri.query;
|
||||
l = &r->uri.query;
|
||||
if (NULL == l->ptr) l = (buffer *)&empty_string;
|
||||
break;
|
||||
|
||||
case COMP_SERVER_SOCKET:
|
||||
l = con->srv_socket->srv_token;
|
||||
l = r->con->srv_socket->srv_token;
|
||||
break;
|
||||
|
||||
case COMP_HTTP_REQUEST_HEADER:
|
||||
*((const buffer **)&l) = http_header_request_get(con, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(dc->comp_tag));
|
||||
*((const buffer **)&l) = http_header_request_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(dc->comp_tag));
|
||||
if (NULL == l) l = (buffer *)&empty_string;
|
||||
break;
|
||||
case COMP_HTTP_REQUEST_METHOD:
|
||||
l = con->srv->tmp_buf;
|
||||
l = r->tmp_buf;
|
||||
buffer_clear(l);
|
||||
http_method_append(l, con->request.http_method);
|
||||
http_method_append(l, r->http_method);
|
||||
break;
|
||||
default:
|
||||
return COND_RESULT_FALSE;
|
||||
}
|
||||
|
||||
if (NULL == l) { /*(should not happen)*/
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"%s () compare to NULL", dc->comp_key->ptr);
|
||||
return COND_RESULT_FALSE;
|
||||
}
|
||||
else if (debug_cond) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"%s (%s) compare to %s", dc->comp_key->ptr, l->ptr, dc->string.ptr);
|
||||
}
|
||||
|
||||
|
@ -591,7 +601,7 @@ static cond_result_t config_check_cond_nocache(connection *con, const data_confi
|
|||
}
|
||||
case CONFIG_COND_NOMATCH:
|
||||
case CONFIG_COND_MATCH: {
|
||||
cond_match_t *cond_match = con->request.cond_match + dc->context_ndx;
|
||||
cond_match_t *cond_match = r->cond_match + dc->context_ndx;
|
||||
if (data_config_pcre_exec(dc, cache, l, cond_match) > 0) {
|
||||
return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
|
||||
} else {
|
||||
|
@ -608,24 +618,23 @@ static cond_result_t config_check_cond_nocache(connection *con, const data_confi
|
|||
}
|
||||
|
||||
__attribute_noinline__
|
||||
static cond_result_t config_check_cond_calc(connection *con, const int context_ndx, cond_cache_t * const cache) {
|
||||
const data_config * const dc = (const data_config *)
|
||||
con->srv->config_context->data[context_ndx];
|
||||
const int debug_cond = con->conf.log_condition_handling;
|
||||
static cond_result_t config_check_cond_calc(request_st * const r, const int context_ndx, cond_cache_t * const cache) {
|
||||
const data_config * const dc = config_reference.data[context_ndx];
|
||||
const int debug_cond = r->conf.log_condition_handling;
|
||||
if (debug_cond) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"=== start of condition block ===");
|
||||
}
|
||||
return config_check_cond_nocache_calc(con, dc, debug_cond, cache);
|
||||
return config_check_cond_nocache_calc(r, dc, debug_cond, cache);
|
||||
}
|
||||
|
||||
/* future: might make static inline in header for plugins */
|
||||
int config_check_cond(connection * const con, const int context_ndx) {
|
||||
cond_cache_t * const cache = &con->request.cond_cache[context_ndx];
|
||||
int config_check_cond(request_st * const r, const int context_ndx) {
|
||||
cond_cache_t * const cache = &r->cond_cache[context_ndx];
|
||||
return COND_RESULT_TRUE
|
||||
== (COND_RESULT_UNSET != cache->result
|
||||
? (cond_result_t)cache->result
|
||||
: config_check_cond_calc(con, context_ndx, cache));
|
||||
: config_check_cond_calc(r, context_ndx, cache));
|
||||
}
|
||||
|
||||
/* if we reset the cache result for a node, we also need to clear all
|
||||
|
@ -651,11 +660,12 @@ static void config_cond_clear_node(cond_cache_t * const cond_cache, const data_c
|
|||
*
|
||||
* if the item is COND_LAST_ELEMENT we reset all items
|
||||
*/
|
||||
void config_cond_cache_reset_item(connection *con, comp_key_t item) {
|
||||
cond_cache_t * const cond_cache = con->request.cond_cache;
|
||||
const array * const config_context = con->srv->config_context;
|
||||
for (uint32_t i = 0; i < config_context->used; ++i) {
|
||||
const data_config *dc = (data_config *)config_context->data[i];
|
||||
void config_cond_cache_reset_item(request_st * const r, comp_key_t item) {
|
||||
cond_cache_t * const cond_cache = r->cond_cache;
|
||||
const data_config * const * const data = config_reference.data;
|
||||
const uint32_t used = config_reference.used;
|
||||
for (uint32_t i = 0; i < used; ++i) {
|
||||
const data_config * const dc = data[i];
|
||||
|
||||
if (item == dc->comp) {
|
||||
/* clear local_result */
|
||||
|
@ -669,13 +679,13 @@ void config_cond_cache_reset_item(connection *con, comp_key_t item) {
|
|||
/**
|
||||
* reset the config cache to its initial state at connection start
|
||||
*/
|
||||
void config_cond_cache_reset(connection *con) {
|
||||
con->request.conditional_is_valid = 0;
|
||||
void config_cond_cache_reset(request_st * const r) {
|
||||
r->conditional_is_valid = 0;
|
||||
/* resetting all entries; no need to follow children as in config_cond_cache_reset_item */
|
||||
/* static_assert(0 == COND_RESULT_UNSET); */
|
||||
const uint32_t used = con->srv->config_context->used;
|
||||
const uint32_t used = config_reference.used;
|
||||
if (used > 1)
|
||||
memset(con->request.cond_cache, 0, used*sizeof(cond_cache_t));
|
||||
memset(r->cond_cache, 0, used*sizeof(cond_cache_t));
|
||||
}
|
||||
|
||||
#ifdef HAVE_PCRE_H
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "base.h"
|
||||
#include "burl.h"
|
||||
#include "etag.h"
|
||||
#include "fdevent.h"
|
||||
#include "keyvalue.h"
|
||||
#include "log.h"
|
||||
|
@ -196,23 +197,23 @@ static void config_merge_config(request_config * const pconf, const config_plugi
|
|||
} while ((++cpv)->k_id != -1);
|
||||
}
|
||||
|
||||
void config_patch_config(connection * const con) {
|
||||
config_data_base * const p = con->config_data_base;
|
||||
void config_patch_config(request_st * const r) {
|
||||
config_data_base * const p = r->con->config_data_base;
|
||||
|
||||
/* performed by config_reset_config() */
|
||||
/*memcpy(&con->conf, &p->defaults, sizeof(request_config));*/
|
||||
/*memcpy(&r->conf, &p->defaults, sizeof(request_config));*/
|
||||
|
||||
for (int i = 1, used = p->nconfig; i < used; ++i) {
|
||||
if (config_check_cond(con, (uint32_t)p->cvlist[i].k_id))
|
||||
config_merge_config(&con->conf, p->cvlist + p->cvlist[i].v.u2[0]);
|
||||
if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
|
||||
config_merge_config(&r->conf, p->cvlist + p->cvlist[i].v.u2[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void config_reset_config(connection * const con) {
|
||||
/* initialize request_config (con->conf) from top-level request_config */
|
||||
config_data_base * const p = con->config_data_base;
|
||||
con->request.server_name = p->defaults.server_name;
|
||||
memcpy(&con->conf, &p->defaults, sizeof(request_config));
|
||||
void config_reset_config(request_st * const r) {
|
||||
/* initialize request_config (r->conf) from top-level request_config */
|
||||
config_data_base * const p = r->con->config_data_base;
|
||||
r->server_name = p->defaults.server_name;
|
||||
memcpy(&r->conf, &p->defaults, sizeof(request_config));
|
||||
}
|
||||
|
||||
static int config_burl_normalize_cond (server *srv) {
|
||||
|
@ -951,7 +952,7 @@ static int config_insert(server *srv) {
|
|||
/* use 2 to detect later if value is set by user config in global section */
|
||||
p->defaults.force_lowercase_filenames = 2;
|
||||
|
||||
/*(global, but store in con->conf.http_parseopts)*/
|
||||
/*(global, but store in r->conf.http_parseopts)*/
|
||||
p->defaults.http_parseopts =
|
||||
(srv->srvconf.http_header_strict ? HTTP_PARSEOPT_HEADER_STRICT :0)
|
||||
| (srv->srvconf.http_host_strict ? (HTTP_PARSEOPT_HOST_STRICT
|
||||
|
@ -2125,14 +2126,14 @@ int config_set_defaults(server *srv) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
buffer * const tb = srv->tmp_buf;
|
||||
buffer_copy_buffer(tb, s->document_root);
|
||||
|
||||
buffer_to_lower(tb);
|
||||
|
||||
if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */
|
||||
s->force_lowercase_filenames = 0; /* default to 0 */
|
||||
|
||||
buffer * const tb = srv->tmp_buf;
|
||||
buffer_copy_buffer(tb, s->document_root);
|
||||
|
||||
buffer_to_lower(tb);
|
||||
|
||||
if (0 == stat(tb->ptr, &st1)) {
|
||||
int is_lower = 0;
|
||||
|
||||
|
|
|
@ -762,7 +762,7 @@ stringop(A) ::= expression(B). {
|
|||
A = buffer_init_buffer(&((data_string*)B)->value);
|
||||
} else if (B->type == TYPE_INTEGER) {
|
||||
A = buffer_init();
|
||||
buffer_copy_int(A, ((data_integer *)B)->value);
|
||||
buffer_append_int(A, ((data_integer *)B)->value);
|
||||
} else {
|
||||
fprintf(stderr, "operand must be string");
|
||||
ctx->ok = 0;
|
||||
|
|
|
@ -109,23 +109,23 @@ static int connection_handle_read_post_chunked_crlf(chunkqueue *cq) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
handler_t connection_handle_read_post_error(connection *con, int http_status) {
|
||||
con->request.keep_alive = 0;
|
||||
handler_t connection_handle_read_post_error(request_st * const r, int http_status) {
|
||||
r->keep_alive = 0;
|
||||
|
||||
/*(do not change status if response headers already set and possibly sent)*/
|
||||
if (0 != con->response.resp_header_len) return HANDLER_ERROR;
|
||||
if (0 != r->resp_header_len) return HANDLER_ERROR;
|
||||
|
||||
http_response_body_clear(con, 0);
|
||||
con->http_status = http_status;
|
||||
con->response.handler_module = NULL;
|
||||
http_response_body_clear(r, 0);
|
||||
r->http_status = http_status;
|
||||
r->handler_module = NULL;
|
||||
return HANDLER_FINISHED;
|
||||
}
|
||||
|
||||
static handler_t connection_handle_read_post_chunked(connection *con, chunkqueue *cq, chunkqueue *dst_cq) {
|
||||
static handler_t connection_handle_read_post_chunked(request_st * const r, chunkqueue * const cq, chunkqueue * const dst_cq) {
|
||||
|
||||
/* con->conf.max_request_size is in kBytes */
|
||||
const off_t max_request_size = (off_t)con->conf.max_request_size << 10;
|
||||
off_t te_chunked = con->request.te_chunked;
|
||||
/* r->conf.max_request_size is in kBytes */
|
||||
const off_t max_request_size = (off_t)r->conf.max_request_size << 10;
|
||||
off_t te_chunked = r->te_chunked;
|
||||
do {
|
||||
off_t len = cq->bytes_in - cq->bytes_out;
|
||||
|
||||
|
@ -140,29 +140,29 @@ static handler_t connection_handle_read_post_chunked(connection *con, chunkqueue
|
|||
unsigned char *s = (unsigned char *)c->mem->ptr+c->offset;
|
||||
for (unsigned char u;(u=(unsigned char)hex2int(*s))!=0xFF;++s) {
|
||||
if (te_chunked > (off_t)(1uLL<<(8*sizeof(off_t)-5))-1) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"chunked data size too large -> 400");
|
||||
/* 400 Bad Request */
|
||||
return connection_handle_read_post_error(con, 400);
|
||||
return connection_handle_read_post_error(r, 400);
|
||||
}
|
||||
te_chunked <<= 4;
|
||||
te_chunked |= u;
|
||||
}
|
||||
while (*s == ' ' || *s == '\t') ++s;
|
||||
if (*s != '\r' && *s != ';') {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"chunked header invalid chars -> 400");
|
||||
/* 400 Bad Request */
|
||||
return connection_handle_read_post_error(con, 400);
|
||||
return connection_handle_read_post_error(r, 400);
|
||||
}
|
||||
|
||||
if (hsz >= 1024) {
|
||||
/* prevent theoretical integer overflow
|
||||
* casting to (size_t) and adding 2 (for "\r\n") */
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"chunked header line too long -> 400");
|
||||
/* 400 Bad Request */
|
||||
return connection_handle_read_post_error(con, 400);
|
||||
return connection_handle_read_post_error(r, 400);
|
||||
}
|
||||
|
||||
if (0 == te_chunked) {
|
||||
|
@ -186,24 +186,24 @@ static handler_t connection_handle_read_post_chunked(connection *con, chunkqueue
|
|||
* potentially received by backend, if in the future
|
||||
* these trailers are added to request headers)*/
|
||||
if ((off_t)buffer_string_length(c->mem) - c->offset
|
||||
< (off_t)con->conf.max_request_field_size) {
|
||||
< (off_t)r->conf.max_request_field_size) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* ignore excessively long trailers;
|
||||
* disable keep-alive on connection */
|
||||
con->request.keep_alive = 0;
|
||||
r->keep_alive = 0;
|
||||
p = c->mem->ptr + buffer_string_length(c->mem) - 4;
|
||||
}
|
||||
}
|
||||
hsz = p + 4 - (c->mem->ptr+c->offset);
|
||||
/* trailers currently ignored, but could be processed
|
||||
* here if 0 == con->conf.stream_request_body, taking
|
||||
* here if 0 == r->conf.stream_request_body, taking
|
||||
* care to reject any fields forbidden in trailers,
|
||||
* making trailers available to CGI and other backends*/
|
||||
}
|
||||
chunkqueue_mark_written(cq, (size_t)hsz);
|
||||
con->request.reqbody_length = dst_cq->bytes_in;
|
||||
r->reqbody_length = dst_cq->bytes_in;
|
||||
break; /* done reading HTTP chunked request body */
|
||||
}
|
||||
|
||||
|
@ -214,11 +214,11 @@ static handler_t connection_handle_read_post_chunked(connection *con, chunkqueue
|
|||
if (0 !=max_request_size
|
||||
&& (max_request_size < te_chunked
|
||||
|| max_request_size - te_chunked < dst_cq->bytes_in)) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"request-size too long: %lld -> 413",
|
||||
(long long)(dst_cq->bytes_in + te_chunked));
|
||||
/* 413 Payload Too Large */
|
||||
return connection_handle_read_post_error(con, 413);
|
||||
return connection_handle_read_post_error(r, 413);
|
||||
}
|
||||
|
||||
te_chunked += 2; /*(for trailing "\r\n" after chunked data)*/
|
||||
|
@ -229,10 +229,10 @@ static handler_t connection_handle_read_post_chunked(connection *con, chunkqueue
|
|||
/*(likely better ways to handle chunked header crossing chunkqueue
|
||||
* chunks, but this situation is not expected to occur frequently)*/
|
||||
if ((off_t)buffer_string_length(c->mem) - c->offset >= 1024) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"chunked header line too long -> 400");
|
||||
/* 400 Bad Request */
|
||||
return connection_handle_read_post_error(con, 400);
|
||||
return connection_handle_read_post_error(r, 400);
|
||||
}
|
||||
else if (!connection_handle_read_post_cq_compact(cq)) {
|
||||
break;
|
||||
|
@ -247,9 +247,9 @@ static handler_t connection_handle_read_post_chunked(connection *con, chunkqueue
|
|||
chunkqueue_steal(dst_cq, cq, len);
|
||||
}
|
||||
else if (0 != chunkqueue_steal_with_tempfiles(dst_cq, cq, len,
|
||||
con->conf.errh)) {
|
||||
r->conf.errh)) {
|
||||
/* 500 Internal Server Error */
|
||||
return connection_handle_read_post_error(con, 500);
|
||||
return connection_handle_read_post_error(r, 500);
|
||||
}
|
||||
te_chunked -= len;
|
||||
len = cq->bytes_in - cq->bytes_out;
|
||||
|
@ -259,10 +259,10 @@ static handler_t connection_handle_read_post_chunked(connection *con, chunkqueue
|
|||
|
||||
if (2 == te_chunked) {
|
||||
if (-1 == connection_handle_read_post_chunked_crlf(cq)) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"chunked data missing end CRLF -> 400");
|
||||
/* 400 Bad Request */
|
||||
return connection_handle_read_post_error(con, 400);
|
||||
return connection_handle_read_post_error(r, 400);
|
||||
}
|
||||
chunkqueue_mark_written(cq, 2);/*consume \r\n at end of chunk data*/
|
||||
te_chunked -= 2;
|
||||
|
@ -270,29 +270,30 @@ static handler_t connection_handle_read_post_chunked(connection *con, chunkqueue
|
|||
|
||||
} while (!chunkqueue_is_empty(cq));
|
||||
|
||||
con->request.te_chunked = te_chunked;
|
||||
r->te_chunked = te_chunked;
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
static handler_t connection_handle_read_body_unknown(connection *con, chunkqueue *cq, chunkqueue *dst_cq) {
|
||||
/* con->conf.max_request_size is in kBytes */
|
||||
const off_t max_request_size = (off_t)con->conf.max_request_size << 10;
|
||||
static handler_t connection_handle_read_body_unknown(request_st * const r, chunkqueue * const cq, chunkqueue * const dst_cq) {
|
||||
/* r->conf.max_request_size is in kBytes */
|
||||
const off_t max_request_size = (off_t)r->conf.max_request_size << 10;
|
||||
chunkqueue_append_chunkqueue(dst_cq, cq);
|
||||
if (0 != max_request_size && dst_cq->bytes_in > max_request_size) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"request-size too long: %lld -> 413", (long long)dst_cq->bytes_in);
|
||||
/* 413 Payload Too Large */
|
||||
return connection_handle_read_post_error(con, 413);
|
||||
return connection_handle_read_post_error(r, 413);
|
||||
}
|
||||
return HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
static off_t connection_write_throttle(connection *con, off_t max_bytes) {
|
||||
if (con->conf.global_bytes_per_second) {
|
||||
off_t limit = (off_t)con->conf.global_bytes_per_second - *(con->conf.global_bytes_per_second_cnt_ptr);
|
||||
static off_t connection_write_throttle(connection * const con, off_t max_bytes) {
|
||||
request_st * const r = &con->request;
|
||||
if (r->conf.global_bytes_per_second) {
|
||||
off_t limit = (off_t)r->conf.global_bytes_per_second - *(r->conf.global_bytes_per_second_cnt_ptr);
|
||||
if (limit <= 0) {
|
||||
/* we reached the global traffic limit */
|
||||
con->traffic_limit_reached = 1;
|
||||
r->con->traffic_limit_reached = 1;
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
|
@ -300,11 +301,11 @@ static off_t connection_write_throttle(connection *con, off_t max_bytes) {
|
|||
}
|
||||
}
|
||||
|
||||
if (con->conf.bytes_per_second) {
|
||||
off_t limit = (off_t)con->conf.bytes_per_second - con->bytes_written_cur_second;
|
||||
if (r->conf.bytes_per_second) {
|
||||
off_t limit = (off_t)r->conf.bytes_per_second - con->bytes_written_cur_second;
|
||||
if (limit <= 0) {
|
||||
/* we reached the traffic limit */
|
||||
con->traffic_limit_reached = 1;
|
||||
r->con->traffic_limit_reached = 1;
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
|
@ -316,22 +317,20 @@ static off_t connection_write_throttle(connection *con, off_t max_bytes) {
|
|||
}
|
||||
|
||||
int connection_write_chunkqueue(connection *con, chunkqueue *cq, off_t max_bytes) {
|
||||
int ret = -1;
|
||||
off_t written = 0;
|
||||
#ifdef TCP_CORK
|
||||
int corked = 0;
|
||||
#endif
|
||||
con->write_request_ts = log_epoch_secs;
|
||||
|
||||
max_bytes = connection_write_throttle(con, max_bytes);
|
||||
if (0 == max_bytes) return 1;
|
||||
|
||||
written = cq->bytes_out;
|
||||
off_t written = cq->bytes_out;
|
||||
int ret;
|
||||
|
||||
#ifdef TCP_CORK
|
||||
/* Linux: put a cork into socket as we want to combine write() calls
|
||||
* but only if we really have multiple chunks including non-MEM_CHUNK,
|
||||
* and only if TCP socket
|
||||
*/
|
||||
int corked = 0;
|
||||
if (cq->first && cq->first->next) {
|
||||
const int sa_family = sock_addr_get_family(&con->srv_socket->addr);
|
||||
if (sa_family == AF_INET || sa_family == AF_INET6) {
|
||||
|
@ -360,20 +359,18 @@ int connection_write_chunkqueue(connection *con, chunkqueue *cq, off_t max_bytes
|
|||
written = cq->bytes_out - written;
|
||||
con->bytes_written += written;
|
||||
con->bytes_written_cur_second += written;
|
||||
if (con->conf.global_bytes_per_second_cnt_ptr)
|
||||
*(con->conf.global_bytes_per_second_cnt_ptr) += written;
|
||||
request_st * const r = &con->request;
|
||||
if (r->conf.global_bytes_per_second_cnt_ptr)
|
||||
*(r->conf.global_bytes_per_second_cnt_ptr) += written;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int connection_write_100_continue(connection *con) {
|
||||
static int connection_write_100_continue(request_st * const r, connection * const con) {
|
||||
/* Make best effort to send all or none of "HTTP/1.1 100 Continue" */
|
||||
/* (Note: also choosing not to update con->write_request_ts
|
||||
* which differs from connections.c:connection_handle_write()) */
|
||||
* which differs from connection_write_chunkqueue()) */
|
||||
static const char http_100_continue[] = "HTTP/1.1 100 Continue\r\n\r\n";
|
||||
chunkqueue *cq;
|
||||
off_t written;
|
||||
int rc;
|
||||
|
||||
off_t max_bytes =
|
||||
connection_write_throttle(con, sizeof(http_100_continue)-1);
|
||||
|
@ -381,20 +378,20 @@ static int connection_write_100_continue(connection *con) {
|
|||
return 1; /* success; skip sending if throttled to partial */
|
||||
}
|
||||
|
||||
cq = con->write_queue;
|
||||
written = cq->bytes_out;
|
||||
chunkqueue * const cq = r->write_queue;
|
||||
off_t written = cq->bytes_out;
|
||||
|
||||
chunkqueue_append_mem(cq,http_100_continue,sizeof(http_100_continue)-1);
|
||||
rc = con->network_write(con, cq, sizeof(http_100_continue)-1);
|
||||
int rc = con->network_write(con, cq, sizeof(http_100_continue)-1);
|
||||
|
||||
written = cq->bytes_out - written;
|
||||
con->bytes_written += written;
|
||||
con->bytes_written_cur_second += written;
|
||||
if (con->conf.global_bytes_per_second_cnt_ptr)
|
||||
*(con->conf.global_bytes_per_second_cnt_ptr) += written;
|
||||
if (r->conf.global_bytes_per_second_cnt_ptr)
|
||||
*(r->conf.global_bytes_per_second_cnt_ptr) += written;
|
||||
|
||||
if (rc < 0) {
|
||||
con->request.state = CON_STATE_ERROR;
|
||||
r->state = CON_STATE_ERROR;
|
||||
return 0; /* error */
|
||||
}
|
||||
|
||||
|
@ -412,18 +409,19 @@ static int connection_write_100_continue(connection *con) {
|
|||
return 1; /* success; sent all or none of "HTTP/1.1 100 Continue" */
|
||||
}
|
||||
|
||||
handler_t connection_handle_read_post_state(connection *con) {
|
||||
chunkqueue *cq = con->read_queue;
|
||||
chunkqueue *dst_cq = con->request.reqbody_queue;
|
||||
handler_t connection_handle_read_post_state(request_st * const r) {
|
||||
connection * const con = r->con;
|
||||
chunkqueue * const cq = r->read_queue;
|
||||
chunkqueue * const dst_cq = r->reqbody_queue;
|
||||
|
||||
int is_closed = 0;
|
||||
|
||||
if (con->is_readable) {
|
||||
con->read_idle_ts = log_epoch_secs;
|
||||
|
||||
switch(con->network_read(con, con->read_queue, MAX_READ_LIMIT)) {
|
||||
switch(con->network_read(con, cq, MAX_READ_LIMIT)) {
|
||||
case -1:
|
||||
con->request.state = CON_STATE_ERROR;
|
||||
r->state = CON_STATE_ERROR;
|
||||
return HANDLER_ERROR;
|
||||
case -2:
|
||||
is_closed = 1;
|
||||
|
@ -438,69 +436,69 @@ handler_t connection_handle_read_post_state(connection *con) {
|
|||
/* Check for Expect: 100-continue in request headers
|
||||
* if no request body received yet */
|
||||
if (chunkqueue_is_empty(cq) && 0 == dst_cq->bytes_in
|
||||
&& con->request.http_version != HTTP_VERSION_1_0
|
||||
&& chunkqueue_is_empty(con->write_queue) && con->is_writable) {
|
||||
const buffer *vb = http_header_request_get(con, HTTP_HEADER_EXPECT, CONST_STR_LEN("Expect"));
|
||||
&& r->http_version != HTTP_VERSION_1_0
|
||||
&& chunkqueue_is_empty(r->write_queue) && con->is_writable) {
|
||||
const buffer *vb = http_header_request_get(r, HTTP_HEADER_EXPECT, CONST_STR_LEN("Expect"));
|
||||
if (NULL != vb && buffer_eq_icase_slen(vb, CONST_STR_LEN("100-continue"))) {
|
||||
http_header_request_unset(con, HTTP_HEADER_EXPECT, CONST_STR_LEN("Expect"));
|
||||
if (!connection_write_100_continue(con)) {
|
||||
http_header_request_unset(r, HTTP_HEADER_EXPECT, CONST_STR_LEN("Expect"));
|
||||
if (!connection_write_100_continue(r, con)) {
|
||||
return HANDLER_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (con->request.reqbody_length < 0) {
|
||||
if (r->reqbody_length < 0) {
|
||||
/*(-1: Transfer-Encoding: chunked, -2: unspecified length)*/
|
||||
handler_t rc = (-1 == con->request.reqbody_length)
|
||||
? connection_handle_read_post_chunked(con, cq, dst_cq)
|
||||
: connection_handle_read_body_unknown(con, cq, dst_cq);
|
||||
handler_t rc = (-1 == r->reqbody_length)
|
||||
? connection_handle_read_post_chunked(r, cq, dst_cq)
|
||||
: connection_handle_read_body_unknown(r, cq, dst_cq);
|
||||
if (HANDLER_GO_ON != rc) return rc;
|
||||
}
|
||||
else if (con->request.reqbody_length <= 64*1024) {
|
||||
else if (r->reqbody_length <= 64*1024) {
|
||||
/* don't buffer request bodies <= 64k on disk */
|
||||
chunkqueue_steal(dst_cq, cq, (off_t)con->request.reqbody_length - dst_cq->bytes_in);
|
||||
chunkqueue_steal(dst_cq, cq, (off_t)r->reqbody_length - dst_cq->bytes_in);
|
||||
}
|
||||
else if (0 != chunkqueue_steal_with_tempfiles(dst_cq, cq, (off_t)con->request.reqbody_length - dst_cq->bytes_in, con->conf.errh)) {
|
||||
else if (0 != chunkqueue_steal_with_tempfiles(dst_cq, cq, (off_t)r->reqbody_length - dst_cq->bytes_in, r->conf.errh)) {
|
||||
/* writing to temp file failed */
|
||||
return connection_handle_read_post_error(con, 500); /* Internal Server Error */
|
||||
return connection_handle_read_post_error(r, 500); /* Internal Server Error */
|
||||
}
|
||||
|
||||
chunkqueue_remove_finished_chunks(cq);
|
||||
|
||||
if (dst_cq->bytes_in == (off_t)con->request.reqbody_length) {
|
||||
if (dst_cq->bytes_in == (off_t)r->reqbody_length) {
|
||||
/* Content is ready */
|
||||
con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
|
||||
if (con->request.state == CON_STATE_READ_POST) {
|
||||
con->request.state = CON_STATE_HANDLE_REQUEST;
|
||||
r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
|
||||
if (r->state == CON_STATE_READ_POST) {
|
||||
r->state = CON_STATE_HANDLE_REQUEST;
|
||||
}
|
||||
return HANDLER_GO_ON;
|
||||
} else if (is_closed) {
|
||||
#if 0
|
||||
return connection_handle_read_post_error(con, 400); /* Bad Request */
|
||||
return connection_handle_read_post_error(r, 400); /* Bad Request */
|
||||
#endif
|
||||
return HANDLER_ERROR;
|
||||
} else {
|
||||
con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
|
||||
return (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)
|
||||
r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
|
||||
return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)
|
||||
? HANDLER_GO_ON
|
||||
: HANDLER_WAIT_FOR_EVENT;
|
||||
}
|
||||
}
|
||||
|
||||
void connection_response_reset(connection *con) {
|
||||
con->http_status = 0;
|
||||
con->is_writable = 1;
|
||||
con->response.resp_body_finished = 0;
|
||||
con->response.resp_body_started = 0;
|
||||
con->response.handler_module = NULL;
|
||||
if (con->physical.path) { /*(skip for mod_fastcgi authorizer)*/
|
||||
buffer_clear(con->physical.doc_root);
|
||||
buffer_reset(con->physical.path);
|
||||
buffer_clear(con->physical.basedir);
|
||||
buffer_reset(con->physical.rel_path);
|
||||
buffer_clear(con->physical.etag);
|
||||
void connection_response_reset(request_st * const r) {
|
||||
r->http_status = 0;
|
||||
r->con->is_writable = 1;
|
||||
r->resp_body_finished = 0;
|
||||
r->resp_body_started = 0;
|
||||
r->handler_module = NULL;
|
||||
if (r->physical.path.ptr) { /*(skip for mod_fastcgi authorizer)*/
|
||||
buffer_clear(&r->physical.doc_root);
|
||||
buffer_clear(&r->physical.basedir);
|
||||
buffer_clear(&r->physical.etag);
|
||||
buffer_reset(&r->physical.path);
|
||||
buffer_reset(&r->physical.rel_path);
|
||||
}
|
||||
con->response.htags = 0;
|
||||
array_reset_data_strings(&con->response.headers);
|
||||
http_response_body_clear(con, 0);
|
||||
r->resp_htags = 0;
|
||||
array_reset_data_strings(&r->resp_headers);
|
||||
http_response_body_clear(r, 0);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,10 +18,13 @@ connection * connection_accepted(server *srv, server_socket *srv_socket, sock_ad
|
|||
const char * connection_get_state(request_state_t state);
|
||||
const char * connection_get_short_state(request_state_t state);
|
||||
int connection_state_machine(connection *con);
|
||||
handler_t connection_handle_read_post_state(connection *con);
|
||||
handler_t connection_handle_read_post_error(connection *con, int http_status);
|
||||
handler_t connection_handle_read_post_state(request_st *r);
|
||||
|
||||
__attribute_cold__
|
||||
handler_t connection_handle_read_post_error(request_st *r, int http_status);
|
||||
|
||||
int connection_write_chunkqueue(connection *con, chunkqueue *c, off_t max_bytes);
|
||||
void connection_response_reset(connection *con);
|
||||
void connection_response_reset(request_st *r);
|
||||
|
||||
#define joblist_append(con) connection_list_append(&(con)->srv->joblist, (con))
|
||||
void connection_list_append(connections *conns, connection *con);
|
||||
|
|
357
src/gw_backend.c
357
src/gw_backend.c
|
@ -212,21 +212,21 @@ static int gw_extension_insert(gw_exts *ext, const buffer *key, gw_host *fh) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void gw_proc_connect_success(connection *con, gw_host *host, gw_proc *proc, int debug) {
|
||||
static void gw_proc_connect_success(gw_host *host, gw_proc *proc, int debug, request_st * const r) {
|
||||
gw_proc_tag_inc(host, proc, CONST_STR_LEN(".connected"));
|
||||
proc->last_used = log_epoch_secs;
|
||||
|
||||
if (debug) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"got proc: pid: %d socket: %s load: %d",
|
||||
proc->pid, proc->connection_name->ptr, proc->load);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute_cold__
|
||||
static void gw_proc_connect_error(connection *con, gw_host *host, gw_proc *proc, pid_t pid, int errnum, int debug) {
|
||||
static void gw_proc_connect_error(request_st * const r, gw_host *host, gw_proc *proc, pid_t pid, int errnum, int debug) {
|
||||
const time_t cur_ts = log_epoch_secs;
|
||||
log_error_st * const errh = con->conf.errh;
|
||||
log_error_st * const errh = r->conf.errh;
|
||||
log_error(errh, __FILE__, __LINE__,
|
||||
"establishing connection failed: socket: %s: %s",
|
||||
proc->connection_name->ptr, strerror(errnum));
|
||||
|
@ -775,9 +775,11 @@ enum {
|
|||
GW_BALANCE_STICKY
|
||||
};
|
||||
|
||||
static gw_host * gw_host_get(connection *con, gw_extension *extension, int balance, int debug) {
|
||||
static gw_host * gw_host_get(request_st * const r, gw_extension *extension, int balance, int debug) {
|
||||
gw_host *host;
|
||||
buffer *dst_addr_buf;
|
||||
unsigned long last_max = ULONG_MAX;
|
||||
unsigned long base_crc32c;
|
||||
int max_usage = INT_MAX;
|
||||
int ndx = -1;
|
||||
uint32_t k;
|
||||
|
@ -791,23 +793,25 @@ static gw_host * gw_host_get(connection *con, gw_extension *extension, int balan
|
|||
/* hash balancing */
|
||||
|
||||
if (debug) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"proxy - used hash balancing, hosts: %u", extension->used);
|
||||
}
|
||||
|
||||
base_crc32c = generate_crc32c(CONST_BUF_LEN(&r->uri.path))
|
||||
+ generate_crc32c(CONST_BUF_LEN(&r->uri.authority));
|
||||
|
||||
for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
|
||||
unsigned long cur_max;
|
||||
host = extension->hosts[k];
|
||||
if (0 == host->active_procs) continue;
|
||||
|
||||
cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path))
|
||||
+ generate_crc32c(CONST_BUF_LEN(host->host)) /* cachable */
|
||||
+ generate_crc32c(CONST_BUF_LEN(con->uri.authority));
|
||||
cur_max = base_crc32c
|
||||
+ generate_crc32c(CONST_BUF_LEN(host->host)); /* cachable */
|
||||
|
||||
if (debug) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
"proxy - election: %s %s %s %lu", con->uri.path->ptr,
|
||||
host->host->ptr, con->uri.authority->ptr, cur_max);
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"proxy - election: %s %s %s %lu", r->uri.path.ptr,
|
||||
host->host->ptr, r->uri.authority.ptr, cur_max);
|
||||
}
|
||||
|
||||
if (last_max < cur_max || last_max == ULONG_MAX) {
|
||||
|
@ -820,7 +824,7 @@ static gw_host * gw_host_get(connection *con, gw_extension *extension, int balan
|
|||
case GW_BALANCE_LEAST_CONNECTION:
|
||||
/* fair balancing */
|
||||
if (debug) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"proxy - used least connection");
|
||||
}
|
||||
|
||||
|
@ -838,7 +842,7 @@ static gw_host * gw_host_get(connection *con, gw_extension *extension, int balan
|
|||
case GW_BALANCE_RR:
|
||||
/* round robin */
|
||||
if (debug) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"proxy - used round-robin balancing");
|
||||
}
|
||||
|
||||
|
@ -873,25 +877,28 @@ static gw_host * gw_host_get(connection *con, gw_extension *extension, int balan
|
|||
break;
|
||||
case GW_BALANCE_STICKY:
|
||||
/* source sticky balancing */
|
||||
dst_addr_buf = r->con->dst_addr_buf;
|
||||
|
||||
if (debug) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"proxy - used sticky balancing, hosts: %u", extension->used);
|
||||
}
|
||||
|
||||
base_crc32c = generate_crc32c(CONST_BUF_LEN(dst_addr_buf));
|
||||
|
||||
for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
|
||||
unsigned long cur_max;
|
||||
host = extension->hosts[k];
|
||||
|
||||
if (0 == host->active_procs) continue;
|
||||
|
||||
cur_max = generate_crc32c(CONST_BUF_LEN(con->dst_addr_buf))
|
||||
+ generate_crc32c(CONST_BUF_LEN(host->host))
|
||||
cur_max = base_crc32c
|
||||
+ generate_crc32c(CONST_BUF_LEN(host->host)) /* cachable */
|
||||
+ host->port;
|
||||
|
||||
if (debug) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
"proxy - election: %s %s %hu %ld", con->dst_addr_buf->ptr,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"proxy - election: %s %s %hu %ld", dst_addr_buf->ptr,
|
||||
host->host->ptr, host->port, cur_max);
|
||||
}
|
||||
|
||||
|
@ -911,18 +918,18 @@ static gw_host * gw_host_get(connection *con, gw_extension *extension, int balan
|
|||
host = extension->hosts[ndx];
|
||||
|
||||
if (debug) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"gw - found a host %s %hu", host->host->ptr, host->port);
|
||||
}
|
||||
|
||||
return host;
|
||||
} else if (0 == con->srv->srvconf.max_worker) {
|
||||
} else if (0 == r->con->srv->srvconf.max_worker) {
|
||||
/* special-case adaptive spawning and 0 == host->min_procs */
|
||||
for (k = 0; k < extension->used; ++k) {
|
||||
host = extension->hosts[k];
|
||||
if (0 == host->min_procs && 0 == host->num_procs
|
||||
&& !buffer_string_is_empty(host->bin_path)) {
|
||||
gw_proc_spawn(host, con->srv->errh, debug);
|
||||
gw_proc_spawn(host, r->con->srv->errh, debug);
|
||||
if (host->num_procs) return host;
|
||||
}
|
||||
}
|
||||
|
@ -930,39 +937,39 @@ static gw_host * gw_host_get(connection *con, gw_extension *extension, int balan
|
|||
|
||||
/* all hosts are down */
|
||||
/* sorry, we don't have a server alive for this ext */
|
||||
con->http_status = 503; /* Service Unavailable */
|
||||
con->response.handler_module = NULL;
|
||||
r->http_status = 503; /* Service Unavailable */
|
||||
r->handler_module = NULL;
|
||||
|
||||
/* only send the 'no handler' once */
|
||||
if (!extension->note_is_sent) {
|
||||
extension->note_is_sent = 1;
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"all handlers for %s?%.*s on %s are down.",
|
||||
con->uri.path->ptr, BUFFER_INTLEN_PTR(con->uri.query),
|
||||
r->uri.path.ptr, BUFFER_INTLEN_PTR(&r->uri.query),
|
||||
extension->key.ptr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int gw_establish_connection(connection *con, gw_host *host, gw_proc *proc, pid_t pid, int gw_fd, int debug) {
|
||||
static int gw_establish_connection(request_st * const r, gw_host *host, gw_proc *proc, pid_t pid, int gw_fd, int debug) {
|
||||
if (-1 == connect(gw_fd, proc->saddr, proc->saddrlen)) {
|
||||
if (errno == EINPROGRESS || errno == EALREADY || errno == EINTR) {
|
||||
if (debug > 2) {
|
||||
log_error(con->conf.errh, __FILE__, __LINE__,
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"connect delayed; will continue later: %s",
|
||||
proc->connection_name->ptr);
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
gw_proc_connect_error(con, host, proc, pid, errno, debug);
|
||||
gw_proc_connect_error(r, host, proc, pid, errno, debug);
|
||||
return -1;
|
||||