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;
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)
{
((qse_httpd_serverstd_root_t*)result)->type = QSE_HTTPD_SERVERSTD_ROOT_NWAD;
@ -574,30 +585,47 @@ static int query_server (
case QSE_HTTPD_SERVERSTD_DIRACC:
case QSE_HTTPD_SERVERSTD_FILEACC:
{
qse_size_t i;
const qse_mchar_t* xpath_base;
int id;
id = (code == QSE_HTTPD_SERVERSTD_DIRACC)? 0: 1;
xpath_base = qse_mbsbasename (xpath);
*(int*)result = 200;
for (i = 0; i < QSE_COUNTOF(loccfg->access[id]); i++)
switch (qse_htre_getqmethodtype(req))
{
struct access_t* access;
for (access = loccfg->access[id][i].head; access; access = access->next)
case QSE_HTTP_OPTIONS:
case QSE_HTTP_HEAD:
case QSE_HTTP_GET:
case QSE_HTTP_POST:
case QSE_HTTP_PUT:
case QSE_HTTP_DELETE:
{
if ((access->type == ACCESS_PREFIX && qse_mbsbeg (xpath_base, access->spec)) ||
(access->type == ACCESS_SUFFIX && qse_mbsend (xpath_base, access->spec)) ||
(access->type == ACCESS_NAME && qse_mbscmp (xpath_base, access->spec) == 0) ||
access->type == ACCESS_OTHER)
qse_size_t i;
const qse_mchar_t* xpath_base;
int id;
id = (code == QSE_HTTPD_SERVERSTD_DIRACC)? 0: 1;
xpath_base = qse_mbsbasename (xpath);
*(int*)result = 200;
for (i = 0; i < QSE_COUNTOF(loccfg->access[id]); i++)
{
*(int*)result = access->value;
return 0;
struct access_t* access;
for (access = loccfg->access[id][i].head; access; access = access->next)
{
if ((access->type == ACCESS_PREFIX && qse_mbsbeg (xpath_base, access->spec)) ||
(access->type == ACCESS_SUFFIX && qse_mbsend (xpath_base, access->spec)) ||
(access->type == ACCESS_NAME && qse_mbscmp (xpath_base, access->spec) == 0) ||
access->type == ACCESS_OTHER)
{
*(int*)result = access->value;
return 0;
}
}
}
break;
}
default:
*(int*)result = 405; /* method not allowed */
break;
}
return 0;
}
}

View File

@ -166,6 +166,7 @@ struct qse_httpd_scb_t
int (*stat) (
qse_httpd_t* httpd, const qse_mchar_t* path,
qse_httpd_stat_t* stat);
int (*purge) (qse_httpd_t* httpd, const qse_mchar_t* path);
int (*ropen) (
qse_httpd_t* httpd, const qse_mchar_t* path,
@ -185,6 +186,12 @@ struct qse_httpd_scb_t
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) (
qse_httpd_t* httpd, const qse_mchar_t* path,
qse_ubi_t* handle);
@ -808,6 +815,14 @@ QSE_EXPORT qse_httpd_task_t* qse_httpd_entasknomod (
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_httpd_t* httpd,
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
{
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;
@ -57,6 +58,11 @@ struct qse_httpd_serverstd_root_t
qse_size_t rpl; /* replacement length */
} path;
qse_nwad_t nwad;
struct
{
const qse_mchar_t* ptr;
const qse_mchar_t* mime;
} text;
} u;
};

View File

@ -318,6 +318,12 @@
# define QSE_RENAME(oldpath,newpath) rename(oldpath,newpath)
#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)
# define QSE_RMDIR(path) syscall(SYS_rmdir,path)
#else

View File

