📄 http.c
字号:
/////////////////////////////////////////////////////////////////////////////
//
// http.c
//
// MiniWeb - mini webserver implementation
//
/////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "httppil.h"
#include "httpapi.h"
#include "httpint.h"
////////////////////////////////////////////////////////////////////////////
// global variables
////////////////////////////////////////////////////////////////////////////
// default pages
const char g_chPasswordPage[]="password.htm";
char* contentTypeTable[]={
HTTPTYPE_OCTET,HTTPTYPE_HTML,HTTPTYPE_XML,HTTPTYPE_TEXT,HTTPTYPE_CSS,HTTPTYPE_PNG,HTTPTYPE_JPEG,HTTPTYPE_GIF,HTTPTYPE_SWF,
HTTPTYPE_MPA,HTTPTYPE_MPEG,HTTPTYPE_AVI,HTTPTYPE_QUICKTIME,HTTPTYPE_QUICKTIME,HTTPTYPE_JS,HTTPTYPE_OCTET,HTTPTYPE_STREAM
};
char* defaultPages[]={"index.htm","index.html","default.htm",NULL};
FILE *fpLog=NULL;
#define LOG_INFO fpLog
////////////////////////////////////////////////////////////////////////////
// API callsc
////////////////////////////////////////////////////////////////////////////
const char *dayNames="Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
const char *monthNames="Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
const char *httpDateTimeFormat="%s, %02d %s %02d %02d:%02d:%02d GMT";
char* mwGetVarValue(HttpVariables* vars, char *varname)
{
int i;
if (vars && varname) {
for (i=0; (vars+i)->name; i++) {
if (!strcmp((vars+i)->name,varname))
return (vars+i)->value;
}
}
return NULL;
}
int mwGetVarValueInt(HttpVariables* vars, char *varname, int defval)
{
int i;
if (vars && varname) {
for (i=0; (vars+i)->name; i++) {
if (!strcmp((vars+i)->name,varname)) {
char *p = (vars+i)->value;
return p ? atoi(p) : defval;
}
}
}
return defval;
}
int mwGetHttpDateTime(time_t timer, char *buf)
{
struct tm *btm;
btm=gmtime(&timer);
return sprintf(buf,httpDateTimeFormat,
dayNames+(btm->tm_wday<<2),
btm->tm_mday,
monthNames+(btm->tm_mon<<2),
1900+btm->tm_year,
btm->tm_hour,
btm->tm_min,
btm->tm_sec);
}
////////////////////////////////////////////////////////////////////////////
// mwServerStart
// Start the webserver
////////////////////////////////////////////////////////////////////////////
int mwServerStart(HttpParam* hp)
{
if (hp->bWebserverRunning) {
DBG("Error: Webserver thread already running\n");
return -1;
}
if (!fpLog) fpLog=stderr;
if (!InitSocket()) {
DBG("Error initializing Winsock\n");
return -1;
}
{
int i;
for (i=0;(hp->pxUrlHandler+i)->pchUrlPrefix;i++) {
if ((hp->pxUrlHandler+i)->pfnEventHandler &&
(hp->pxUrlHandler+i)->pfnEventHandler(MW_INIT, hp)) {
//remove the URL handler
(hp->pxUrlHandler+i)->pfnUrlHandler=NULL;
}
}
}
/*
#ifdef HTTPPOST
if (!hp->pfnPost)
hp->pfnPost=DefaultWebPostCallback;
if (!hp->pfnFileUpload)
hp->pfnFileUpload=DefaultWebFileUploadCallback;
#endif
*/
if (!(hp->listenSocket=_mwStartListening(hp))) return -1;
hp->stats.startTime=time(NULL);
hp->bKillWebserver=FALSE;
hp->bWebserverRunning=TRUE;
#ifndef NOTHREAD
if (ThreadCreate(&hp->tidHttpThread,_mwHttpThread,(void*)hp)) {
DBG("Error creating server thread\n");
return -1;
}
#else
_mwHttpThread((void*)hp);
#endif
return 0;
}
////////////////////////////////////////////////////////////////////////////
// mwServerShutdown
// Shutdown the webserver
////////////////////////////////////////////////////////////////////////////
int mwServerShutdown(HttpParam* hp)
{
int i;
DBG("Shutting down web server thread\n");
// signal webserver thread to quit
hp->bKillWebserver=TRUE;
// and wait for thread to exit
for (i=0;hp->bWebserverRunning && i<10;i++) msleep(100);
for (i=0;(hp->pxUrlHandler+i)->pchUrlPrefix;i++) {
if ((hp->pxUrlHandler+i)->pfnUrlHandler && (hp->pxUrlHandler+i)->pfnEventHandler)
(hp->pxUrlHandler+i)->pfnEventHandler(MW_UNINIT, hp);
}
UninitSocket();
DBG("Webserver shutdown complete\n");
return 0;
}
int mwGetLocalFileName(HttpFilePath* hfp)
{
char ch;
char *p=hfp->cFilePath,*s=hfp->pchHttpPath,*upLevel=NULL;
hfp->pchExt=NULL;
hfp->fTailSlash=0;
if (hfp->pchRootPath) {
p+=_mwStrCopy(hfp->cFilePath,hfp->pchRootPath);
if (*(p-1)!=SLASH) {
*p=SLASH;
*(++p)=0;
}
}
while ((ch=*s) && ch!='?' && (int)p-(int)hfp->cFilePath<sizeof(hfp->cFilePath)-1) {
if (ch=='%') {
*(p++) = _mwDecodeCharacter(++s);
s += 2;
} else if (ch=='/') {
*p=SLASH;
upLevel=(++p);
while (*(++s)=='/');
continue;
} else if (ch=='+') {
*(p++)=' ';
s++;
} else if (ch=='.') {
if (upLevel && GETWORD(s+1)==DEFWORD('.','/')) {
s+=2;
p=upLevel;
} else {
*(p++)='.';
hfp->pchExt=p;
while (*(++s)=='.'); //avoid '..' appearing in filename for security issue
}
} else {
*(p++)=*(s++);
}
}
if (*(p-1)==SLASH) {
p--;
hfp->fTailSlash=1;
}
*p=0;
return (int)p-(int)hfp->cFilePath;
}
////////////////////////////////////////////////////////////////////////////
// Internal (private) helper functions
////////////////////////////////////////////////////////////////////////////
SOCKET _mwStartListening(HttpParam* hp)
{
SOCKET listenSocket;
int iRc;
// create a new socket
listenSocket=socket(AF_INET,SOCK_STREAM,0);
if (listenSocket<0) return 0;
#if 0
// allow reuse of port number
{
int iOptVal=1;
iRc=setsockopt(listenSocket,SOL_SOCKET,SO_REUSEADDR,
(char*)&iOptVal,sizeof(iOptVal));
if (iRc<0) return 0;
}
#endif
// bind it to the http port
{
struct sockaddr_in sinAddress;
memset(&sinAddress,0,sizeof(struct sockaddr_in));
sinAddress.sin_family=AF_INET;
sinAddress.sin_addr.s_addr=htonl((hp->flags & FLAG_LOCAL_BIND) ? 0x7f000001 : INADDR_ANY);
sinAddress.sin_port=htons(hp->httpPort); // http port
iRc=bind(listenSocket,(struct sockaddr*)&sinAddress,
sizeof(struct sockaddr_in));
if (iRc<0) {
DBG("Error binding on port %d\n",hp->httpPort);
return 0;
}
}
#ifndef WIN32
// set to non-blocking to avoid lockout issue (see Section 15.6
// in Unix network programming book)
{
int iSockFlags;
iSockFlags = fcntl(listenSocket, F_GETFL, 0);
iSockFlags |= O_NONBLOCK;
iRc = fcntl(listenSocket, F_SETFL, iSockFlags);
}
#endif
// listen on the socket for incoming calls
if (listen(listenSocket,hp->maxClients-1)) {
DBG("Unable to listen on socket %d\n",listenSocket);
return 0;
}
DBG("Http listen socket %d opened\n",listenSocket);
return listenSocket;
}
void _mwInitSocketData(HttpSocket *phsSocket)
{
memset(&phsSocket->response,0,sizeof(HttpResponse));
memset(&phsSocket->request,0,sizeof(HttpRequest));
phsSocket->fd=0;
phsSocket->flags=FLAG_RECEIVING;
phsSocket->pucData=phsSocket->buffer;
phsSocket->iDataLength=0;
phsSocket->response.iBufferSize=HTTP_BUFFER_SIZE;
phsSocket->ptr=NULL;
}
////////////////////////////////////////////////////////////////////////////
// _mwHttpThread
// Webserver independant processing thread. Handles all connections
////////////////////////////////////////////////////////////////////////////
void* _mwHttpThread(HttpParam *hp)
{
HttpSocket *phsSocketCur;
SOCKET socket;
struct sockaddr_in sinaddr;
int iRc;
// main processing loop
while (!hp->bKillWebserver) {
time_t tmCurrentTime;
SOCKET iSelectMaxFds;
fd_set fdsSelectRead;
fd_set fdsSelectWrite;
// clear descriptor sets
FD_ZERO(&fdsSelectRead);
FD_ZERO(&fdsSelectWrite);
FD_SET(hp->listenSocket,&fdsSelectRead);
iSelectMaxFds=hp->listenSocket;
// get current time
tmCurrentTime=time(NULL);
// build descriptor sets and close timed out sockets
for (phsSocketCur=hp->phsSocketHead; phsSocketCur; phsSocketCur=phsSocketCur->next) {
int iError=0;
int iOptSize=sizeof(int);
// get socket fd
socket=phsSocketCur->socket;
if (!socket) continue;
if (getsockopt(socket,SOL_SOCKET,SO_ERROR,(char*)&iError,&iOptSize)) {
// if a socket contains a error, close it
SYSLOG(LOG_INFO,"[%d] Socket no longer vaild.\n",socket);
phsSocketCur->flags=FLAG_CONN_CLOSE;
_mwCloseSocket(hp, phsSocketCur);
continue;
}
// check expiration timer (for non-listening, in-use sockets)
if (tmCurrentTime > phsSocketCur->tmExpirationTime) {
SYSLOG(LOG_INFO,"[%d] Http socket expired\n",phsSocketCur->socket);
hp->stats.timeOutCount++;
// close connection
phsSocketCur->flags=FLAG_CONN_CLOSE;
_mwCloseSocket(hp, phsSocketCur);
} else {
if (ISFLAGSET(phsSocketCur,FLAG_RECEIVING)) {
// add to read descriptor set
FD_SET(socket,&fdsSelectRead);
}
if (ISFLAGSET(phsSocketCur,FLAG_SENDING)) {
// add to write descriptor set
FD_SET(socket,&fdsSelectWrite);
}
// check if new max socket
if (socket>iSelectMaxFds) {
iSelectMaxFds=socket;
}
}
}
{
struct timeval tvSelectWait;
// initialize select delay
tvSelectWait.tv_sec = 1;
tvSelectWait.tv_usec = 0; // note: using timeval here -> usec not nsec
// and check sockets (may take a while!)
iRc=select(iSelectMaxFds+1, &fdsSelectRead, &fdsSelectWrite,
NULL, &tvSelectWait);
}
if (iRc<0) {
if (hp->bKillWebserver) break;
DBG("Select error\n");
msleep(1000);
continue;
}
if (iRc>0) {
HttpSocket *phsSocketNext;
// check which sockets are read/write able
phsSocketCur=hp->phsSocketHead;
while (phsSocketCur) {
BOOL bRead;
BOOL bWrite;
phsSocketNext=phsSocketCur->next;
// get socket fd
socket=phsSocketCur->socket;
// get read/write status for socket
bRead=FD_ISSET(socket, &fdsSelectRead);
bWrite=FD_ISSET(socket, &fdsSelectWrite);
if ((bRead|bWrite)!=0) {
//DBG("socket %d bWrite=%d, bRead=%d\n",phsSocketCur->socket,bWrite,bRead);
// if readable or writeable then process
if (bWrite && ISFLAGSET(phsSocketCur,FLAG_SENDING)) {
iRc=_mwProcessWriteSocket(hp, phsSocketCur);
} else if (bRead && ISFLAGSET(phsSocketCur,FLAG_RECEIVING)) {
iRc=_mwProcessReadSocket(hp,phsSocketCur);
} else {
iRc=-1;
DBG("Invalid socket state (flag: %08x)\n",phsSocketCur->flags);
SETFLAG(phsSocketCur,FLAG_CONN_CLOSE);
}
if (!iRc) {
// and reset expiration timer
phsSocketCur->tmExpirationTime=time(NULL)+HTTP_EXPIRATION_TIME;
} else {
_mwCloseSocket(hp, phsSocketCur);
}
}
phsSocketCur=phsSocketNext;
}
// check if any socket to accept and accept the socket
if (FD_ISSET(hp->listenSocket, &fdsSelectRead) &&
hp->stats.clientCount<hp->maxClients &&
(socket=_mwAcceptSocket(hp,&sinaddr))) {
// create a new socket structure and insert it in the linked list
phsSocketCur=(HttpSocket*)malloc(sizeof(HttpSocket));
if (!phsSocketCur) {
DBG("Out of memory\n");
break;
}
phsSocketCur->next=hp->phsSocketHead;
hp->phsSocketHead=phsSocketCur; //set new header of the list
//fill structure with data
_mwInitSocketData(phsSocketCur);
phsSocketCur->tmAcceptTime=time(NULL);
phsSocketCur->socket=socket;
phsSocketCur->tmExpirationTime=time(NULL)+HTTP_EXPIRATION_TIME;
phsSocketCur->iRequestCount=0;
phsSocketCur->request.ipAddr.laddr=ntohl(sinaddr.sin_addr.s_addr);
hp->stats.clientCount++;
//update max client count
if (hp->stats.clientCount>hp->stats.clientCountMax)
hp->stats.clientCountMax=hp->stats.clientCount;
{
IP ip=phsSocketCur->request.ipAddr;
SYSLOG(LOG_INFO,"[%d] IP: %d.%d.%d.%d\n",phsSocketCur->socket,ip.caddr[3],ip.caddr[2],ip.caddr[1],ip.caddr[0]);
}
SYSLOG(LOG_INFO,"Connected clients: %d\n",hp->stats.clientCount);
}
} else {
HttpSocket *phsSocketPrev=NULL;
// select timeout
// clean up the link list
phsSocketCur=hp->phsSocketHead;
while (phsSocketCur) {
if (!phsSocketCur->socket) {
DBG("Free up socket structure 0x%08x\n",phsSocketCur);
if (phsSocketPrev) {
phsSocketPrev->next=phsSocketCur->next;
free(phsSocketCur);
phsSocketCur=phsSocketPrev->next;
} else {
hp->phsSocketHead=phsSocketCur->next;
free(phsSocketCur);
phsSocketCur=hp->phsSocketHead;
}
} else {
phsSocketPrev=phsSocketCur;
phsSocketCur=phsSocketCur->next;
}
}
}
}
{
phsSocketCur=hp->phsSocketHead;
while (phsSocketCur) {
HttpSocket *phsSocketNext;
phsSocketCur->flags=FLAG_CONN_CLOSE;
_mwCloseSocket(hp, phsSocketCur);
phsSocketNext=phsSocketCur->next;
free(phsSocketCur);
phsSocketCur=phsSocketNext;
}
}
// clear state vars
hp->bKillWebserver=FALSE;
hp->bWebserverRunning=FALSE;
return NULL;
} // end of _mwHttpThread
////////////////////////////////////////////////////////////////////////////
// _mwAcceptSocket
// Accept an incoming connection
////////////////////////////////////////////////////////////////////////////
SOCKET _mwAcceptSocket(HttpParam* hp,struct sockaddr_in *sinaddr)
{
SOCKET socket;
int namelen=sizeof(struct sockaddr);
socket=accept(hp->listenSocket, (struct sockaddr*)sinaddr,&namelen);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -