enhanced qse_httpd_entaskfile() and qse_httpd_entaskdir() to handle more methods.

added qse_httpd_entaskallow().
removed some unused functions.
This commit is contained in:
hyung-hwan 2013-03-28 09:43:59 +00:00
parent 595e3be043
commit 28119c7289
13 changed files with 667 additions and 385 deletions

View File

@ -466,6 +466,17 @@ static int query_server (
return 0; return 0;
case QSE_HTTPD_SERVERSTD_ROOT: case QSE_HTTPD_SERVERSTD_ROOT:
#if 0
if (qse_mbscmp (qse_htre_getqpath(req), QSE_MT("/version")) == 0)
{
/* return static text without inspecting further */
((qse_httpd_serverstd_root_t*)result)->type = QSE_HTTPD_SERVERSTD_ROOT_TEXT;
((qse_httpd_serverstd_root_t*)result)->u.text.ptr = QSE_MT(QSE_PACKAGE_NAME " " QSE_PACKAGE_VERSION);
((qse_httpd_serverstd_root_t*)result)->u.text.mime = QSE_MT("text/plain");
}
else
#endif
if (loccfg->root_is_nwad) if (loccfg->root_is_nwad)
{ {
((qse_httpd_serverstd_root_t*)result)->type = QSE_HTTPD_SERVERSTD_ROOT_NWAD; ((qse_httpd_serverstd_root_t*)result)->type = QSE_HTTPD_SERVERSTD_ROOT_NWAD;
@ -573,6 +584,15 @@ static int query_server (
case QSE_HTTPD_SERVERSTD_DIRACC: case QSE_HTTPD_SERVERSTD_DIRACC:
case QSE_HTTPD_SERVERSTD_FILEACC: case QSE_HTTPD_SERVERSTD_FILEACC:
{
switch (qse_htre_getqmethodtype(req))
{
case QSE_HTTP_OPTIONS:
case QSE_HTTP_HEAD:
case QSE_HTTP_GET:
case QSE_HTTP_POST:
case QSE_HTTP_PUT:
case QSE_HTTP_DELETE:
{ {
qse_size_t i; qse_size_t i;
const qse_mchar_t* xpath_base; const qse_mchar_t* xpath_base;
@ -598,6 +618,14 @@ static int query_server (
} }
} }
} }
break;
}
default:
*(int*)result = 405; /* method not allowed */
break;
}
return 0; return 0;
} }
} }

View File

@ -166,6 +166,7 @@ struct qse_httpd_scb_t
int (*stat) ( int (*stat) (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_t* httpd, const qse_mchar_t* path,
qse_httpd_stat_t* stat); qse_httpd_stat_t* stat);
int (*purge) (qse_httpd_t* httpd, const qse_mchar_t* path);
int (*ropen) ( int (*ropen) (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_t* httpd, const qse_mchar_t* path,
@ -185,6 +186,12 @@ struct qse_httpd_scb_t
struct struct
{ {
int (*stat) (
qse_httpd_t* httpd, const qse_mchar_t* path,
qse_httpd_stat_t* stat);
int (*make) (qse_httpd_t* httpd, const qse_mchar_t* path);
int (*purge) (qse_httpd_t* httpd, const qse_mchar_t* path);
int (*open) ( int (*open) (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_t* httpd, const qse_mchar_t* path,
qse_ubi_t* handle); qse_ubi_t* handle);
@ -808,6 +815,14 @@ QSE_EXPORT qse_httpd_task_t* qse_httpd_entasknomod (
qse_htre_t* req qse_htre_t* req
); );
QSE_EXPORT qse_httpd_task_t* qse_httpd_entaskallow (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* allow,
qse_htre_t* req
);
QSE_EXPORT qse_httpd_task_t* qse_httpd_entaskdir ( QSE_EXPORT qse_httpd_task_t* qse_httpd_entaskdir (
qse_httpd_t* httpd, qse_httpd_t* httpd,
qse_httpd_client_t* client, qse_httpd_client_t* client,

View File

@ -41,7 +41,8 @@ typedef void (*qse_httpd_serverstd_freersrc_t) (
enum qse_httpd_serverstd_root_type_t enum qse_httpd_serverstd_root_type_t
{ {
QSE_HTTPD_SERVERSTD_ROOT_PATH, QSE_HTTPD_SERVERSTD_ROOT_PATH,
QSE_HTTPD_SERVERSTD_ROOT_NWAD QSE_HTTPD_SERVERSTD_ROOT_NWAD,
QSE_HTTPD_SERVERSTD_ROOT_TEXT
}; };
typedef enum qse_httpd_serverstd_root_type_t qse_httpd_serverstd_root_type_t; typedef enum qse_httpd_serverstd_root_type_t qse_httpd_serverstd_root_type_t;
@ -57,6 +58,11 @@ struct qse_httpd_serverstd_root_t
qse_size_t rpl; /* replacement length */ qse_size_t rpl; /* replacement length */
} path; } path;
qse_nwad_t nwad; qse_nwad_t nwad;
struct
{
const qse_mchar_t* ptr;
const qse_mchar_t* mime;
} text;
} u; } u;
}; };

View File

@ -318,6 +318,12 @@
# define QSE_RENAME(oldpath,newpath) rename(oldpath,newpath) # define QSE_RENAME(oldpath,newpath) rename(oldpath,newpath)
#endif #endif
#if defined(SYS_mkdir) && defined(QSE_USE_SYSCALL)
# define QSE_MKDIR(path,mode) syscall(SYS_mkdir,path,mode)
#else
# define QSE_MKDIR(path,mode) mkdir(path,mode)
#endif
#if defined(SYS_rmdir) && defined(QSE_USE_SYSCALL) #if defined(SYS_rmdir) && defined(QSE_USE_SYSCALL)
# define QSE_RMDIR(path) syscall(SYS_rmdir,path) # define QSE_RMDIR(path) syscall(SYS_rmdir,path)
#else #else

View File

