⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 http.c

📁 A small implementation of web server
💻 C
📖 第 1 页 / 共 3 页
字号:
/////////////////////////////////////////////////////////////////////////////
//
// 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;
	}
	hp->bKillWebserver=FALSE;
	hp->bWebserverRunning=TRUE;

	{
		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);
#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;

    // 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((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);

    SYSLOG(LOG_INFO,"[%d] connection accepted @ %s\n",socket,GetTimeString());

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -