httpd.c
#define EX_UTILS_NO_USE_INIT 1
#define EX_UTILS_NO_USE_EXIT 1
#define EX_UTILS_NO_USE_LIMIT 1
#define EX_UTILS_NO_USE_BLOCK 1
#define EX_UTILS_NO_USE_GET 1
#define EX_UTILS_NO_USE_PUT 1
#define EX_UTILS_USE_NONBLOCKING_OPEN 1
#define EX_UTILS_RET_FAIL 1
#include "ex_utils.h"
#include "mk.h"
#include "vlg.h"
#define HTTPD_HAVE_GLOBAL_OPTS 1
#include "httpd.h"
#include "httpd_policy.h"
#ifndef POSIX_FADV_SEQUENTIAL
# define posix_fadvise64(x1, x2, x3, x4) (errno = ENOSYS, -1)
#endif
#ifdef VSTR_AUTOCONF_NDEBUG
# define HTTP_CONF_MMAP_LIMIT_MIN (16 * 1024)
# define HTTP_CONF_SAFE_PRINT_REQ TRUE
#else
# define HTTP_CONF_MMAP_LIMIT_MIN 8
# define HTTP_CONF_SAFE_PRINT_REQ FALSE
#endif
#define HTTP_CONF_MMAP_LIMIT_MAX (50 * 1024 * 1024)
#define CLEN VSTR__AT_COMPILE_STRLEN
#define VPREFIX(vstr, p, l, cstr) \
(((l) >= CLEN(cstr)) && \
vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
#define VSUFFIX(vstr, p, l, cstr) \
(((l) >= CLEN(cstr)) && \
vstr_cmp_eod_buf_eq(vstr, p, l, cstr, CLEN(cstr)))
#define VIPREFIX(vstr, p, l, cstr) \
(((l) >= CLEN(cstr)) && \
vstr_cmp_case_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
#define VEQ(vstr, p, l, cstr) vstr_cmp_cstr_eq(vstr, p, l, cstr)
#define VIEQ(vstr, p, l, cstr) vstr_cmp_case_cstr_eq(vstr, p, l, cstr)
#ifndef SWAP_TYPE
#define SWAP_TYPE(x, y, type) do { \
type internal_local_tmp = (x); \
(x) = (y); \
(y) = internal_local_tmp; \
} while (FALSE)
#endif
#define HTTP__HDR_SET(req, h, p, l) do { \
(req)-> http_hdrs -> hdr_ ## h ->pos = (p); \
(req)-> http_hdrs -> hdr_ ## h ->len = (l); \
} while (FALSE)
#define HTTP__HDR_MULTI_SET(req, h, p, l) do { \
(req)-> http_hdrs -> multi -> hdr_ ## h ->pos = (p); \
(req)-> http_hdrs -> multi -> hdr_ ## h ->len = (l); \
} while (FALSE)
#define HTTP__XTRA_HDR_INIT(x) do { \
req-> x ## _vs1 = NULL; \
req-> x ## _pos = 0; \
req-> x ## _len = 0; \
} while (FALSE)
#define HTTP__XTRA_HDR_PARAMS(req, x) \
(req)-> x ## _vs1, (req)-> x ## _pos, (req)-> x ## _len
HTTPD_CONF_MAIN_DECL_OPTS(httpd_opts);
static Vlg *vlg = NULL;
void httpd_init(Vlg *passed_vlg)
{
ASSERT(passed_vlg && !vlg);
vlg = passed_vlg;
}
void httpd_exit(void)
{
ASSERT(vlg);
vlg = NULL;
}
static void http__clear_hdrs(struct Httpd_req_data *req)
{
Vstr_base *tmp = req->http_hdrs->multi->combiner_store;
ASSERT(tmp);
HTTP__HDR_SET(req, ua, 0, 0);
HTTP__HDR_SET(req, referer, 0, 0);
HTTP__HDR_SET(req, expect, 0, 0);
HTTP__HDR_SET(req, host, 0, 0);
HTTP__HDR_SET(req, if_modified_since, 0, 0);
HTTP__HDR_SET(req, if_range, 0, 0);
HTTP__HDR_SET(req, if_unmodified_since, 0, 0);
HTTP__HDR_SET(req, authorization, 0, 0);
vstr_del(tmp, 1, tmp->len);
HTTP__HDR_MULTI_SET(req, accept, 0, 0);
HTTP__HDR_MULTI_SET(req, accept_charset, 0, 0);
HTTP__HDR_MULTI_SET(req, accept_encoding, 0, 0);
HTTP__HDR_MULTI_SET(req, accept_language, 0, 0);
HTTP__HDR_MULTI_SET(req, connection, 0, 0);
HTTP__HDR_MULTI_SET(req, if_match, 0, 0);
HTTP__HDR_MULTI_SET(req, if_none_match, 0, 0);
HTTP__HDR_MULTI_SET(req, range, 0, 0);
}
static void http__clear_xtra(struct Httpd_req_data *req)
{
if (req->xtra_content)
vstr_del(req->xtra_content, 1, req->xtra_content->len);
HTTP__XTRA_HDR_INIT(content_type);
HTTP__XTRA_HDR_INIT(content_language);
HTTP__XTRA_HDR_INIT(content_location);
HTTP__XTRA_HDR_INIT(content_md5);
HTTP__XTRA_HDR_INIT(gzip_content_md5);
HTTP__XTRA_HDR_INIT(bzip2_content_md5);
HTTP__XTRA_HDR_INIT(cache_control);
HTTP__XTRA_HDR_INIT(etag);
HTTP__XTRA_HDR_INIT(gzip_etag);
HTTP__XTRA_HDR_INIT(bzip2_etag);
HTTP__XTRA_HDR_INIT(expires);
HTTP__XTRA_HDR_INIT(link);
HTTP__XTRA_HDR_INIT(p3p);
HTTP__XTRA_HDR_INIT(ext_vary_a);
HTTP__XTRA_HDR_INIT(ext_vary_ac);
HTTP__XTRA_HDR_INIT(ext_vary_al);
}
Httpd_req_data *http_req_make(struct Con *con)
{
static Httpd_req_data real_req[1];
Httpd_req_data *req = real_req;
const Httpd_policy_opts *policy = NULL;
ASSERT(!req->using_req);
if (!req->done_once)
{
Vstr_conf *conf = NULL;
if (con)
conf = con->evnt->io_w->conf;
if (!(req->fname = vstr_make_base(conf)) ||
!(req->http_hdrs->multi->combiner_store = vstr_make_base(conf)) ||
!(req->sects = vstr_sects_make(8)))
return (NULL);
req->f_mmap = NULL;
req->xtra_content = NULL;
}
http__clear_hdrs(req);
http__clear_xtra(req);
req->http_hdrs->multi->comb = con ? con->evnt->io_r : NULL;
vstr_del(req->fname, 1, req->fname->len);
req->now = evnt_sc_time();
req->len = 0;
req->path_pos = 0;
req->path_len = 0;
req->error_code = 0;
req->error_line = "";
req->error_msg = "";
req->error_len = 0;
req->sects->num = 0;
if (con)
req->orig_io_w_len = con->evnt->io_w->len;
ASSERT(!req->f_mmap || !req->f_mmap->len);
if (req->f_mmap)
vstr_del(req->f_mmap, 1, req->f_mmap->len);
ASSERT(!req->xtra_content || !req->xtra_content->len);
if (req->xtra_content)
vstr_del(req->xtra_content, 1, req->xtra_content->len);
req->vhost_prefix_len = 0;
req->sects->malloc_bad = FALSE;
req->content_encoding_gzip = FALSE;
req->content_encoding_bzip2 = FALSE;
req->content_encoding_identity = TRUE;
req->output_keep_alive_hdr = FALSE;
req->user_return_error_code = FALSE;
req->vary_star = con ? con->vary_star : FALSE;
req->vary_a = FALSE;
req->vary_ac = FALSE;
req->vary_ae = FALSE;
req->vary_al = FALSE;
req->vary_rf = FALSE;
req->vary_ua = FALSE;
req->direct_uri = FALSE;
req->direct_filename = FALSE;
req->skip_document_root = FALSE;
req->ver_0_9 = FALSE;
req->ver_1_1 = FALSE;
req->head_op = FALSE;
req->chked_encoded_path = FALSE;
req->neg_content_type_done = FALSE;
req->neg_content_lang_done = FALSE;
req->done_once = TRUE;
req->using_req = TRUE;
req->malloc_bad = FALSE;
if (con)
policy = con->policy;
else
policy = (Httpd_policy_opts *)httpd_opts->s->def_policy;
httpd_policy_change_req(req, policy);
return (req);
}
void http_req_free(Httpd_req_data *req)
{
if (!req)
return;
ASSERT(req->done_once && req->using_req);
vstr_del(req->fname, 1, req->fname->len);
ASSERT(!req->http_hdrs->multi->combiner_store->len);
if (req->f_mmap)
vstr_del(req->f_mmap, 1, req->f_mmap->len);
http__clear_xtra(req);
req->http_hdrs->multi->comb = NULL;
req->using_req = FALSE;
}
void http_req_exit(void)
{
Httpd_req_data *req = http_req_make(NULL);
struct Http_hdrs__multi *tmp = NULL;
if (!req)
return;
tmp = req->http_hdrs->multi;
vstr_free_base(req->fname); req->fname = NULL;
vstr_free_base(tmp->combiner_store); tmp->combiner_store = NULL;
vstr_free_base(req->f_mmap); req->f_mmap = NULL;
vstr_free_base(req->xtra_content); req->xtra_content = NULL;
vstr_sects_free(req->sects); req->sects = NULL;
req->done_once = FALSE;
req->using_req = FALSE;
}
static void http__skip_lws(const Vstr_base *s1, size_t *pos, size_t *len)
{
size_t lws__len = 0;
ASSERT(s1 && pos && len);
while (TRUE)
{
if (VPREFIX(s1, *pos, *len, HTTP_EOL))
{
*len -= CLEN(HTTP_EOL); *pos += CLEN(HTTP_EOL);
}
else if (lws__len)
break;
if (!(lws__len = vstr_spn_cstr_chrs_fwd(s1, *pos, *len, HTTP_LWS)))
break;
*len -= lws__len;
*pos += lws__len;
}
}
#define PCUR (pos + (base->len - orig_len))
static int http_app_vstr_escape(Vstr_base *base, size_t pos,
Vstr_base *sf, size_t sf_pos, size_t sf_len)
{
unsigned int sf_flags = VSTR_TYPE_ADD_BUF_PTR;
Vstr_iter iter[1];
size_t orig_len = base->len;
int saved_malloc_bad = FALSE;
size_t norm_chr = 0;
if (!sf_len)
return (TRUE);
if (!vstr_iter_fwd_beg(sf, sf_pos, sf_len, iter))
return (FALSE);
saved_malloc_bad = base->conf->malloc_bad;
base->conf->malloc_bad = FALSE;
while (sf_len)
{
char scan = vstr_iter_fwd_chr(iter, NULL);
if ((scan >= ' ') && (scan <= '~') && (scan != '"') && (scan != '\\'))
++norm_chr;
else
{
vstr_add_vstr(base, PCUR, sf, sf_pos, norm_chr, sf_flags);
sf_pos += norm_chr;
norm_chr = 0;
if (0) {}
else if (scan == '"') vstr_add_cstr_buf(base, PCUR, "\\\"");
else if (scan == '\\') vstr_add_cstr_buf(base, PCUR, "\\\\");
else if (scan == '\t') vstr_add_cstr_buf(base, PCUR, "\\t");
else if (scan == '\v') vstr_add_cstr_buf(base, PCUR, "\\v");
else if (scan == '\r') vstr_add_cstr_buf(base, PCUR, "\\r");
else if (scan == '\n') vstr_add_cstr_buf(base, PCUR, "\\n");
else if (scan == '\b') vstr_add_cstr_buf(base, PCUR, "\\b");
else
vstr_add_sysfmt(base, PCUR, "\\x%02hhx", scan);
++sf_pos;
}
--sf_len;
}
vstr_add_vstr(base, PCUR, sf, sf_pos, norm_chr, sf_flags);
if (base->conf->malloc_bad)
return (FALSE);
base->conf->malloc_bad = saved_malloc_bad;
return (TRUE);
}
#undef PCUR
static int http__fmt__add_vstr_add_vstr(Vstr_base *base, size_t pos,
Vstr_fmt_spec *spec)
{
Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
size_t sf_pos = VSTR_FMT_CB_ARG_VAL(spec, size_t, 1);
size_t sf_len = VSTR_FMT_CB_ARG_VAL(spec, size_t, 2);
return (http_app_vstr_escape(base, pos, sf, sf_pos, sf_len));
}
int http_fmt_add_vstr_add_vstr(Vstr_conf *conf, const char *name)
{
return (vstr_fmt_add(conf, name, http__fmt__add_vstr_add_vstr,
VSTR_TYPE_FMT_PTR_VOID,
VSTR_TYPE_FMT_SIZE_T,
VSTR_TYPE_FMT_SIZE_T,
VSTR_TYPE_FMT_END));
}
static int http__fmt__add_vstr_add_sect_vstr(Vstr_base *base, size_t pos,
Vstr_fmt_spec *spec)
{
Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
Vstr_sects *sects = VSTR_FMT_CB_ARG_PTR(spec, 1);
unsigned int num = VSTR_FMT_CB_ARG_VAL(spec, unsigned int, 2);
size_t sf_pos = VSTR_SECTS_NUM(sects, num)->pos;
size_t sf_len = VSTR_SECTS_NUM(sects, num)->len;
return (http_app_vstr_escape(base, pos, sf, sf_pos, sf_len));
}
int http_fmt_add_vstr_add_sect_vstr(Vstr_conf *conf, const char *name)
{
return (vstr_fmt_add(conf, name, http__fmt__add_vstr_add_sect_vstr,
VSTR_TYPE_FMT_PTR_VOID,
VSTR_TYPE_FMT_PTR_VOID,
VSTR_TYPE_FMT_UINT,
VSTR_TYPE_FMT_END));
}
static void http__app_hdr_hdr(Vstr_base *out, const char *hdr)
{
vstr_add_cstr_buf(out, out->len, hdr);
vstr_add_cstr_buf(out, out->len, ": ");
}
static void http__app_hdr_eol(Vstr_base *out)
{
vstr_add_cstr_buf(out, out->len, HTTP_EOL);
}
#define HTTP_APP_HDR_CONST_CSTR(o, h, c) \
vstr_add_cstr_ptr(out, (out)->len, h ": " c HTTP_EOL)
static void http_app_hdr_cstr(Vstr_base *out, const char *hdr, const char *data)
{
http__app_hdr_hdr(out, hdr);
vstr_add_cstr_buf(out, out->len, data);
http__app_hdr_eol(out);
}
static void http_app_hdr_vstr(Vstr_base *out, const char *hdr,
const Vstr_base *s1, size_t vpos, size_t vlen,
unsigned int type)
{
http__app_hdr_hdr(out, hdr);
vstr_add_vstr(out, out->len, s1, vpos, vlen, type);
http__app_hdr_eol(out);
}
static void http_app_hdr_vstr_def(Vstr_base *out, const char *hdr,
const Vstr_base *s1, size_t vpos, size_t vlen)
{
http__app_hdr_hdr(out, hdr);
vstr_add_vstr(out, out->len, s1, vpos, vlen, VSTR_TYPE_ADD_DEF);
http__app_hdr_eol(out);
}
static void http_app_hdr_conf_vstr(Vstr_base *out, const char *hdr,
const Vstr_base *s1)
{
http__app_hdr_hdr(out, hdr);
vstr_add_vstr(out, out->len, s1, 1, s1->len, VSTR_TYPE_ADD_DEF);
http__app_hdr_eol(out);
}
static void http_app_hdr_fmt(Vstr_base *out, const char *hdr,
const char *fmt, ...)
VSTR__COMPILE_ATTR_FMT(3, 4);
static void http_app_hdr_fmt(Vstr_base *out, const char *hdr,
const char *fmt, ...)
{
va_list ap;
http__app_hdr_hdr(out, hdr);
va_start(ap, fmt);
vstr_add_vfmt(out, out->len, fmt, ap);
va_end(ap);
http__app_hdr_eol(out);
}
static void http_app_hdr_uintmax(Vstr_base *out, const char *hdr,
VSTR_AUTOCONF_uintmax_t data)
{
http__app_hdr_hdr(out, hdr);
vstr_add_fmt(out, out->len, "%ju", data);
http__app_hdr_eol(out);
}
#define HTTP__VARY_ADD(x, y) do { \
ASSERT((x) < (sizeof(varies_ptr) / sizeof(varies_ptr[0]))); \
\
varies_ptr[(x)] = (y); \
varies_len[(x)] = CLEN(y); \
++(x); \
} while (FALSE)
void http_app_def_hdrs(struct Con *con, struct Httpd_req_data *req,
unsigned int http_ret_code,
const char *http_ret_line, time_t mtime,
const char *custom_content_type,
int use_range,
VSTR_AUTOCONF_uintmax_t content_length)
{
Vstr_base *out = con->evnt->io_w;
Date_store *ds = httpd_opts->date;
if (use_range)
use_range = req->policy->use_range;
vstr_add_fmt(out, out->len, "%s %03u %s" HTTP_EOL,
"HTTP/1.1", http_ret_code, http_ret_line);
http_app_hdr_cstr(out, "Date", date_rfc1123(ds, req->now));
http_app_hdr_conf_vstr(out, "Server", req->policy->server_name);
if (mtime)
{
if (difftime(req->now, mtime) <= 0)
mtime = req->now;
http_app_hdr_cstr(out, "Last-Modified", date_rfc1123(ds, mtime));
}
switch (con->keep_alive)
{
case HTTP_NON_KEEP_ALIVE:
HTTP_APP_HDR_CONST_CSTR(out, "Connection", "close");
break;
case HTTP_1_0_KEEP_ALIVE:
HTTP_APP_HDR_CONST_CSTR(out, "Connection", "Keep-Alive");
case HTTP_1_1_KEEP_ALIVE:
if (req->output_keep_alive_hdr)
http_app_hdr_fmt(out, "Keep-Alive",
"%s=%u", "timeout", req->policy->s->idle_timeout);
ASSERT_NO_SWITCH_DEF();
}
switch (http_ret_code)
{
case 200:
case 301: case 302: case 303: case 307:
case 304:
case 406:
case 412:
case 413:
case 416:
case 417:
if (use_range)
HTTP_APP_HDR_CONST_CSTR(out, "Accept-Ranges", "bytes");
}
if (req->vary_star)
HTTP_APP_HDR_CONST_CSTR(out, "Vary", "*");
else if (req->vary_a || req->vary_ac || req->vary_ae || req->vary_al ||
req->vary_rf || req->vary_ua)
{
const char *varies_ptr[6];
size_t varies_len[6];
unsigned int num = 0;
if (req->vary_ua) HTTP__VARY_ADD(num, "User-Agent");
if (req->vary_rf) HTTP__VARY_ADD(num, "Referer");
if (req->vary_al) HTTP__VARY_ADD(num, "Accept-Language");
if (req->vary_ae) HTTP__VARY_ADD(num, "Accept-Encoding");
if (req->vary_ac) HTTP__VARY_ADD(num, "Accept-Charset");
if (req->vary_a) HTTP__VARY_ADD(num, "Accept");
ASSERT(num && (num <= 5));
http__app_hdr_hdr(out, "Vary");
while (num-- > 1)
{
vstr_add_buf(out, out->len, varies_ptr[num], varies_len[num]);
vstr_add_cstr_buf(out, out->len, ",");
}
ASSERT(num == 0);
vstr_add_buf(out, out->len, varies_ptr[0], varies_len[0]);
http__app_hdr_eol(out);
}
if (con->use_mpbr)
{
ASSERT(con->mpbr_ct && !con->mpbr_ct->len);
if (req->content_type_vs1 && req->content_type_len)
http_app_hdr_vstr_def(con->mpbr_ct, "Content-Type",
HTTP__XTRA_HDR_PARAMS(req, content_type));
http_app_hdr_cstr(out, "Content-Type",
"multipart/byteranges; boundary=SEP");
}
else if (req->content_type_vs1 && req->content_type_len)
http_app_hdr_vstr_def(out, "Content-Type",
HTTP__XTRA_HDR_PARAMS(req, content_type));
else if (custom_content_type)
http_app_hdr_cstr(out, "Content-Type", custom_content_type);
if (req->content_encoding_bzip2)
HTTP_APP_HDR_CONST_CSTR(out, "Content-Encoding", "bzip2");
else if (req->content_encoding_gzip)
{
if (req->content_encoding_xgzip)
HTTP_APP_HDR_CONST_CSTR(out, "Content-Encoding", "x-gzip");
else
HTTP_APP_HDR_CONST_CSTR(out, "Content-Encoding", "gzip");
}
if (!con->use_mpbr)
http_app_hdr_uintmax(out, "Content-Length", content_length);
}
#undef HTTP__VARY_ADD
static void http_app_end_hdrs(Vstr_base *out)
{
http__app_hdr_eol(out);
}
static void http_vlg_def(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
Vstr_sect_node *h_ua = req->http_hdrs->hdr_ua;
Vstr_sect_node *h_r = req->http_hdrs->hdr_referer;
vlg_info(vlg, (" host[\"$<http-esc.vstr:%p%zu%zu>\"]"
" UA[\"$<http-esc.vstr:%p%zu%zu>\"]"
" ref[\"$<http-esc.vstr:%p%zu%zu>\"]"),
data, h_h->pos, h_h->len,
data, h_ua->pos, h_ua->len,
data, h_r->pos, h_r->len);
if (req->ver_0_9)
vlg_info(vlg, " ver[\"HTTP/0.9]\"");
else
vlg_info(vlg, " ver[\"$<vstr.sect:%p%p%u>\"]", data, req->sects, 3);
vlg_info(vlg, ": $<http-esc.vstr:%p%zu%zu>\n",
data, req->path_pos, req->path_len);
}
static struct File_sect *httpd__fd_next(struct Con *con)
{
struct File_sect *fs = NULL;
ASSERT(con->fs &&
(con->fs_off <= con->fs_num) && (con->fs_num <= con->fs_sz));
if (++con->fs_off >= con->fs_num)
{
fs = &con->fs[con->fs_off - 1];
fs->len = 0;
if (fs->fd != -1)
close(fs->fd);
fs->fd = -1;
fs = con->fs;
fs->fd = -1;
fs->len = 0;
con->fs_off = 0;
con->fs_num = 0;
con->use_mpbr = FALSE;
if (con->mpbr_ct)
vstr_del(con->mpbr_ct, 1, con->mpbr_ct->len);
return (NULL);
}
ASSERT(con->fs[con->fs_off - 1].fd == con->fs[con->fs_off].fd);
fs = &con->fs[con->fs_off];
if (con->use_posix_fadvise)
posix_fadvise64(fs->fd, fs->off, fs->len, POSIX_FADV_SEQUENTIAL);
return (fs);
}
void httpd_fin_fd_close(struct Con *con)
{
con->use_posix_fadvise = FALSE;
while (httpd__fd_next(con))
{ }
}
static int http_fin_req(struct Con *con, Httpd_req_data *req)
{
Vstr_base *out = con->evnt->io_w;
ASSERT(!out->conf->malloc_bad);
http__clear_hdrs(req);
if (!con->keep_alive)
{
evnt_wait_cntl_del(con->evnt, POLLIN);
req->len = con->evnt->io_r->len;
}
vstr_del(con->evnt->io_r, 1, req->len);
evnt_put_pkt(con->evnt);
if (req->policy->use_tcp_cork)
evnt_fd_set_cork(con->evnt, TRUE);
http_req_free(req);
return (httpd_serv_send(con));
}
static int http_fin_fd_req(struct Con *con, Httpd_req_data *req)
{
ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
ASSERT(!con->fs_off);
if (req->head_op || con->use_mmap || !con->fs->len)
httpd_fin_fd_close(con);
else if ((con->use_posix_fadvise = req->policy->use_posix_fadvise))
{
struct File_sect *fs = con->fs;
posix_fadvise64(fs->fd, fs->off, fs->len, POSIX_FADV_SEQUENTIAL);
}
return (http_fin_req(con, req));
}
static int http_con_cleanup(struct Con *con, Httpd_req_data *req)
{
con->evnt->io_r->conf->malloc_bad = FALSE;
con->evnt->io_w->conf->malloc_bad = FALSE;
http__clear_hdrs(req);
http_req_free(req);
return (FALSE);
}
static int http_con_close_cleanup(struct Con *con, Httpd_req_data *req)
{
httpd_fin_fd_close(con);
return (http_con_cleanup(con, req));
}
static int http__try_encoded_content(struct Con *con, Httpd_req_data *req,
const struct stat64 *req_f_stat,
Vstr_base *fname,
const char *zip_ext, size_t zip_len)
{
const char *fname_cstr = NULL;
int fd = -1;
int ret = FALSE;
vstr_add_cstr_ptr(fname, fname->len, zip_ext);
fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
if (fname->conf->malloc_bad)
vlg_warn(vlg, "Failed to export cstr for '%s'\n", zip_ext);
else if ((fd = io_open_nonblock(fname_cstr)) == -1)
vstr_sc_reduce(fname, 1, fname->len, zip_len);
else
{
struct stat64 f_stat[1];
if (fstat64(fd, f_stat) == -1)
vlg_warn(vlg, "fstat: %m\n");
else if ((req->policy->use_public_only && !(f_stat->st_mode & S_IROTH)) ||
(S_ISDIR(f_stat->st_mode)) || (!S_ISREG(f_stat->st_mode)) ||
(req_f_stat->st_mtime > f_stat->st_mtime) ||
!f_stat->st_size ||
(req_f_stat->st_size <= f_stat->st_size))
{ }
else
{
ASSERT(con->fs && !con->fs_num && !con->fs_off);
SWAP_TYPE(con->fs->fd, fd, int);
ASSERT(con->fs->len == (VSTR_AUTOCONF_uintmax_t)req_f_stat->st_size);
con->fs->len = req->f_stat->st_size = f_stat->st_size;
req->encoded_mtime = f_stat->st_mtime;
ret = TRUE;
}
close(fd);
}
return (ret);
}
#define HTTP__PARSE_CHK_RET_OK() do { \
HTTP_SKIP_LWS(data, pos, len); \
\
if (!len || \
VPREFIX(data, pos, len, ",") || \
(allow_more && VPREFIX(data, pos, len, ";"))) \
{ \
*passed_pos = pos; \
*passed_len = len; \
\
return (TRUE); \
} \
} while (FALSE)
static int http_parse_quality(Vstr_base *data,
size_t *passed_pos, size_t *passed_len,
int allow_more, unsigned int *val)
{
size_t pos = *passed_pos;
size_t len = *passed_len;
ASSERT(val);
*val = 1000;
HTTP_SKIP_LWS(data, pos, len);
*passed_pos = pos;
*passed_len = len;
if (!len || VPREFIX(data, pos, len, ","))
return (TRUE);
else if (VPREFIX(data, pos, len, ";"))
{
int lead_zero = FALSE;
unsigned int num_len = 0;
unsigned int parse_flags = VSTR_FLAG02(PARSE_NUM, NO_BEG_PM, NO_NEGATIVE);
len -= 1; pos += 1;
HTTP_SKIP_LWS(data, pos, len);
if (!VPREFIX(data, pos, len, "q"))
return (!!allow_more);
len -= 1; pos += 1;
HTTP_SKIP_LWS(data, pos, len);
if (!VPREFIX(data, pos, len, "="))
return (!!allow_more);
len -= 1; pos += 1;
HTTP_SKIP_LWS(data, pos, len);
if (!(lead_zero = VPREFIX(data, pos, len, "0")) &&
!VPREFIX(data, pos, len, "1"))
return (FALSE);
*val = (!lead_zero) * 1000;
len -= 1; pos += 1;
HTTP__PARSE_CHK_RET_OK();
if (!VPREFIX(data, pos, len, "."))
return (FALSE);
len -= 1; pos += 1;
HTTP_SKIP_LWS(data, pos, len);
*val += vstr_parse_uint(data, pos, len, 10 | parse_flags, &num_len, NULL);
if (!num_len || (num_len > 3) || (*val > 1000))
return (FALSE);
if (num_len < 3) *val *= 10;
if (num_len < 2) *val *= 10;
ASSERT(*val <= 1000);
len -= num_len; pos += num_len;
HTTP__PARSE_CHK_RET_OK();
}
return (FALSE);
}
#undef HTTP__PARSE_CHK_RET_OK
static int http_parse_accept_encoding(struct Httpd_req_data *req)
{
Vstr_base *data = req->http_hdrs->multi->comb;
size_t pos = 0;
size_t len = 0;
unsigned int num = 0;
unsigned int gzip_val = 1001;
unsigned int bzip2_val = 1001;
unsigned int identity_val = 1001;
unsigned int star_val = 1001;
pos = req->http_hdrs->multi->hdr_accept_encoding->pos;
len = req->http_hdrs->multi->hdr_accept_encoding->len;
if (!req->policy->use_err_406 && !req->allow_accept_encoding)
return (FALSE);
req->vary_ae = TRUE;
if (!len)
return (FALSE);
req->content_encoding_xgzip = FALSE;
while (len)
{
size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
HTTP_EOL HTTP_LWS ";,");
++num;
if (0) { }
else if (VEQ(data, pos, tmp, "identity"))
{
len -= tmp; pos += tmp;
if (!http_parse_quality(data, &pos, &len, FALSE, &identity_val))
return (FALSE);
}
else if (req->allow_accept_encoding && VEQ(data, pos, tmp, "gzip"))
{
len -= tmp; pos += tmp;
req->content_encoding_xgzip = FALSE;
if (!http_parse_quality(data, &pos, &len, FALSE, &gzip_val))
return (FALSE);
}
else if (req->allow_accept_encoding && VEQ(data, pos, tmp, "bzip2"))
{
len -= tmp; pos += tmp;
if (!http_parse_quality(data, &pos, &len, FALSE, &bzip2_val))
return (FALSE);
}
else if (req->allow_accept_encoding && VEQ(data, pos, tmp, "x-gzip"))
{
len -= tmp; pos += tmp;
req->content_encoding_xgzip = TRUE;
if (!http_parse_quality(data, &pos, &len, FALSE, &gzip_val))
return (FALSE);
gzip_val = 1000;
}
else if (VEQ(data, pos, tmp, "*"))
{
len -= tmp; pos += tmp;
if (!http_parse_quality(data, &pos, &len, FALSE, &star_val))
return (FALSE);
}
else
{
len -= tmp; pos += tmp;
}
tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len, ",");
len -= tmp; pos += tmp;
if (!len)
break;
assert(VPREFIX(data, pos, len, ","));
len -= 1; pos += 1;
HTTP_SKIP_LWS(data, pos, len);
if (req->policy->max_AE_nodes && (num >= req->policy->max_AE_nodes))
return (FALSE);
}
if (!req->allow_accept_encoding)
{
gzip_val = 0;
bzip2_val = 0;
}
if (gzip_val == 1001) gzip_val = star_val;
if (bzip2_val == 1001) bzip2_val = star_val;
if (identity_val == 1001) identity_val = star_val;
if (gzip_val == 1001) gzip_val = 0;
if (bzip2_val == 1001) bzip2_val = 0;
if (identity_val == 1001) identity_val = 1;
if (!identity_val)
req->content_encoding_identity = FALSE;
if ((identity_val > gzip_val) && (identity_val > bzip2_val))
return (FALSE);
if (gzip_val <= bzip2_val)
{
req->content_encoding_gzip = !!gzip_val;
req->content_encoding_bzip2 = !!bzip2_val;
}
else
{
ASSERT(gzip_val);
req->content_encoding_gzip = TRUE;
req->content_encoding_bzip2 = FALSE;
}
return (req->content_encoding_gzip || req->content_encoding_bzip2);
}
static void httpd__try_fd_encoding(struct Con *con, Httpd_req_data *req,
const struct stat64 *fs, Vstr_base *fname)
{
if (http_parse_accept_encoding(req))
{
if ( req->content_encoding_bzip2 &&
!http__try_encoded_content(con, req, fs, fname, ".bz2", CLEN(".bz2")))
req->content_encoding_bzip2 = FALSE;
if (!req->content_encoding_bzip2 && req->content_encoding_gzip &&
!http__try_encoded_content(con, req, fs, fname, ".gz", CLEN(".gz")))
req->content_encoding_gzip = FALSE;
}
}
static void httpd__disable_sendfile(void)
{
Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
while (scan)
{
Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
tmp->use_sendfile = FALSE;
scan = scan->next;
}
}
static void httpd__disable_mmap(void)
{
Opt_serv_policy_opts *scan = httpd_opts->s->def_policy;
while (scan)
{
Httpd_policy_opts *tmp = (Httpd_policy_opts *)scan;
tmp->use_mmap = FALSE;
scan = scan->next;
}
}
static void httpd_serv_call_mmap(struct Con *con, struct Httpd_req_data *req,
struct File_sect *fs)
{
static long pagesz = 0;
Vstr_base *data = con->evnt->io_r;
VSTR_AUTOCONF_uintmax_t mmoff = fs->off;
VSTR_AUTOCONF_uintmax_t mmlen = fs->len;
ASSERT(!req->f_mmap || !req->f_mmap->len);
ASSERT(!con->use_mmap);
ASSERT(!req->head_op);
if (con->use_sendfile)
return;
if (con->fs_num > 1)
return;
if (!pagesz)
pagesz = sysconf(_SC_PAGESIZE);
if (pagesz == -1)
httpd__disable_mmap();
if (!req->policy->use_mmap ||
(mmlen < HTTP_CONF_MMAP_LIMIT_MIN) || (mmlen > HTTP_CONF_MMAP_LIMIT_MAX))
return;
mmoff /= pagesz;
mmoff *= pagesz;
ASSERT(mmoff <= fs->off);
mmlen += fs->off - mmoff;
if (!req->f_mmap && !(req->f_mmap = vstr_make_base(data->conf)))
VLG_WARN_RET_VOID((vlg,
"failed to allocate mmap Vstr.\n"));
ASSERT(!req->f_mmap->len);
if (!vstr_sc_mmap_fd(req->f_mmap, 0, fs->fd, mmoff, mmlen, NULL))
{
if (errno == ENOSYS)
httpd__disable_mmap();
VLG_WARN_RET_VOID((vlg,
"mmap($<http-esc.vstr:%p%zu%zu>,"
"(%ju,%ju)->(%ju,%ju)): %m\n",
req->fname, (size_t)1, req->fname->len,
fs->off, fs->len, mmoff, mmlen));
}
con->use_mmap = TRUE;
vstr_del(req->f_mmap, 1, fs->off - mmoff);
ASSERT(req->f_mmap->len == fs->len);
}
static int httpd__serv_call_seek(struct Con *con, struct File_sect *fs)
{
if (con->use_mmap || con->use_sendfile)
return (TRUE);
if (fs->off && fs->len && (lseek64(fs->fd, fs->off, SEEK_SET) == -1))
return (FALSE);
return (TRUE);
}
static void httpd_serv_call_seek(struct Con *con, struct Httpd_req_data *req,
struct File_sect *fs,
unsigned int *http_ret_code,
const char ** http_ret_line)
{
ASSERT(!req->head_op);
if (!httpd__serv_call_seek(con, fs))
{
vlg_warn(vlg, "lseek($<http-esc.vstr:%p%zu%zu>,off=%ju): %m\n",
req->fname, (size_t)1, req->fname->len, fs->off);
req->http_hdrs->multi->hdr_range->pos = 0;
*http_ret_code = 200;
*http_ret_line = "OK - Range Failed";
}
}
static int http__conf_req(struct Con *con, Httpd_req_data *req)
{
Conf_parse *conf = NULL;
if (!(conf = conf_parse_make(NULL)))
return (FALSE);
if (!httpd_conf_req_parse_file(conf, con, req))
{
Vstr_base *s1 = req->policy->s->policy_name;
Vstr_base *s2 = conf->tmp;
if (!req->user_return_error_code)
vlg_info(vlg, "CONF-REQ-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
" backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1, s2);
conf_parse_free(conf);
return (TRUE);
}
conf_parse_free(conf);
if (req->direct_uri)
{
Vstr_base *s1 = req->policy->s->policy_name;
vlg_info(vlg, "CONF-REQ-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
" Has URI.\n", CON_CEVNT_SA(con), s1);
HTTPD_ERR_RET(req, 503, TRUE);
}
return (TRUE);
}
static void http_app_hdrs_url(struct Con *con, Httpd_req_data *req)
{
Vstr_base *out = con->evnt->io_w;
if (req->cache_control_vs1)
http_app_hdr_vstr_def(out, "Cache-Control",
HTTP__XTRA_HDR_PARAMS(req, cache_control));
if (req->expires_vs1 && (req->expires_time > req->f_stat->st_mtime))
http_app_hdr_vstr_def(out, "Expires",
HTTP__XTRA_HDR_PARAMS(req, expires));
if (req->p3p_vs1)
http_app_hdr_vstr_def(out, "P3P",
HTTP__XTRA_HDR_PARAMS(req, p3p));
}
static void http_app_hdrs_file(struct Con *con, Httpd_req_data *req)
{
Vstr_base *out = con->evnt->io_w;
time_t mtime = req->f_stat->st_mtime;
time_t enc_mtime = req->encoded_mtime;
if (req->content_language_vs1)
http_app_hdr_vstr_def(out, "Content-Language",
HTTP__XTRA_HDR_PARAMS(req, content_language));
if (req->content_encoding_bzip2)
{
if (req->bzip2_content_md5_vs1 && (req->content_md5_time > enc_mtime))
http_app_hdr_vstr_def(out, "Content-MD5",
HTTP__XTRA_HDR_PARAMS(req, bzip2_content_md5));
}
else if (req->content_encoding_gzip)
{
if (req->gzip_content_md5_vs1 && (req->content_md5_time > enc_mtime))
http_app_hdr_vstr_def(out, "Content-MD5",
HTTP__XTRA_HDR_PARAMS(req, gzip_content_md5));
}
else if (req->content_md5_vs1 && (req->content_md5_time > mtime))
http_app_hdr_vstr_def(out, "Content-MD5",
HTTP__XTRA_HDR_PARAMS(req, content_md5));
if (req->content_encoding_bzip2)
{
if (req->bzip2_etag_vs1 && (req->etag_time > enc_mtime))
http_app_hdr_vstr_def(out, "ETag",
HTTP__XTRA_HDR_PARAMS(req, bzip2_etag));
}
else if (req->content_encoding_gzip)
{
if (req->gzip_etag_vs1 && (req->etag_time > enc_mtime))
http_app_hdr_vstr_def(out, "ETag",
HTTP__XTRA_HDR_PARAMS(req, gzip_etag));
}
else if (req->etag_vs1 && (req->etag_time > mtime))
http_app_hdr_vstr_def(out, "ETag", HTTP__XTRA_HDR_PARAMS(req, etag));
if (req->link_vs1)
http_app_hdr_vstr_def(out, "Link", HTTP__XTRA_HDR_PARAMS(req, link));
}
static void http_app_hdrs_mpbr(struct Con *con, struct File_sect *fs)
{
Vstr_base *out = con->evnt->io_w;
VSTR_AUTOCONF_uintmax_t range_beg;
VSTR_AUTOCONF_uintmax_t range_end;
ASSERT(fs && (fs->fd != -1));
range_beg = fs->off;
range_end = range_beg + fs->len - 1;
vstr_add_cstr_ptr(out, out->len, "--SEP" HTTP_EOL);
HTTPD_APP_REF_ALLVSTR(out, con->mpbr_ct);
http_app_hdr_fmt(out, "Content-Range",
"%s %ju-%ju/%ju", "bytes", range_beg, range_end,
con->mpbr_fs_len);
http_app_end_hdrs(out);
}
static void http_prepend_doc_root(Vstr_base *fname, Httpd_req_data *req)
{
Vstr_base *dir = req->policy->document_root;
ASSERT((dir->len >= 1) && vstr_cmp_cstr_eq(dir, dir->len, 1, "/"));
ASSERT((fname->len >= 1) && vstr_cmp_cstr_eq(fname, 1, 1, "/"));
vstr_add_vstr(fname, 0, dir, 1, dir->len - 1, VSTR_TYPE_ADD_BUF_REF);
}
static void http_app_err_file(struct Con *con, Httpd_req_data *req,
Vstr_base *fname, size_t *vhost_prefix_len)
{
Vstr_base *dir = NULL;
ASSERT(con && req);
ASSERT(vhost_prefix_len && !*vhost_prefix_len);
dir = req->policy->req_err_dir;
ASSERT((dir->len >= 1) && vstr_cmp_cstr_eq(dir, 1, 1, "/"));
ASSERT((dir->len >= 1) && vstr_cmp_cstr_eq(dir, dir->len, 1, "/"));
HTTPD_APP_REF_ALLVSTR(fname, dir);
vstr_add_fmt(fname, fname->len, "%u", req->error_code);
if (req->policy->use_vhosts_name)
{
Vstr_base *data = con->evnt->io_r;
Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
size_t orig_len = fname->len;
if (!h_h->len)
httpd_sc_add_default_hostname(con, req, fname, 0);
else if (vstr_add_vstr(fname, 0, data,
h_h->pos, h_h->len, VSTR_TYPE_ADD_DEF))
vstr_conv_lowercase(fname, 1, h_h->len);
vstr_add_cstr_ptr(fname, 0, "/");
*vhost_prefix_len = (fname->len - orig_len);
}
}
static int http_fin_err_req(struct Con *con, Httpd_req_data *req)
{
Vstr_base *out = con->evnt->io_w;
int use_cust_err_msg = FALSE;
req->content_encoding_gzip = FALSE;
req->content_encoding_bzip2 = FALSE;
if ((req->error_code == 400) || (req->error_code == 405) ||
(req->error_code == 413) ||
(req->error_code == 500) || (req->error_code == 501))
con->keep_alive = HTTP_NON_KEEP_ALIVE;
ASSERT(con->fs && !con->fs_num);
vlg_info(vlg, "ERREQ from[$<sa:%p>] err[%03u %s]",
CON_CEVNT_SA(con), req->error_code, req->error_line);
if (req->sects->num >= 2)
http_vlg_def(con, req);
else
vlg_info(vlg, "%s", "\n");
if (req->malloc_bad)
{
ASSERT(req->error_code == 500);
vstr_del(con->evnt->io_r, 1, con->evnt->io_r->len);
}
else if (((req->error_code == 400) ||
(req->error_code == 403) ||
(req->error_code == 404) ||
(req->error_code == 410) ||
(req->error_code == 500) ||
(req->error_code == 503)) && req->policy->req_err_dir->len)
{
Vstr_base *fname = vstr_make_base(req->fname->conf);
size_t vhost_prefix_len = 0;
const char *fname_cstr = NULL;
struct stat64 f_stat[1];
if (!fname)
goto fail_custom_err;
if (!req->user_return_error_code)
{
req->cache_control_vs1 = NULL;
req->expires_vs1 = NULL;
req->p3p_vs1 = NULL;
}
if (req->user_return_error_code && req->direct_filename)
{
HTTPD_APP_REF_ALLVSTR(fname, req->fname);
if (!req->skip_document_root)
http_prepend_doc_root(fname, req);
}
else if (!req->policy->req_conf_dir)
{
http_app_err_file(con, req, fname, &vhost_prefix_len);
http_prepend_doc_root(fname, req);
}
else
{
int conf_ret = FALSE;
unsigned int code = req->error_code;
unsigned int ncode = 0;
http_app_err_file(con, req, fname, &vhost_prefix_len);
req->content_type_vs1 = NULL;
req->error_code = 0;
req->skip_document_root = FALSE;
SWAP_TYPE(fname, req->fname, Vstr_base *);
SWAP_TYPE(vhost_prefix_len, req->vhost_prefix_len, size_t);
conf_ret = http__conf_req(con, req);
SWAP_TYPE(fname, req->fname, Vstr_base *);
SWAP_TYPE(vhost_prefix_len, req->vhost_prefix_len, size_t);
ncode = req->error_code;
switch (code)
{
case 400: HTTPD_ERR(req, 400); break;
case 403: HTTPD_ERR(req, 403); break;
case 404: HTTPD_ERR(req, 404); break;
case 410: HTTPD_ERR(req, 410); break;
case 500: HTTPD_ERR(req, 500); break;
case 503: HTTPD_ERR(req, 503);
ASSERT_NO_SWITCH_DEF();
}
if (!conf_ret)
goto fail_custom_err;
if (ncode)
goto fail_custom_err;
if (!req->skip_document_root)
http_prepend_doc_root(fname, req);
}
fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
if (fname->conf->malloc_bad)
goto fail_custom_err;
ASSERT(con->fs && (con->fs->fd == -1));
if ((con->fs->fd = io_open_nonblock(fname_cstr)) == -1)
goto fail_custom_err;
if (fstat64(con->fs->fd, f_stat) == -1)
{
httpd_fin_fd_close(con);
goto fail_custom_err;
}
if (req->policy->use_public_only && !(f_stat->st_mode & S_IROTH))
{
httpd_fin_fd_close(con);
goto fail_custom_err;
}
if (!S_ISREG(f_stat->st_mode))
{
httpd_fin_fd_close(con);
goto fail_custom_err;
}
con->fs_off = 0;
con->fs_num = 1;
con->fs->off = 0;
con->fs->len = f_stat->st_size;
if (!req->ver_0_9)
httpd__try_fd_encoding(con, req, f_stat, fname);
con->use_mmap = FALSE;
if (!req->head_op)
httpd_serv_call_mmap(con, req, con->fs);
req->error_len = con->fs->len;
use_cust_err_msg = TRUE;
fail_custom_err:
fname->conf->malloc_bad = FALSE;
vstr_free_base(fname);
}
if (!use_cust_err_msg)
req->content_type_vs1 = NULL;
if (!req->ver_0_9)
{
http_app_def_hdrs(con, req, req->error_code, req->error_line,
httpd_opts->beg_time, "text/html", TRUE, req->error_len);
if (req->error_code == 416)
http_app_hdr_fmt(out, "Content-Range", "%s */%ju", "bytes",
(VSTR_AUTOCONF_uintmax_t)req->f_stat->st_size);
if (req->error_code == 401)
http_app_hdr_fmt(out, "WWW-Authenticate",
"Basic realm=\"$<vstr.all:%p>\"",
req->policy->auth_realm);
if ((req->error_code == 405) || (req->error_code == 501))
HTTP_APP_HDR_CONST_CSTR(out, "Allow", "GET, HEAD, OPTIONS, TRACE");
if ((req->error_code == 301) || (req->error_code == 302) ||
(req->error_code == 303) || (req->error_code == 307))
{
Vstr_base *tmp = req->fname;
ASSERT(!vstr_srch_cstr_chrs_fwd(tmp, 1, tmp->len, HTTP_EOL));
http_app_hdr_vstr(out, "Location",
tmp, 1, tmp->len, VSTR_TYPE_ADD_ALL_BUF);
}
if (req->user_return_error_code || use_cust_err_msg)
http_app_hdrs_url(con, req);
if (use_cust_err_msg)
http_app_hdrs_file(con, req);
http_app_end_hdrs(out);
}
if (!req->head_op)
{
Vstr_base *loc = req->fname;
switch (req->error_code)
{
case 301: vstr_add_fmt(out, out->len, CONF_MSG_FMT_301,
CONF_MSG__FMT_301_BEG,
loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
CONF_MSG__FMT_30x_END); break;
case 302: vstr_add_fmt(out, out->len, CONF_MSG_FMT_302,
CONF_MSG__FMT_302_BEG,
loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
CONF_MSG__FMT_30x_END); break;
case 303: vstr_add_fmt(out, out->len, CONF_MSG_FMT_303,
CONF_MSG__FMT_303_BEG,
loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
CONF_MSG__FMT_30x_END); break;
case 307: vstr_add_fmt(out, out->len, CONF_MSG_FMT_307,
CONF_MSG__FMT_307_BEG,
loc, (size_t)1, loc->len, VSTR_TYPE_ADD_ALL_BUF,
CONF_MSG__FMT_30x_END); break;
default:
if (use_cust_err_msg)
{
if (con->use_mmap && !vstr_mov(con->evnt->io_w, con->evnt->io_w->len,
req->f_mmap, 1, req->f_mmap->len))
return (http_con_close_cleanup(con, req));
vlg_dbg3(vlg, "ERROR CUSTOM-404 REPLY:\n$<vstr.all:%p>\n", out);
if (out->conf->malloc_bad)
return (http_con_close_cleanup(con, req));
return (http_fin_fd_req(con, req));
}
assert(req->error_len < SIZE_MAX);
vstr_add_ptr(out, out->len, req->error_msg, req->error_len);
}
}
vlg_dbg3(vlg, "ERROR REPLY:\n$<vstr.all:%p>\n", out);
if (out->conf->malloc_bad)
return (http_con_cleanup(con, req));
return (http_fin_req(con, req));
}
static int http_fin_errmem_req(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *out = con->evnt->io_w;
vstr_sc_reduce(out, 1, out->len, out->len - req->orig_io_w_len);
con->evnt->io_r->conf->malloc_bad = FALSE;
con->evnt->io_w->conf->malloc_bad = FALSE;
req->malloc_bad = TRUE;
HTTPD_ERR_RET(req, 500, http_fin_err_req(con, req));
}
static int http_fin_err_close_req(struct Con *con, Httpd_req_data *req)
{
httpd_fin_fd_close(con);
return (http_fin_err_req(con, req));
}
int httpd_sc_add_default_hostname(struct Con *con,
Httpd_req_data *req,
Vstr_base *lfn, size_t pos)
{
const Httpd_policy_opts *opts = req->policy;
const Vstr_base *d_h = opts->default_hostname;
Acpt_data *acpt_data = con->acpt_sa_ref->ptr;
struct sockaddr_in *sinv4 = ACPT_SA_IN4(acpt_data);
int ret = FALSE;
ret = vstr_add_vstr(lfn, pos, d_h, 1, d_h->len, VSTR_TYPE_ADD_DEF);
ASSERT(sinv4->sin_family == AF_INET);
if (ret && req->policy->add_def_port && (ntohs(sinv4->sin_port) != 80))
ret = vstr_add_fmt(lfn, pos + d_h->len, ":%hu", ntohs(sinv4->sin_port));
return (ret);
}
static void httpd_sc_add_default_filename(Httpd_req_data *req, Vstr_base *fname)
{
if (vstr_export_chr(fname, fname->len) == '/')
HTTPD_APP_REF_ALLVSTR(fname, req->policy->dir_filename);
}
int httpd_canon_path(Vstr_base *s1)
{
size_t tmp = 0;
while ((tmp = vstr_srch_cstr_buf_fwd(s1, 1, s1->len, "//")))
{
if (!vstr_del(s1, tmp, 1))
return (FALSE);
}
return (TRUE);
}
int httpd_canon_dir_path(Vstr_base *s1)
{
if (!httpd_canon_path(s1))
return (FALSE);
if (s1->len && (vstr_export_chr(s1, s1->len) != '/'))
return (vstr_add_cstr_ptr(s1, s1->len, "/"));
return (TRUE);
}
int httpd_canon_abs_dir_path(Vstr_base *s1)
{
if (!httpd_canon_dir_path(s1))
return (FALSE);
if (s1->len && (vstr_export_chr(s1, 1) != '/'))
return (vstr_add_cstr_ptr(s1, 0, "/"));
return (TRUE);
}
void httpd_req_absolute_uri(struct Con *con, Httpd_req_data *req,
Vstr_base *lfn, size_t pos, size_t len)
{
Vstr_base *data = con->evnt->io_r;
Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
size_t apos = pos - 1;
size_t alen = lfn->len;
int has_schema = TRUE;
int has_abs_path = TRUE;
int has_data = TRUE;
unsigned int prev_num = 0;
if (!VPREFIX(lfn, pos, len, "http://"))
{
has_schema = FALSE;
if (!VPREFIX(lfn, pos, len, "/"))
{
while (VPREFIX(lfn, pos, len, "../"))
{
++prev_num;
vstr_del(lfn, pos, CLEN("../")); len -= CLEN("../");
}
if (prev_num)
alen = lfn->len;
else
has_data = !!lfn->len;
has_abs_path = FALSE;
}
}
if (!has_schema)
{
vstr_add_cstr_buf(lfn, apos, "http://");
apos += lfn->len - alen;
alen = lfn->len;
if (!h_h->len)
httpd_sc_add_default_hostname(con, req, lfn, apos);
else
vstr_add_vstr(lfn, apos,
data, h_h->pos, h_h->len, VSTR_TYPE_ADD_ALL_BUF);
apos += lfn->len - alen;
}
if (!has_abs_path)
{
size_t path_len = req->path_len;
if (has_data || prev_num)
{
path_len -= vstr_cspn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
while (path_len && prev_num--)
{
path_len -= vstr_spn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
path_len -= vstr_cspn_cstr_chrs_rev(data, req->path_pos, path_len, "/");
}
if (!path_len) path_len = 1;
}
vstr_add_vstr(lfn, apos, data, req->path_pos, path_len, VSTR_TYPE_ADD_DEF);
}
}
int http_req_chk_dir(struct Con *con, Httpd_req_data *req)
{
Vstr_base *fname = req->fname;
Vstr_base *dir_fname = req->policy->dir_filename;
struct stat64 d_stat[1];
int ret = -1;
ASSERT(fname->len);
if (req->policy->use_secure_dirs)
{
const char *fname_cstr = NULL;
vstr_add_cstr_buf(fname, fname->len, "/");
HTTPD_APP_REF_ALLVSTR(fname, dir_fname);
fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
if (fname->conf->malloc_bad)
return (http_fin_errmem_req(con, req));
ret = stat64(fname_cstr, d_stat);
}
vstr_del(fname, 1, fname->len);
if (req->policy->use_secure_dirs)
if ((ret == -1) || !S_ISREG(d_stat->st_mode))
HTTPD_ERR_RET(req, 404, http_fin_err_req(con, req));
httpd_req_absolute_uri(con, req, fname, 1, fname->len);
if (fname->len && (vstr_export_chr(fname, fname->len) == '/'))
HTTPD_APP_REF_ALLVSTR(fname, dir_fname);
vstr_add_cstr_buf(fname, fname->len, "/");
HTTPD_ERR_301(req);
if (fname->conf->malloc_bad)
return (http_fin_errmem_req(con, req));
return (http_fin_err_req(con, req));
}
static void http_req_split_method(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *s1 = con->evnt->io_r;
size_t pos = 1;
size_t len = req->len;
size_t el = 0;
size_t skip_len = 0;
unsigned int orig_num = req->sects->num;
el = vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_EOL);
ASSERT(el >= pos);
len = el - pos;
if (!(el = vstr_srch_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
return;
vstr_sects_add(req->sects, pos, el - pos);
len -= (el - pos); pos += (el - pos);
if ((skip_len = vstr_spn_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
{ len -= skip_len; pos += skip_len; }
if (!len)
{
req->sects->num = orig_num;
return;
}
if (!(el = vstr_srch_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
{
vstr_sects_add(req->sects, pos, len);
len = 0;
}
else
{
vstr_sects_add(req->sects, pos, el - pos);
len -= (el - pos); pos += (el - pos);
if ((skip_len = vstr_spn_cstr_chrs_fwd(s1, pos, len, HTTP_LWS)))
{ len -= skip_len; pos += skip_len; }
}
if (len)
vstr_sects_add(req->sects, pos, len);
else
req->ver_0_9 = TRUE;
}
static void http_req_split_hdrs(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *s1 = con->evnt->io_r;
size_t pos = 1;
size_t len = req->len;
size_t el = 0;
size_t hpos = 0;
ASSERT(req->sects->num >= 3);
el = (VSTR_SECTS_NUM(req->sects, req->sects->num)->pos +
VSTR_SECTS_NUM(req->sects, req->sects->num)->len);
assert(VEQ(s1, el, CLEN(HTTP_EOL), HTTP_EOL));
len -= (el - pos) + CLEN(HTTP_EOL); pos += (el - pos) + CLEN(HTTP_EOL);
if (VPREFIX(s1, pos, len, HTTP_EOL))
return;
ASSERT(vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_END_OF_REQUEST));
hpos = pos;
while ((el = vstr_srch_cstr_buf_fwd(s1, pos, len, HTTP_EOL)) != pos)
{
char chr = 0;
len -= (el - pos) + CLEN(HTTP_EOL); pos += (el - pos) + CLEN(HTTP_EOL);
chr = vstr_export_chr(s1, pos);
if (chr == ' ' || chr == '\t')
continue;
vstr_sects_add(req->sects, hpos, el - hpos);
hpos = pos;
}
}
static size_t http__len_quoted_string(const Vstr_base *data,
size_t pos, size_t len)
{
size_t orig_pos = pos;
if (!VPREFIX(data, pos, len, "\""))
return (0);
len -= 1; pos += 1;
while (TRUE)
{
size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len, "\"\\");
len -= tmp; pos += tmp;
if (!len)
return (0);
if (vstr_export_chr(data, pos) == '"')
return (vstr_sc_posdiff(orig_pos, pos));
ASSERT(vstr_export_chr(data, pos) == '\\');
if (len < 3)
return (0);
len -= 2; pos += 2;
}
assert_ret(FALSE, 0);
}
static int http__skip_quoted_string(const Vstr_base *data,
size_t *pos, size_t *len)
{
size_t qlen = http__len_quoted_string(data, *pos, *len);
assert(VPREFIX(data, *pos, *len, "\""));
if (!qlen)
return (FALSE);
*len -= qlen; *pos += qlen;
HTTP_SKIP_LWS(data, *pos, *len);
return (TRUE);
}
static int httpd_match_etags(struct Httpd_req_data *req,
const Vstr_base *hdr, size_t hpos, size_t hlen,
const Vstr_base *vs1, size_t epos, size_t elen,
int allow_weak)
{
int need_comma = FALSE;
ASSERT(hdr);
if (!vs1)
return (FALSE);
while (hlen)
{
int weak = FALSE;
size_t htlen = 0;
if (vstr_export_chr(hdr, hpos) == ',')
{
hlen -= 1; hpos += 1;
HTTP_SKIP_LWS(hdr, hpos, hlen);
need_comma = FALSE;
continue;
}
else if (need_comma)
return (FALSE);
if (VPREFIX(hdr, hpos, hlen, "W/"))
{
weak = TRUE;
hlen -= CLEN("W/"); hpos += CLEN("W/");
}
if (!(htlen = http__len_quoted_string(hdr, hpos, hlen)))
return (FALSE);
if (allow_weak || !weak)
{
size_t orig_epos = epos;
size_t orig_elen = elen;
unsigned int num = 0;
while (elen)
{
size_t etlen = 0;
if (vstr_export_chr(vs1, epos) == ',')
{
elen -= 1; epos += 1;
HTTP_SKIP_LWS(vs1, epos, elen);
need_comma = FALSE;
continue;
}
else if (need_comma)
return (FALSE);
++num;
if (!VPREFIX(vs1, epos, elen, "W/"))
weak = FALSE;
else
{
weak = TRUE;
elen -= CLEN("W/"); epos += CLEN("W/");
}
if (!(etlen = http__len_quoted_string(vs1, epos, elen)))
return (FALSE);
if ((allow_weak || !weak) &&
vstr_cmp_eq(hdr, hpos, htlen, vs1, epos, etlen))
return (TRUE);
elen -= etlen; epos += etlen;
HTTP_SKIP_LWS(vs1, epos, elen);
need_comma = TRUE;
if (req->policy->max_etag_nodes && (num >= req->policy->max_etag_nodes))
return (FALSE);
}
epos = orig_epos;
elen = orig_elen;
}
hlen -= htlen; hpos += htlen;
HTTP_SKIP_LWS(hdr, hpos, hlen);
need_comma = TRUE;
}
return (FALSE);
}
#define HTTPD__HD_EQ(x) \
VEQ(hdrs, h_ ## x ->pos, h_ ## x ->len, date)
static int http_response_ok(struct Con *con, struct Httpd_req_data *req,
unsigned int *http_ret_code,
const char ** http_ret_line)
{
const Vstr_base *hdrs = con->evnt->io_r;
time_t mtime = req->f_stat->st_mtime;
Vstr_sect_node *h_ims = req->http_hdrs->hdr_if_modified_since;
Vstr_sect_node *h_ir = req->http_hdrs->hdr_if_range;
Vstr_sect_node *h_iums = req->http_hdrs->hdr_if_unmodified_since;
Vstr_sect_node *h_r = req->http_hdrs->multi->hdr_range;
Vstr_base *comb = req->http_hdrs->multi->comb;
Vstr_sect_node *h_im = req->http_hdrs->multi->hdr_if_match;
Vstr_sect_node *h_inm = req->http_hdrs->multi->hdr_if_none_match;
int h_ir_tst = FALSE;
int h_iums_tst = FALSE;
int req_if_range = FALSE;
int cached_output = FALSE;
const char *date = NULL;
if (req->ver_1_1 && h_iums->pos)
h_iums_tst = TRUE;
if (req->ver_1_1 && h_ir->pos && h_r->pos)
h_ir_tst = TRUE;
if (difftime(req->now, mtime) > 0)
{
Date_store *ds = httpd_opts->date;
date = date_rfc1123(ds, mtime);
if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
cached_output = TRUE;
if (h_iums_tst && HTTPD__HD_EQ(iums))
return (FALSE);
if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
req_if_range = TRUE;
date = date_rfc850(ds, mtime);
if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
cached_output = TRUE;
if (h_iums_tst && HTTPD__HD_EQ(iums))
return (FALSE);
if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
req_if_range = TRUE;
date = date_asctime(ds, mtime);
if (h_ims->pos && !cached_output && HTTPD__HD_EQ(ims))
cached_output = TRUE;
if (h_iums_tst && HTTPD__HD_EQ(iums))
return (FALSE);
if (h_ir_tst && !req_if_range && HTTPD__HD_EQ(ir))
req_if_range = TRUE;
}
if (req->ver_1_1)
{
const Vstr_base *vs1 = NULL;
size_t pos = 0;
size_t len = 0;
if (req->content_encoding_bzip2)
{
if (req->bzip2_etag_vs1 && (req->etag_time > mtime))
{
vs1 = req->bzip2_etag_vs1;
pos = req->bzip2_etag_pos;
len = req->bzip2_etag_len;
}
}
else if (req->content_encoding_gzip)
{
if (req->gzip_etag_vs1 && (req->etag_time > mtime))
{
vs1 = req->gzip_etag_vs1;
pos = req->gzip_etag_pos;
len = req->gzip_etag_len;
}
}
else if (req->etag_vs1 && (req->etag_time > mtime))
{
vs1 = req->etag_vs1;
pos = req->etag_pos;
len = req->etag_len;
}
if (h_ir_tst && !req_if_range &&
httpd_match_etags(req,
hdrs, h_ir->pos, h_ir->len, vs1, pos, len, FALSE))
req_if_range = TRUE;
if (h_ir_tst && !req_if_range)
h_r->pos = 0;
if (h_inm->pos && (VEQ(hdrs, h_inm->pos, h_inm->len, "*") ||
httpd_match_etags(req, comb, h_inm->pos, h_inm->len,
vs1, pos, len, !h_r->pos)))
cached_output = TRUE;
if (h_im->pos && !(VEQ(hdrs, h_im->pos, h_im->len, "*") ||
httpd_match_etags(req, comb, h_im->pos, h_im->len,
vs1, pos, len, !h_r->pos)))
return (FALSE);
}
else if (h_ir_tst && !req_if_range)
h_r->pos = 0;
if (cached_output)
{
req->head_op = TRUE;
*http_ret_code = 304;
*http_ret_line = "Not Modified";
}
else if (h_r->pos)
{
*http_ret_code = 206;
*http_ret_line = "Partial content";
}
return (TRUE);
}
static void http__multi_hdr_fixup(Vstr_sect_node *hdr_ignore,
Vstr_sect_node *hdr, size_t pos, size_t len)
{
if (hdr == hdr_ignore)
return;
if (hdr->pos <= pos)
return;
hdr->pos += len;
}
static int http__multi_hdr_cp(Vstr_base *comb,
Vstr_base *data, Vstr_sect_node *hdr)
{
size_t pos = comb->len + 1;
if (!hdr->len)
return (TRUE);
if (!vstr_add_vstr(comb, comb->len,
data, hdr->pos, hdr->len, VSTR_TYPE_ADD_BUF_PTR))
return (FALSE);
hdr->pos = pos;
return (TRUE);
}
static int http__app_multi_hdr(Vstr_base *data, struct Http_hdrs *hdrs,
Vstr_sect_node *hdr, size_t pos, size_t len)
{
Vstr_base *comb = hdrs->multi->comb;
ASSERT(comb);
ASSERT((hdr == hdrs->multi->hdr_accept) ||
(hdr == hdrs->multi->hdr_accept_charset) ||
(hdr == hdrs->multi->hdr_accept_encoding) ||
(hdr == hdrs->multi->hdr_accept_language) ||
(hdr == hdrs->multi->hdr_connection) ||
(hdr == hdrs->multi->hdr_if_match) ||
(hdr == hdrs->multi->hdr_if_none_match) ||
(hdr == hdrs->multi->hdr_range));
ASSERT((comb == data) || (comb == hdrs->multi->combiner_store));
if ((data == comb) && !hdr->pos)
{
hdr->pos = pos;
hdr->len = len;
return (TRUE);
}
if (data == comb)
{
comb = hdrs->multi->comb = hdrs->multi->combiner_store;
if (!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept) ||
!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_charset) ||
!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_encoding) ||
!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_language) ||
!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_connection) ||
!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_if_match) ||
!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_if_none_match) ||
!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_range) ||
FALSE)
return (FALSE);
}
if (!hdr->pos)
{
hdr->pos = comb->len + 1;
hdr->len = len;
return (vstr_add_vstr(comb, comb->len,
data, pos, len, VSTR_TYPE_ADD_BUF_PTR));
}
if (!vstr_add_cstr_ptr(comb, hdr->pos - 1, ","))
return (FALSE);
if (!vstr_add_vstr(comb, hdr->pos - 1,
data, pos, len, VSTR_TYPE_ADD_BUF_PTR))
return (FALSE);
hdr->len += ++len;
pos = hdr->pos - 1;
http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept, pos, len);
http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_charset, pos, len);
http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_encoding, pos, len);
http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_language, pos, len);
http__multi_hdr_fixup(hdr, hdrs->multi->hdr_connection, pos, len);
http__multi_hdr_fixup(hdr, hdrs->multi->hdr_if_match, pos, len);
http__multi_hdr_fixup(hdr, hdrs->multi->hdr_if_none_match, pos, len);
http__multi_hdr_fixup(hdr, hdrs->multi->hdr_range, pos, len);
return (TRUE);
}
static int http__hdr_eq(struct Con *con, size_t pos, size_t len,
const char *hdr, size_t hdr_len, size_t *hdr_val_pos)
{
Vstr_base *data = con->evnt->io_r;
ASSERT(CLEN(hdr) == hdr_len);
ASSERT(hdr[hdr_len - 1] == ':');
if (!con->policy->use_non_spc_hdrs)
--hdr_len;
if ((len < hdr_len) ||
!vstr_cmp_case_buf_eq(data, pos, hdr_len, hdr, hdr_len))
return (FALSE);
len -= hdr_len; pos += hdr_len;
if (!con->policy->use_non_spc_hdrs)
{
HTTP_SKIP_LWS(data, pos, len);
if (!len)
return (FALSE);
if (vstr_export_chr(data, pos) != ':')
return (FALSE);
--len; ++pos;
}
*hdr_val_pos = pos;
return (TRUE);
}
static void http__hdr_fixup(Vstr_base *data, size_t *pos, size_t *len,
size_t hdr_val_pos)
{
size_t tmp = 0;
*len -= hdr_val_pos - *pos; *pos += hdr_val_pos - *pos;
HTTP_SKIP_LWS(data, *pos, *len);
while ((tmp = vstr_spn_cstr_chrs_rev(data, *pos, *len, HTTP_LWS)))
{
*len -= tmp;
if (VSUFFIX(data, *pos, *len, HTTP_EOL))
*len -= CLEN(HTTP_EOL);
}
}
#define HDR__EQ(x) http__hdr_eq(con, pos, len, x ":", CLEN(x ":"), &hdr_val_pos)
#define HDR__SET(h) do { \
if (req->policy->use_x2_hdr_chk && http_hdrs-> hdr_ ## h ->pos) \
HTTPD_ERR_RET(req, 400, FALSE); \
http__hdr_fixup(data, &pos, &len, hdr_val_pos); \
http_hdrs-> hdr_ ## h ->pos = pos; \
http_hdrs-> hdr_ ## h ->len = len; \
} while (FALSE)
#define HDR__MULTI_SET(h) do { \
http__hdr_fixup(data, &pos, &len, hdr_val_pos); \
if (!http__app_multi_hdr(data, http_hdrs, \
http_hdrs->multi-> hdr_ ## h, pos, len)) \
{ \
req->malloc_bad = TRUE; \
HTTPD_ERR_RET(req, 500, FALSE); \
} \
} while (FALSE)
static int http__parse_hdrs(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
struct Http_hdrs *http_hdrs = req->http_hdrs;
unsigned int num = 3;
int got_content_length = FALSE;
int got_transfer_encoding = FALSE;
while (++num <= req->sects->num)
{
size_t pos = VSTR_SECTS_NUM(req->sects, num)->pos;
size_t len = VSTR_SECTS_NUM(req->sects, num)->len;
size_t hdr_val_pos = 0;
if (0) { }
else if (HDR__EQ("User-Agent")) HDR__SET(ua);
else if (HDR__EQ("Referer")) HDR__SET(referer);
else if (HDR__EQ("Expect")) HDR__SET(expect);
else if (HDR__EQ("Host")) HDR__SET(host);
else if (HDR__EQ("If-Modified-Since")) HDR__SET(if_modified_since);
else if (HDR__EQ("If-Range")) HDR__SET(if_range);
else if (HDR__EQ("If-Unmodified-Since")) HDR__SET(if_unmodified_since);
else if (HDR__EQ("Authorization")) HDR__SET(authorization);
else if (HDR__EQ("Accept")) HDR__MULTI_SET(accept);
else if (HDR__EQ("Accept-Charset")) HDR__MULTI_SET(accept_charset);
else if (HDR__EQ("Accept-Encoding")) HDR__MULTI_SET(accept_encoding);
else if (HDR__EQ("Accept-Language")) HDR__MULTI_SET(accept_language);
else if (HDR__EQ("Connection")) HDR__MULTI_SET(connection);
else if (HDR__EQ("If-Match")) HDR__MULTI_SET(if_match);
else if (HDR__EQ("If-None-Match")) HDR__MULTI_SET(if_none_match);
else if (HDR__EQ("Range")) HDR__MULTI_SET(range);
else if (HDR__EQ("Content-Length"))
{
unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
VSTR_FLAG_PARSE_NUM_OVERFLOW);
size_t num_len = 0;
if (req->policy->use_x2_hdr_chk && got_content_length)
HTTPD_ERR_RET(req, 400, FALSE);
got_content_length = TRUE;
http__hdr_fixup(data, &pos, &len, hdr_val_pos);
if (vstr_parse_uint(data, pos, len, num_flags, &num_len, NULL))
HTTPD_ERR_RET(req, 413, FALSE);
if (num_len != len)
HTTPD_ERR_RET(req, 400, FALSE);
}
else if (HDR__EQ("Transfer-Encoding"))
{
if (req->policy->use_x2_hdr_chk && got_transfer_encoding)
HTTPD_ERR_RET(req, 400, FALSE);
got_transfer_encoding = TRUE;
http__hdr_fixup(data, &pos, &len, hdr_val_pos);
if (!VEQ(data, pos, len, "identity"))
HTTPD_ERR_RET(req, 413, FALSE);
}
}
return (TRUE);
}
#undef HDR__EQ
#undef HDR__SET
#undef HDR__MUTLI_SET
static int http_req_parse_version(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
size_t op_pos = VSTR_SECTS_NUM(req->sects, 3)->pos;
size_t op_len = VSTR_SECTS_NUM(req->sects, 3)->len;
unsigned int major = 0;
unsigned int minor = 0;
size_t num_len = 0;
unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
VSTR_FLAG_PARSE_NUM_OVERFLOW);
if (!VPREFIX(data, op_pos, op_len, "HTTP"))
HTTPD_ERR_RET(req, 400, FALSE);
op_len -= CLEN("HTTP"); op_pos += CLEN("HTTP");
HTTP_SKIP_LWS(data, op_pos, op_len);
if (!VPREFIX(data, op_pos, op_len, "/"))
HTTPD_ERR_RET(req, 400, FALSE);
op_len -= CLEN("/"); op_pos += CLEN("/");
HTTP_SKIP_LWS(data, op_pos, op_len);
major = vstr_parse_uint(data, op_pos, op_len, num_flags, &num_len, NULL);
op_len -= num_len; op_pos += num_len;
HTTP_SKIP_LWS(data, op_pos, op_len);
if (!num_len || !VPREFIX(data, op_pos, op_len, "."))
HTTPD_ERR_RET(req, 400, FALSE);
op_len -= CLEN("."); op_pos += CLEN(".");
HTTP_SKIP_LWS(data, op_pos, op_len);
minor = vstr_parse_uint(data, op_pos, op_len, num_flags, &num_len, NULL);
op_len -= num_len; op_pos += num_len;
HTTP_SKIP_LWS(data, op_pos, op_len);
if (!num_len || op_len)
HTTPD_ERR_RET(req, 400, FALSE);
if (0) { }
else if ((major == 1) && (minor >= 1))
req->ver_1_1 = TRUE;
else if ((major == 1) && (minor == 0))
{ }
else
HTTPD_ERR_RET(req, 505, FALSE);
return (TRUE);
}
#define HDR__CON_1_0_FIXUP(name, h) \
else if (VIEQ(data, pos, tmp, name)) \
do { \
req -> http_hdrs -> hdr_ ## h ->pos = 0; \
req -> http_hdrs -> hdr_ ## h ->len = 0; \
} while (FALSE)
#define HDR__CON_1_0_MULTI_FIXUP(name, h) \
else if (VIEQ(data, pos, tmp, name)) \
do { \
req -> http_hdrs -> multi -> hdr_ ## h ->pos = 0; \
req -> http_hdrs -> multi -> hdr_ ## h ->len = 0; \
} while (FALSE)
static void http__parse_connection(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *data = req->http_hdrs->multi->comb;
size_t pos = 0;
size_t len = 0;
unsigned int num = 0;
pos = req->http_hdrs->multi->hdr_connection->pos;
len = req->http_hdrs->multi->hdr_connection->len;
if (req->ver_1_1)
con->keep_alive = HTTP_1_1_KEEP_ALIVE;
if (!len)
return;
while (len)
{
size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
HTTP_EOL HTTP_LWS ",");
++num;
if (req->ver_1_1)
{
if (VIEQ(data, pos, tmp, "close"))
con->keep_alive = HTTP_NON_KEEP_ALIVE;
}
else if (VIEQ(data, pos, tmp, "keep-alive"))
{
if (req->policy->use_keep_alive_1_0)
con->keep_alive = HTTP_1_0_KEEP_ALIVE;
}
HDR__CON_1_0_FIXUP("User-Agent", ua);
HDR__CON_1_0_FIXUP("Referer", referer);
HDR__CON_1_0_FIXUP("Expect", expect);
HDR__CON_1_0_FIXUP("Host", host);
HDR__CON_1_0_FIXUP("If-Modified-Since", if_modified_since);
HDR__CON_1_0_FIXUP("If-Range", if_range);
HDR__CON_1_0_FIXUP("If-Unmodified-Since", if_unmodified_since);
HDR__CON_1_0_FIXUP("Authorization", authorization);
HDR__CON_1_0_MULTI_FIXUP("Accept", accept);
HDR__CON_1_0_MULTI_FIXUP("Accept-Charset", accept_charset);
HDR__CON_1_0_MULTI_FIXUP("Accept-Encoding", accept_encoding);
HDR__CON_1_0_MULTI_FIXUP("Accept-Language", accept_language);
HDR__CON_1_0_MULTI_FIXUP("If-Match", if_match);
HDR__CON_1_0_MULTI_FIXUP("If-None-Match", if_none_match);
HDR__CON_1_0_MULTI_FIXUP("Range", range);
tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len, ",");
len -= tmp; pos += tmp;
if (!len)
break;
assert(VPREFIX(data, pos, len, ","));
len -= 1; pos += 1;
HTTP_SKIP_LWS(data, pos, len);
if (req->policy->max_connection_nodes &&
(num >= req->policy->max_connection_nodes))
return;
}
}
#undef HDR__CON_1_0_FIXUP
#undef HDR__CON_1_0_MULTI_FIXUP
static int http__parse_1_x(struct Con *con, struct Httpd_req_data *req)
{
ASSERT(!req->ver_0_9);
if (!http_req_parse_version(con, req))
return (FALSE);
if (!http__parse_hdrs(con, req))
return (FALSE);
if (req->policy->max_requests &&
(req->policy->max_requests <= con->evnt->acct.req_got) && req->ver_1_1)
return (TRUE);
if (!req->policy->use_keep_alive && req->ver_1_1)
return (TRUE);
http__parse_connection(con, req);
if (req->policy->max_requests &&
(req->policy->max_requests <= con->evnt->acct.req_got))
con->keep_alive = HTTP_NON_KEEP_ALIVE;
return (TRUE);
}
static int http__chk_single_crlf(Vstr_base *data, size_t pos, size_t len)
{
if (vstr_srch_chr_fwd(data, pos, len, '\r') ||
vstr_srch_chr_fwd(data, pos, len, '\n'))
return (TRUE);
return (FALSE);
}
static int http_parse_host(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
size_t op_pos = req->path_pos;
size_t op_len = req->path_len;
if (VIPREFIX(data, op_pos, op_len, "http://"))
{
size_t tmp = CLEN("http://");
op_len -= tmp; op_pos += tmp;
tmp = vstr_srch_chr_fwd(data, op_pos, op_len, '/');
if (!tmp)
{
HTTP__HDR_SET(req, host, op_pos, op_len);
op_len = 1;
--op_pos;
}
else
{
size_t host_len = tmp - op_pos;
HTTP__HDR_SET(req, host, op_pos, host_len);
op_len -= host_len; op_pos += host_len;
}
assert(VPREFIX(data, op_pos, op_len, "/"));
}
if (req->ver_1_1 && !req->http_hdrs->hdr_host->pos)
return (FALSE);
if (req->http_hdrs->hdr_host->len)
{
size_t pos = req->http_hdrs->hdr_host->pos;
size_t len = req->http_hdrs->hdr_host->len;
size_t tmp = 0;
if (vstr_srch_chr_fwd(data, pos, len, '/'))
return (FALSE);
if (http__chk_single_crlf(data, pos, len))
return (FALSE);
if ((tmp = vstr_srch_chr_fwd(data, pos, len, ':')))
{
len -= tmp - pos; pos = tmp;
if (VEQ(data, pos, len, ":80") || VEQ(data, pos, len, ":"))
req->http_hdrs->hdr_host->len -= len;
else
{
len -= 1; pos += 1;
if (vstr_spn_cstr_chrs_fwd(data, pos, len, "0123456789") != len)
return (FALSE);
}
}
}
if (http__chk_single_crlf(data, op_pos, op_len))
return (FALSE);
if (req->policy->remove_url_frag)
op_len = vstr_cspn_cstr_chrs_fwd(data, op_pos, op_len, "#");
if (req->policy->remove_url_query)
op_len = vstr_cspn_cstr_chrs_fwd(data, op_pos, op_len, "?");
req->path_pos = op_pos;
req->path_len = op_len;
return (TRUE);
}
static void http__parse_skip_blanks(Vstr_base *data,
size_t *passed_pos, size_t *passed_len)
{
size_t pos = *passed_pos;
size_t len = *passed_len;
HTTP_SKIP_LWS(data, pos, len);
while (VPREFIX(data, pos, len, ","))
{
len -= CLEN(","); pos += CLEN(",");
HTTP_SKIP_LWS(data, pos, len);
}
*passed_pos = pos;
*passed_len = len;
}
static int httpd__file_sect_add(struct Con *con, Httpd_req_data *req,
VSTR_AUTOCONF_uintmax_t range_beg,
VSTR_AUTOCONF_uintmax_t range_end, size_t len)
{
struct File_sect *fs = NULL;
ASSERT(con->fs && (con->fs_sz >= 1));
if (!con->fs_num)
{
ASSERT((con->fs == con->fs_store) || (con->fs_sz > 1));
ASSERT(!con->use_mpbr);
goto file_sect_add;
}
con->use_mpbr = TRUE;
if (con->fs == con->fs_store)
{
ASSERT(con->fs_num == 1);
if (!(con->mpbr_ct = vstr_make_base(con->evnt->io_w->conf)))
return (FALSE);
if (!(fs = MK(sizeof(struct File_sect) * 16)))
return (FALSE);
con->fs = fs;
con->fs_sz = 16;
con->fs->fd = con->fs_store->fd;
con->fs->off = con->fs_store->off;
con->fs->len = con->fs_store->len;
++fs;
}
else if (con->fs_num >= con->fs_sz)
{
unsigned int num = (con->fs_sz << 1) + 1;
ASSERT(con->fs_num == con->fs_sz);
if (!MV(con->fs, fs, sizeof(struct File_sect) * num))
return (FALSE);
con->fs_sz = num;
}
file_sect_add:
fs = con->fs + con->fs_num++;
fs->fd = con->fs->fd;
fs->off = range_beg;
fs->len = (range_end - range_beg) + 1;
return (!len || (req->policy->max_range_nodes > con->fs_num));
}
static int http_parse_range(struct Con *con, Httpd_req_data *req)
{
Vstr_base *data = req->http_hdrs->multi->comb;
Vstr_sect_node *h_r = req->http_hdrs->multi->hdr_range;
size_t pos = h_r->pos;
size_t len = h_r->len;
VSTR_AUTOCONF_uintmax_t fsize = req->f_stat->st_size;
unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
VSTR_FLAG_PARSE_NUM_OVERFLOW);
size_t num_len = 0;
if (!VPREFIX(data, pos, len, "bytes"))
return (0);
len -= CLEN("bytes"); pos += CLEN("bytes");
HTTP_SKIP_LWS(data, pos, len);
if (!VPREFIX(data, pos, len, "="))
return (0);
len -= CLEN("="); pos += CLEN("=");
http__parse_skip_blanks(data, &pos, &len);
while (len)
{
VSTR_AUTOCONF_uintmax_t range_beg = 0;
VSTR_AUTOCONF_uintmax_t range_end = 0;
if (VPREFIX(data, pos, len, "-"))
{
VSTR_AUTOCONF_uintmax_t tmp = 0;
len -= CLEN("-"); pos += CLEN("-");
HTTP_SKIP_LWS(data, pos, len);
tmp = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, NULL);
len -= num_len; pos += num_len;
if (!num_len)
return (0);
if (!tmp)
return (416);
if (tmp >= fsize)
return (0);
range_beg = fsize - tmp;
range_end = fsize - 1;
}
else
{
range_beg = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, NULL);
len -= num_len; pos += num_len;
HTTP_SKIP_LWS(data, pos, len);
if (!VPREFIX(data, pos, len, "-"))
return (0);
len -= CLEN("-"); pos += CLEN("-");
HTTP_SKIP_LWS(data, pos, len);
if (!len || VPREFIX(data, pos, len, ","))
range_end = fsize - 1;
else
{
range_end = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, 0);
len -= num_len; pos += num_len;
if (!num_len)
return (0);
if (range_end >= fsize)
range_end = fsize - 1;
}
if ((range_beg >= fsize) || (range_beg > range_end))
return (416);
if ((range_beg == 0) &&
(range_end == (fsize - 1)))
return (0);
}
http__parse_skip_blanks(data, &pos, &len);
if (!httpd__file_sect_add(con, req, range_beg, range_end, len))
return (0);
}
return (200);
}
static void httpd_serv_file_sects_none(struct Con *con, Httpd_req_data *req)
{
con->use_mpbr = FALSE;
con->fs_num = 1;
con->fs->off = 0;
con->fs->len = req->f_stat->st_size;
}
static void httpd_serv_call_file_init(struct Con *con, Httpd_req_data *req,
unsigned int *http_ret_code,
const char ** http_ret_line)
{
ASSERT(req);
con->use_mmap = FALSE;
if (!req->head_op)
{
httpd_serv_call_mmap(con, req, con->fs);
httpd_serv_call_seek(con, req, con->fs, http_ret_code, http_ret_line);
}
}
static int http_req_1_x(struct Con *con, Httpd_req_data *req,
unsigned int *http_ret_code,
const char **http_ret_line)
{
Vstr_base *out = con->evnt->io_w;
Vstr_sect_node *h_r = req->http_hdrs->multi->hdr_range;
time_t mtime = -1;
if (req->ver_1_1 && req->http_hdrs->hdr_expect->len)
HTTPD_ERR_RET(req, 417, FALSE);
httpd__try_fd_encoding(con, req, req->f_stat, req->fname);
if (req->policy->use_err_406 &&
!req->content_encoding_identity &&
!req->content_encoding_bzip2 && !req->content_encoding_gzip)
HTTPD_ERR_RET(req, 406, FALSE);
if (h_r->pos)
{
int ret_code = 0;
if (!(req->policy->use_range &&
(req->ver_1_1 || req->policy->use_range_1_0)))
h_r->pos = 0;
else if (!(ret_code = http_parse_range(con, req)))
h_r->pos = 0;
ASSERT(!ret_code || (ret_code == 200) || (ret_code == 416));
if (ret_code == 416)
{
if (!req->http_hdrs->hdr_if_range->pos)
HTTPD_ERR_RET(req, 416, FALSE);
h_r->pos = 0;
}
}
if (!http_response_ok(con, req, http_ret_code, http_ret_line))
HTTPD_ERR_RET(req, 412, FALSE);
if (!h_r->pos)
httpd_serv_file_sects_none(con, req);
httpd_serv_call_file_init(con, req, http_ret_code, http_ret_line);
ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
ASSERT(!con->fs_off);
mtime = req->f_stat->st_mtime;
http_app_def_hdrs(con, req, *http_ret_code, *http_ret_line,
mtime, NULL, TRUE, con->fs->len);
if (h_r->pos && !con->use_mpbr)
http_app_hdr_fmt(out, "Content-Range", "%s %ju-%ju/%ju", "bytes",
con->fs->off, con->fs->off + (con->fs->len - 1),
(VSTR_AUTOCONF_uintmax_t)req->f_stat->st_size);
if (req->content_location_vs1)
http_app_hdr_vstr_def(out, "Content-Location",
HTTP__XTRA_HDR_PARAMS(req, content_location));
http_app_hdrs_url(con, req);
http_app_hdrs_file(con, req);
http_app_end_hdrs(out);
if (h_r->pos && con->use_mpbr)
{
con->mpbr_fs_len = req->f_stat->st_size;
http_app_hdrs_mpbr(con, con->fs);
}
return (TRUE);
}
static int http__skip_parameters(Vstr_base *data, size_t *pos, size_t *len)
{
while (*len && (vstr_export_chr(data, *pos) != ','))
{
size_t tmp = 0;
if (vstr_export_chr(data, *pos) != ';')
return (FALSE);
*len -= 1; *pos += 1;
HTTP_SKIP_LWS(data, *pos, *len);
tmp = vstr_cspn_cstr_chrs_fwd(data, *pos, *len, ";,=");
*len -= tmp; *pos += tmp;
if (!*len)
break;
switch (vstr_export_chr(data, *pos))
{
case ';': break;
case ',': break;
case '=':
*len -= 1; *pos += 1;
HTTP_SKIP_LWS(data, *pos, *len);
if (!*len)
return (FALSE);
if (vstr_export_chr(data, *pos) == '"')
{
if (!http__skip_quoted_string(data, pos, len))
return (FALSE);
}
else
{
tmp = vstr_cspn_cstr_chrs_fwd(data, *pos, *len, ";,");
*len -= tmp; *pos += tmp;
}
break;
}
}
return (TRUE);
}
unsigned int http_parse_accept(Httpd_req_data *req,
const Vstr_base *ct_vs1,
size_t ct_pos, size_t ct_len)
{
Vstr_base *data = req->http_hdrs->multi->comb;
size_t pos = 0;
size_t len = 0;
unsigned int num = 0;
unsigned int quality = 1001;
int done_sub_type = FALSE;
size_t ct_sub_len = 0;
pos = req->http_hdrs->multi->hdr_accept->pos;
len = req->http_hdrs->multi->hdr_accept->len;
if (!len)
return (1000);
ASSERT(ct_vs1);
if (!(ct_sub_len = vstr_srch_chr_fwd(ct_vs1, ct_pos, ct_len, '/')))
{
if (ct_vs1 == req->content_type_vs1)
req->content_type_vs1 = NULL;
return (1);
}
ct_sub_len = vstr_sc_posdiff(ct_pos, ct_sub_len);
while (len)
{
size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
HTTP_EOL HTTP_LWS ";,");
++num;
if (0) { }
else if (vstr_cmp_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
{
len -= tmp; pos += tmp;
if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
return (1);
return (quality);
}
else if ((tmp == (ct_sub_len + 1)) &&
vstr_cmp_eq(data, pos, ct_sub_len, ct_vs1, ct_pos, ct_sub_len) &&
(vstr_export_chr(data, vstr_sc_poslast(pos, tmp)) == '*'))
{
len -= tmp; pos += tmp;
if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
return (1);
done_sub_type = TRUE;
}
else if (!done_sub_type && VEQ(data, pos, tmp, "*/*"))
{
len -= tmp; pos += tmp;
if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
return (1);
}
else
{
len -= tmp; pos += tmp;
HTTP_SKIP_LWS(data, pos, len);
}
if (!http__skip_parameters(data, &pos, &len))
return (1);
if (!len)
break;
assert(VPREFIX(data, pos, len, ","));
len -= 1; pos += 1;
HTTP_SKIP_LWS(data, pos, len);
if (req->policy->max_A_nodes && (num >= req->policy->max_A_nodes))
return (FALSE);
}
ASSERT(quality <= 1001);
if (quality == 1001)
return (0);
return (quality);
}
static int http__cmp_lang_eq(const Vstr_base *s1, size_t p1, size_t l1,
const Vstr_base *s2, size_t p2, size_t l2)
{
if (l1 == l2)
return (FALSE);
if (l1 > l2)
return ((vstr_export_chr(s1, p1 + l2) == '-') &&
vstr_cmp_eq(s1, p1, l2, s2, p2, l2));
return ((vstr_export_chr(s2, p2 + l1) == '-') &&
vstr_cmp_eq(s1, p1, l1, s2, p2, l1));
}
unsigned int http_parse_accept_language(Httpd_req_data *req,
const Vstr_base *ct_vs1,
size_t ct_pos, size_t ct_len)
{
Vstr_base *data = req->http_hdrs->multi->comb;
size_t pos = 0;
size_t len = 0;
unsigned int num = 0;
unsigned int quality = 1001;
size_t done_sub_type = 0;
pos = req->http_hdrs->multi->hdr_accept_language->pos;
len = req->http_hdrs->multi->hdr_accept_language->len;
if (!len)
return (1000);
ASSERT(ct_vs1);
while (len)
{
size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
HTTP_EOL HTTP_LWS ";,");
++num;
if (0) { }
else if (vstr_cmp_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
{
len -= tmp; pos += tmp;
if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
return (1);
return (quality);
}
else if ((!done_sub_type || (done_sub_type >= tmp)) &&
http__cmp_lang_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
{
unsigned int sub_type_qual = 0;
len -= tmp; pos += tmp;
if (!http_parse_quality(data, &pos, &len, TRUE, &sub_type_qual))
return (1);
ASSERT(sub_type_qual <= 1000);
if (!done_sub_type || (done_sub_type > tmp) || (sub_type_qual > quality))
quality = sub_type_qual;
done_sub_type = tmp;
}
else if (!done_sub_type && VEQ(data, pos, tmp, "*"))
{
len -= tmp; pos += tmp;
if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
return (1);
}
else
{
len -= tmp; pos += tmp;
HTTP_SKIP_LWS(data, pos, len);
}
if (!len)
break;
assert(VPREFIX(data, pos, len, ","));
len -= 1; pos += 1;
HTTP_SKIP_LWS(data, pos, len);
if (req->policy->max_AL_nodes && (num >= req->policy->max_AL_nodes))
return (FALSE);
}
ASSERT(quality <= 1001);
if (quality == 1001)
return (0);
return (quality);
}
int http_req_content_type(Httpd_req_data *req)
{
const Vstr_base *vs1 = NULL;
size_t pos = 0;
size_t len = 0;
if (req->content_type_vs1)
return (TRUE);
mime_types_match(req->policy->mime_types,
req->fname, 1, req->fname->len, &vs1, &pos, &len);
if (!len)
{
req->parse_accept = FALSE;
return (TRUE);
}
if ((vstr_export_chr(vs1, pos) == '/') && (len > 2) &&
(vstr_export_chr(vs1, vstr_sc_poslast(pos, len)) == '/'))
{
size_t num_len = 1;
len -= 2;
++pos;
req->user_return_error_code = TRUE;
req->direct_filename = FALSE;
switch (vstr_parse_uint(vs1, pos, len, 0, &num_len, NULL))
{
case 400: if (num_len == len) HTTPD_ERR_RET(req, 400, FALSE);
case 403: if (num_len == len) HTTPD_ERR_RET(req, 403, FALSE);
case 404: if (num_len == len) HTTPD_ERR_RET(req, 404, FALSE);
case 410: if (num_len == len) HTTPD_ERR_RET(req, 410, FALSE);
case 500: if (num_len == len) HTTPD_ERR_RET(req, 500, FALSE);
case 503: if (num_len == len) HTTPD_ERR_RET(req, 503, FALSE);
default:
req->user_return_error_code = FALSE;
return (TRUE);
}
}
req->content_type_vs1 = vs1;
req->content_type_pos = pos;
req->content_type_len = len;
return (TRUE);
}
static int http__policy_req(struct Con *con, Httpd_req_data *req)
{
if (!httpd_policy_request(con, req,
httpd_opts->conf, httpd_opts->match_request))
{
Vstr_base *s1 = httpd_opts->conf->tmp;
if (!req->user_return_error_code)
vlg_info(vlg, "CONF-MATCH-REQ-ERR from[$<sa:%p>]:"
" backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1);
return (TRUE);
}
if (con->evnt->flag_q_closed)
{
Vstr_base *s1 = req->policy->s->policy_name;
vlg_info(vlg, "BLOCKED from[$<sa:%p>]: policy $<vstr.all:%p>\n",
CON_CEVNT_SA(con), s1);
return (FALSE);
}
if (req->direct_uri)
{
Vstr_base *s1 = req->policy->s->policy_name;
vlg_info(vlg, "CONF-MATCH-REQ-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
" Has URI.\n", CON_CEVNT_SA(con), s1);
HTTPD_ERR_RET(req, 503, TRUE);
}
if (req->policy->auth_token->len)
{
Vstr_base *data = con->evnt->io_r;
size_t pos = req->http_hdrs->hdr_authorization->pos;
size_t len = req->http_hdrs->hdr_authorization->len;
Vstr_base *auth_token = req->policy->auth_token;
int auth_ok = TRUE;
if (!VIPREFIX(data, pos, len, "Basic"))
auth_ok = FALSE;
else
{
len -= CLEN("Basic"); pos += CLEN("Basic");
HTTP_SKIP_LWS(data, pos, len);
if (!vstr_cmp_eq(data, pos, len, auth_token, 1, auth_token->len))
auth_ok = FALSE;
}
if (!auth_ok)
{
req->user_return_error_code = FALSE;
HTTPD_ERR(req, 401);
}
}
return (TRUE);
}
int http_req_op_get(struct Con *con, Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
Vstr_base *out = con->evnt->io_w;
Vstr_base *fname = req->fname;
const char *fname_cstr = NULL;
unsigned int http_ret_code = 200;
const char * http_ret_line = "OK";
if (fname->conf->malloc_bad)
goto malloc_err;
assert(VPREFIX(fname, 1, fname->len, "/"));
if (vstr_srch_cstr_buf_fwd(data, req->path_pos, req->path_len, "//"))
{
vstr_sub_vstr(fname, 1, fname->len, data, req->path_pos, req->path_len, 0);
if (!httpd_canon_path(fname))
goto malloc_err;
httpd_req_absolute_uri(con, req, fname, 1, fname->len);
HTTPD_ERR_301(req);
return (http_fin_err_req(con, req));
}
if (!http__policy_req(con, req))
return (FALSE);
if (req->error_code)
return (http_fin_err_req(con, req));
httpd_sc_add_default_filename(req, fname);
if (!http__conf_req(con, req))
return (FALSE);
if (req->error_code)
return (http_fin_err_req(con, req));
if (!http_req_content_type(req))
return (http_fin_err_req(con, req));
if (req->parse_accept)
{
if (!http_parse_accept(req, HTTP__XTRA_HDR_PARAMS(req, content_type)))
HTTPD_ERR_RET(req, 406, http_fin_err_req(con, req));
}
if (!req->content_language_vs1 || !req->content_language_len)
req->parse_accept_language = FALSE;
if (req->parse_accept_language)
{
if (!http_parse_accept_language(req,
HTTP__XTRA_HDR_PARAMS(req,
content_language)))
HTTPD_ERR_RET(req, 406, http_fin_err_req(con, req));
}
if (!req->skip_document_root)
http_prepend_doc_root(fname, req);
fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
if (fname->conf->malloc_bad)
goto malloc_err;
ASSERT(con->fs && !con->fs_num && !con->fs_off && (con->fs->fd == -1));
if ((con->fs->fd = io_open_nonblock(fname_cstr)) == -1)
{
if (0) { }
else if (req->direct_filename && (errno == EISDIR))
HTTPD_ERR(req, 404);
else if (errno == EISDIR)
return (http_req_chk_dir(con, req));
else if (errno == EACCES)
HTTPD_ERR(req, 403);
else if ((errno == ENOENT) ||
(errno == ENODEV) ||
(errno == ENXIO) ||
(errno == ELOOP) ||
(errno == ENOTDIR) ||
(errno == ENAMETOOLONG) ||
FALSE)
HTTPD_ERR(req, 404);
else
HTTPD_ERR(req, 500);
return (http_fin_err_req(con, req));
}
if (fstat64(con->fs->fd, req->f_stat) == -1)
HTTPD_ERR_RET(req, 500, http_fin_err_close_req(con, req));
if (req->policy->use_public_only && !(req->f_stat->st_mode & S_IROTH))
HTTPD_ERR_RET(req, 403, http_fin_err_close_req(con, req));
if (S_ISDIR(req->f_stat->st_mode))
{
if (req->direct_filename)
HTTPD_ERR_RET(req, 404, http_fin_err_close_req(con, req));
httpd_fin_fd_close(con);
return (http_req_chk_dir(con, req));
}
if (!S_ISREG(req->f_stat->st_mode))
HTTPD_ERR_RET(req, 403, http_fin_err_close_req(con, req));
con->fs->len = req->f_stat->st_size;
if (req->ver_0_9)
{
httpd_serv_file_sects_none(con, req);
httpd_serv_call_file_init(con, req, &http_ret_code, &http_ret_line);
http_ret_line = "OK - HTTP/0.9";
}
else if (!http_req_1_x(con, req, &http_ret_code, &http_ret_line))
return (http_fin_err_close_req(con, req));
if (out->conf->malloc_bad)
goto malloc_close_err;
vlg_dbg3(vlg, "REPLY:\n$<vstr.all:%p>\n", out);
if (con->use_mmap && !vstr_mov(con->evnt->io_w, con->evnt->io_w->len,
req->f_mmap, 1, req->f_mmap->len))
goto malloc_close_err;
vlg_info(vlg, "REQ $<vstr.sect:%p%p%u> from[$<sa:%p>] ret[%03u %s]"
" sz[${BKMG.ju:%ju}:%ju]", data, req->sects, 1U, CON_CEVNT_SA(con),
http_ret_code, http_ret_line, con->fs->len, con->fs->len);
http_vlg_def(con, req);
return (http_fin_fd_req(con, req));
malloc_close_err:
httpd_fin_fd_close(con);
malloc_err:
VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "op_get(): %m\n"));
}
int http_req_op_opts(struct Con *con, Httpd_req_data *req)
{
Vstr_base *out = con->evnt->io_w;
Vstr_base *fname = req->fname;
VSTR_AUTOCONF_uintmax_t tmp = 0;
if (fname->conf->malloc_bad)
goto malloc_err;
assert(VPREFIX(fname, 1, fname->len, "/") ||
!req->policy->use_vhosts_name ||
!req->policy->use_host_err_chk ||
!req->policy->use_host_err_400 ||
VEQ(con->evnt->io_r, req->path_pos, req->path_len, "*"));
http_app_def_hdrs(con, req, 200, "OK", 0, NULL, TRUE, 0);
HTTP_APP_HDR_CONST_CSTR(out, "Allow", "GET, HEAD, OPTIONS, TRACE");
http_app_end_hdrs(out);
if (out->conf->malloc_bad)
goto malloc_err;
vlg_info(vlg, "REQ %s from[$<sa:%p>] ret[%03u %s] sz[${BKMG.ju:%ju}:%ju]",
"OPTIONS", CON_CEVNT_SA(con), 200, "OK", tmp, tmp);
http_vlg_def(con, req);
return (http_fin_req(con, req));
malloc_err:
VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "op_opts(): %m\n"));
}
int http_req_op_trace(struct Con *con, Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
Vstr_base *out = con->evnt->io_w;
VSTR_AUTOCONF_uintmax_t tmp = 0;
http_app_def_hdrs(con, req, 200, "OK", req->now,
"message/http", FALSE, req->len);
http_app_end_hdrs(out);
vstr_add_vstr(out, out->len, data, 1, req->len, VSTR_TYPE_ADD_DEF);
if (out->conf->malloc_bad)
VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "op_trace(): %m\n"));
tmp = req->len;
vlg_info(vlg, "REQ %s from[$<sa:%p>] ret[%03u %s] sz[${BKMG.ju:%ju}:%ju]",
"TRACE", CON_CEVNT_SA(con), 200, "OK", tmp, tmp);
http_vlg_def(con, req);
return (http_fin_req(con, req));
}
#define HTTPD__VALID_CSTR_CHRS_URL_FILENAME \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"abcdefghijklmnopqrstuvwxyz" \
"0123456789" \
":.-_~"
int httpd_valid_url_filename(Vstr_base *s1, size_t pos, size_t len)
{
const char *const cstr = HTTPD__VALID_CSTR_CHRS_URL_FILENAME;
return (vstr_spn_cstr_chrs_fwd(s1, pos, len, cstr) == s1->len);
}
int httpd_init_default_hostname(Opt_serv_policy_opts *sopts)
{
Httpd_policy_opts *popts = (Httpd_policy_opts *)sopts;
Vstr_base *nhn = popts->default_hostname;
Vstr_base *chn = NULL;
if (!httpd_valid_url_filename(nhn, 1, nhn->len))
vstr_del(nhn, 1, nhn->len);
if (nhn->len)
return (TRUE);
if (sopts != sopts->beg->def_policy)
chn = ((Httpd_policy_opts *)sopts->beg->def_policy)->default_hostname;
if (chn)
HTTPD_APP_REF_ALLVSTR(nhn, chn);
else
{
char buf[256];
if (gethostname(buf, sizeof(buf)) == -1)
err(EXIT_FAILURE, "gethostname");
buf[sizeof(buf) - 1] = 0;
vstr_add_cstr_buf(nhn, 0, buf);
vstr_conv_lowercase(nhn, 1, nhn->len);
}
return (!nhn->conf->malloc_bad);
}
static int httpd__chk_vhost(const Httpd_policy_opts *popts,
Vstr_base *lfn, size_t pos, size_t len)
{
const char *vhost = NULL;
struct stat64 v_stat[1];
const Vstr_base *def_hname = popts->default_hostname;
int ret = -1;
ASSERT(pos);
if (!popts->use_host_err_chk)
return (TRUE);
if (vstr_cmp_eq(lfn, pos, len, def_hname, 1, def_hname->len))
return (TRUE);
vstr_add_vstr(lfn, pos - 1,
popts->document_root, 1, popts->document_root->len,
VSTR_TYPE_ADD_BUF_PTR);
len += popts->document_root->len;
if (lfn->conf->malloc_bad || !(vhost = vstr_export_cstr_ptr(lfn, pos, len)))
return (TRUE);
ret = stat64(vhost, v_stat);
vstr_del(lfn, pos, popts->document_root->len);
if (ret == -1)
return (FALSE);
if (!S_ISDIR(v_stat->st_mode))
return (FALSE);
return (TRUE);
}
static int httpd_serv_add_vhost(struct Con *con, struct Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
Vstr_base *fname = req->fname;
Vstr_sect_node *h_h = req->http_hdrs->hdr_host;
size_t h_h_pos = h_h->pos;
size_t h_h_len = h_h->len;
size_t orig_len = 0;
if (!req->policy->use_vhosts_name)
return (TRUE);
if (h_h_len && req->policy->use_canonize_host)
{
size_t dots = 0;
if (VIPREFIX(data, h_h_pos, h_h_len, "www."))
{ h_h_len -= CLEN("www."); h_h_pos += CLEN("www."); }
dots = vstr_spn_cstr_chrs_rev(data, h_h_pos, h_h_len, ".");
h_h_len -= dots;
}
h_h->pos = h_h_pos;
h_h->len = h_h_len;
orig_len = fname->len;
if (!h_h_len)
httpd_sc_add_default_hostname(con, req, fname, 0);
else if (vstr_add_vstr(fname, 0, data,
h_h_pos, h_h_len, VSTR_TYPE_ADD_DEF))
{
vstr_conv_lowercase(fname, 1, h_h_len);
if (!httpd__chk_vhost(req->policy, fname, 1, h_h_len))
{
if (req->policy->use_host_err_400)
HTTPD_ERR_RET(req, 400, FALSE);
else
{
if (fname->conf->malloc_bad)
return (TRUE);
h_h->len = 0;
vstr_del(fname, 1, h_h_len);
httpd_sc_add_default_hostname(con, req, fname, 0);
}
}
}
vstr_add_cstr_ptr(fname, 0, "/");
req->vhost_prefix_len = (fname->len - orig_len);
return (TRUE);
}
static int http_req_make_path(struct Con *con, Httpd_req_data *req)
{
Vstr_base *data = con->evnt->io_r;
Vstr_base *fname = req->fname;
ASSERT(!fname->len);
assert(VPREFIX(data, req->path_pos, req->path_len, "/") ||
VEQ(data, req->path_pos, req->path_len, "*"));
if (req->chk_encoded_slash &&
vstr_srch_case_cstr_buf_fwd(data, req->path_pos, req->path_len, "%2f"))
HTTPD_ERR_RET(req, 403, FALSE);
if (req->chk_encoded_dot &&
vstr_srch_case_cstr_buf_fwd(data, req->path_pos, req->path_len, "%2e"))
HTTPD_ERR_RET(req, 403, FALSE);
req->chked_encoded_path = TRUE;
vstr_add_vstr(fname, 0,
data, req->path_pos, req->path_len, VSTR_TYPE_ADD_BUF_PTR);
vstr_conv_decode_uri(fname, 1, fname->len);
if (fname->conf->malloc_bad)
return (TRUE);
if (!httpd_serv_add_vhost(con, req))
return (FALSE);
if (vstr_srch_chr_fwd(fname, 1, fname->len, 0))
HTTPD_ERR_RET(req, 403, FALSE);
if (vstr_srch_cstr_buf_fwd(fname, 1, fname->len, "/../") ||
VSUFFIX(req->fname, 1, req->fname->len, "/.."))
HTTPD_ERR_RET(req, 400, FALSE);
if (req->policy->chk_dot_dir &&
(vstr_srch_cstr_buf_fwd(fname, 1, fname->len, "/./") ||
VSUFFIX(req->fname, 1, req->fname->len, "/.")))
HTTPD_ERR_RET(req, 400, FALSE);
ASSERT(fname->len);
assert(VPREFIX(fname, 1, fname->len, "/") ||
VEQ(fname, 1, fname->len, "*") ||
fname->conf->malloc_bad);
if (fname->conf->malloc_bad)
return (TRUE);
return (TRUE);
}
static int http_parse_wait_io_r(struct Con *con)
{
if (con->evnt->io_r_shutdown)
return (!!con->evnt->io_w->len);
evnt_fd_set_cork(con->evnt, FALSE);
return (TRUE);
}
static int httpd_serv__parse_no_req(struct Con *con, struct Httpd_req_data *req)
{
if (req->policy->max_header_sz &&
(con->evnt->io_r->len > req->policy->max_header_sz))
HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
http_req_free(req);
return (http_parse_wait_io_r(con));
}
static int http__parse_req_all(struct Con *con, struct Httpd_req_data *req,
const char *eol, int *ern)
{
Vstr_base *data = con->evnt->io_r;
ASSERT(eol && ern);
*ern = FALSE;
if (!(req->len = vstr_srch_cstr_buf_fwd(data, 1, data->len, eol)))
goto no_req;
if (req->len == 1)
{
while (VPREFIX(data, 1, data->len, HTTP_EOL))
vstr_del(data, 1, CLEN(HTTP_EOL));
if (!(req->len = vstr_srch_cstr_buf_fwd(data, 1, data->len, eol)))
goto no_req;
ASSERT(req->len > 1);
}
req->len += CLEN(eol) - 1;
return (TRUE);
no_req:
*ern = httpd_serv__parse_no_req(con, req);
return (FALSE);
}
static int http_parse_req(struct Con *con)
{
Vstr_base *data = con->evnt->io_r;
struct Httpd_req_data *req = NULL;
int ern_req_all = FALSE;
ASSERT(con->fs && !con->fs_num);
if (!data->len)
return (http_parse_wait_io_r(con));
if (!(req = http_req_make(con)))
return (FALSE);
if (con->parsed_method_ver_1_0)
{
if (!http__parse_req_all(con, req, HTTP_END_OF_REQUEST, &ern_req_all))
return (ern_req_all);
}
else
{
if (!http__parse_req_all(con, req, HTTP_EOL, &ern_req_all))
return (ern_req_all);
}
con->keep_alive = HTTP_NON_KEEP_ALIVE;
http_req_split_method(con, req);
if (req->sects->malloc_bad)
VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "split: %m\n"));
else if (req->sects->num < 2)
HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
else
{
size_t op_pos = 0;
size_t op_len = 0;
if (req->ver_0_9)
vlg_dbg1(vlg, "Method(0.9):"
" $<http-esc.vstr.sect:%p%p%u> $<http-esc.vstr.sect:%p%p%u>\n",
con->evnt->io_r, req->sects, 1U,
con->evnt->io_r, req->sects, 2U);
else
{
if (!con->parsed_method_ver_1_0)
{
con->parsed_method_ver_1_0 = TRUE;
if (!http__parse_req_all(con, req, HTTP_END_OF_REQUEST, &ern_req_all))
return (ern_req_all);
}
vlg_dbg1(vlg, "Method(1.x):"
" $<http-esc.vstr.sect:%p%p%u> $<http-esc.vstr.sect:%p%p%u>"
" $<http-esc.vstr.sect:%p%p%u>\n", data, req->sects, 1U,
data, req->sects, 2U, data, req->sects, 3U);
http_req_split_hdrs(con, req);
}
evnt_got_pkt(con->evnt);
if (HTTP_CONF_SAFE_PRINT_REQ)
vlg_dbg3(vlg, "REQ:\n$<vstr.hexdump:%p%zu%zu>",
data, (size_t)1, data->len);
else
vlg_dbg3(vlg, "REQ:\n$<vstr.all:%p>", data);
assert(((req->sects->num >= 3) && !req->ver_0_9) || (req->sects->num == 2));
op_pos = VSTR_SECTS_NUM(req->sects, 1)->pos;
op_len = VSTR_SECTS_NUM(req->sects, 1)->len;
req->path_pos = VSTR_SECTS_NUM(req->sects, 2)->pos;
req->path_len = VSTR_SECTS_NUM(req->sects, 2)->len;
if (!req->ver_0_9 && !http__parse_1_x(con, req))
{
if (req->error_code == 500)
return (http_fin_errmem_req(con, req));
return (http_fin_err_req(con, req));
}
if (!http_parse_host(con, req))
HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
if (0) { }
else if (VEQ(data, op_pos, op_len, "GET"))
{
if (!VPREFIX(data, req->path_pos, req->path_len, "/"))
HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
if (!http_req_make_path(con, req))
return (http_fin_err_req(con, req));
return (http_req_op_get(con, req));
}
else if (req->ver_0_9)
HTTPD_ERR_RET(req, 501, http_fin_err_req(con, req));
else if (VEQ(data, op_pos, op_len, "HEAD"))
{
req->head_op = TRUE;
if (!VPREFIX(data, req->path_pos, req->path_len, "/"))
HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
if (!http_req_make_path(con, req))
return (http_fin_err_req(con, req));
return (http_req_op_get(con, req));
}
else if (VEQ(data, op_pos, op_len, "OPTIONS"))
{
if (!VPREFIX(data, req->path_pos, req->path_len, "/") &&
!VEQ(data, req->path_pos, req->path_len, "*"))
HTTPD_ERR_RET(req, 400, http_fin_err_req(con, req));
if (req->policy->use_vhosts_name &&
req->policy->use_host_err_chk && req->policy->use_host_err_400 &&
!VEQ(data, req->path_pos, req->path_len, "*") &&
!http_req_make_path(con, req))
return (http_fin_err_req(con, req));
return (http_req_op_opts(con, req));
}
else if (req->policy->use_trace_op && VEQ(data, op_pos, op_len, "TRACE"))
return (http_req_op_trace(con, req));
else if (VEQ(data, op_pos, op_len, "TRACE") ||
VEQ(data, op_pos, op_len, "POST") ||
VEQ(data, op_pos, op_len, "PUT") ||
VEQ(data, op_pos, op_len, "DELETE") ||
VEQ(data, op_pos, op_len, "CONNECT") ||
FALSE)
HTTPD_ERR_RET(req, 405, http_fin_err_req(con, req));
else
HTTPD_ERR_RET(req, 501, http_fin_err_req(con, req));
}
ASSERT_NOT_REACHED();
}
static int httpd__serv_send_err(struct Con *con, const char *msg)
{
if (errno != EPIPE)
vlg_warn(vlg, "send(%s): %m\n", msg);
else
vlg_dbg2(vlg, "send(%s): SIGPIPE $<sa:%p>\n", msg, CON_CEVNT_SA(con));
return (FALSE);
}
static int httpd_serv_q_send(struct Con *con)
{
vlg_dbg2(vlg, "http Q send $<sa:%p>\n", CON_CEVNT_SA(con));
if (!evnt_send_add(con->evnt, TRUE, HTTPD_CONF_MAX_WAIT_SEND))
return (httpd__serv_send_err(con, "Q"));
return (TRUE);
}
static int httpd__serv_fin_send(struct Con *con)
{
if (con->keep_alive)
{
if (!con->evnt->io_r_shutdown)
evnt_wait_cntl_add(con->evnt, POLLIN);
return (http_parse_req(con));
}
vlg_dbg2(vlg, "shutdown_w = %p\n", con->evnt);
return (evnt_shutdown_w(con->evnt));
}
static int httpd__serv_send_lim(struct Con *con, const char *emsg,
unsigned int lim, int *cont)
{
Vstr_base *out = con->evnt->io_w;
*cont = FALSE;
while (out->len >= lim)
{
if (!con->io_limit_num--) return (httpd_serv_q_send(con));
if (!evnt_send(con->evnt))
return (httpd__serv_send_err(con, emsg));
}
*cont = TRUE;
return (TRUE);
}
int httpd_serv_send(struct Con *con)
{
Vstr_base *out = con->evnt->io_w;
int cont = FALSE;
int ret = FALSE;
struct File_sect *fs = NULL;
ASSERT(!out->conf->malloc_bad);
if (!con->fs_num)
{
ASSERT(!con->fs_off);
ret = httpd__serv_send_lim(con, "end", 1, &cont);
if (!cont)
return (ret);
return (httpd__serv_fin_send(con));
}
ASSERT(con->fs && (con->fs_off < con->fs_num) && (con->fs_num <= con->fs_sz));
fs = &con->fs[con->fs_off];
ASSERT((fs->fd != -1) && fs->len);
if (con->use_sendfile)
{
unsigned int ern = 0;
ret = httpd__serv_send_lim(con, "sendfile", 1, &cont);
if (!cont)
return (ret);
while (fs->len)
{
if (!con->io_limit_num--) return (httpd_serv_q_send(con));
if (!evnt_sendfile(con->evnt, fs->fd, &fs->off, &fs->len, &ern))
{
if (ern == VSTR_TYPE_SC_READ_FD_ERR_EOF)
goto file_eof_end;
if (errno == EPIPE)
{
vlg_dbg2(vlg, "sendfile: SIGPIPE $<sa:%p>\n", CON_CEVNT_SA(con));
return (FALSE);
}
if (errno == ENOSYS)
httpd__disable_sendfile();
vlg_warn(vlg, "sendfile: %m\n");
if (lseek64(fs->fd, fs->off, SEEK_SET) == -1)
VLG_WARN_RET(FALSE,
(vlg, "lseek(<sendfile>,off=%ju): %m\n", fs->off));
con->use_sendfile = FALSE;
return (httpd_serv_send(con));
}
}
goto file_end;
}
while (fs->len)
{
ret = httpd__serv_send_lim(con, "max", EX_MAX_W_DATA_INCORE, &cont);
if (!cont)
return (ret);
switch (evnt_sc_read_send(con->evnt, fs->fd, &fs->len))
{
case EVNT_IO_OK:
ASSERT_NO_SWITCH_DEF();
case EVNT_IO_READ_ERR:
vlg_warn(vlg, "read: %m\n");
out->conf->malloc_bad = FALSE;
case EVNT_IO_READ_FIN: goto file_end;
case EVNT_IO_READ_EOF: goto file_eof_end;
case EVNT_IO_SEND_ERR:
return (httpd__serv_send_err(con, "io_get"));
}
}
ASSERT_NOT_REACHED();
file_end:
ASSERT(!fs->len);
if (con->use_mpbr)
{
ASSERT(con->mpbr_ct);
ASSERT(con->mpbr_fs_len);
if (!(fs = httpd__fd_next(con)))
{
vstr_add_cstr_ptr(out, out->len, "--SEP--" HTTP_EOL);
return (httpd_serv_send(con));
}
con->use_mmap = FALSE;
if (!httpd__serv_call_seek(con, fs))
VLG_WARN_RET(FALSE, (vlg, "lseek(<mpbr>,off=%ju): %m\n", fs->off));
http_app_hdrs_mpbr(con, fs);
return (httpd_serv_send(con));
}
file_eof_end:
if (fs->len)
con->keep_alive = HTTP_NON_KEEP_ALIVE;
httpd_fin_fd_close(con);
return (httpd_serv_send(con));
}
int httpd_serv_recv(struct Con *con)
{
unsigned int ern = 0;
int ret = 0;
Vstr_base *data = con->evnt->io_r;
ASSERT(!con->evnt->io_r_shutdown);
if (!con->io_limit_num--)
return (TRUE);
if (!(ret = evnt_recv(con->evnt, &ern)))
{
if (ern != VSTR_TYPE_SC_READ_FD_ERR_EOF)
{
vlg_dbg2(vlg, "RECV ERR from[$<sa:%p>]: %u\n", CON_CEVNT_SA(con), ern);
goto con_cleanup;
}
if (!evnt_shutdown_r(con->evnt, TRUE))
goto con_cleanup;
}
if (con->fs_num)
{
ASSERT(con->keep_alive || con->parsed_method_ver_1_0);
if (con->policy->max_header_sz && (data->len > con->policy->max_header_sz))
evnt_wait_cntl_del(con->evnt, POLLIN);
return (TRUE);
}
if (http_parse_req(con))
return (TRUE);
con_cleanup:
con->evnt->io_r->conf->malloc_bad = FALSE;
con->evnt->io_w->conf->malloc_bad = FALSE;
return (FALSE);
}
int httpd_con_init(struct Con *con, struct Acpt_listener *acpt_listener)
{
int ret = TRUE;
con->mpbr_ct = NULL;
con->fs = con->fs_store;
con->fs->len = 0;
con->fs->fd = -1;
con->fs_off = 0;
con->fs_num = 0;
con->fs_sz = 1;
con->vary_star = FALSE;
con->keep_alive = HTTP_NON_KEEP_ALIVE;
con->acpt_sa_ref = vstr_ref_add(acpt_listener->ref);
con->use_mpbr = FALSE;
con->use_mmap = FALSE;
con->parsed_method_ver_1_0 = FALSE;
httpd_policy_change_con(con, (Httpd_policy_opts *)httpd_opts->s->def_policy);
if (!httpd_policy_connection(con,
httpd_opts->conf, httpd_opts->match_connection))
{
Vstr_base *s1 = con->policy->s->policy_name;
Vstr_base *s2 = httpd_opts->conf->tmp;
vlg_info(vlg, "CONF-MAIN-ERR from[$<sa:%p>]: policy $<vstr.all:%p>"
" backtrace: $<vstr.all:%p>\n", CON_CEVNT_SA(con), s1, s2);
ret = FALSE;
}
else if (con->evnt->flag_q_closed)
{
Vstr_base *s1 = con->policy->s->policy_name;
vlg_info(vlg, "BLOCKED from[$<sa:%p>]: policy $<vstr.all:%p>\n",
CON_CEVNT_SA(con), s1);
ret = FALSE;
}
return (ret);
}