@ -128,6 +128,7 @@
{ \ { \
case ENOMEM: return __SYSERRNUM__ (obj2, ENOMEM); \ case ENOMEM: return __SYSERRNUM__ (obj2, ENOMEM); \
case EINVAL: return __SYSERRNUM__ (obj2, EINVAL); \ case EINVAL: return __SYSERRNUM__ (obj2, EINVAL); \
case EBUSY: \
case EACCES: return __SYSERRNUM__ (obj2, EACCES); \ case EACCES: return __SYSERRNUM__ (obj2, EACCES); \
case ENOTDIR: \ case ENOTDIR: \
case ENOENT: return __SYSERRNUM__ (obj2, ENOENT); \ case ENOENT: return __SYSERRNUM__ (obj2, ENOENT); \

View File

@ -50,6 +50,8 @@ struct task_cgi_t
const qse_mchar_t* suffix; const qse_mchar_t* suffix;
const qse_mchar_t* root; const qse_mchar_t* root;
const qse_mchar_t* shebang; const qse_mchar_t* shebang;
int method;
qse_http_version_t version; qse_http_version_t version;
int keepalive; /* taken from the request */ int keepalive; /* taken from the request */
int nph; int nph;
@ -746,6 +748,7 @@ static int task_init_cgi (
qse_mbscpy ((qse_mchar_t*)cgi->root, arg->root.ptr); qse_mbscpy ((qse_mchar_t*)cgi->root, arg->root.ptr);
qse_mbscpy ((qse_mchar_t*)cgi->shebang, arg->shebang.ptr); qse_mbscpy ((qse_mchar_t*)cgi->shebang, arg->shebang.ptr);
cgi->method = qse_htre_getqmethodtype(arg->req);
cgi->version = *qse_htre_getversion(arg->req); cgi->version = *qse_htre_getversion(arg->req);
cgi->keepalive = (arg->req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE); cgi->keepalive = (arg->req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
cgi->nph = arg->nph; cgi->nph = arg->nph;
@ -1355,7 +1358,7 @@ qse_printf (QSE_T("TRAILING DATA=[%.*hs]\n"), (int)QSE_MBS_LEN(cgi->res), QSE_MB
return 1; return 1;
oops: oops:
return (qse_httpd_entask_err (httpd, client, task, 500, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0; return (qse_httpd_entask_err (httpd, client, task, 500, cgi->method, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0;
} }
static int task_main_cgi ( static int task_main_cgi (
@ -1502,7 +1505,7 @@ oops:
return (qse_httpd_entask_err ( return (qse_httpd_entask_err (
httpd, client, task, http_errnum, httpd, client, task, http_errnum,
&cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0; cgi->method, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0;
} }
/* TODO: global option or individual paramter for max cgi lifetime /* TODO: global option or individual paramter for max cgi lifetime

View File

@ -30,7 +30,7 @@ struct task_dir_t
qse_mcstr_t qpath; qse_mcstr_t qpath;
qse_http_version_t version; qse_http_version_t version;
int keepalive; int keepalive;
int headonly; int method;
}; };
typedef struct task_dseg_t task_dseg_t; typedef struct task_dseg_t task_dseg_t;
@ -370,7 +370,7 @@ static qse_httpd_task_t* entask_directory_segment (
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
static int task_init_dir ( static int task_init_getdir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{ {
task_dir_t* xtn = qse_httpd_gettaskxtn (httpd, task); task_dir_t* xtn = qse_httpd_gettaskxtn (httpd, task);
@ -390,7 +390,7 @@ static int task_init_dir (
return 0; return 0;
} }
static QSE_INLINE int task_main_dir ( static QSE_INLINE int task_main_getdir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{ {
task_dir_t* dir; task_dir_t* dir;
@ -415,11 +415,27 @@ static QSE_INLINE int task_main_dir (
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500; (httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_err ( x = qse_httpd_entask_err (
httpd, client, x, http_errnum, httpd, client, x, http_errnum,
&dir->version, dir->keepalive); dir->method, &dir->version, dir->keepalive);
return (x == QSE_NULL)? -1: 0; return (x == QSE_NULL)? -1: 0;
} }
else else
{
if (dir->method == QSE_HTTP_HEAD)
{
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\n"),
dir->version.major, dir->version.minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(dir->keepalive? QSE_MT("keep-alive"): QSE_MT("close"))
);
httpd->opt.scb.dir.close (httpd, handle);
return (x == QSE_NULL)? -1: 0;
}
else
{ {
x = qse_httpd_entaskformat ( x = qse_httpd_entaskformat (
httpd, client, x, httpd, client, x,
@ -430,12 +446,16 @@ static QSE_INLINE int task_main_dir (
(dir->keepalive? QSE_MT("keep-alive"): QSE_MT("close")), (dir->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(dir->keepalive? QSE_MT("Transfer-Encoding: chunked\r\n"): QSE_MT("")) (dir->keepalive? QSE_MT("Transfer-Encoding: chunked\r\n"): QSE_MT(""))
); );
if (x) x = entask_directory_segment (httpd, client, x, handle, dir); if (x)
{
x = entask_directory_segment (httpd, client, x, handle, dir);
if (x) return 0; if (x) return 0;
}
httpd->opt.scb.dir.close (httpd, handle); httpd->opt.scb.dir.close (httpd, handle);
return -1; return -1;
} }
}
/* /*
} }
else else
@ -458,9 +478,6 @@ qse_httpd_task_t* qse_httpd_entaskdir (
{ {
qse_httpd_task_t task; qse_httpd_task_t task;
task_dir_t data; task_dir_t data;
int meth;
meth = qse_htre_getqmethodtype(req);
QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.path.ptr = path; data.path.ptr = path;
@ -469,28 +486,71 @@ qse_httpd_task_t* qse_httpd_entaskdir (
data.qpath.len = qse_mbslen(data.qpath.ptr); data.qpath.len = qse_mbslen(data.qpath.ptr);
data.version = *qse_htre_getversion(req); data.version = *qse_htre_getversion(req);
data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE); data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
data.method = qse_htre_getqmethodtype(req);
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
qse_htre_discardcontent (req); /* TODO: don't discard for put??? */ /* i don't need contents for directories */
switch (meth) qse_htre_discardcontent (req);
switch (data.method)
{ {
case QSE_HTTP_HEAD: case QSE_HTTP_OPTIONS:
data.headonly = 1; return qse_httpd_entaskallow (httpd, client, pred,
break; QSE_MT("OPTIONS,GET,HEAD,POST,PUT,DELETE"), req);
case QSE_HTTP_HEAD:
case QSE_HTTP_GET: case QSE_HTTP_GET:
case QSE_HTTP_POST: case QSE_HTTP_POST:
case QSE_HTTP_PUT: task.init = task_init_getdir;
task.main = task_main_getdir;
break; break;
case QSE_HTTP_PUT:
{
int status = 201; /* 201 Created */
if (httpd->opt.scb.dir.make (httpd, path) <= -1)
{
if (httpd->errnum == QSE_HTTPD_EEXIST)
{
/* an entry with the same name exists.
* if it is a directory, it's considered ok.
* if not, send '403 forbidden' indicating you can't
* change a file to a directory */
qse_httpd_stat_t st;
status = (httpd->opt.scb.dir.stat (httpd, path, &st) <= -1)? 403: 204;
}
else
{
status = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
}
}
return qse_httpd_entaskerr (httpd, client, pred, status, req);
}
case QSE_HTTP_DELETE:
{
int status = 200;
if (httpd->opt.scb.dir.purge (httpd, path) <= -1)
{
status = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
}
return qse_httpd_entaskerr (httpd, client, pred, status, req);
}
default: default:
/* Method not allowed */ /* Method not allowed */
return qse_httpd_entaskerr (httpd, client, pred, 405, req); return qse_httpd_entaskerr (httpd, client, pred, 405, req);
} }
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_dir;
task.main = task_main_dir;
task.ctx = &data; task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, return qse_httpd_entask (httpd, client, pred, &task,

View File

@ -26,29 +26,31 @@
#define ETAG_LEN_MAX 127 #define ETAG_LEN_MAX 127
#define PUTFILE_INIT_FAILED (1 << 0) #define PUTFILE_INIT_FAILED (1 << 0)
#define PUTFILE_WRITE_FAILED (1 << 1)
typedef struct task_file_t task_file_t; typedef struct task_file_t task_file_t;
struct task_file_t struct task_file_t
{ {
qse_mcstr_t path; qse_mcstr_t path;
qse_http_range_t range;
qse_mchar_t if_none_match[ETAG_LEN_MAX + 1];
qse_ntime_t if_modified_since;
qse_http_version_t version; qse_http_version_t version;
int keepalive; int keepalive;
int method; int method;
/* only for put file... */
union union
{ {
struct struct
{ {
qse_mcstr_t mime; qse_mcstr_t mime;
qse_mchar_t if_none_match[ETAG_LEN_MAX + 1];
qse_ntime_t if_modified_since;
qse_http_range_t range;
} get; } get;
struct struct
{ {
qse_httpd_t* httpd;
int flags; int flags;
int status;
qse_htre_t* req; qse_htre_t* req;
qse_ubi_t handle; qse_ubi_t handle;
} put; } put;
@ -91,7 +93,6 @@ static int task_main_getfseg (
count = MAX_SEND_SIZE; count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left; if (count >= ctx->left) count = ctx->left;
/* TODO: more adjustment needed for OS with different sendfile semantics... */
n = httpd->opt.scb.client.sendfile ( n = httpd->opt.scb.client.sendfile (
httpd, client, ctx->handle, &ctx->offset, count); httpd, client, ctx->handle, &ctx->offset, count);
if (n <= -1) if (n <= -1)
@ -184,7 +185,7 @@ static QSE_INLINE int task_main_getfile (
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500; (httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_err ( x = qse_httpd_entask_err (
httpd, client, x, http_errnum, httpd, client, x, http_errnum,
&file->version, file->keepalive); file->method, &file->version, file->keepalive);
goto no_file_send; goto no_file_send;
} }
@ -196,37 +197,37 @@ static QSE_INLINE int task_main_getfile (
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500; (httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_err ( x = qse_httpd_entask_err (
httpd, client, x, http_errnum, httpd, client, x, http_errnum,
&file->version, file->keepalive); file->method, &file->version, file->keepalive);
goto no_file_send; goto no_file_send;
} }
fileopen = 1; fileopen = 1;
if (file->range.type != QSE_HTTP_RANGE_NONE) if (file->u.get.range.type != QSE_HTTP_RANGE_NONE)
{ {
qse_mchar_t tmp[4][64]; qse_mchar_t tmp[4][64];
qse_mchar_t etag[ETAG_LEN_MAX + 1]; qse_mchar_t etag[ETAG_LEN_MAX + 1];
qse_size_t etag_len; qse_size_t etag_len;
if (file->range.type == QSE_HTTP_RANGE_SUFFIX) if (file->u.get.range.type == QSE_HTTP_RANGE_SUFFIX)
{ {
if (file->range.to > st.size) file->range.to = st.size; if (file->u.get.range.to > st.size) file->u.get.range.to = st.size;
file->range.from = st.size - file->range.to; file->u.get.range.from = st.size - file->u.get.range.to;
file->range.to = file->range.to + file->range.from; file->u.get.range.to = file->u.get.range.to + file->u.get.range.from;
if (st.size > 0) file->range.to--; if (st.size > 0) file->u.get.range.to--;
} }
if (file->range.from >= st.size) if (file->u.get.range.from >= st.size)
{ {
x = qse_httpd_entask_err ( x = qse_httpd_entask_err (
httpd, client, x, 416, &file->version, file->keepalive); httpd, client, x, 416, file->method, &file->version, file->keepalive);
goto no_file_send; goto no_file_send;
} }
if (file->range.to >= st.size) file->range.to = st.size - 1; if (file->u.get.range.to >= st.size) file->u.get.range.to = st.size - 1;
qse_fmtuintmaxtombs (tmp[0], QSE_COUNTOF(tmp[0]), (file->range.to - file->range.from + 1), 10, -1, QSE_MT('\0'), QSE_NULL); qse_fmtuintmaxtombs (tmp[0], QSE_COUNTOF(tmp[0]), (file->u.get.range.to - file->u.get.range.from + 1), 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (tmp[1], QSE_COUNTOF(tmp[1]), file->range.from, 10, -1, QSE_MT('\0'), QSE_NULL); qse_fmtuintmaxtombs (tmp[1], QSE_COUNTOF(tmp[1]), file->u.get.range.from, 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (tmp[2], QSE_COUNTOF(tmp[2]), file->range.to, 10, -1, QSE_MT('\0'), QSE_NULL); qse_fmtuintmaxtombs (tmp[2], QSE_COUNTOF(tmp[2]), file->u.get.range.to, 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (tmp[3], QSE_COUNTOF(tmp[3]), st.size, 10, -1, QSE_MT('\0'), QSE_NULL); qse_fmtuintmaxtombs (tmp[3], QSE_COUNTOF(tmp[3]), st.size, 10, -1, QSE_MT('\0'), QSE_NULL);
etag_len = qse_fmtuintmaxtombs (&etag[0], QSE_COUNTOF(etag), st.mtime.sec, 16, -1, QSE_MT('\0'), QSE_NULL); etag_len = qse_fmtuintmaxtombs (&etag[0], QSE_COUNTOF(etag), st.mtime.sec, 16, -1, QSE_MT('\0'), QSE_NULL);
@ -249,7 +250,10 @@ static QSE_INLINE int task_main_getfile (
(file->u.get.mime.len > 0? QSE_MT("Content-Type: "): QSE_MT("")), (file->u.get.mime.len > 0? QSE_MT("Content-Type: "): QSE_MT("")),
(file->u.get.mime.len > 0? file->u.get.mime.ptr: QSE_MT("")), (file->u.get.mime.len > 0? file->u.get.mime.ptr: QSE_MT("")),
(file->u.get.mime.len > 0? QSE_MT("\r\n"): QSE_MT("")), (file->u.get.mime.len > 0? QSE_MT("\r\n"): QSE_MT("")),
tmp[0], tmp[1], tmp[2], tmp[3], etag ((file->method == QSE_HTTP_HEAD)? QSE_MT("0"): tmp[0]),
tmp[1], tmp[2], tmp[3],
qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1),
etag
); );
if (x) if (x)
{ {
@ -257,8 +261,8 @@ static QSE_INLINE int task_main_getfile (
x = entask_getfseg ( x = entask_getfseg (
httpd, client, x, httpd, client, x,
handle, handle,
file->range.from, file->u.get.range.from,
(file->range.to - file->range.from + 1) (file->u.get.range.to - file->u.get.range.from + 1)
); );
} }
} }
@ -278,13 +282,13 @@ static QSE_INLINE int task_main_getfile (
etag[etag_len++] = QSE_MT('-'); etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.dev, 16, -1, QSE_MT('\0'), QSE_NULL); etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.dev, 16, -1, QSE_MT('\0'), QSE_NULL);
if ((file->if_none_match[0] != QSE_MT('\0') && qse_mbscmp (etag, file->if_none_match) == 0) || if ((file->u.get.if_none_match[0] != QSE_MT('\0') && qse_mbscmp (etag, file->u.get.if_none_match) == 0) ||
(file->if_modified_since.sec > 0 && st.mtime.sec <= file->if_modified_since.sec)) (file->u.get.if_modified_since.sec > 0 && st.mtime.sec <= file->u.get.if_modified_since.sec))
{ {
/* i've converted milliseconds to seconds before timestamp comparison /* i've converted milliseconds to seconds before timestamp comparison
* because st.mtime has the actual milliseconds less than 1 second * because st.mtime has the actual milliseconds less than 1 second
* while if_modified_since doesn't have such small milliseconds */ * while if_modified_since doesn't have such small milliseconds */
x = qse_httpd_entask_nomod (httpd, client, x, &file->version, file->keepalive); x = qse_httpd_entask_nomod (httpd, client, x, file->method, &file->version, file->keepalive);
goto no_file_send; goto no_file_send;
} }
@ -304,7 +308,7 @@ static QSE_INLINE int task_main_getfile (
(file->u.get.mime.len > 0? QSE_MT("Content-Type: "): QSE_MT("")), (file->u.get.mime.len > 0? QSE_MT("Content-Type: "): QSE_MT("")),
(file->u.get.mime.len > 0? file->u.get.mime.ptr: QSE_MT("")), (file->u.get.mime.len > 0? file->u.get.mime.ptr: QSE_MT("")),
(file->u.get.mime.len > 0? QSE_MT("\r\n"): QSE_MT("")), (file->u.get.mime.len > 0? QSE_MT("\r\n"): QSE_MT("")),
b_fsize, ((file->method == QSE_HTTP_HEAD)? QSE_MT("0"): b_fsize),
qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1), qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1),
etag etag
); );
@ -326,6 +330,24 @@ no_file_send:
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
static int write_file (qse_httpd_t* httpd, qse_ubi_t handle, const qse_mchar_t* ptr, qse_size_t len)
{
qse_ssize_t n;
qse_size_t pos = 0;
/* this implementation assumes that file writing will never get
* blocked in practice. so no i/o multiplexing is performed over
* file descriptors */
while (pos < len)
{
n = httpd->opt.scb.file.write (httpd, handle, &ptr[pos], len - pos);
if (n <= 0) return -1;
pos += n;
}
return 0;
}
static int putfile_snatch_client_input ( static int putfile_snatch_client_input (
qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx) qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx)
{ {
@ -354,10 +376,13 @@ static int putfile_snatch_client_input (
* the trigger is not needed any more. */ * the trigger is not needed any more. */
task->trigger[0].mask = 0; task->trigger[0].mask = 0;
} }
else /*if (!(file->reqflags & PROXY_REQ_FWDERR))*/ else if (!(file->u.put.flags & PUTFILE_WRITE_FAILED))
{ {
/* TODO: write to file */ if (write_file (file->u.put.httpd, file->u.put.handle, ptr, len) <= -1)
qse_printf (QSE_T("WRITING 4 [%.*hs]\n"), (int)len, ptr); {
file->u.put.flags |= PUTFILE_WRITE_FAILED;
file->u.put.status = 500;
}
} }
return 0; return 0;
@ -368,7 +393,7 @@ static int task_init_putfile (
{ {
task_file_t* file = qse_httpd_gettaskxtn (httpd, task); task_file_t* file = qse_httpd_gettaskxtn (httpd, task);
task_file_t* arg = (task_file_t*)task->ctx; task_file_t* arg = (task_file_t*)task->ctx;
int snatch_needed; qse_httpd_stat_t st;
/* zero out the task's extension area */ /* zero out the task's extension area */
QSE_MEMCPY (file, arg, QSE_SIZEOF(*file)); QSE_MEMCPY (file, arg, QSE_SIZEOF(*file));
@ -377,87 +402,36 @@ static int task_init_putfile (
/* copy in the path name to the area */ /* copy in the path name to the area */
file->path.ptr = (qse_mchar_t*)(file + 1); file->path.ptr = (qse_mchar_t*)(file + 1);
qse_mbscpy ((qse_mchar_t*)file->path.ptr, arg->path.ptr); qse_mbscpy ((qse_mchar_t*)file->path.ptr, arg->path.ptr);
file->u.put.status = 204; /* 200 should also be ok to indicate success. */
task->ctx = file; /* switch the task context to the extension area */
snatch_needed = 0;
#if 0
httpd->errnum = QSE_HTTPD_ENOERR; httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->opt.scb.file.stat (httpd, file->path.ptr, &st) <= -1) if (httpd->opt.scb.file.stat (httpd, file->path.ptr, &st) <= -1)
{ {
int http_errnum = 500; if (httpd->errnum == QSE_HTTPD_ENOENT)
switch (httpd->errnum)
{ {
case QSE_HTTPD_ENOENT: /* stat found no such file. so if the request is achived
/* nothing to do */ * successfully, it should send '201 Created' */
break; file->u.put.status = 201;
case QSE_HTTPD_EACCES:
http_errnum = 403;
default:
goto no_file_write;
} }
} }
#endif
if (httpd->opt.scb.file.wopen (httpd, file->path.ptr, &file->u.put.handle) <= -1) goto oops; httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->opt.scb.file.wopen (httpd, file->path.ptr, &file->u.put.handle) <= -1)
if (arg->u.put.req->state & QSE_HTRE_DISCARDED)
{ {
/* no content to add */ file->u.put.status = (httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
/* TODO: return what??? */ goto oops;
qse_printf (QSE_T("ALL DISCARDED...\n"));
}
else if (arg->u.put.req->state & QSE_HTRE_COMPLETED)
{
#if 0
len = qse_htre_getcontentlen(arg->u.put.req);
if (len > 0)
{
ptr = qse_htre_getcontentptr(arg->u.put.req);
/* TODO: write this to a file */
}
#endif
qse_printf (QSE_T("WRITING 1 [%.*hs]\n"), (int)qse_htre_getcontentlen(arg->u.put.req), qse_htre_getcontentptr(arg->u.put.req));
}
else if (arg->u.put.req->attr.flags & QSE_HTRE_ATTR_LENGTH)
{
/* Content-Length is included and the content
* has been received partially so far */
#if 0
len = qse_htre_getcontentlen(arg->u.put.req);
if (len > 0)
{
ptr = qse_htre_getcontentptr(arg->u.put.req);
/* TODO: write to a file */
} }
#endif if (write_file (httpd, file->u.put.handle, qse_htre_getcontentptr(arg->u.put.req), qse_htre_getcontentlen(arg->u.put.req)) <= -1)
qse_printf (QSE_T("WRITING 2 [%.*hs]\n"), (int)qse_htre_getcontentlen(arg->u.put.req), qse_htre_getcontentptr(arg->u.put.req));
snatch_needed = 1;
}
else
{ {
/* if this request is not chunked nor not length based, httpd->opt.scb.file.close (httpd, file->u.put.handle);
* the state should be QSE_HTRE_COMPLETED. so only a file->u.put.status = 500;
* chunked request should reach here */ goto oops;
QSE_ASSERT (arg->u.put.req->attr.flags & QSE_HTRE_ATTR_CHUNKED);
qse_printf (QSE_T("WRITING 3 [%.*hs]\n"), (int)qse_htre_getcontentlen(arg->u.put.req), qse_htre_getcontentptr(arg->u.put.req));
#if 0
len = qse_htre_getcontentlen(arg->u.put.req);
if (len > 0)
{
ptr = qse_htre_getcontentptr(arg->u.put.req);
}
#endif
snatch_needed = 1;
} }
if (snatch_needed) if (!(arg->u.put.req->state & QSE_HTRE_DISCARDED) &&
!(arg->u.put.req->state & QSE_HTRE_COMPLETED))
{ {
/* set up a callback to be called when the request content /* set up a callback to be called when the request content
* is fed to the htrd reader. qse_htre_addcontent() that * is fed to the htrd reader. qse_htre_addcontent() that
@ -470,16 +444,14 @@ qse_printf (QSE_T("WRITING 3 [%.*hs]\n"), (int)qse_htre_getcontentlen(arg->u.put
* triggers in the task initializer. however the main task handler * triggers in the task initializer. however the main task handler
* will be invoked so long as the client handle is writable by * will be invoked so long as the client handle is writable by
* the main loop. */ * the main loop. */
task->ctx = file; /* switch the task context to the extension area */
return 0; return 0;
oops: oops:
/* since a new task can't be added in the initializer, /* since a new task can't be added in the initializer,
* i mark that initialization failed and let task_main_putfile() * i mark that initialization failed and let task_main_putfile()
* add an error task */ * add an error task */
qse_htre_discardcontent (arg->u.put.req);
file->u.put.flags |= PUTFILE_INIT_FAILED; file->u.put.flags |= PUTFILE_INIT_FAILED;
task->ctx = file;
return 0; return 0;
} }
@ -488,37 +460,58 @@ static void task_fini_putfile (
{ {
task_file_t* file = (task_file_t*)task->ctx; task_file_t* file = (task_file_t*)task->ctx;
qse_printf (QSE_T("put fini....\n"));
if (!(file->u.put.flags & PUTFILE_INIT_FAILED)) if (!(file->u.put.flags & PUTFILE_INIT_FAILED))
httpd->opt.scb.file.close (httpd, file->u.put.handle); httpd->opt.scb.file.close (httpd, file->u.put.handle);
} }
static int task_main_putfile_2 (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file = (task_file_t*)task->ctx;
if (file->u.put.req)
{
/* file->u.put.req is set to a non-NULL value if snatching
* is needed in the task_init_putfile(). and it's reset to
* QSE_NULL when snatching is over in putfile_snatch_client_input().
* i set a trigger so that the task is executed
* while there is input from the client side */
task->trigger[0].mask = QSE_HTTPD_TASK_TRIGGER_READ;
task->trigger[0].handle = client->handle;
return 1; /* trigger me when a client sends data */
}
/* snatching is over. writing error may have occurred as well.
* file->u.put.status should hold a proper status code */
if (qse_httpd_entask_err (
httpd, client, task, file->u.put.status,
file->method, &file->version, file->keepalive) == QSE_NULL) return -1;
return 0; /* no more data to read. task over */
}
static int task_main_putfile ( static int task_main_putfile (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{ {
task_file_t* file = (task_file_t*)task->ctx; task_file_t* file = (task_file_t*)task->ctx;
qse_printf (QSE_T("put main....\n"));
if (file->u.put.req) if (!(file->u.put.flags & PUTFILE_INIT_FAILED) && file->u.put.req)
{ {
qse_printf (QSE_T("put xxxxx....\n")); /* initialization was successful and snatching is required.
/* still snatching the content body */ * switch to the next phase. */
task->trigger[0].mask = QSE_HTTPD_TASK_TRIGGER_READ; task->main = task_main_putfile_2;
task->trigger[0].handle = client->handle; return task_main_putfile_2 (httpd, client, task);
return 1;
} }
qse_printf (QSE_T("put what....\n")); /* snatching is not required or initialization error has occurred.
return 0; * file->u.put.status should hold a proper status code.
} *
* note: if initialization error occurred and there is contents for the
/*------------------------------------------------------------------------*/ * client to send, this reply may get to the client before it finishes
* sending the contents. */
static QSE_INLINE int task_main_delfile ( if (qse_httpd_entask_err (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) httpd, client, task, file->u.put.status,
{ file->method, &file->version, file->keepalive) == QSE_NULL) return -1;
/* TODO: implement this */ return 0; /* task over */
return -1;
} }
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
@ -549,6 +542,11 @@ qse_httpd_task_t* qse_httpd_entaskfile (
switch (data.method) switch (data.method)
{ {
case QSE_HTTP_OPTIONS:
qse_htre_discardcontent (req);
return qse_httpd_entaskallow (httpd, client, pred,
QSE_MT("OPTIONS,GET,HEAD,POST,PUT,DELETE"), req);
case QSE_HTTP_HEAD: case QSE_HTTP_HEAD:
case QSE_HTTP_GET: case QSE_HTTP_GET:
case QSE_HTTP_POST: case QSE_HTTP_POST:
@ -561,49 +559,13 @@ qse_httpd_task_t* qse_httpd_entaskfile (
xtnsize += data.u.get.mime.len + 1; xtnsize += data.u.get.mime.len + 1;
} }
task.init = task_init_getfile;
task.main = task_main_getfile;
break;
case QSE_HTTP_PUT:
data.u.put.req = req;
task.init = task_init_putfile;
task.main = task_main_putfile;
task.fini = task_fini_putfile;
break;
#if 0
case QSE_HTTP_DELETE:
task.main = task_main_delfile;
break;
#endif
default:
/* Method not allowed */
return qse_httpd_entaskerr (httpd, client, pred, 405, req);
}
tmp = qse_htre_getheaderval(req, QSE_MT("Range"));
if (tmp)
{
while (tmp->next) tmp = tmp->next; /* get the last value */
if (qse_parsehttprange (tmp->ptr, &data.range) <= -1)
{
return qse_httpd_entaskerr (httpd, client, pred, 416, req);
}
}
else
{
data.range.type = QSE_HTTP_RANGE_NONE;
}
tmp = qse_htre_getheaderval(req, QSE_MT("If-None-Match")); tmp = qse_htre_getheaderval(req, QSE_MT("If-None-Match"));
if (tmp) if (tmp)
{ {
while (tmp->next) tmp = tmp->next; /* get the last value */ /*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
qse_mbsxcpy (data.if_none_match, QSE_COUNTOF(data.if_none_match), tmp->ptr); qse_mbsxcpy (data.u.get.if_none_match, QSE_COUNTOF(data.u.get.if_none_match), tmp->ptr);
} }
if (data.if_none_match[0] == QSE_MT('\0')) if (data.u.get.if_none_match[0] == QSE_MT('\0'))
{ {
/* Both ETag and Last-Modified are included in the reply. /* Both ETag and Last-Modified are included in the reply.
* If the client understand ETag, it can choose to include * If the client understand ETag, it can choose to include
@ -618,15 +580,63 @@ qse_httpd_task_t* qse_httpd_entaskfile (
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since")); tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
if (tmp) if (tmp)
{ {
while (tmp->next) tmp = tmp->next; /* get the last value */ /*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
if (qse_parsehttptime (tmp->ptr, &data.if_modified_since) <= -1) if (qse_parsehttptime (tmp->ptr, &data.u.get.if_modified_since) <= -1)
{ {
data.if_modified_since.sec = 0; data.u.get.if_modified_since.sec = 0;
data.if_modified_since.nsec = 0; data.u.get.if_modified_since.nsec = 0;
} }
} }
} }
tmp = qse_htre_getheaderval(req, QSE_MT("Range"));
if (tmp)
{
/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
if (qse_parsehttprange (tmp->ptr, &data.u.get.range) <= -1)
{
return qse_httpd_entaskerr (httpd, client, pred, 416, req);
}
}
else
{
data.u.get.range.type = QSE_HTTP_RANGE_NONE;
}
task.init = task_init_getfile;
task.main = task_main_getfile;
break;
case QSE_HTTP_PUT:
/* note that no partial update is supported for PUT */
data.u.put.httpd = httpd;
data.u.put.req = req;
task.init = task_init_putfile;
task.main = task_main_putfile;
task.fini = task_fini_putfile;
break;
case QSE_HTTP_DELETE:
{
int status = 200;
qse_htre_discardcontent (req);
if (httpd->opt.scb.file.purge (httpd, path) <= -1)
{
status = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
}
return qse_httpd_entaskerr (httpd, client, pred, status, req);
}
default:
/* Method not allowed */
qse_htre_discardcontent (req);
return qse_httpd_entaskerr (httpd, client, pred, 405, req);
}
task.ctx = &data; task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, xtnsize); return qse_httpd_entask (httpd, client, pred, &task, xtnsize);
} }

View File

@ -39,6 +39,8 @@ struct task_proxy_t
qse_httpd_t* httpd; qse_httpd_t* httpd;
const qse_mchar_t* host; const qse_mchar_t* host;
int method;
qse_http_version_t version; qse_http_version_t version;
int keepalive; /* taken from the request */ int keepalive; /* taken from the request */
@ -714,6 +716,7 @@ static int task_init_proxy (
QSE_MEMSET (proxy, 0, QSE_SIZEOF(*proxy)); QSE_MEMSET (proxy, 0, QSE_SIZEOF(*proxy));
proxy->httpd = httpd; proxy->httpd = httpd;
proxy->method = qse_htre_getqmethodtype(arg->req);
proxy->version = *qse_htre_getversion(arg->req); proxy->version = *qse_htre_getversion(arg->req);
proxy->keepalive = (arg->req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE); proxy->keepalive = (arg->req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
proxy->peer.nwad = *arg->peer_nwad; proxy->peer.nwad = *arg->peer_nwad;
@ -1356,7 +1359,7 @@ qse_printf (QSE_T("TRAILING DATA=%d, [%hs]\n"), (int)QSE_MBS_LEN(proxy->res), QS
oops: oops:
if (proxy->resflags & PROXY_RES_EVER_SENTBACK) return -1; if (proxy->resflags & PROXY_RES_EVER_SENTBACK) return -1;
return (qse_httpd_entask_err (httpd, client, task, http_errnum, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0; return (qse_httpd_entask_err (httpd, client, task, http_errnum, proxy->method, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0;
} }
static int task_main_proxy_1 ( static int task_main_proxy_1 (
@ -1419,7 +1422,7 @@ static int task_main_proxy_1 (
return 1; return 1;
oops: oops:
return (qse_httpd_entask_err (httpd, client, task, http_errnum, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0; return (qse_httpd_entask_err (httpd, client, task, http_errnum, proxy->method, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0;
} }
static int task_main_proxy ( static int task_main_proxy (
@ -1509,7 +1512,7 @@ oops:
return (qse_httpd_entask_err ( return (qse_httpd_entask_err (
httpd, client, task, http_errnum, httpd, client, task, http_errnum,
&proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0; proxy->method, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0;
} }
qse_httpd_task_t* qse_httpd_entaskproxy ( qse_httpd_task_t* qse_httpd_entaskproxy (

View File

@ -33,6 +33,9 @@
#include <qse/cmn/dir.h> #include <qse/cmn/dir.h>
#include <qse/cmn/fio.h> #include <qse/cmn/fio.h>
#define STAT_REG 1
#define STAT_DIR 2
#if defined(_WIN32) #if defined(_WIN32)
# include <winsock2.h> # include <winsock2.h>
# include <ws2tcpip.h> /* sockaddr_in6 */ # include <ws2tcpip.h> /* sockaddr_in6 */
@ -1250,7 +1253,7 @@ static int mux_writable (qse_httpd_t* httpd, qse_ubi_t handle, const qse_ntime_t
static int stat_file ( static int stat_file (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_t* httpd, const qse_mchar_t* path,
qse_httpd_stat_t* hst, int regonly) qse_httpd_stat_t* hst, int filter)
{ {
#if defined(_WIN32) #if defined(_WIN32)
@ -1360,9 +1363,8 @@ static int stat_file (
return -1; return -1;
} }
/* stating for a file. it should be a regular file. if ((filter == STAT_REG && !S_ISREG(st.st_mode)) ||
* i don't allow other file types. */ (filter == STAT_DIR && !S_ISDIR(st.st_mode)))
if (regonly && !S_ISREG(st.st_mode))
{ {
qse_httpd_seterrnum (httpd, QSE_HTTPD_EACCES); qse_httpd_seterrnum (httpd, QSE_HTTPD_EACCES);
return -1; return -1;
@ -1397,8 +1399,47 @@ static int file_stat (
/* this callback is not required to be a general stat function /* this callback is not required to be a general stat function
* for a file. it is mainly used to get a file size and timestamps * for a file. it is mainly used to get a file size and timestamps
* of a regular file. so it should fail for a non-regular file. * of a regular file. so it should fail for a non-regular file.
* note that 1 passes 1 to stat_file for it */ * note that STAT_REG is passed to stat_file for it */
return stat_file (httpd, path, hst, 1); return stat_file (httpd, path, hst, STAT_REG);
}
static int file_purge (qse_httpd_t* httpd, const qse_mchar_t* path)
{
#if defined(_WIN32)
if (DeleteFileA (path) == FALSE)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(GetLastError()));
return -1;
}
return 0;
#elif defined(__OS2__)
APIRET rc;
rc = DosDelete (path); /* TODO: is DosForceDelete better? */
if (rc != NO_ERROR)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(rc));
return -1;
}
return 0;
#elif defined(__DOS__)
/* TODO: */
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL);
return -1;
#else
if (QSE_UNLINK (path) <= -1)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
return -1;
}
return 0;
#endif
} }
static int file_ropen ( static int file_ropen (
@ -1503,6 +1544,90 @@ static qse_ssize_t file_write (
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
static int dir_stat (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_stat_t* hst)
{
/* this callback is not required to be a general stat function
* for a file. it is mainly used to get a file size and timestamps
* of a regular file. so it should fail for a non-regular file.
* note that STAT_REG is passed to stat_file for it */
return stat_file (httpd, path, hst, STAT_DIR);
}
static int dir_make (qse_httpd_t* httpd, const qse_mchar_t* path)
{
#if defined(_WIN32)
if (CreateDirectoryA (path, QSE_NULL) == FALSE)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(GetLastError()));
return -1;
}
return 0;
#elif defined(__OS2__)
APIRET rc;
rc = DosCreateDir (path, QSE_NULL); /* TODO: is DosForceDelete better? */
if (rc != NO_ERROR)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(rc));
return -1;
}
return 0;
#elif defined(__DOS__)
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL);
return -1;
#else
if (QSE_MKDIR (path, 0755) <= -1)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
return -1;
}
return 0;
#endif
}
static int dir_purge (qse_httpd_t* httpd, const qse_mchar_t* path)
{
#if defined(_WIN32)
if (RemoveDirectoryA (path) == FALSE)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(GetLastError()));
return -1;
}
#elif defined(__OS2__)
APIRET rc;
rc = DosDeleteDir (path); /* TODO: is DosForceDelete better? */
if (rc != NO_ERROR)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(rc));
return -1;
}
return 0;
#elif defined(__DOS__)
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL);
return -1;
#else
if (QSE_RMDIR (path) <= -1)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
return -1;
}
return 0;
#endif
}
typedef struct dir_t dir_t; typedef struct dir_t dir_t;
struct dir_t struct dir_t
{ {
@ -1868,6 +1993,19 @@ if (qse_htre_getcontentlen(req) > 0)
* if you don't like this behavior, you must implement your own * if you don't like this behavior, you must implement your own
* callback function for request handling. */ * callback function for request handling. */
#if 0
/* TODO support X-HTTP-Method-Override */
if (data.method == QSE_HTTP_POST)
{
tmp = qse_htre_getheaderval(req, QSE_MT("X-HTTP-Method-Override"));
if (tmp)
{
/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
data.method = qse_mbstohttpmethod (tmp->ptr);
}
}
#endif
if (qse_htre_getqmethodtype(req) == QSE_HTTP_POST && if (qse_htre_getqmethodtype(req) == QSE_HTTP_POST &&
!(req->attr.flags & QSE_HTRE_ATTR_LENGTH) && !(req->attr.flags & QSE_HTRE_ATTR_LENGTH) &&
!(req->attr.flags & QSE_HTRE_ATTR_CHUNKED)) !(req->attr.flags & QSE_HTRE_ATTR_CHUNKED))
@ -1908,7 +2046,6 @@ if (qse_htre_getcontentlen(req) > 0)
* discard the contents since i won't return them */ * discard the contents since i won't return them */
if (rsrc.type == QSE_HTTPD_RSRC_ERR) if (rsrc.type == QSE_HTTPD_RSRC_ERR)
{ {
qse_printf (QSE_T("DISCARD 3\n"));
qse_httpd_discardcontent (httpd, req); qse_httpd_discardcontent (httpd, req);
} }
} }
@ -2122,6 +2259,7 @@ static qse_httpd_scb_t httpd_system_callbacks =
/* file operation */ /* file operation */
{ file_stat, { file_stat,
file_purge,
file_ropen, file_ropen,
file_wopen, file_wopen,
file_close, file_close,
@ -2130,7 +2268,10 @@ static qse_httpd_scb_t httpd_system_callbacks =
}, },
/* directory operation */ /* directory operation */
{ dir_open, { dir_stat,
dir_make,
dir_purge,
dir_open,
dir_close, dir_close,
dir_read dir_read
}, },
@ -2418,7 +2559,7 @@ static int make_resource (
struct rsrc_tmp_t tmp; struct rsrc_tmp_t tmp;
qse_httpd_stat_t st; qse_httpd_stat_t st;
int n, stx, method; int n, stx, acc;
QSE_MEMSET (&tmp, 0, QSE_SIZEOF(tmp)); QSE_MEMSET (&tmp, 0, QSE_SIZEOF(tmp));
tmp.qpath = qse_htre_getqpath(req); tmp.qpath = qse_htre_getqpath(req);
@ -2429,8 +2570,9 @@ static int make_resource (
server_xtn = qse_httpd_getserverxtn (httpd, client->server); server_xtn = qse_httpd_getserverxtn (httpd, client->server);
if (server_xtn->query (httpd, client->server, req, QSE_NULL, QSE_HTTPD_SERVERSTD_ROOT, &tmp.root) <= -1) return -1; if (server_xtn->query (httpd, client->server, req, QSE_NULL, QSE_HTTPD_SERVERSTD_ROOT, &tmp.root) <= -1) return -1;
if (tmp.root.type == QSE_HTTPD_SERVERSTD_ROOT_NWAD) switch (tmp.root.type)
{ {
case QSE_HTTPD_SERVERSTD_ROOT_NWAD:
/* proxy the request */ /* proxy the request */
target->type = QSE_HTTPD_RSRC_PROXY; target->type = QSE_HTTPD_RSRC_PROXY;
/*target->u.proxy.dst = client->orgdst_addr;*/ /*target->u.proxy.dst = client->orgdst_addr;*/
@ -2440,30 +2582,15 @@ static int make_resource (
/* mark that this request is going to be proxied. */ /* mark that this request is going to be proxied. */
req->attr.flags |= QSE_HTRE_ATTR_PROXIED; req->attr.flags |= QSE_HTRE_ATTR_PROXIED;
return 0; return 0;
case QSE_HTTPD_SERVERSTD_ROOT_TEXT:
target->type = QSE_HTTPD_RSRC_TEXT;
target->u.text.ptr = tmp.root.u.text.ptr;
target->u.text.mime = tmp.root.u.text.mime;
return 0;
} }
/* handle the request locally */ /* handle the request locally */
#if 0
method = qse_htre_getqmethodtype(req);
switch (method)
{
case QSE_HTTP_HEAD:
case QSE_HTTP_GET:
case QSE_HTTP_POST:
case QSE_HTTP_PUT:
case QSE_HTTP_DELETE:
case QSE_HTTP_OPTIONS:
/* let these methods be handled locally */
break;
default:
/* method not allowed */
target->type = QSE_HTTPD_RSRC_ERR;
target->u.err.code = 405;
return 0;
}
#endif
QSE_ASSERT (tmp.root.type == QSE_HTTPD_SERVERSTD_ROOT_PATH); QSE_ASSERT (tmp.root.type == QSE_HTTPD_SERVERSTD_ROOT_PATH);
if (server_xtn->query (httpd, client->server, req, QSE_NULL, QSE_HTTPD_SERVERSTD_REALM, &tmp.realm) <= -1 || if (server_xtn->query (httpd, client->server, req, QSE_NULL, QSE_HTTPD_SERVERSTD_REALM, &tmp.realm) <= -1 ||
@ -2543,6 +2670,7 @@ auth_ok:
else else
{ {
/* Expectation Failed */ /* Expectation Failed */
qse_htre_discardcontent (req);
target->type = QSE_HTTPD_RSRC_ERR; target->type = QSE_HTTPD_RSRC_ERR;
target->u.err.code = 417; target->u.err.code = 417;
return 0; return 0;
@ -2611,8 +2739,9 @@ auth_ok:
/* it is a directory - should i allow it? */ /* it is a directory - should i allow it? */
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_DIRACC, &target->u.err.code) <= -1) target->u.err.code = 500; if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_DIRACC, &target->u.err.code) <= -1) target->u.err.code = 500;
if (target->u.err.code != 200) if (target->u.err.code < 200 || target->u.err.code > 299)
{ {
qse_htre_discardcontent (req);
target->type = QSE_HTTPD_RSRC_ERR; target->type = QSE_HTTPD_RSRC_ERR;
/* free xpath since it won't be used */ /* free xpath since it won't be used */
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
@ -2620,6 +2749,7 @@ auth_ok:
else if (tmp.qpath[tmp.qpath_len - 1] != QSE_MT('/')) else if (tmp.qpath[tmp.qpath_len - 1] != QSE_MT('/'))
{ {
/* the query path doesn't end with a slash. so redirect it */ /* the query path doesn't end with a slash. so redirect it */
qse_htre_discardcontent (req);
target->type = QSE_HTTPD_RSRC_REDIR; target->type = QSE_HTTPD_RSRC_REDIR;
target->u.redir.dst = tmp.qpath; target->u.redir.dst = tmp.qpath;
/* free xpath since it won't be used */ /* free xpath since it won't be used */
@ -2651,11 +2781,16 @@ auth_ok:
} }
if (n >= 1) return 0; if (n >= 1) return 0;
acc = (tmp.idxfile || !qse_mbsend(tmp.qpath, QSE_MT("/")))?
QSE_HTTPD_SERVERSTD_FILEACC: QSE_HTTPD_SERVERSTD_DIRACC;
/* check file's access permission */ /* check file's access permission */
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_FILEACC, &target->u.err.code) <= -1) target->u.err.code = 500; if (server_xtn->query (httpd, client->server, req, tmp.xpath, acc, &target->u.err.code) <= -1) target->u.err.code = 500;
if (target->u.err.code != 200)
if (target->u.err.code < 200 || target->u.err.code > 299)
{ {
/* free xpath since it won't be used */ /* free xpath since it won't be used */
qse_htre_discardcontent (req);
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
target->type = QSE_HTTPD_RSRC_ERR; target->type = QSE_HTTPD_RSRC_ERR;
} }
@ -2664,6 +2799,8 @@ auth_ok:
/* fall back to a normal file. */ /* fall back to a normal file. */
if (tmp.idxfile) if (tmp.idxfile)
{ {
qse_htre_discardcontent (req);
/* free xpath since it won't be used */ /* free xpath since it won't be used */
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath); QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
@ -2672,6 +2809,11 @@ auth_ok:
target->u.reloc.dst = merge_paths (httpd, tmp.qpath, tmp.idxfile); target->u.reloc.dst = merge_paths (httpd, tmp.qpath, tmp.idxfile);
if (target->u.reloc.dst == QSE_NULL) return -1; if (target->u.reloc.dst == QSE_NULL) return -1;
} }
else if (acc == QSE_HTTPD_SERVERSTD_DIRACC)
{
target->type = QSE_HTTPD_RSRC_DIR;
target->u.dir.path = tmp.xpath;
}
else else
{ {
target->type = QSE_HTTPD_RSRC_FILE; target->type = QSE_HTTPD_RSRC_FILE;
@ -2802,9 +2944,27 @@ static int query_server (
case QSE_HTTPD_SERVERSTD_DIRACC: case QSE_HTTPD_SERVERSTD_DIRACC:
case QSE_HTTPD_SERVERSTD_FILEACC: case QSE_HTTPD_SERVERSTD_FILEACC:
{
/* i don't allow PUT or DELET by default.
* override this query result if you want to change
* the behavior. */
switch (qse_htre_getqmethodtype(req))
{
case QSE_HTTP_OPTIONS:
case QSE_HTTP_HEAD:
case QSE_HTTP_GET:
case QSE_HTTP_POST:
*(int*)result = 200; *(int*)result = 200;
break;
default:
/* method not allowed */
*(int*)result = 405;
break;
}
return 0; return 0;
} }
}
qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL);
return -1; return -1;

View File

@ -201,7 +201,8 @@ struct status_reloc_t
static qse_httpd_task_t* entask_status ( static qse_httpd_task_t* entask_status (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code, void* extra, qse_httpd_task_t* pred, int code, void* extra,
const qse_http_version_t* version, int keepalive) qse_http_method_t method, const qse_http_version_t* version,
int keepalive)
{ {
const qse_mchar_t* msg; const qse_mchar_t* msg;
@ -209,27 +210,36 @@ static qse_httpd_task_t* entask_status (
const qse_mchar_t* extrapst = QSE_MT(""); const qse_mchar_t* extrapst = QSE_MT("");
const qse_mchar_t* extraval = QSE_MT(""); const qse_mchar_t* extraval = QSE_MT("");
qse_mchar_t text[1024]; /* TODO: make this buffer dynamic or scalable */ qse_mchar_t text[1024] = QSE_MT(""); /* TODO: make this buffer dynamic or scalable */
msg = qse_httpstatustombs (code); msg = qse_httpstatustombs (code);
if (code == 301 || code == 307) switch (code)
{
case 301:
case 307:
{ {
status_reloc_t* reloc; status_reloc_t* reloc;
reloc = (status_reloc_t*)extra; reloc = (status_reloc_t*)extra;
extrapre = QSE_MT("Location: "); extrapre = QSE_MT("Location: ");
extrapst = reloc->redir? QSE_MT("/\r\n"): QSE_MT("\r\n"); extrapst = reloc->redir? QSE_MT("/\r\n"): QSE_MT("\r\n");
extraval = reloc->dst; extraval = reloc->dst;
break;
}
text[0] = QSE_MT('\0'); case 304:
} case 200:
else if (code == 304) case 201:
{ case 202:
text[0] = QSE_MT('\0'); case 203:
} case 204:
else case 205:
{ case 206:
if (httpd->opt.rcb.fmterr (httpd, client, code, text, QSE_COUNTOF(text)) <= -1) return QSE_NULL; /* nothing to do */
break;
default:
if (method != QSE_HTTP_HEAD &&
httpd->opt.rcb.fmterr (httpd, client, code, text, QSE_COUNTOF(text)) <= -1) return QSE_NULL;
if (code == 401) if (code == 401)
{ {
@ -237,6 +247,7 @@ static qse_httpd_task_t* entask_status (
extrapst = QSE_MT("\"\r\n"); extrapst = QSE_MT("\"\r\n");
extraval = (const qse_mchar_t*)extra; extraval = (const qse_mchar_t*)extra;
} }
break;
} }
return qse_httpd_entaskformat ( return qse_httpd_entaskformat (
@ -254,19 +265,21 @@ static qse_httpd_task_t* entask_status (
qse_httpd_task_t* qse_httpd_entask_err ( qse_httpd_task_t* qse_httpd_entask_err (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code, qse_httpd_task_t* pred, int code,
const qse_http_version_t* version, int keepalive) qse_http_method_t method, const qse_http_version_t* version, int keepalive)
{ {
return entask_status (httpd, client, pred, code, QSE_NULL, version, keepalive); return entask_status (httpd, client, pred, code, QSE_NULL, method, version, keepalive);
} }
qse_httpd_task_t* qse_httpd_entaskerr ( qse_httpd_task_t* qse_httpd_entaskerr (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code, qse_htre_t* req) qse_httpd_task_t* pred, int code, qse_htre_t* req)
{ {
qse_htre_discardcontent (req);
return entask_status ( return entask_status (
httpd, client, pred, code, QSE_NULL, httpd, client, pred, code, QSE_NULL,
qse_htre_getversion(req), (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE)); qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE)
);
} }
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
@ -289,6 +302,7 @@ qse_httpd_task_t* qse_httpd_entaskauth (
{ {
return entask_status ( return entask_status (
httpd, client, pred, 401, (void*)realm, httpd, client, pred, 401, (void*)realm,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req), qse_htre_getversion(req),
(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE)); (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE));
} }
@ -296,21 +310,6 @@ qse_httpd_task_t* qse_httpd_entaskauth (
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entask_reloc (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* dst,
const qse_http_version_t* version, int keepalive)
{
status_reloc_t reloc;
reloc.dst = dst;
reloc.redir = 0;
return entask_status (
httpd, client, pred, 301, (void*)&reloc,
version, keepalive);
}
qse_httpd_task_t* qse_httpd_entaskreloc ( qse_httpd_task_t* qse_httpd_entaskreloc (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* dst, qse_htre_t* req) qse_httpd_task_t* pred, const qse_mchar_t* dst, qse_htre_t* req)
@ -322,25 +321,11 @@ qse_httpd_task_t* qse_httpd_entaskreloc (
return entask_status ( return entask_status (
httpd, client, pred, 301, (void*)&reloc, httpd, client, pred, 301, (void*)&reloc,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req), qse_htre_getversion(req),
(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE)); (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE));
} }
qse_httpd_task_t* qse_httpd_entask_redir (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* dst,
const qse_http_version_t* version, int keepalive)
{
status_reloc_t reloc;
reloc.dst = dst;
reloc.redir = 1;
return entask_status (
httpd, client, pred, 301, (void*)&reloc,
version, keepalive);
}
qse_httpd_task_t* qse_httpd_entaskredir ( qse_httpd_task_t* qse_httpd_entaskredir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* dst, qse_htre_t* req) qse_httpd_task_t* pred, const qse_mchar_t* dst, qse_htre_t* req)
@ -352,6 +337,7 @@ qse_httpd_task_t* qse_httpd_entaskredir (
return entask_status ( return entask_status (
httpd, client, pred, 301, (void*)&reloc, httpd, client, pred, 301, (void*)&reloc,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req), qse_htre_getversion(req),
(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE)); (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE));
} }
@ -359,12 +345,11 @@ qse_httpd_task_t* qse_httpd_entaskredir (
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entask_nomod ( qse_httpd_task_t* qse_httpd_entask_nomod (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* pred,
qse_httpd_task_t* pred, const qse_http_version_t* version, int keepalive) qse_http_method_t method, const qse_http_version_t* version, int keepalive)
{ {
return entask_status ( return entask_status (
httpd, client, pred, 304, httpd, client, pred, 304, QSE_NULL, method, version, keepalive);
QSE_NULL, version, keepalive);
} }
qse_httpd_task_t* qse_httpd_entasknomod ( qse_httpd_task_t* qse_httpd_entasknomod (
@ -372,13 +357,39 @@ qse_httpd_task_t* qse_httpd_entasknomod (
qse_httpd_task_t* pred, qse_htre_t* req) qse_httpd_task_t* pred, qse_htre_t* req)
{ {
return entask_status ( return entask_status (
httpd, client, pred, 304, httpd, client, pred, 304, QSE_NULL,
QSE_NULL, qse_htre_getversion(req), qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE)); (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE));
} }
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entaskallow (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* allow, qse_htre_t* req)
{
int code = 200;
const qse_mchar_t* msg;
const qse_http_version_t* version;
int keepalive;
msg = qse_httpstatustombs (code);
version = qse_htre_getversion(req);
keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
return qse_httpd_entaskformat (
httpd, client, pred,
QSE_MT("HTTP/%d.%d %d %s\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nAllow: %s\r\nContent-Length: 0\r\n\r\n"),
version->major, version->minor,
code, msg, qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
allow
);
}
/*------------------------------------------------------------------------*/
#if 0 #if 0
qse_httpd_task_t* qse_httpd_entaskconnect ( qse_httpd_task_t* qse_httpd_entaskconnect (
qse_httpd_t* httpd, qse_httpd_t* httpd,

View File

@ -63,31 +63,6 @@ static int task_main_text (
return 1; /* more work to do */ return 1; /* more work to do */
} }
qse_httpd_task_t* qse_httpd_entask_text (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* ptr,
qse_size_t len)
{
qse_httpd_task_t task;
task_text_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.ptr = ptr;
data.left = len;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_text;
task.main = task_main_text;
task.ctx = &data;
return qse_httpd_entask (
httpd, client, pred,
&task, QSE_SIZEOF(data) + data.left);
}
qse_httpd_task_t* qse_httpd_entasktext ( qse_httpd_task_t* qse_httpd_entasktext (
qse_httpd_t* httpd, qse_httpd_t* httpd,
qse_httpd_client_t* client, qse_httpd_client_t* client,
@ -98,13 +73,33 @@ qse_httpd_task_t* qse_httpd_entasktext (
{ {
qse_size_t tlen; qse_size_t tlen;
qse_mchar_t b_tlen[64]; qse_mchar_t b_tlen[64];
qse_http_method_t method;
qse_http_version_t* version; qse_http_version_t* version;
qse_httpd_task_t task;
task_text_t data;
method = qse_htre_getqmethodtype (req);
version = qse_htre_getversion (req); version = qse_htre_getversion (req);
tlen = qse_mbslen(text); qse_htre_discardcontent (req);
qse_fmtuintmaxtombs (b_tlen, QSE_COUNTOF(b_tlen), tlen, 10, -1, QSE_MT('\0'), QSE_NULL); switch (method)
{
case QSE_HTTP_HEAD:
tlen = 0;
break;
case QSE_HTTP_GET:
case QSE_HTTP_POST:
tlen = qse_mbslen(text);
break;
default:
/* Method not allowed */
return qse_httpd_entaskerr (httpd, client, pred, 405, req);
}
qse_fmtuintmaxtombs (b_tlen, QSE_COUNTOF(b_tlen), tlen, 10, -1, QSE_MT('\0'), QSE_NULL);
pred = qse_httpd_entaskformat ( pred = qse_httpd_entaskformat (
httpd, client, pred, httpd, client, pred,
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: %s\r\nContent-Length: %s\r\n\r\n"), QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: %s\r\nContent-Length: %s\r\n\r\n"),
@ -116,5 +111,14 @@ qse_httpd_task_t* qse_httpd_entasktext (
); );
if (pred == QSE_NULL) return QSE_NULL; if (pred == QSE_NULL) return QSE_NULL;
return qse_httpd_entask_text (httpd, client, pred, text, tlen); QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.ptr = text;
data.left = tlen;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_text;
task.main = task_main_text;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data) + data.left);
} }

View File

@ -121,6 +121,7 @@ qse_httpd_task_t* qse_httpd_entask_err (
qse_httpd_client_t* client, qse_httpd_client_t* client,
qse_httpd_task_t* pred, qse_httpd_task_t* pred,
int code, int code,
qse_http_method_t method,
const qse_http_version_t* version, const qse_http_version_t* version,
int keepalive int keepalive
); );
@ -129,37 +130,11 @@ qse_httpd_task_t* qse_httpd_entask_nomod (
qse_httpd_t* httpd, qse_httpd_t* httpd,
qse_httpd_client_t* client, qse_httpd_client_t* client,
qse_httpd_task_t* pred, qse_httpd_task_t* pred,
qse_http_method_t method,
const qse_http_version_t* version, const qse_http_version_t* version,
int keepalive int keepalive
); );
qse_httpd_task_t* qse_httpd_entask_reloc (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* dst,
const qse_http_version_t* version,
int keepalive
);
qse_httpd_task_t* qse_httpd_entask_redir (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* dst,
const qse_http_version_t* version,
int keepalive
);
qse_httpd_task_t* qse_httpd_entask_text (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* ptr,
qse_size_t len
);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif