fixed many bugs http server code

This commit is contained in:
hyung-hwan 2022-12-26 17:03:53 +00:00
parent 56bd1af653
commit f06fe3fc4d
5 changed files with 130 additions and 69 deletions

View File

@ -1,6 +1,7 @@
#include <hio-http.h>
#include <hio-tar.h>
#include <hio-opt.h>
#include <hio-prv.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
@ -31,6 +32,7 @@ void untar (hio_t* hio, hio_dev_thr_iopair_t* iop, hio_svc_htts_thr_func_info_t*
ext = hio_svc_htts_getxtn(tfi->htts);
/* TODO: error handling on write() failure */
wfp = fdopen(iop->wfd, "w");
if (!wfp)
{
@ -74,13 +76,23 @@ done:
}
}
static void bfmt_dir (hio_svc_htts_t* htts, int fd, const hio_bch_t* name, int type, void* ctx)
static void bfmt_dir (hio_svc_htts_t* htts, int fd, hio_svc_htts_file_bfmt_dir_type_t type, const hio_bch_t* name, void* ctx)
{
/* TODO: do bufferring */
/* "<a href="urlencoded-name">name</a> */
if (name)
if (type == HIO_SVC_HTTS_FILE_BFMT_DIR_HEADER)
{
write (fd, "<html><body>", 12);
}
else if (type == HIO_SVC_HTTS_FILE_BFMT_DIR_FOOTER)
{
write (fd, "</body></html>", 14);
}
else
{
HIO_ASSERT (hio_svc_htts_gethio(htts), name != HIO_NULL);
/* TODO: get the directory name
check if the entry is a directory or something else */
write (fd, "<li><a href=\"", 13);
@ -89,15 +101,6 @@ check if the entry is a directory or something else */
write (fd, name, strlen(name));
write (fd, "</a>", 4);
}
else if (type == 0)
{
write (fd, "<html><body>", 12);
}
else
{
write (fd, "</body></html>", 14);
}
}
static int process_http_request (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req)
@ -194,7 +197,7 @@ static int process_args (int argc, char* argv[], arg_info_t* ai)
static hio_bopt_t opt =
{
"",
&lopt
lopt
};
hio_bci_t c;

View File

@ -242,9 +242,13 @@ struct hio_htre_t
#define hio_htre_getqmethodname(re) ((re)->u.q.method.name)
#define hio_htre_getqpath(re) ((re)->u.q.path.ptr)
#define hio_htre_getqpathlen(re) ((re)->u.q.path.len)
#define hio_htre_getqparam(re) ((re)->u.q.param.ptr)
#define hio_htre_getqparamlen(re) ((re)->u.q.param.len)
#define hio_htre_getqanchor(re) ((re)->u.q.anchor.ptr)
#define hio_htre_getqanchorlen(re) ((re)->u.q.anchor.len)
#define hio_htre_getorgqpath(re) ((re)->orgqpath.ptr)
#define hio_htre_getorgqpathlen(re) ((re)->orgqpath.ptr)
#define hio_htre_getscodeval(re) ((re)->u.s.code.val)
#define hio_htre_getscodestr(re) ((re)->u.s.code.str)

View File

@ -161,9 +161,17 @@ enum hio_svc_htts_txt_option_t
};
#endif
enum hio_svc_htts_file_bfmt_dir_type_t
{
HIO_SVC_HTTS_FILE_BFMT_DIR_HEADER,
HIO_SVC_HTTS_FILE_BFMT_DIR_ENTRY,
HIO_SVC_HTTS_FILE_BFMT_DIR_FOOTER
};
typedef enum hio_svc_htts_file_bfmt_dir_type_t hio_svc_htts_file_bfmt_dir_type_t;
struct hio_svc_htts_file_cbs_t
{
int (*bfmt_dir) (hio_svc_htts_t* htts, int fd, const hio_bch_t* name, int type, void* ctx);
int (*bfmt_dir) (hio_svc_htts_t* htts, int fd, hio_svc_htts_file_bfmt_dir_type_t type, const hio_bch_t* name, void* ctx);
void *ctx;
};
typedef struct hio_svc_htts_file_cbs_t hio_svc_htts_file_cbs_t;

View File

