?? webs.c
字號:
/*
* webs.c -- GoAhead Embedded HTTP webs server
*
* Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
*
* See the file "license.txt" for usage and redistribution license requirements
*
* $Id: webs.c,v 1.16 2003/04/11 18:02:02 bporter Exp $
*/
/******************************** Description *********************************/
/*
* This module implements an embedded HTTP/1.1 web server. It supports
* loadable URL handlers that define the nature of URL processing performed.
*/
/********************************* Includes ***********************************/
#include "wsIntrn.h"
#ifdef DIGEST_ACCESS_SUPPORT
#include "websda.h"
#endif
/******************************** Global Data *********************************/
websStatsType websStats; /* Web access stats */
webs_t *webs; /* Open connection list head */
sym_fd_t websMime; /* Set of mime types */
int websMax; /* List size */
int websPort; /* Listen port for server */
char_t websHost[64]; /* Host name for the server */
char_t websIpaddr[64]; /* IP address for the server */
char_t *websHostUrl = NULL; /* URL to access server */
char_t *websIpaddrUrl = NULL; /* URL to access server */
/*********************************** Locals ***********************************/
/*
* Standard HTTP error codes
*/
websErrorType websErrors[] = {
{ 200, T("Data follows") },
{ 204, T("No Content") },
{ 301, T("Redirect") },
{ 302, T("Redirect") },
{ 304, T("Use local copy") },
{ 400, T("Page not found") },
{ 401, T("Unauthorized") },
{ 403, T("Forbidden") },
{ 404, T("Site or Page Not Found") },
{ 405, T("Access Denied") },
{ 500, T("Web Error") },
{ 501, T("Not Implemented") },
{ 503, T("Site Temporarily Unavailable. Try again.") },
{ 0, NULL }
};
#ifdef WEBS_LOG_SUPPORT
static char_t websLogname[64] = T("log.txt"); /* Log filename */
static int websLogFd; /* Log file handle */
#endif
static int websListenSock; /* Listen socket */
static char_t websRealm[64] = T("GoAhead"); /* Realm name */
static int websOpenCount = 0; /* count of apps using this module */
/**************************** Forward Declarations ****************************/
/*static char_t *websErrorMsg(int code);*/
static int websGetInput(webs_t wp, char_t **ptext, int *nbytes);
static int websParseFirst(webs_t wp, char_t *text);
static void websParseRequest(webs_t wp);
static void websSocketEvent(int sid, int mask, int data);
static int websGetTimeSinceMark(webs_t wp);
#ifdef WEBS_LOG_SUPPORT
static void websLog(webs_t wp, int code);
#endif
#ifdef WEBS_IF_MODIFIED_SUPPORT
static time_t dateParse(time_t tip, char_t *cmd);
#endif
/*********************************** Code *************************************/
/*
* Open the GoAhead WebServer
*/
int websOpenServer(int port, int retries)
{
websMimeType *mt;
if (++websOpenCount != 1) {
return websPort;
}
a_assert(port > 0);
a_assert(retries >= 0);
#ifdef WEBS_PAGE_ROM
websRomOpen();
#endif
webs = NULL;
websMax = 0;
/*
* Create a mime type lookup table for quickly determining the content type
*/
websMime = symOpen(WEBS_SYM_INIT * 4);
a_assert(websMime >= 0);
for (mt = websMimeList; mt->type; mt++) {
symEnter(websMime, mt->ext, valueString(mt->type, 0), 0);
}
/*
* Open the URL handler module. The caller should create the required
* URL handlers after calling this function.
*/
if (websUrlHandlerOpen() < 0) {
return -1;
}
websFormOpen();
#ifdef WEBS_LOG_SUPPORT
/*
* Optional request log support
*/
websLogFd = gopen(websLogname, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY,
0666);
a_assert(websLogFd >= 0);
#endif
return websOpenListen(port, retries);
}
/******************************************************************************/
/*
* Close the GoAhead WebServer
*/
void websCloseServer()
{
webs_t wp;
int wid;
if (--websOpenCount > 0) {
return;
}
/*
* Close the listen handle first then all open connections.
*/
websCloseListen();
/*
* Close each open browser connection and free all resources
*/
for (wid = websMax; webs && wid >= 0; wid--) {
if ((wp = webs[wid]) == NULL) {
continue;
}
socketCloseConnection(wp->sid);
websFree(wp);
}
#ifdef WEBS_LOG_SUPPORT
if (websLogFd >= 0) {
close(websLogFd);
websLogFd = -1;
}
#endif
#ifdef WEBS_PAGE_ROM
websRomClose();
#endif
symClose(websMime);
websFormClose();
websUrlHandlerClose();
}
/******************************************************************************/
/*
* Open the GoAhead WebServer listen port
*/
int websOpenListen(int port, int retries)
{
int i, orig;
a_assert(port > 0);
a_assert(retries >= 0);
orig = port;
/*
* Open the webs webs listen port. If we fail, try the next port.
*/
for (i = 0; i <= retries; i++) {
websListenSock = socketOpenConnection(NULL, port, websAccept, 0);
if (websListenSock >= 0) {
break;
}
port++;
}
if (i > retries) {
error(E_L, E_USER, T("Couldn't open a socket on ports %d - %d"),
orig, port - 1);
return -1;
}
/*
* Determine the full URL address to access the home page for this web server
*/
websPort = port;
bfreeSafe(B_L, websHostUrl);
bfreeSafe(B_L, websIpaddrUrl);
websIpaddrUrl = websHostUrl = NULL;
if (port == 80) {
websHostUrl = bstrdup(B_L, websHost);
websIpaddrUrl = bstrdup(B_L, websIpaddr);
} else {
fmtAlloc(&websHostUrl, WEBS_MAX_URL + 80, T("%s:%d"), websHost, port);
fmtAlloc(&websIpaddrUrl, WEBS_MAX_URL + 80, T("%s:%d"),
websIpaddr, port);
}
trace(0, T("webs: Listening for HTTP requests at address %s\n"),
websIpaddrUrl);
return port;
}
/******************************************************************************/
/*
* Close webs listen port
*/
void websCloseListen()
{
if (websListenSock >= 0) {
socketCloseConnection(websListenSock);
websListenSock = -1;
}
bfreeSafe(B_L, websHostUrl);
bfreeSafe(B_L, websIpaddrUrl);
websIpaddrUrl = websHostUrl = NULL;
}
/******************************************************************************/
/*
* Accept a connection
*/
int websAccept(int sid, char *ipaddr, int port, int listenSid)
{
webs_t wp;
int wid;
a_assert(ipaddr && *ipaddr);
a_assert(sid >= 0);
a_assert(port >= 0);
/*
* Allocate a new handle for this accepted connection. This will allocate
* a webs_t structure in the webs[] list
*/
if ((wid = websAlloc(sid)) < 0) {
return -1;
}
wp = webs[wid];
a_assert(wp);
wp->listenSid = listenSid;
ascToUni(wp->ipaddr, ipaddr, min(sizeof(wp->ipaddr), strlen(ipaddr) + 1));
/*
* Check if this is a request from a browser on this system. This is useful
* to know for permitting administrative operations only for local access
*/
if (gstrcmp(wp->ipaddr, T("127.0.0.1")) == 0 ||
gstrcmp(wp->ipaddr, websIpaddr) == 0 ||
gstrcmp(wp->ipaddr, websHost) == 0) {
wp->flags |= WEBS_LOCAL_REQUEST;
}
/*
* Arrange for websSocketEvent to be called when read data is available
*/
socketCreateHandler(sid, SOCKET_READABLE, websSocketEvent, (int) wp);
/*
* Arrange for a timeout to kill hung requests
*/
wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, (void *) wp);
trace(8, T("webs: accept request\n"));
return 0;
}
/******************************************************************************/
/*
* The webs socket handler. Called in response to I/O. We just pass control
* to the relevant read or write handler. A pointer to the webs structure
* is passed as an (int) in iwp.
*/
static void websSocketEvent(int sid, int mask, int iwp)
{
webs_t wp;
wp = (webs_t) iwp;
a_assert(wp);
if (! websValid(wp)) {
return;
}
if (mask & SOCKET_READABLE) {
websReadEvent(wp);
}
if (mask & SOCKET_WRITABLE) {
if (websValid(wp) && wp->writeSocket) {
(*wp->writeSocket)(wp);
}
}
}
/******************************************************************************/
/*
* The webs read handler. This is the primary read event loop. It uses a
* state machine to track progress while parsing the HTTP request.
* Note: we never block as the socket is always in non-blocking mode.
*/
void websReadEvent(webs_t wp)
{
char_t *text;
int rc, nbytes, len, done, fd;
a_assert(wp);
a_assert(websValid(wp));
websSetTimeMark(wp);
/*
* Read as many lines as possible. socketGets is called to read the header
* and socketRead is called to read posted data.
*/
text = NULL;
fd = -1;
for (done = 0; !done; ) {
if (text) {
bfree(B_L, text);
text = NULL;
}
/*
* Get more input into "text". Returns 0, if more data is needed
* to continue, -1 if finished with the request, or 1 if all
* required data is available for current state.
*/
while ((rc = websGetInput(wp, &text, &nbytes)) == 0) {
;
}
/*
* websGetInput returns -1 if it finishes with the request
*/
if (rc < 0) {
break;
}
/*
* This is the state machine for the web server.
*/
switch(wp->state) {
case WEBS_BEGIN:
/*
* Parse the first line of the Http header
*/
if (websParseFirst(wp, text) < 0) {
done++;
break;
}
wp->state = WEBS_HEADER;
break;
case WEBS_HEADER:
/*
* Store more of the HTTP header. As we are doing line reads, we
* need to separate the lines with '\n'
*/
if (ringqLen(&wp->header) > 0) {
ringqPutStr(&wp->header, T("\n"));
}
ringqPutStr(&wp->header, text);
break;
case WEBS_POST_CLEN:
/*
* POST request with content specified by a content length.
* If this is a CGI request, write the data to the cgi stdin.
* socketGets was used to get the data and it strips \n's so
* add them back in here.
*/
#ifndef __NO_CGI_BIN
if (wp->flags & WEBS_CGI_REQUEST) {
if (fd == -1) {
fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY,
0666);
}
gwrite(fd, text, gstrlen(text));
/*
* NOTE that the above comment is wrong -- if the content length
* is set, websGetInput() does NOT use socketGets(), it uses
* socketRead(), so the line below that adds an additional newline
* is destructive.
*/
/*gwrite(fd, T("\n"), sizeof(char_t));*/
/*
* Line removed as per BUG02488
*
nbytes += 1;
*/
} else
#endif
if (wp->query) {
if (wp->query[0] && !(wp->flags & WEBS_POST_DATA)) {
/*
* Special case where the POST request also had query data
* specified in the URL, ie. url?query_data. In this case
* the URL query data is separated by a '&' from the posted
* query data.
*/
len = gstrlen(wp->query);
wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
2) * sizeof(char_t));
wp->query[len++] = '&';
gstrcpy(&wp->query[len], text);
} else {
/*
* The existing query data came from the POST request so just
* append it.
*/
if (text != NULL)
{
len = gstrlen(wp->query);
wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
1) * sizeof(char_t));
if (wp->query) {
gstrcpy(&wp->query[len], text);
}
}
}
} else {
wp->query = bstrdup(B_L, text);
}
/*
* Calculate how much more post data is to be read.
*/
wp->flags |= WEBS_POST_DATA;
wp->clen -= nbytes;
if (wp->clen > 0) {
if (nbytes > 0) {
break;
}
done++;
break;
}
/*
* No more data so process the request, (but be sure to close
* the input file first!).
*/
if (fd != -1) {
gclose (fd);
fd = -1;
}
websUrlHandlerRequest(wp);
done++;
break;
case WEBS_POST:
/*
* POST without content-length specification
* If this is a CGI request, write the data to the cgi stdin.
* socketGets was used to get the data and it strips \n's so
* add them back in here.
*/
#ifndef __NO_CGI_BIN
if (wp->flags & WEBS_CGI_REQUEST) {
if (fd == -1) {
fd = gopen(wp->cgiStdin, O_CREAT | O_WRONLY | O_BINARY,
0666);
}
gwrite(fd, text, gstrlen(text));
gwrite(fd, T("\n"), sizeof(char_t));
} else
#endif
if (wp->query && *wp->query && !(wp->flags & WEBS_POST_DATA)) {
len = gstrlen(wp->query);
wp->query = brealloc(B_L, wp->query, (len + gstrlen(text) +
2) * sizeof(char_t));
if (wp->query) {
wp->query[len++] = '&';
gstrcpy(&wp->query[len], text);
}
} else {
wp->query = bstrdup(B_L, text);
}
wp->flags |= WEBS_POST_DATA;
done++;
break;
default:
websError(wp, 404, T("Bad state"));
done++;
break;
}
}
if (fd != -1) {
fd = gclose (fd);
}
if (text) {
bfree(B_L, text);
}
}
/******************************************************************************/
/*
* Get input from the browser. Return TRUE (!0) if the request has been
* handled. Return -1 on errors or if the request has been processed,
* 1 if input read, and 0 to instruct the caller to call again for more input.
*
* Note: socketRead will Return the number of bytes read if successful. This
* may be less than the requested "bufsize" and may be zero. It returns -1 for
* errors. It returns 0 for EOF. Otherwise it returns the number of bytes
* read. Since this may be zero, callers should use socketEof() to
* distinguish between this and EOF.
*/
static int websGetInput(webs_t wp, char_t **ptext, int *pnbytes)
{
char_t *text;
char buf[WEBS_SOCKET_BUFSIZ+1];
int nbytes, len, clen;
a_assert(websValid(wp));
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -