added qse_httpd_setname(), qse_httpd_getname(), qse_httpd_fmtgmtimetobb()

added qse_fmthttptime()
added Date and Server to httpd tasks
This commit is contained in:
hyung-hwan 2012-09-13 12:31:01 +00:00
parent adb9f387f9
commit 7466287f93
13 changed files with 214 additions and 71 deletions

View File

@ -63,6 +63,7 @@
#define QSE_SECNSEC_TO_MSEC(sec,nsec) \
(((qse_ntime_t)(sec) * QSE_MSECS_PER_SEC) + ((qse_ntime_t)(nsec) / QSE_NSECS_PER_MSEC))
#define QSE_SEC_TO_MSEC(sec) ((sec) * QSE_MSECS_PER_SEC)
/**
* The qse_ntime_t type defines a numeric time type expressed in the
* number of milliseconds since the Epoch (00:00:00 UTC, Jan 1, 1970).

View File

@ -181,12 +181,18 @@ int qse_parsehttprange (
);
/*
int qse_parsehttpdatetime (
int qse_parsehttptime (
const qse_mchar_t* str,
qse_ntime_t* t
);
*/
qse_mchar_t* qse_fmthttptime (
qse_ntime_t nt,
qse_mchar_t* buf,
qse_size_t bufsz
);
/* percent-decode a string */
qse_size_t qse_perdechttpstr (
const qse_mchar_t* str,

View File

@ -422,6 +422,38 @@ void qse_httpd_completecontent (
qse_htre_t* req
);
/**
* The qse_httpd_setname() function changes the string
* to be used as the value for the server header.
*/
void qse_httpd_setname (
qse_httpd_t* httpd,
const qse_mchar_t* name
);
/**
* The qse_httpd_getname() function returns the
* pointer to the string used as the value for the server
* header.
*/
qse_mchar_t* qse_httpd_getname (
qse_httpd_t* httpd
);
/**
* The qse_httpd_fmtgmtimetobb() function converts a numeric time @a nt
* to a string and stores it in a built-in buffer.
* If @a nt is QSE_NULL, the current time is used.
*/
const qse_mchar_t* qse_httpd_fmtgmtimetobb (
qse_httpd_t* httpd,
const qse_ntime_t* nt,
int idx
);
#define qse_httpd_gettaskxtn(httpd,task) ((void*)(task+1))
qse_httpd_task_t* qse_httpd_entask (
@ -506,6 +538,12 @@ qse_httpd_task_t* qse_httpd_entaskfile (
qse_htre_t* req
);
/**
* The qse_httpd_entaskphat() functions a dispatcher between
* qse_httpd_entaskdir() and qse_httpd_entaskfile(). It calls
* the former if @a name is a directory and calls the latter
* otherwise.
*/
qse_httpd_task_t* qse_httpd_entaskpath (
qse_httpd_t* httpd,
qse_httpd_client_t* client,

View File

@ -23,19 +23,25 @@
#if defined(_WIN32)
# include <windows.h>
# include <time.h>
#elif defined(__OS2__)
# define INCL_DOSDATETIME
# define INCL_DOSERRORS
# include <os2.h>
# include <time.h>
#elif defined(__DOS__)
# include <dos.h>
# include <time.h>
#else
# include "syscall.h"
# if defined(HAVE_SYS_TIME_H)
# include <sys/time.h>
# endif
# if defined(HAVE_TIME_H)
# include <time.h>
# endif
#endif
#include <time.h>
#if defined(_WIN32)
#define WIN_EPOCH_YEAR (1601)
#define WIN_EPOCH_MON (1)
@ -479,4 +485,3 @@ int qse_timelocal (const qse_btime_t* bt, qse_ntime_t* nt)
return 0;
#endif
}

View File

@ -24,6 +24,8 @@
#include <qse/cmn/htb.h>
#include "../cmn/mem.h"
#include <qse/cmn/stdio.h> /* for snprintf. TODO: remove this. */
int qse_comparehttpversions (
const qse_http_version_t* v1,
const qse_http_version_t* v2)
@ -188,13 +190,59 @@ int qse_parsehttprange (const qse_mchar_t* str, qse_http_range_t* range)
}
#if 0
int qse_parsehttpdatetime (const qse_mchar_t* str, qse_ntime_t* t)
int qse_parsehttptime (const qse_mchar_t* str, qse_ntime_t* t)
{
/* TODO: */
return -1;
}
#endif
qse_mchar_t* qse_fmthttptime (
qse_ntime_t nt, qse_mchar_t* buf, qse_size_t bufsz)
{
static const qse_mchar_t* wday_name[] =
{
QSE_MT("Sun"),
QSE_MT("Mon"),
QSE_MT("Tue"),
QSE_MT("Wed"),
QSE_MT("Thu"),
QSE_MT("Fri"),
QSE_MT("Sat")
};
static const qse_mchar_t* mon_name[] =
{
QSE_MT("Jan"),
QSE_MT("Feb"),
QSE_MT("Mar"),
QSE_MT("Apr"),
QSE_MT("May"),
QSE_MT("Jun"),
QSE_MT("Jul"),
QSE_MT("Aug"),
QSE_MT("Sep"),
QSE_MT("Oct"),
QSE_MT("Nov"),
QSE_MT("Dec")
};
qse_btime_t bt;
qse_gmtime (nt, &bt);
/* TODO: avoid using snprintf () */
snprintf (buf, bufsz,
QSE_MT("%s, %d %s %d %02d:%02d:%02d GMT"),
wday_name[bt.wday],
bt.mday,
mon_name[bt.mon],
bt.year + QSE_BTIME_YEAR_BASE,
bt.hour, bt.min, bt.sec);
return buf;
}
qse_size_t qse_perdechttpstr (const qse_mchar_t* str, qse_mchar_t* buf)
{
const qse_mchar_t* p = str;

View File

@ -181,10 +181,11 @@ static int cgi_capture_script_header (qse_htre_t* req, const qse_mchar_t* key, c
{
task_cgi_t* cgi = (task_cgi_t*)ctx;
/* capture a header excluding Status and Connection */
/* capture a header except Status, Connection, Transfer-Encoding, and Server */
if (qse_mbscasecmp (key, QSE_MT("Status")) != 0 &&
qse_mbscasecmp (key, QSE_MT("Connection")) != 0 &&
qse_mbscasecmp (key, QSE_MT("Transfer-Encoding")) != 0)
qse_mbscasecmp (key, QSE_MT("Transfer-Encoding")) != 0 &&
qse_mbscasecmp (key, QSE_MT("Server")) != 0)
{
return cgi_add_header_to_buffer (cgi, cgi->res, key, val);
}
@ -268,6 +269,28 @@ static int cgi_htrd_peek_script_output (qse_htrd_t* htrd, qse_htre_t* req)
}
}
/* Add the server header. the server header in the cgi output will
* be ignored by cgi_capture_script_header() */
if (qse_mbs_cat (cgi->res, QSE_MT("Server: ")) == (qse_size_t)-1 ||
qse_mbs_cat (cgi->res, qse_httpd_getname (cgi->httpd)) == (qse_size_t)-1 ||
qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1)
{
cgi->httpd->errnum = QSE_HTTPD_ENOMEM;
return -1;
}
if (qse_htre_getheaderval (req, QSE_MT("Date")) == QSE_NULL)
{
/* generate the Date header if it's not included in the script output */
if (qse_mbs_cat (cgi->res, QSE_MT("Date: ")) == (qse_size_t)-1 ||
qse_mbs_cat (cgi->res, qse_httpd_fmtgmtimetobb (cgi->httpd, QSE_NULL, 0)) == (qse_size_t)-1 ||
qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1)
{
cgi->httpd->errnum = QSE_HTTPD_ENOMEM;
return -1;
}
}
keepalive = cgi->keepalive;
if (req->attr.flags & QSE_HTRE_ATTR_LENGTH)
{

View File

@ -164,6 +164,7 @@ static int task_main_dseg (
}
/* the buffer size is fixed to QSE_COUNTOF(ctx->buf).
*
* the number of digits need to hold the the size converted to
* a hexadecimal notation is roughly (log16(QSE_COUNTOF(ctx->buf) + 1).
* it should be safter to use ceil(log16(QSE_COUNTOF(ctx->buf)) + 1
@ -410,8 +411,10 @@ static QSE_INLINE int task_main_dir (
{
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\nContent-Type: text/html\r\n%s\r\n"),
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(""))
);
@ -438,8 +441,10 @@ static QSE_INLINE int task_main_dir (
{
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 301 Moved Permanently\r\nContent-Length: 0\r\nConnection: %s\r\nLocation: %s/\r\n\r\n"),
QSE_MT("HTTP/%d.%d 301 Moved Permanently\r\nServer: %s\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\nLocation: %s/\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")),
dir->path
);

View File

@ -22,7 +22,9 @@
#include "../cmn/mem.h"
#include "../cmn/syscall.h"
#include <qse/cmn/str.h>
#include <qse/cmn/fmt.h>
#include <qse/cmn/path.h>
#include <qse/cmn/time.h>
#include <qse/cmn/stdio.h> /* TODO: remove this */
typedef struct task_file_t task_file_t;
@ -30,6 +32,7 @@ struct task_file_t
{
const qse_mchar_t* path;
qse_http_range_t range;
qse_ntime_t if_modified_since;
qse_http_version_t version;
int keepalive;
};
@ -174,6 +177,8 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
if (file->range.type != QSE_HTTP_RANGE_NONE)
{
qse_mchar_t tmp[4][64];
if (file->range.type == QSE_HTTP_RANGE_SUFFIX)
{
if (file->range.to > st.size) file->range.to = st.size;
@ -191,37 +196,23 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
if (file->range.to >= st.size) file->range.to = st.size - 1;
#if (QSE_SIZEOF_LONG_LONG > 0)
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[3], QSE_COUNTOF(tmp[3]), st.size, 10, -1, QSE_MT('\0'), QSE_NULL);
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\nContent-Range: bytes %llu-%llu/%llu\r\n\r\n"),
file->version.major,
file->version.minor,
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\n%s%s%sContent-Length: %s\r\nContent-Range: bytes %s-%s/%s\r\nAccept-Ranges: bytes\r\nLast-Modified: %s\r\n\r\n"),
file->version.major, file->version.minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long long)(file->range.to - file->range.from + 1),
(unsigned long long)file->range.from,
(unsigned long long)file->range.to,
(unsigned long long)st.size
tmp[0], tmp[1], tmp[2], tmp[3]
);
#else
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\nContent-Range: bytes %lu-%lu/%lu\r\n\r\n"),
file->version.major,
file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long)(file->range.to - file->range.from + 1),
(unsigned long)file->range.from,
(unsigned long)file->range.to,
(unsigned long)st.size
);
#endif
if (x)
{
x = entask_file_segment (
@ -234,36 +225,28 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
}
else
{
/* TODO: int64 format.... don't hard code it llu */
qse_mchar_t b_fsize[64];
qse_fmtuintmaxtombs (b_fsize, QSE_COUNTOF(b_fsize), st.size, 10, -1, QSE_MT('\0'), QSE_NULL);
/* wget 1.8.2 set 'Connection: keep-alive' in the http 1.0 header.
* if the reply doesn't contain 'Connection: keep-alive', it didn't
* close connection.*/
#if (QSE_SIZEOF_LONG_LONG > 0)
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\n\r\n"),
file->version.major, file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long long)st.size
);
#else
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\n\r\n"),
file->version.major,
file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long)st.size
);
#endif
if (x) x = entask_file_segment (httpd, client, x, handle, 0, st.size);
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\n%s%s%sContent-Length: %s\r\nAccept-Ranges: bytes\r\nLast-Modified: %s\r\n\r\n"),
file->version.major, file->version.minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
b_fsize,
qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1)
);
if (x) x = entask_file_segment (httpd, client, x, handle, 0, st.size);
}
if (x) return 0;
@ -305,11 +288,13 @@ qse_httpd_task_t* qse_httpd_entaskfile (
data.range.type = QSE_HTTP_RANGE_NONE;
}
data.if_modified_since = QSE_TYPE_MIN(qse_ntime_t);
/*
TODO: If-Modified-Since...
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
if (tmp)
{
qse_httpd_parsegmtime (tmp, &
}
*/