@ -73,10 +73,12 @@ struct file_t
hio_svc_htts_cli_t* client;
hio_http_version_t req_version; /* client request */
hio_http_method_t req_method;
hio_bch_t* req_qpath;
unsigned int over: 4; /* must be large enough to accomodate FILE_OVER_ALL */
unsigned int keep_alive: 1;
unsigned int req_content_length_unlimited: 1;
unsigned int req_qpath_ending_with_slash: 1;
unsigned int ever_attempted_to_write_to_client: 1;
unsigned int client_eof_detected: 1;
unsigned int client_disconnected: 1;
@ -134,7 +136,7 @@ static int file_sendfile_to_client (file_t* file, hio_foff_t foff, hio_iolen_t l
return 0;
}
static int file_send_final_status_to_client (file_t* file, int status_code, int force_close)
static int file_send_final_status_to_client (file_t* file, int status_code, int force_close, int dir_redirect)
{
hio_svc_htts_cli_t* cli = file->client;
hio_bch_t dtbuf[64];
@ -144,12 +146,22 @@ static int file_send_final_status_to_client (file_t* file, int status_code, int
status_msg = hio_http_status_to_bcstr(status_code);
if (!force_close) force_close = !file->keep_alive;
if (hio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: %hs\r\nContent-Length: %zu\r\n\r\n%s",
if (hio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: %hs\r\nContent-Length: %zu\r\n",
file->req_version.major, file->req_version.minor,
status_code, status_msg,
cli->htts->server_name, dtbuf,
(force_close? "close": "keep-alive"),
hio_count_bcstr(status_msg), status_msg) == (hio_oow_t)-1) return -1;
hio_count_bcstr(status_msg)) == (hio_oow_t)-1) return -1;
if (dir_redirect)
{
/* don't send content body when the status code is 3xx. include the Location header only. */
if (hio_becs_fcat(cli->sbuf, "Location: %hs/\r\n\r\n", file->req_qpath) == (hio_oow_t)-1) return -1;
}
else
{
if (hio_becs_fcat(cli->sbuf, "\r\n%hs", status_msg) == (hio_oow_t)-1) return -1;
}
return (file_write_to_client(file, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)) <= -1 ||
(force_close && file_write_to_client(file, HIO_NULL, 0) <= -1))? -1: 0;
@ -266,6 +278,12 @@ static void file_on_kill (file_t* file)
file_close_peer (file);
if (file->req_qpath)
{
hio_freemem (hio, file->req_qpath);
file->req_qpath = HIO_NULL;
}
if (file->client_org_on_read)
{
file->client->sck->on_read = file->client_org_on_read;
@ -441,7 +459,7 @@ static int file_client_htrd_poke (hio_htrd_t* htrd, hio_htre_t* req)
if (file->req_method != HIO_HTTP_GET)
{
if (file_send_final_status_to_client(file, HIO_HTTP_STATUS_OK, 0) <= -1) return -1;
if (file_send_final_status_to_client(file, HIO_HTTP_STATUS_OK, 0, 0) <= -1) return -1;
}
file_mark_over (file, FILE_OVER_READ_FROM_CLIENT);
@ -491,8 +509,11 @@ static int file_send_header_to_client (file_t* file, int status_code, int force_
if (mime_type && mime_type[0] != '\0' &&
hio_becs_fcat(cli->sbuf, "Content-Type: %hs\r\n", mime_type) == (hio_oow_t)-1) return -1;
if (file->req_method == HIO_HTTP_GET && hio_becs_fcat(cli->sbuf, "ETag: %hs\r\n", file->peer_etag) == (hio_oow_t)-1) return -1;
if (status_code == HIO_HTTP_STATUS_PARTIAL_CONTENT && hio_becs_fcat(cli->sbuf, "Content-Ranges: bytes %ju-%ju/%ju\r\n", (hio_uintmax_t)file->start_offset, (hio_uintmax_t)file->end_offset, (hio_uintmax_t)file->total_size) == (hio_oow_t)-1) return -1;
if ((file->req_method == HIO_HTTP_GET || file->req_method == HIO_HTTP_HEAD) &&
hio_becs_fcat(cli->sbuf, "ETag: %hs\r\n", file->peer_etag) == (hio_oow_t)-1) return -1;
if (status_code == HIO_HTTP_STATUS_PARTIAL_CONTENT &&
hio_becs_fcat(cli->sbuf, "Content-Ranges: bytes %ju-%ju/%ju\r\n", (hio_uintmax_t)file->start_offset, (hio_uintmax_t)file->end_offset, (hio_uintmax_t)file->total_size) == (hio_oow_t)-1) return -1;
/* ----- */
// TODO: Allow-Contents
@ -574,7 +595,7 @@ static int file_send_contents_to_client (file_t* file)
}
#define ERRNO_TO_STATUS_CODE(x) ( \
((x) == ENOENT)? HIO_HTTP_STATUS_NOT_FOUND: \
((x) == ENOENT || (x) == ENOTDIR)? HIO_HTTP_STATUS_NOT_FOUND: \
((x) == EPERM || (x) == EACCES)? HIO_HTTP_STATUS_FORBIDDEN: HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR \
)
@ -597,28 +618,26 @@ static HIO_INLINE int process_range_header (file_t* file, hio_htre_t* req, int*
return -1;
}
if (file->req_method == HIO_HTTP_GET)
{
#if defined(HAVE_STRUCT_STAT_MTIM)
etag_len = hio_fmt_uintmax_to_bcstr(&file->peer_etag[0], HIO_COUNTOF(file->peer_etag), st.st_mtim.tv_sec, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
#if defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
etag_len += hio_fmt_uintmax_to_bcstr(&file->peer_etag[etag_len], HIO_COUNTOF(file->peer_etag), st.st_mtim.tv_nsec, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
#endif
#else
etag_len = hio_fmt_uintmax_to_bcstr(&file->peer_etag[0], HIO_COUNTOF(file->peer_etag), st.st_mtime, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
#if defined(HAVE_STRUCT_STAT_MTIM)
etag_len = hio_fmt_uintmax_to_bcstr(&file->peer_etag[0], HIO_COUNTOF(file->peer_etag), st.st_mtim.tv_sec, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
#if defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
etag_len += hio_fmt_uintmax_to_bcstr(&file->peer_etag[etag_len], HIO_COUNTOF(file->peer_etag), st.st_mtim.tv_nsec, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
#endif
etag_len += hio_fmt_uintmax_to_bcstr(&file->peer_etag[etag_len], HIO_COUNTOF(file->peer_etag) - etag_len, st.st_size, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
etag_len += hio_fmt_uintmax_to_bcstr(&file->peer_etag[etag_len], HIO_COUNTOF(file->peer_etag) - etag_len, st.st_ino, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
hio_fmt_uintmax_to_bcstr (&file->peer_etag[etag_len], HIO_COUNTOF(file->peer_etag) - etag_len, st.st_dev, 16, -1, '\0', HIO_NULL);
#else
etag_len = hio_fmt_uintmax_to_bcstr(&file->peer_etag[0], HIO_COUNTOF(file->peer_etag), st.st_mtime, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
#endif
etag_len += hio_fmt_uintmax_to_bcstr(&file->peer_etag[etag_len], HIO_COUNTOF(file->peer_etag) - etag_len, st.st_size, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
etag_len += hio_fmt_uintmax_to_bcstr(&file->peer_etag[etag_len], HIO_COUNTOF(file->peer_etag) - etag_len, st.st_ino, 16, -1, '\0', HIO_NULL);
file->peer_etag[etag_len++] = '-';
hio_fmt_uintmax_to_bcstr (&file->peer_etag[etag_len], HIO_COUNTOF(file->peer_etag) - etag_len, st.st_dev, 16, -1, '\0', HIO_NULL);
tmp = hio_htre_getheaderval(req, "If-None-Match");
if (tmp && hio_comp_bcstr(file->peer_etag, tmp->ptr, 0) == 0) file->etag_match = 1;
tmp = hio_htre_getheaderval(req, "If-None-Match");
if (tmp && hio_comp_bcstr(file->peer_etag, tmp->ptr, 0) == 0) file->etag_match = 1;
}
file->end_offset = st.st_size;
tmp = hio_htre_getheaderval(req, "Range"); /* TODO: support multiple ranges? */
@ -703,6 +722,14 @@ static int open_peer_with_mode (file_t* file, const hio_bch_t* actual_file, int
hio_bch_t* alt_file;
int alt_fd;
if (!file->req_qpath_ending_with_slash)
{
*error_status = HIO_HTTP_STATUS_MOVED_PERMANENTLY;
close (file->peer);
file->peer = -1;
return -1;
}
alt_file = hio_svc_htts_dupmergepaths(file->htts, actual_file, "index.html"); /* TODO: make the default index files configurable */
if (alt_file)
{
@ -737,12 +764,12 @@ static int open_peer_with_mode (file_t* file, const hio_bch_t* actual_file, int
unlink (alt_file);
if (file->cbs && file->cbs->bfmt_dir)
{
file->cbs->bfmt_dir (file->htts, alt_fd, HIO_NULL, 0, file->cbs->ctx);
file->cbs->bfmt_dir (file->htts, alt_fd, HIO_SVC_HTTS_FILE_BFMT_DIR_HEADER, HIO_NULL, file->cbs->ctx);
while ((de = readdir(dp)))
{
file->cbs->bfmt_dir (file->htts, alt_fd, de->d_name, 1, file->cbs->ctx);
file->cbs->bfmt_dir (file->htts, alt_fd, HIO_SVC_HTTS_FILE_BFMT_DIR_ENTRY, de->d_name, file->cbs->ctx);
}
file->cbs->bfmt_dir (file->htts, alt_fd, HIO_NULL, 2, file->cbs->ctx);
file->cbs->bfmt_dir (file->htts, alt_fd, HIO_SVC_HTTS_FILE_BFMT_DIR_FOOTER, HIO_NULL, file->cbs->ctx);
}
else
{
@ -809,6 +836,8 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t*
hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(csck);
file_t* file = HIO_NULL;
hio_bch_t* actual_file = HIO_NULL;
hio_bch_t* req_qpath = HIO_NULL;
int dir_redirect = 0;
int status_code;
/* ensure that you call this function before any contents is received */
@ -817,6 +846,10 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t*
HIO_DEBUG5 (hio, "HTTS(%p) - file(c=%d) - [%hs] %hs%hs\n", htts, (int)csck->hnd, cli->cli_addr_bcstr, (docroot[0] == '/' && docroot[1] == '\0' && filepath[0] == '/'? "": docroot), filepath);
/* TODO: may have to use getorgqpath() for the percent-decoding... */
req_qpath = hio_dupbcstr(hio, hio_htre_getqpath(req), HIO_NULL);
if (HIO_UNLIKELY(!req_qpath)) goto oops;
actual_file = hio_svc_htts_dupmergepaths(htts, docroot, filepath);
if (HIO_UNLIKELY(!actual_file)) goto oops;
@ -832,6 +865,9 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t*
file->req_version = *hio_htre_getversion(req);
file->req_method = hio_htre_getqmethodtype(req);
file->req_content_length_unlimited = hio_htre_getreqcontentlen(req, &file->req_content_length);
file->req_qpath = req_qpath;
req_qpath = HIO_NULL; /* delegated to the file resource */
file->req_qpath_ending_with_slash = (hio_htre_getqpathlen(req) > 1 && hio_htre_getqpath(req)[hio_htre_getqpathlen(req) - 1] == '/');
file->client_org_on_read = csck->on_read;
file->client_org_on_write = csck->on_write;
@ -840,11 +876,12 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t*
csck->on_write = file_client_on_write;
csck->on_disconnect = file_client_on_disconnect;
file->peer_tmridx = HIO_TMRIDX_INVALID;
file->peer = -1;
HIO_ASSERT (hio, cli->rsrc == HIO_NULL); /* you must not call this function while cli->rsrc is not HIO_NULL */
HIO_SVC_HTTS_RSRC_ATTACH (file, cli->rsrc); /* cli->rsrc = file with ref-count up */
file->peer_tmridx = HIO_TMRIDX_INVALID;
file->peer = -1;
#if !defined(FILE_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
if (file->req_content_length_unlimited)
@ -854,7 +891,7 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t*
/* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large.
* option 2. send 411 Length Required immediately
* option 3. set Content-Length to -1 and use EOF to indicate the end of content [Non-Standard] */
if (file_send_final_status_to_client(file, HIO_HTTP_STATUS_LENGTH_REQUIRED, 1) <= -1) goto oops;
if (file_send_final_status_to_client(file, HIO_HTTP_STATUS_LENGTH_REQUIRED, 1, 0) <= -1) goto oops;
}
#endif
@ -876,7 +913,7 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t*
else if (req->flags & HIO_HTRE_ATTR_EXPECT)
{
/* 417 Expectation Failed */
file_send_final_status_to_client (file, HIO_HTTP_STATUS_EXPECTATION_FAILED, 1);
file_send_final_status_to_client (file, HIO_HTTP_STATUS_EXPECTATION_FAILED, 1, HIO_NULL);
goto oops;
}
@ -932,36 +969,43 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t*
switch (file->req_method)
{
case HIO_HTTP_GET:
case HIO_HTTP_HEAD:
{
const hio_bch_t* actual_mime_type = mime_type;
if (open_peer_with_mode(file, actual_file, O_RDONLY, &status_code, (mime_type? HIO_NULL: &actual_mime_type)) <= -1) goto done_with_status_code;
if (process_range_header(file, req, &status_code) <= -1) goto done_with_status_code;
if (file->etag_match)
if (open_peer_with_mode(file, actual_file, O_RDONLY, &status_code, (mime_type? HIO_NULL: &actual_mime_type)) <= -1)
{
status_code = HIO_HTTP_STATUS_NOT_MODIFIED;
dir_redirect = (status_code == HIO_HTTP_STATUS_MOVED_PERMANENTLY); /* to handle the special rediret of a directory not ending with a slash in the request url */
goto done_with_status_code;
}
if (process_range_header(file, req, &status_code) <= -1) goto done_with_status_code;
/* normal full transfer */
#if defined(HAVE_POSIX_FADVISE)
posix_fadvise (file->peer, file->start_offset, file->end_offset - file->start_offset + 1, POSIX_FADV_SEQUENTIAL);
#endif
set_tcp_cork (file->client->sck);
if (HIO_LIKELY(file->req_method == HIO_HTTP_GET))
{
if (file->etag_match)
{
status_code = HIO_HTTP_STATUS_NOT_MODIFIED;
goto done_with_status_code;
}
if (file_send_header_to_client(file, HIO_HTTP_STATUS_OK, 0, actual_mime_type) <= -1 ||
file_send_contents_to_client(file) <= -1) goto oops;
/* normal full transfer */
#if defined(HAVE_POSIX_FADVISE)
posix_fadvise (file->peer, file->start_offset, file->end_offset - file->start_offset + 1, POSIX_FADV_SEQUENTIAL);
#endif
set_tcp_cork (file->client->sck);
if (file_send_header_to_client(file, HIO_HTTP_STATUS_OK, 0, actual_mime_type) <= -1) goto oops;
if (file_send_contents_to_client(file) <= -1) goto oops;
}
else
{
if (file_send_header_to_client(file, HIO_HTTP_STATUS_OK, 0, actual_mime_type) <= -1) goto oops;
/* no content must be transmitted for HEAD despite Content-Length in the header. */
goto done_with_status_code_2;
}
break;
}
case HIO_HTTP_HEAD:
if (open_peer_with_mode(file, actual_file, O_RDONLY, &status_code, HIO_NULL) <= -1) goto done_with_status_code;
if (process_range_header(file, req, &status_code) <= -1) goto done_with_status_code;
status_code = HIO_HTTP_STATUS_OK;
goto done_with_status_code;
case HIO_HTTP_POST:
case HIO_HTTP_PUT:
if (file->options & HIO_SVC_HTTS_FILE_READ_ONLY)
@ -998,7 +1042,8 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t*
default:
status_code = HIO_HTTP_STATUS_METHOD_NOT_ALLOWED;
done_with_status_code:
if (file_send_final_status_to_client(file, status_code, 0) <= -1) goto oops;
if (file_send_final_status_to_client(file, status_code, 0, dir_redirect) <= -1) goto oops;
done_with_status_code_2:
file_mark_over (file, FILE_OVER_READ_FROM_PEER | FILE_OVER_WRITE_TO_PEER);
break;
}
@ -1012,5 +1057,6 @@ oops:
HIO_DEBUG2 (hio, "HTTS(%p) - file(c=%d) failure\n", htts, csck->hnd);
if (file) file_halt_participating_devices (file);
if (actual_file) hio_freemem (hio, actual_file);
if (req_qpath) hio_freemem (hio, req_qpath);
return -1;
}

View File

@ -5,7 +5,7 @@
test_default_index()
{
local msg="hi-webs default index.html"
local msg="hio-webs default index.html"
local srvaddr=127.0.0.1:54321
local tmpdir="/tmp/s-001.$$"
@ -39,7 +39,7 @@ EOF
test_file_list_dir()
{
local msg="hi-webs file-list-dir"
local msg="hio-webs file-list-dir"
local srvaddr=127.0.0.1:54321
local tmpdir="/tmp/s-001.$$"