@ -128,6 +128,7 @@
{ \
case ENOMEM: return __SYSERRNUM__ (obj2, ENOMEM); \
case EINVAL: return __SYSERRNUM__ (obj2, EINVAL); \
case EBUSY: \
case EACCES: return __SYSERRNUM__ (obj2, EACCES); \
case ENOTDIR: \
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* root;
const qse_mchar_t* shebang;
int method;
qse_http_version_t version;
int keepalive; /* taken from the request */
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->shebang, arg->shebang.ptr);
cgi->method = qse_htre_getqmethodtype(arg->req);
cgi->version = *qse_htre_getversion(arg->req);
cgi->keepalive = (arg->req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
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;
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 (
@ -1502,7 +1505,7 @@ oops:
return (qse_httpd_entask_err (
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

View File

@ -30,7 +30,7 @@ struct task_dir_t
qse_mcstr_t qpath;
qse_http_version_t version;
int keepalive;
int headonly;
int method;
};
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)
{
task_dir_t* xtn = qse_httpd_gettaskxtn (httpd, task);
@ -390,7 +390,7 @@ static int task_init_dir (
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)
{
task_dir_t* dir;
@ -415,26 +415,46 @@ static QSE_INLINE int task_main_dir (
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_err (
httpd, client, x, http_errnum,
&dir->version, dir->keepalive);
dir->method, &dir->version, dir->keepalive);
return (x == QSE_NULL)? -1: 0;
}
else
{
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\n%s\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")),
(dir->keepalive? QSE_MT("Transfer-Encoding: chunked\r\n"): QSE_MT(""))
);
if (x) x = entask_directory_segment (httpd, client, x, handle, dir);
if (x) return 0;
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 -1;
httpd->opt.scb.dir.close (httpd, handle);
return (x == QSE_NULL)? -1: 0;
}
else
{
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\n%s\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")),
(dir->keepalive? QSE_MT("Transfer-Encoding: chunked\r\n"): QSE_MT(""))
);
if (x)
{
x = entask_directory_segment (httpd, client, x, handle, dir);
if (x) return 0;
}
httpd->opt.scb.dir.close (httpd, handle);
return -1;
}
}
/*
}
@ -458,9 +478,6 @@ qse_httpd_task_t* qse_httpd_entaskdir (
{
qse_httpd_task_t task;
task_dir_t data;
int meth;
meth = qse_htre_getqmethodtype(req);
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.path.ptr = path;
@ -469,28 +486,71 @@ qse_httpd_task_t* qse_httpd_entaskdir (
data.qpath.len = qse_mbslen(data.qpath.ptr);
data.version = *qse_htre_getversion(req);
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??? */
switch (meth)
/* i don't need contents for directories */
qse_htre_discardcontent (req);
switch (data.method)
{
case QSE_HTTP_OPTIONS:
return qse_httpd_entaskallow (httpd, client, pred,
QSE_MT("OPTIONS,GET,HEAD,POST,PUT,DELETE"), req);
case QSE_HTTP_HEAD:
data.headonly = 1;
break;
case QSE_HTTP_GET:
case QSE_HTTP_POST:
case QSE_HTTP_PUT:
task.init = task_init_getdir;
task.main = task_main_getdir;
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:
/* Method not allowed */
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;
return qse_httpd_entask (httpd, client, pred, &task,

View File

@ -25,30 +25,32 @@
#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;
struct task_file_t
{
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;
int keepalive;
int method;
/* only for put file... */
union
{
struct
{
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;
struct
{
qse_httpd_t* httpd;
int flags;
int status;
qse_htre_t* req;
qse_ubi_t handle;
} put;
@ -91,7 +93,6 @@ static int task_main_getfseg (
count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left;
/* TODO: more adjustment needed for OS with different sendfile semantics... */
n = httpd->opt.scb.client.sendfile (
httpd, client, ctx->handle, &ctx->offset, count);
if (n <= -1)
@ -184,7 +185,7 @@ static QSE_INLINE int task_main_getfile (
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_err (
httpd, client, x, http_errnum,
&file->version, file->keepalive);
file->method, &file->version, file->keepalive);
goto no_file_send;
}
@ -196,37 +197,37 @@ static QSE_INLINE int task_main_getfile (
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_err (
httpd, client, x, http_errnum,
&file->version, file->keepalive);
file->method, &file->version, file->keepalive);
goto no_file_send;
}
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 etag[ETAG_LEN_MAX + 1];
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;
file->range.from = st.size - file->range.to;
file->range.to = file->range.to + file->range.from;
if (st.size > 0) file->range.to--;
if (file->u.get.range.to > st.size) file->u.get.range.to = st.size;
file->u.get.range.from = st.size - file->u.get.range.to;
file->u.get.range.to = file->u.get.range.to + file->u.get.range.from;
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 (
httpd, client, x, 416, &file->version, file->keepalive);
httpd, client, x, 416, file->method, &file->version, file->keepalive);
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[1], QSE_COUNTOF(tmp[1]), file->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[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->u.get.range.from, 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);
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? file->u.get.mime.ptr: 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)
{
@ -257,8 +261,8 @@ static QSE_INLINE int task_main_getfile (
x = entask_getfseg (
httpd, client, x,
handle,
file->range.from,
(file->range.to - file->range.from + 1)
file->u.get.range.from,
(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_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) ||
(file->if_modified_since.sec > 0 && st.mtime.sec <= file->if_modified_since.sec))
if ((file->u.get.if_none_match[0] != QSE_MT('\0') && qse_mbscmp (etag, file->u.get.if_none_match) == 0) ||
(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
* because st.mtime has the actual milliseconds less than 1 second
* 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;
}
@ -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? file->u.get.mime.ptr: 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),
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 (
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. */
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 */
qse_printf (QSE_T("WRITING 4 [%.*hs]\n"), (int)len, ptr);
if (write_file (file->u.put.httpd, file->u.put.handle, ptr, len) <= -1)
{
file->u.put.flags |= PUTFILE_WRITE_FAILED;
file->u.put.status = 500;
}
}
return 0;
@ -368,7 +393,7 @@ static int task_init_putfile (
{
task_file_t* file = qse_httpd_gettaskxtn (httpd, task);
task_file_t* arg = (task_file_t*)task->ctx;
int snatch_needed;
qse_httpd_stat_t st;
/* zero out the task's extension area */
QSE_MEMCPY (file, arg, QSE_SIZEOF(*file));
@ -377,87 +402,36 @@ static int task_init_putfile (
/* copy in the path name to the area */
file->path.ptr = (qse_mchar_t*)(file + 1);
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;
if (httpd->opt.scb.file.stat (httpd, file->path.ptr, &st) <= -1)
{
int http_errnum = 500;
switch (httpd->errnum)
if (httpd->errnum == QSE_HTTPD_ENOENT)
{
case QSE_HTTPD_ENOENT:
/* nothing to do */
break;
case QSE_HTTPD_EACCES:
http_errnum = 403;
default:
goto no_file_write;
/* stat found no such file. so if the request is achived
* successfully, it should send '201 Created' */
file->u.put.status = 201;
}
}
#endif
if (httpd->opt.scb.file.wopen (httpd, file->path.ptr, &file->u.put.handle) <= -1) goto oops;
if (arg->u.put.req->state & QSE_HTRE_DISCARDED)
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->opt.scb.file.wopen (httpd, file->path.ptr, &file->u.put.handle) <= -1)
{
/* no content to add */
/* TODO: return what??? */
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
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,
* the state should be QSE_HTRE_COMPLETED. so only a
* chunked request should reach here */
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;
file->u.put.status = (httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
goto oops;
}
if (snatch_needed)
if (write_file (httpd, file->u.put.handle, qse_htre_getcontentptr(arg->u.put.req), qse_htre_getcontentlen(arg->u.put.req)) <= -1)
{
httpd->opt.scb.file.close (httpd, file->u.put.handle);
file->u.put.status = 500;
goto oops;
}
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
* 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
* will be invoked so long as the client handle is writable by
* the main loop. */
task->ctx = file; /* switch the task context to the extension area */
return 0;
oops:
/* since a new task can't be added in the initializer,
* i mark that initialization failed and let task_main_putfile()
* add an error task */
qse_htre_discardcontent (arg->u.put.req);
file->u.put.flags |= PUTFILE_INIT_FAILED;
task->ctx = file;
return 0;
}
@ -488,37 +460,58 @@ static void task_fini_putfile (
{
task_file_t* file = (task_file_t*)task->ctx;
qse_printf (QSE_T("put fini....\n"));
if (!(file->u.put.flags & PUTFILE_INIT_FAILED))
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 (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
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"));
/* still snatching the content body */
task->trigger[0].mask = QSE_HTTPD_TASK_TRIGGER_READ;
task->trigger[0].handle = client->handle;
return 1;
/* initialization was successful and snatching is required.
* switch to the next phase. */
task->main = task_main_putfile_2;
return task_main_putfile_2 (httpd, client, task);
}
qse_printf (QSE_T("put what....\n"));
return 0;
}
/*------------------------------------------------------------------------*/
static QSE_INLINE int task_main_delfile (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
/* TODO: implement this */
return -1;
/* snatching is not required or initialization error has occurred.
* 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. */
if (qse_httpd_entask_err (
httpd, client, task, file->u.put.status,
file->method, &file->version, file->keepalive) == QSE_NULL) return -1;
return 0; /* task over */
}
/*------------------------------------------------------------------------*/
@ -549,6 +542,11 @@ qse_httpd_task_t* qse_httpd_entaskfile (
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_GET:
case QSE_HTTP_POST:
@ -561,72 +559,84 @@ qse_httpd_task_t* qse_httpd_entaskfile (
xtnsize += data.u.get.mime.len + 1;
}
tmp = qse_htre_getheaderval(req, QSE_MT("If-None-Match"));
if (tmp)
{
/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
qse_mbsxcpy (data.u.get.if_none_match, QSE_COUNTOF(data.u.get.if_none_match), tmp->ptr);
}
if (data.u.get.if_none_match[0] == QSE_MT('\0'))
{
/* Both ETag and Last-Modified are included in the reply.
* If the client understand ETag, it can choose to include
* If-None-Match in the request. If it understands Last-Modified,
* it can choose to include If-Modified-Since. I don't care
* the client understands both and include both of them
* in the request.
*
* I check If-None-Match if it's included.
* I check If-Modified-Since if If-None-Match is not included.
*/
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
if (tmp)
{
/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
if (qse_parsehttptime (tmp->ptr, &data.u.get.if_modified_since) <= -1)
{
data.u.get.if_modified_since.sec = 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;
#if 0
case QSE_HTTP_DELETE:
task.main = task_main_delfile;
break;
#endif
{
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);
}
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"));
if (tmp)
{
while (tmp->next) tmp = tmp->next; /* get the last value */
qse_mbsxcpy (data.if_none_match, QSE_COUNTOF(data.if_none_match), tmp->ptr);
}
if (data.if_none_match[0] == QSE_MT('\0'))
{
/* Both ETag and Last-Modified are included in the reply.
* If the client understand ETag, it can choose to include
* If-None-Match in the request. If it understands Last-Modified,
* it can choose to include If-Modified-Since. I don't care
* the client understands both and include both of them
* in the request.
*
* I check If-None-Match if it's included.
* I check If-Modified-Since if If-None-Match is not included.
*/
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
if (tmp)
{
while (tmp->next) tmp = tmp->next; /* get the last value */
if (qse_parsehttptime (tmp->ptr, &data.if_modified_since) <= -1)
{
data.if_modified_since.sec = 0;
data.if_modified_since.nsec = 0;
}
}
}
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, xtnsize);
}

View File

@ -39,6 +39,8 @@ struct task_proxy_t
qse_httpd_t* httpd;
const qse_mchar_t* host;
int method;
qse_http_version_t version;
int keepalive; /* taken from the request */
@ -714,6 +716,7 @@ static int task_init_proxy (
QSE_MEMSET (proxy, 0, QSE_SIZEOF(*proxy));
proxy->httpd = httpd;
proxy->method = qse_htre_getqmethodtype(arg->req);
proxy->version = *qse_htre_getversion(arg->req);
proxy->keepalive = (arg->req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
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:
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 (
@ -1419,7 +1422,7 @@ static int task_main_proxy_1 (
return 1;
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 (
@ -1509,7 +1512,7 @@ oops:
return (qse_httpd_entask_err (
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 (

View File

@ -33,6 +33,9 @@
#include <qse/cmn/dir.h>
#include <qse/cmn/fio.h>
#define STAT_REG 1
#define STAT_DIR 2
#if defined(_WIN32)
# include <winsock2.h>
# 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 (
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)
@ -1360,9 +1363,8 @@ static int stat_file (
return -1;
}
/* stating for a file. it should be a regular file.
* i don't allow other file types. */
if (regonly && !S_ISREG(st.st_mode))
if ((filter == STAT_REG && !S_ISREG(st.st_mode)) ||
(filter == STAT_DIR && !S_ISDIR(st.st_mode)))
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_EACCES);
return -1;
@ -1397,8 +1399,47 @@ static int file_stat (
/* 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 1 passes 1 to stat_file for it */
return stat_file (httpd, path, hst, 1);
* note that STAT_REG is passed to stat_file for it */
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 (
@ -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;
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
* 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 &&
!(req->attr.flags & QSE_HTRE_ATTR_LENGTH) &&
!(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 */
if (rsrc.type == QSE_HTTPD_RSRC_ERR)
{
qse_printf (QSE_T("DISCARD 3\n"));
qse_httpd_discardcontent (httpd, req);
}
}
@ -2122,6 +2259,7 @@ static qse_httpd_scb_t httpd_system_callbacks =
/* file operation */
{ file_stat,
file_purge,
file_ropen,
file_wopen,
file_close,
@ -2130,7 +2268,10 @@ static qse_httpd_scb_t httpd_system_callbacks =
},
/* directory operation */
{ dir_open,
{ dir_stat,
dir_make,
dir_purge,
dir_open,
dir_close,
dir_read
},
@ -2418,7 +2559,7 @@ static int make_resource (
struct rsrc_tmp_t tmp;
qse_httpd_stat_t st;
int n, stx, method;
int n, stx, acc;
QSE_MEMSET (&tmp, 0, QSE_SIZEOF(tmp));
tmp.qpath = qse_htre_getqpath(req);
@ -2429,41 +2570,27 @@ static int make_resource (
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 (tmp.root.type == QSE_HTTPD_SERVERSTD_ROOT_NWAD)
switch (tmp.root.type)
{
/* proxy the request */
target->type = QSE_HTTPD_RSRC_PROXY;
/*target->u.proxy.dst = client->orgdst_addr;*/
target->u.proxy.dst = tmp.root.u.nwad;
target->u.proxy.src = client->remote_addr;
case QSE_HTTPD_SERVERSTD_ROOT_NWAD:
/* proxy the request */
target->type = QSE_HTTPD_RSRC_PROXY;
/*target->u.proxy.dst = client->orgdst_addr;*/
target->u.proxy.dst = tmp.root.u.nwad;
target->u.proxy.src = client->remote_addr;
/* mark that this request is going to be proxied. */
req->attr.flags |= QSE_HTRE_ATTR_PROXIED;
return 0;
/* mark that this request is going to be proxied. */
req->attr.flags |= QSE_HTRE_ATTR_PROXIED;
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 */
#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);
if (server_xtn->query (httpd, client->server, req, QSE_NULL, QSE_HTTPD_SERVERSTD_REALM, &tmp.realm) <= -1 ||
@ -2543,6 +2670,7 @@ auth_ok:
else
{
/* Expectation Failed */
qse_htre_discardcontent (req);
target->type = QSE_HTTPD_RSRC_ERR;
target->u.err.code = 417;
return 0;
@ -2611,8 +2739,9 @@ auth_ok:
/* 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 (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;
/* free xpath since it won't be used */
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
@ -2620,6 +2749,7 @@ auth_ok:
else if (tmp.qpath[tmp.qpath_len - 1] != QSE_MT('/'))
{
/* the query path doesn't end with a slash. so redirect it */
qse_htre_discardcontent (req);
target->type = QSE_HTTPD_RSRC_REDIR;
target->u.redir.dst = tmp.qpath;
/* free xpath since it won't be used */
@ -2651,11 +2781,16 @@ auth_ok:
}
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 */
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 (target->u.err.code != 200)
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 || target->u.err.code > 299)
{
/* free xpath since it won't be used */
qse_htre_discardcontent (req);
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
target->type = QSE_HTTPD_RSRC_ERR;
}
@ -2664,6 +2799,8 @@ auth_ok:
/* fall back to a normal file. */
if (tmp.idxfile)
{
qse_htre_discardcontent (req);
/* free xpath since it won't be used */
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
@ -2672,6 +2809,11 @@ auth_ok:
target->u.reloc.dst = merge_paths (httpd, tmp.qpath, tmp.idxfile);
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
{
target->type = QSE_HTTPD_RSRC_FILE;
@ -2802,8 +2944,26 @@ static int query_server (
case QSE_HTTPD_SERVERSTD_DIRACC:
case QSE_HTTPD_SERVERSTD_FILEACC:
*(int*)result = 200;
{
/* 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;
break;
default:
/* method not allowed */
*(int*)result = 405;
break;
}
return 0;
}
}
qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL);

View File

@ -201,7 +201,8 @@ struct status_reloc_t
static qse_httpd_task_t* entask_status (
qse_httpd_t* httpd, qse_httpd_client_t* client,
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;
@ -209,34 +210,44 @@ static qse_httpd_task_t* entask_status (
const qse_mchar_t* extrapst = 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);
if (code == 301 || code == 307)
switch (code)
{
status_reloc_t* reloc;
reloc = (status_reloc_t*)extra;
extrapre = QSE_MT("Location: ");
extrapst = reloc->redir? QSE_MT("/\r\n"): QSE_MT("\r\n");
extraval = reloc->dst;
text[0] = QSE_MT('\0');
}
else if (code == 304)
{
text[0] = QSE_MT('\0');
}
else
{
if (httpd->opt.rcb.fmterr (httpd, client, code, text, QSE_COUNTOF(text)) <= -1) return QSE_NULL;
if (code == 401)
case 301:
case 307:
{
extrapre = QSE_MT("WWW-Authenticate: Basic realm=\"");
extrapst = QSE_MT("\"\r\n");
extraval = (const qse_mchar_t*)extra;
status_reloc_t* reloc;
reloc = (status_reloc_t*)extra;
extrapre = QSE_MT("Location: ");
extrapst = reloc->redir? QSE_MT("/\r\n"): QSE_MT("\r\n");
extraval = reloc->dst;
break;
}
case 304:
case 200:
case 201:
case 202:
case 203:
case 204:
case 205:
case 206:
/* 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)
{
extrapre = QSE_MT("WWW-Authenticate: Basic realm=\"");
extrapst = QSE_MT("\"\r\n");
extraval = (const qse_mchar_t*)extra;
}
break;
}
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_t* httpd, qse_httpd_client_t* client,
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_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, int code, qse_htre_t* req)
{
qse_htre_discardcontent (req);
return entask_status (
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 (
httpd, client, pred, 401, (void*)realm,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(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_t* httpd, qse_httpd_client_t* client,
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 (
httpd, client, pred, 301, (void*)&reloc,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(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_t* httpd, qse_httpd_client_t* client,
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 (
httpd, client, pred, 301, (void*)&reloc,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(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_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_http_version_t* version, int keepalive)
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* pred,
qse_http_method_t method, const qse_http_version_t* version, int keepalive)
{
return entask_status (
httpd, client, pred, 304,
QSE_NULL, version, keepalive);
httpd, client, pred, 304, QSE_NULL, method, version, keepalive);
}
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)
{
return entask_status (
httpd, client, pred, 304,
QSE_NULL, qse_htre_getversion(req),
httpd, client, pred, 304, QSE_NULL,
qse_htre_getqmethodtype(req),
qse_htre_getversion(req),
(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
qse_httpd_task_t* qse_httpd_entaskconnect (
qse_httpd_t* httpd,

View File

@ -63,31 +63,6 @@ static int task_main_text (
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_t* httpd,
qse_httpd_client_t* client,
@ -98,13 +73,33 @@ qse_httpd_task_t* qse_httpd_entasktext (
{
qse_size_t tlen;
qse_mchar_t b_tlen[64];
qse_http_method_t method;
qse_http_version_t* version;
qse_httpd_task_t task;
task_text_t data;
method = qse_htre_getqmethodtype (req);
version = qse_htre_getversion (req);
tlen = qse_mbslen(text);
qse_fmtuintmaxtombs (b_tlen, QSE_COUNTOF(b_tlen), tlen, 10, -1, QSE_MT('\0'), QSE_NULL);
qse_htre_discardcontent (req);
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 (
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"),
@ -116,5 +111,14 @@ qse_httpd_task_t* qse_httpd_entasktext (
);
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_task_t* pred,
int code,
qse_http_method_t method,
const qse_http_version_t* version,
int keepalive
);
@ -129,37 +130,11 @@ qse_httpd_task_t* qse_httpd_entask_nomod (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
qse_http_method_t method,
const qse_http_version_t* version,
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
}
#endif