View File

@ -987,7 +987,7 @@ static int file_stat (
#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
hst->mtime = QSE_SECNSEC_TO_MSEC(st.st_mtimespec.tv_sec,st.st_mtimespec.tv_nsec);
#else
hst->mtime = st.st_mtime * QSE_MSECS_PER_SEC;
hst->mtime = QSE_SEC_TO_MSEC(st.st_mtime);
#endif
hst->mime = qse_mbsend (path, QSE_MT(".html"))? QSE_MT("text/html"):

View File

@ -392,8 +392,10 @@ qse_httpd_task_t* qse_httpd_entask_error (
return qse_httpd_entaskformat (
httpd, client, pred,
QSE_MT("HTTP/%d.%d %d %s\r\nConnection: %s\r\nContent-Type: text/html\r\nContent-Length: %lu\r\n\r\n%s\r\n\r\n"),
QSE_MT("HTTP/%d.%d %d %s\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: text/html\r\nContent-Length: %lu\r\n\r\n%s\r\n\r\n"),
version->major, version->minor, code, smsg,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
(keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(unsigned long)qse_mbslen(lmsg) + 4, lmsg
);
@ -435,8 +437,10 @@ qse_httpd_task_t* qse_httpd_entaskauth (
return qse_httpd_entaskformat (
httpd, client, pred,
QSE_MT("HTTP/%d.%d 401 Unauthorized\r\nConnection: %s\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Type: text/html\r\nContent-Length: %lu\r\n\r\n%s\r\n\r\n"),
QSE_MT("HTTP/%d.%d 401 Unauthorized\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Type: text/html\r\nContent-Length: %lu\r\n\r\n%s\r\n\r\n"),
version->major, version->minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
((req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE)? QSE_MT("keep-alive"): QSE_MT("close")),
realm, (unsigned long)qse_mbslen(lmsg) + 4, lmsg);
}

View File

@ -18,14 +18,10 @@
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_WIN32) || defined(__DOS__) || defined(__OS2__)
/* UNSUPPORTED YET.. */
/* TODO: IMPLEMENT THIS */
#else
#include "httpd.h"
#include "../cmn/mem.h"
#include "../cmn/syscall.h"
#include <qse/cmn/chr.h>
#include <qse/cmn/str.h>
#include <qse/cmn/mbwc.h>
@ -97,6 +93,7 @@ int qse_httpd_init (qse_httpd_t* httpd, qse_mmgr_t* mmgr)
{
QSE_MEMSET (httpd, 0, QSE_SIZEOF(*httpd));
httpd->mmgr = mmgr;
qse_mbscpy (httpd->sname, QSE_MT("QSE-HTTPD " QSE_PACKAGE_VERSION));
return 0;
}
@ -593,8 +590,7 @@ static void free_server_list (qse_httpd_t* httpd, qse_httpd_server_t* server)
}
}
static qse_httpd_server_t* parse_server_uri (
qse_httpd_t* httpd, const qse_char_t* uri)
static qse_httpd_server_t* parse_server_uri (qse_httpd_t* httpd, const qse_char_t* uri)
{
qse_httpd_server_t* server;
qse_uint16_t default_port;
@ -1234,4 +1230,31 @@ void qse_httpd_completecontent (qse_httpd_t* httpd, qse_htre_t* req)
qse_htre_completecontent (req);
}
#endif
/* --------------------------------------------------- */
void qse_httpd_setname (qse_httpd_t* httpd, const qse_mchar_t* name)
{
qse_mbsxcpy (httpd->sname, QSE_COUNTOF(httpd->sname), name);
}
qse_mchar_t* qse_httpd_getname (qse_httpd_t* httpd)
{
return httpd->sname;
}
const qse_mchar_t* qse_httpd_fmtgmtimetobb (
qse_httpd_t* httpd, const qse_ntime_t* nt, int idx)
{
qse_ntime_t now;
QSE_ASSERT (idx >= 0 && idx < QSE_COUNTOF(httpd->gtbuf));
if (nt == QSE_NULL)
{
if (qse_gettime(&now) <= -1) now = 0;
nt = &now;
}
qse_fmthttptime (*nt, httpd->gtbuf[idx], QSE_COUNTOF(httpd->gtbuf[idx]));
return httpd->gtbuf[idx];
}

View File

@ -36,6 +36,9 @@ struct qse_httpd_t
int option;
int stopreq;
qse_mchar_t sname[128]; /* server name for the server header */
qse_mchar_t gtbuf[10][64]; /* GMT time buffers */
struct
{
struct

View File

@ -209,6 +209,8 @@ static int httpd_main (int argc, qse_char_t* argv[])
signal (SIGINT, sigint);
signal (SIGPIPE, SIG_IGN);
qse_httpd_setname (httpd, QSE_MT("httpd02/qse 1.0"));
qse_httpd_setoption (httpd, QSE_HTTPD_CGIERRTONUL);
ret = qse_httpd_loopstd (httpd, &rcb, 10000);