📄 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/types.h>#include <sys/stat.h>#include "httppil.h"#include "httpapi.h"#include "httpint.h"////////////////////////////////////////////////////////////////////////////// global variables////////////////////////////////////////////////////////////////////////////// default pageschar g_chDefaultPage[]="index.htm";char g_chPasswordPage[]="password.htm";// post and substitution callbacksPFNSUBSTCALLBACK g_pfnSubst=NULL;UrlProcParam *g_urlProcParams=NULL;int g_urlProcCount=0;#ifdef HTTPPOSTextern PFNFILEUPLOADCALLBACK g_pfnFileUpload;extern PFNPOSTCALLBACK g_pfnPost;#endifchar *pchFileTypeTable[]={ HTTPTYPE_HTML,HTTPTYPE_TEXT,HTTPTYPE_GIF,HTTPTYPE_JPEG, HTTPTYPE_PNG,HTTPTYPE_JS,HTTPTYPE_CSS,HTTPTYPE_SWF,HTTPTYPE_OCTET}; int g_bKillWebserver; int g_bWebserverRunning=FALSE; #ifdef HTTPAUTHextern DWORD g_dwAuthenticatedNode;extern DWORD g_dwAuthenticationExpirationTime;extern OCTET g_bAuthenticationOn;#endif#ifndef NOTHREADpthread_t g_tidHttpThread;#endifchar g_nSocketRcvBuf = 0;char *g_webRoot;HttpStats g_stats;////////////////////////////////////////////////////////////////////////////// API calls///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// mwSetRcvBufSize. Change the TCP windows size of acceped sockets///////////////////////////////////////////////////////////////////////int mwSetRcvBufSize(WORD wSize){ g_nSocketRcvBuf = (char)wSize; return wSize;}HttpStats* mwGetHttpStats(){ return &g_stats;}////////////////////////////////////////////////////////////////////////////// mwServerStart// Start the webserver////////////////////////////////////////////////////////////////////////////int mwServerStart(httpParam *param){ SOCKET listenSocket; if (!g_bWebserverRunning) {#ifdef WIN32#ifndef NOTHREAD DWORD dwId;#endif#endif if (!initSocket()) { DEBUG("Error initializing Winsock\n"); return 0; } g_bKillWebserver=FALSE; g_bWebserverRunning=TRUE; memset(&g_stats,0,sizeof(g_stats));#ifdef HTTPPOST if (g_pfnPost==NULL || g_pfnFileUpload==NULL) return 0;#endif listenSocket=_mwStartListening(param); if (!listenSocket) return 0; if (g_urlProcCount>0) { DEBUG("Number of URL preprocessors: %d\n",g_urlProcCount); } if (param->webPath) { g_webRoot=malloc(strlen(param->webPath)+1); strcpy(g_webRoot,param->webPath); DEBUG("Web document root: %s\n",g_webRoot); } else { g_webRoot=NULL; } g_stats.startTime=gettime();#ifndef NOTHREAD#ifndef WIN32 pthread_create(&g_tidHttpThread,NULL,_mwHttpThread, (void*)listenSocket);#else g_tidHttpThread=CreateThread(0,0,(LPTHREAD_START_ROUTINE)_mwHttpThread,(void*)listenSocket,0,&dwId);#endif#else _mwHttpThread(listenSocket);#endif } else { DEBUG("Error: Webserver thread already running\n"); } return g_bWebserverRunning;}////////////////////////////////////////////////////////////////////////////// mwServerShutdown// Shutdown the webserver////////////////////////////////////////////////////////////////////////////int mwServerShutdown(){ DEBUG("Shutting down web server thread\n"); // signal webserver thread to quit g_bKillWebserver=TRUE; // and wait for thread to exit#ifndef NOTHREAD#ifndef WIN32 pthread_join(g_tidHttpThread,0);#else WaitForSingleObject(g_tidHttpThread,3); WSACleanup( );#endif#endif if (g_webRoot) free(g_webRoot); if (g_urlProcParams) free(g_urlProcParams); DEBUG("Webserver shutdown complete\n"); return 0;}////////////////////////////////////////////////////////////////////////////// HttpUrlRegister// Register a URL preprocessing callback////////////////////////////////////////////////////////////////////////////int mwUrlProcessorRegister(char *pchUrlBegin, PFNURLCALLBACK pfnCallback) { int i; for (i=0;i<g_urlProcCount;i++) { if (!strcmp(pchUrlBegin,(g_urlProcParams+i)->pchUrlPrefix)) { DEBUG("The preprocessor for '%s' is already registered\n",pchUrlBegin); return -1; } } g_urlProcCount++; g_urlProcParams=(UrlProcParam*)realloc(g_urlProcParams,g_urlProcCount*sizeof(UrlProcParam)); (g_urlProcParams+g_urlProcCount-1)->pchUrlPrefix=pchUrlBegin; (g_urlProcParams+g_urlProcCount-1)->pfnUrlProcCallback=pfnCallback; return g_urlProcCount;}////////////////////////////////////////////////////////////////////////////// mwSubstRegister// Register a substitution callback////////////////////////////////////////////////////////////////////////////PFNSUBSTCALLBACK mwSubstRegister(PFNSUBSTCALLBACK pfnSubstCb) { PFNSUBSTCALLBACK pfnSubstPrevCb=g_pfnSubst; // save new CB if (pfnSubstCb==NULL) return NULL; g_pfnSubst=pfnSubstCb; // return previous CB (so app can chain onto it) return pfnSubstPrevCb;}////////////////////////////////////////////////////////////////////////////// Internal (private) helper functions////////////////////////////////////////////////////////////////////////////SOCKET _mwStartListening(httpParam *param){ SOCKET listenSocket; // open listening socket 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(param->httpPort); // http port iRc=bind(listenSocket,(struct sockaddr*)&sinAddress, sizeof(struct sockaddr_in)); if (iRc<0) { DEBUG("Error binding on port %d\n",param->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,HTTPMAXPENDINGCON-1)) { DEBUG("Unable to listen on socket %d\n",listenSocket); return 0; } DEBUG("Http listen socket %d opened\n",listenSocket); return listenSocket;}////////////////////////////////////////////////////////////////////////////// _mwHttpThread// Webserver independant processing thread. Handles all connections////////////////////////////////////////////////////////////////////////////void* _mwHttpThread(SOCKET listenSocket){ HttpSocket hsSockets[HTTPMAXPENDINGCON]; int iMaxSocket; struct timeval tvSelectWait; // clear pending sockets array memset(hsSockets, 0, sizeof(hsSockets)); // and add to socket list (always first entry) hsSockets[0].hssState=HTTPSOCKETSTATE_LISTENING; hsSockets[0].iSocket=listenSocket; // main processing loop while (!g_bKillWebserver) { int lCurrentTime; int i; int iRc; SOCKET iSelectMaxFds=(SOCKET)0; fd_set fdsSelectRead; fd_set fdsSelectWrite; // clear descriptor sets FD_ZERO(&fdsSelectRead); FD_ZERO(&fdsSelectWrite); // build descriptor sets for (i=0;i<HTTPMAXPENDINGCON;i++) { if (hsSockets[i].hssState!=HTTPSOCKETSTATE_NOTINUSE) { SOCKET iSocket; // get socket fd iSocket=hsSockets[i].iSocket; iMaxSocket=i; switch(hsSockets[i].hssState) { case HTTPSOCKETSTATE_LISTENING: case HTTPSOCKETSTATE_RECEIVING: // add to read descriptor set FD_SET(iSocket,&fdsSelectRead); break; case HTTPSOCKETSTATE_SENDING: // add to write descriptor set FD_SET(iSocket,&fdsSelectWrite); break; } // check if new max socket if (iSocket>iSelectMaxFds) { iSelectMaxFds=iSocket; } } } // initialize select delay tvSelectWait.tv_sec = 10; 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); //get current time lCurrentTime=gettime(); for (i=1;i<=iMaxSocket;i++) { // check expiration timer (for non-listening, in-use sockets) if (hsSockets[i].hssState!=HTTPSOCKETSTATE_NOTINUSE && lCurrentTime > hsSockets[i].lExpirationTime) { DEBUG("Http socket timeout expired\n"); g_stats.timeOutCount++; // close connection _mwCloseSocket(&hsSockets[i]); continue; } } if (iRc>0) { // check if any socket to accept if (FD_ISSET(hsSockets[0].iSocket, &fdsSelectRead)) { int j; // find an empty slot for (j=0;j<HTTPMAXPENDINGCON;j++) { if (hsSockets[j].hssState==HTTPSOCKETSTATE_NOTINUSE) { // accept the socket _mwAcceptSocket(&hsSockets[0], &hsSockets[j]); break; } } } // check which sockets are read/write able for (i=1;i<=iMaxSocket;i++) { if (hsSockets[i].hssState!=HTTPSOCKETSTATE_NOTINUSE) { SOCKET iSocket; BOOL bRead; BOOL bWrite; // get socket fd iSocket=hsSockets[i].iSocket; // get read/write status for socket bRead=FD_ISSET(iSocket, &fdsSelectRead); bWrite=FD_ISSET(iSocket, &fdsSelectWrite); // if readable or writeable then process if (bRead || bWrite) { // process the socket _mwProcessSocket(&hsSockets[i], bRead, bWrite); } } } } } { // Close any open sockets int i; for (i=0;i<HTTPMAXPENDINGCON;i++) { _mwCloseSocket(&hsSockets[i]); } } // clear state vars g_bKillWebserver=FALSE; g_bWebserverRunning=FALSE; return NULL;} // end of _mwHttpThread////////////////////////////////////////////////////////////////////////////// _mwAcceptSocket// Accept an incoming connection////////////////////////////////////////////////////////////////////////////void _mwAcceptSocket(HttpSocket* phsListenSocket, HttpSocket* phsNewSocket){ SOCKET iSocket; DEBUG("Accpeting socket %d\n",phsListenSocket->iSocket); iSocket=accept(phsListenSocket->iSocket, NULL, NULL); if (iSocket<=0) { DEBUG("Error accepting socket\n"); } else { DEBUG("Http connection on socket %d\n",iSocket);#ifndef WIN32 // set to non-blocking to stop sends from locking up thread { int iRc; int iSockFlags; iSockFlags = fcntl(iSocket, F_GETFL, 0); //ASSERT(iSockFlags >= 0); iSockFlags |= O_NONBLOCK; iRc = fcntl(iSocket, F_SETFL, iSockFlags); //ASSERT(iRc >= 0); }#endif if (g_nSocketRcvBuf != 0) { setsockopt(iSocket, SOL_SOCKET, SO_RCVBUF, &g_nSocketRcvBuf, sizeof(g_nSocketRcvBuf)); } // allocate buffer phsNewSocket->pucData=malloc(HTTPMAXFILECHUNK); phsNewSocket->iBufferLength=HTTPMAXFILECHUNK; phsNewSocket->iSocket=iSocket; phsNewSocket->hssState=HTTPSOCKETSTATE_RECEIVING; // set expiration timer phsNewSocket->lExpirationTime=gettime()+HTTPEXPIRATIONTIME; g_stats.reqCount++; }} // end of _mwAcceptSocket////////////////////////////////////////////////////////////////////////////// _mwProcessSocket// Process a socket (read or write)////////////////////////////////////////////////////////////////////////////void _mwProcessSocket(HttpSocket* phsSocket, BOOL bRead, BOOL bWrite){ DEBUG("socket %d bWrite=%s, bRead=%s\n", phsSocket->iSocket,bWrite?"yes":"no",bRead?"yes":"no"); // ready to write if (bWrite && phsSocket->hssState==HTTPSOCKETSTATE_SENDING) { switch (phsSocket->hcsSource) { case HTTPSRC_DEFAULT: case HTTPSRC_FILE: // write next chunk of data if (phsSocket->bRawDataSend) { _mwSendRawDataChunk(phsSocket); } else { _mwSendFileChunk(phsSocket); } break; case HTTPSRC_MEM: _mwSendMemoryChunk(phsSocket); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -