📄 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 pagesconst char g_chPasswordPage[]="password.htm";DWORD fileExtTable[]={ FILEEXT_HTM,FILEEXT_TEXT,FILEEXT_GIF,FILEEXT_JPG,FILEEXT_PNG,FILEEXT_CSS,FILEEXT_SWF, FILEEXT_MPA,FILEEXT_MPG,FILEEXT_JS,0};char* contentTypeTable[]={ HTTPTYPE_HTML,HTTPTYPE_TEXT,HTTPTYPE_GIF,HTTPTYPE_JPEG,HTTPTYPE_PNG,HTTPTYPE_CSS,HTTPTYPE_SWF, HTTPTYPE_MPA,HTTPTYPE_MPEG,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 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) { DEBUG("Error: Webserver thread already running\n"); return -1; } if (!fpLog) fpLog=stderr; if (!InitSocket()) { DEBUG("Error initializing Winsock\n"); return -1; } hp->bKillWebserver=FALSE; hp->bWebserverRunning=TRUE; { int i; for (i=0;(hp->pxUrlHandler+i)->pchUrlPrefix;i++) { if ((hp->pxUrlHandler+i)->pfnUrlHandlerInit && (hp->pxUrlHandler+i)->pfnUrlHandlerInit(hp,0)) { //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);#ifndef NOTHREAD if (ThreadCreate(&hp->tidHttpThread,_mwHttpThread,(void*)hp)) { DEBUG("Error creating server thread\n"); return -1; }#else _mwHttpThread((void*)hp);#endif return 0;}////////////////////////////////////////////////////////////////////////////// mwServerShutdown// Shutdown the webserver////////////////////////////////////////////////////////////////////////////int mwServerShutdown(HttpParam* hp){ int i; DEBUG("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)->pfnUrlHandlerInit) (hp->pxUrlHandler+i)->pfnUrlHandlerInit((void*)hp,1); } UninitSocket(); DEBUG("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=='%') { register unsigned char v; if (*(++s)==0) break; if (*s>='a' && *s<='f') v = (*s-('a'-'A'+7)) << 4; else if (*s>='A' && *s<='F') v = (*s-7) << 4; else v = *s << 4; if (*(++s)==0) break; if (*s>='a' && *s<='f') v |= (*s-('a'-'A'+7)) & 0xf; else if (*s>='A' && *s<='F') v |= (*s-7) & 0xf; else v |= *s & 0xf; *(p++)=v; s++; } 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; // allow reuse of port number { int iOptVal=1; iRc=setsockopt(listenSocket,SOL_SOCKET,SO_REUSEADDR, (char*)&iOptVal,sizeof(iOptVal)); if (iRc<0) return 0; } // 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(INADDR_ANY); sinAddress.sin_port=htons(hp->siHttpPort); // http port iRc=bind(listenSocket,(struct sockaddr*)&sinAddress, sizeof(struct sockaddr_in)); if (iRc<0) { DEBUG("Error binding on port %d\n",hp->siHttpPort); 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->siMaxClients-1)) { DEBUG("Unable to listen on socket %d\n",listenSocket); return 0; } DEBUG("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; DEBUG("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) { //DEBUG("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; DEBUG("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->siMaxClients && (socket=_mwAcceptSocket(hp,&sinaddr))) { // create a new socket structure and insert it in the linked list phsSocketCur=(HttpSocket*)malloc(sizeof(HttpSocket)); if (!phsSocketCur) { DEBUG("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->siRequestCount=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) { DEBUG("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; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -