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

📄 http.c

📁 A small implementation of web server
💻 C
📖 第 1 页 / 共 3 页
字号:

	if ((int)socket<=0) {
		DBG("Error accepting socket\n");
		return 0;
    } 

#ifndef WIN32
    // set to non-blocking to stop sends from locking up thread
	{
        int iRc;
        int iSockFlags;
        iSockFlags = fcntl(socket, F_GETFL, 0);
        iSockFlags |= O_NONBLOCK;
        iRc = fcntl(socket, F_SETFL, iSockFlags);
	}
#endif

	if (hp->socketRcvBufSize) {
		int iSocketBufSize=hp->socketRcvBufSize<<10;
		setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (const char*)&iSocketBufSize, sizeof(int));
	}

	return socket;
} // end of _mwAcceptSocket

int _mwBuildHttpHeader(HttpParam* hp, HttpSocket *phsSocket, time_t contentDateTime, unsigned char* buffer)
{
	char *p=buffer;
	p+=sprintf(p,HTTP200_HEADER,
		(phsSocket->request.iStartByte==0)?"200 OK":"206 Partial content",
		HTTP_KEEPALIVE_TIME,hp->maxReqPerConn,
		ISFLAGSET(phsSocket,FLAG_CONN_CLOSE)?"close":"Keep-Alive");
	p+=mwGetHttpDateTime(contentDateTime, p);
	SETWORD(p,DEFWORD('\r','\n'));
	p+=2;
	p+=sprintf(p,"Content-Type: %s\r\n",contentTypeTable[phsSocket->response.fileType]);
	if (phsSocket->response.iContentLength >= 0) {
		p+=sprintf(p,"Content-Length: %d\r\n",phsSocket->response.iContentLength);
	}
	SETDWORD(p,DEFDWORD('\r','\n',0,0));
	return (int)p-(int)buffer+2;
}

int mwParseQueryString(UrlHandlerParam* up)
{
	if (up->iVarCount==-1) {
		//parsing variables from query string
		char *p,*s = NULL;
		if (ISFLAGSET(up->hs,FLAG_REQUEST_GET)) {
			// get start of query string
			s = strchr(up->pucRequest, '?');
			if (s) {
				*(s++) = 0;
			}
#ifdef HTTPPOST
		} else {
			s = up->hs->request.pucPayload;
#endif
		}
		if (s && *s) {
			int i;
			int n = 1;
			//get number of variables
			for (p = s; *p ; ) if (*(p++)=='&') n++;
			up->pxVars = calloc(n + 1, sizeof(HttpVariables));
			up->iVarCount = n;
			//store variable name and value
			for (i = 0, p = s; i < n; p++) {
				switch (*p) {
				case '=':
					if (!(up->pxVars + i)->name) {
						*p = 0;
						(up->pxVars + i)->name = s;
						s=p+1;
					}
					break;
				case 0:
				case '&':
					*p = 0;
					if ((up->pxVars + i)->name) {
						(up->pxVars + i)->value = s;
						_mwDecodeString(s);
					} else {
						(up->pxVars + i)->name = s;
						(up->pxVars + i)->value = p;
					}
					s = p + 1;
					i++;
					break;
				}
			}
			(up->pxVars + n)->name = NULL;
		}
	}
	return up->iVarCount;
}

int _mwCheckUrlHandlers(HttpParam* hp, HttpSocket* phsSocket)
{
	UrlHandler* puh;
	UrlHandlerParam up;
	int ret=0;

	up.pxVars=NULL;
	for (puh=hp->pxUrlHandler; puh->pchUrlPrefix; puh++) {
		int iPrefixLen=strlen(puh->pchUrlPrefix);
		if (puh->pfnUrlHandler && !strncmp(phsSocket->request.pucPath,puh->pchUrlPrefix,iPrefixLen)) {
			//URL prefix matches
			memset(&up, 0, sizeof(up));
			up.hp=hp;
			up.hs = phsSocket;
			up.iDataBytes=phsSocket->response.iBufferSize;
			up.pucRequest=phsSocket->request.pucPath+iPrefixLen;
			up.pucHeader=phsSocket->buffer;
			up.pucBuffer=phsSocket->pucData;
			up.pucBuffer[0]=0;
			up.iVarCount=-1;
			phsSocket->ptr=(void*)puh->pfnUrlHandler;
			ret=(*(PFNURLCALLBACK)phsSocket->ptr)(&up);
			if (!ret) continue;
			if (ret & FLAG_DATA_REDIRECT) {
				_mwRedirect(phsSocket, up.pucBuffer);
				DBG("URL handler: redirect\n");
			} else {
				phsSocket->flags|=ret;
				phsSocket->response.fileType=up.fileType;
				hp->stats.urlProcessCount++;
				if (ret & FLAG_TO_FREE) {
					phsSocket->ptr=up.pucBuffer;	//keep the pointer which will be used to free memory later
				}
				if (ret & FLAG_DATA_RAW) {
					phsSocket->pucData=up.pucBuffer;
					phsSocket->iDataLength=up.iDataBytes;
					phsSocket->response.iContentLength=up.iContentBytes>0?up.iContentBytes:up.iDataBytes;
					DBG("URL handler: raw data)\n");
				} else if (ret & FLAG_DATA_FILE) {
					phsSocket->flags|=FLAG_DATA_FILE;
					if (up.pucBuffer[0])
						phsSocket->request.pucPath=up.pucBuffer;
					DBG("URL handler: file\n");
				} else if (ret & FLAG_DATA_FD) {
					phsSocket->flags |= FLAG_DATA_FILE;
					DBG("URL handler: file descriptor\n");
				}
				break;
			}
		}
	}
	if (up.pxVars) free(up.pxVars);
	return ret;
}

////////////////////////////////////////////////////////////////////////////
// _mwProcessReadSocket
// Process a socket (read)
////////////////////////////////////////////////////////////////////////////
int _mwProcessReadSocket(HttpParam* hp, HttpSocket* phsSocket)
{
	char *p;

#ifdef HTTPPOST
    if ((HttpMultipart*)phsSocket->ptr != NULL) {
      //_mwProcessMultipartPost(phsSocket);
      return 0;
    }
#endif
	// check if receive buffer full
	if (phsSocket->iDataLength>=MAX_REQUEST_SIZE) {
		// close connection
		SYSLOG(LOG_INFO,"Invalid request header size (%d bytes)\n",phsSocket->iDataLength);
		SETFLAG(phsSocket, FLAG_CONN_CLOSE);
		return -1;
	}
	// read next chunk of data
	{
		int sLength;
		sLength=recv(phsSocket->socket, 
						phsSocket->pucData+phsSocket->iDataLength,
						phsSocket->response.iBufferSize-phsSocket->iDataLength, 0);
		if (sLength <= 0) {
			SYSLOG(LOG_INFO,"[%d] socket closed by client\n",phsSocket->socket);
			SETFLAG(phsSocket, FLAG_CONN_CLOSE);
			return -1;
		}
		// add in new data received
		phsSocket->iDataLength+=sLength;
	}
	//check request type
	switch (GETDWORD(phsSocket->pucData)) {
	case HTTP_GET:
		SETFLAG(phsSocket,FLAG_REQUEST_GET);
		phsSocket->request.pucPath=phsSocket->pucData+5;
		break;
#ifdef HTTPPOST
	case HTTP_POST:
		SETFLAG(phsSocket,FLAG_REQUEST_POST);
		phsSocket->request.pucPath=phsSocket->pucData+6;
		break;
#endif
	}

	// check if end of request
	if (phsSocket->request.siHeaderSize==0) {
		int i=0;
		while (GETDWORD(phsSocket->buffer + i) != HTTP_HEADEREND) {
			if (++i > phsSocket->iDataLength - 3) return 0;
		}
		// reach the end of the header
		if (!ISFLAGSET(phsSocket,FLAG_REQUEST_GET|FLAG_REQUEST_POST)) {
			SYSLOG(LOG_INFO,"[%d] Unsupported method\n",phsSocket->socket);		
			SETFLAG(phsSocket,FLAG_CONN_CLOSE);
			return -1;
		}
		phsSocket->request.siHeaderSize = i + 4;
		DBG("[%d] header size: %d bytes\n",phsSocket->socket,phsSocket->request.siHeaderSize);
		if (_mwParseHttpHeader(phsSocket)) {
			SYSLOG(LOG_INFO,"Error parsing request\n");
			SETFLAG(phsSocket, FLAG_CONN_CLOSE);
			return -1;
#ifdef HTTPPOST
		} else if (ISFLAGSET(phsSocket,FLAG_REQUEST_POST)) {
			hp->stats.reqPostCount++;
			phsSocket->request.pucPayload=malloc(phsSocket->response.iContentLength+1);
			phsSocket->request.pucPayload[phsSocket->response.iContentLength]=0;
			phsSocket->iDataLength -= phsSocket->request.siHeaderSize;
			memcpy(phsSocket->request.pucPayload, phsSocket->buffer + phsSocket->request.siHeaderSize, phsSocket->iDataLength);
			phsSocket->pucData = phsSocket->request.pucPayload;
#endif
		}
		// add header zero terminator
		phsSocket->buffer[phsSocket->request.siHeaderSize]=0;
		DBG("%s",phsSocket->buffer);
	}
	if ( phsSocket->iDataLength < phsSocket->response.iContentLength ) {
		return 0;
	}
	p=phsSocket->buffer + phsSocket->request.siHeaderSize + 4;
	p=(unsigned char*)((unsigned long)p & (-4));	//keep 4-byte aligned
	*p=0;
	//keep request path
	{
		char *q;
		int iPathLen;
		for (q=phsSocket->request.pucPath;*q && *q!=' ';q++);
		iPathLen=(int)q-(int)(phsSocket->request.pucPath);
		if (iPathLen>=MAX_REQUEST_PATH_LEN) {
			DBG("Request path too long and is stripped\n");
			iPathLen=MAX_REQUEST_PATH_LEN-1;
		}
		if (iPathLen>0)
			memcpy(p,phsSocket->request.pucPath,iPathLen);
		*(p+iPathLen)=0;
		phsSocket->request.pucPath=p;
		p=(unsigned char*)(((unsigned long)(p+iPathLen+4+1))&(-4));	//keep 4-byte aligned
	}
	phsSocket->pucData=p;	//free buffer space
	phsSocket->response.iBufferSize=(HTTP_BUFFER_SIZE-(phsSocket->pucData-phsSocket->buffer)-1)&(-4);

	SYSLOG(LOG_INFO,"[%d] request path: /%s\n",phsSocket->socket,phsSocket->request.pucPath);
	hp->stats.reqCount++;
	if (ISFLAGSET(phsSocket,FLAG_REQUEST_GET|FLAG_REQUEST_POST)) {
		if (hp->pxUrlHandler) {
			if (!_mwCheckUrlHandlers(hp,phsSocket))
				SETFLAG(phsSocket,FLAG_DATA_FILE);
		}
		// set state to SENDING (actual sending will occur on next select)
		CLRFLAG(phsSocket,FLAG_RECEIVING)
		SETFLAG(phsSocket,FLAG_SENDING);
		hp->stats.reqGetCount++;
		if (ISFLAGSET(phsSocket,FLAG_DATA_FILE)) {
			// send requested page
			return _mwStartSendFile(hp,phsSocket);
		} else if (ISFLAGSET(phsSocket,FLAG_DATA_RAW)) {
			return _mwStartSendRawData(hp, phsSocket);
		}
	}
	SYSLOG(LOG_INFO,"Error occurred (might be a bug)\n");
	return -1;
} // end of _mwProcessReadSocket

////////////////////////////////////////////////////////////////////////////
// _mwProcessWriteSocket
// Process a socket (write)
////////////////////////////////////////////////////////////////////////////
int _mwProcessWriteSocket(HttpParam *hp, HttpSocket* phsSocket)
{
	if (phsSocket->iDataLength<=0) {
		SYSLOG(LOG_INFO,"[%d] Data sending completed (%d/%d)\n",phsSocket->socket,phsSocket->response.iSentBytes,phsSocket->response.iContentLength);
		return 1;
	}
	SYSLOG(LOG_INFO,"[%d] sending data\n",phsSocket->socket);
	if (ISFLAGSET(phsSocket,FLAG_DATA_RAW|FLAG_DATA_STREAM)) {
		return _mwSendRawDataChunk(hp, phsSocket);
	} else if (ISFLAGSET(phsSocket,FLAG_DATA_FILE)) {
		return _mwSendFileChunk(hp, phsSocket);
	} else {
		SYSLOG(LOG_INFO,"Invalid content source\n");
		return -1;
	}
} // end of _mwProcessWriteSocket

////////////////////////////////////////////////////////////////////////////
// _mwCloseSocket
// Close an open connection
////////////////////////////////////////////////////////////////////////////
void _mwCloseSocket(HttpParam* hp, HttpSocket* phsSocket)
{
	if (phsSocket->fd) {
		close(phsSocket->fd);
		phsSocket->fd = 0;
	}
	if (ISFLAGSET(phsSocket,FLAG_TO_FREE) && phsSocket->ptr) {
		free(phsSocket->ptr);
		phsSocket->ptr=NULL;
	}
#ifdef HTTPPOST
	if (phsSocket->request.pucPayload) {
		free(phsSocket->request.pucPayload);
	}
#endif
	if (!ISFLAGSET(phsSocket,FLAG_CONN_CLOSE) && phsSocket->iRequestCount<hp->maxReqPerConn) {
		_mwInitSocketData(phsSocket);
		//reset flag bits
		phsSocket->iRequestCount++;
		phsSocket->tmExpirationTime=time(NULL)+HTTP_KEEPALIVE_TIME;
		return;
	}
    if (phsSocket->socket != 0) {
		closesocket(phsSocket->socket);
	} else {
		SYSLOG(LOG_INFO,"[%d] bug: socket=0 (structure: 0x%x \n",phsSocket->socket,phsSocket);
	}

	hp->stats.clientCount--;
	phsSocket->iRequestCount=0;
	SYSLOG(LOG_INFO,"[%d] socket closed after responded for %d requests\n",phsSocket->socket,phsSocket->iRequestCount);
	SYSLOG(LOG_INFO,"Connected clients: %d\n",hp->stats.clientCount);
	phsSocket->socket=0;

} // end of _mwCloseSocket

__inline int _mwStrCopy(char *dest, char *src)
{
	int i;
	for (i=0; src[i]; i++) {
		dest[i]=src[i];
	}
	dest[i]=0;
	return i;
}

int _mwListDirectory(HttpSocket* phsSocket, char* dir)
{
	char cFileName[128];
	char cFilePath[MAX_PATH];
	char *p=phsSocket->pucData;
	int ret;
	char *pagebuf=phsSocket->pucData;
	int bufsize=phsSocket->response.iBufferSize;
	
	p+=sprintf(p,"<html><head><title>/%s</title></head><body><table border=0 cellpadding=0 cellspacing=0 width=100%%><h2>Directory of /%s</h2><hr>",
		phsSocket->request.pucPath,phsSocket->request.pucPath);
	if (!*dir) SETWORD(dir,DEFWORD('.',0));
	DBG("Listing directory: %s\n",dir);
	for (ret=ReadDir(dir,cFileName); !ret; ret=ReadDir(NULL,cFileName)) {
		struct stat st;
		char *s;
		int bytes;
		if (GETWORD(cFileName)==DEFWORD('.',0)) continue;
		DBG("Checking %s ...\n",cFileName);
		bytes=p-pagebuf;
		if (bytes+384>bufsize) {
			//need to expand buffer
			bufsize+=2048;
			if (!ISFLAGSET(phsSocket,FLAG_TO_FREE)) {
				//first time expanding
				SETFLAG(phsSocket,FLAG_TO_FREE);
				pagebuf=malloc(bufsize);
				memcpy(pagebuf,phsSocket->pucData,bytes);
			} else {
				pagebuf=realloc(pagebuf,bufsize);
			}
			p=pagebuf+bytes;
			DBG("Buffer expanded to %d bytes\n",bufsize);
		}
		sprintf(cFilePath,"%s/%s",dir,cFileName);
		if (stat(cFilePath,&st)) continue;
		if (st.st_mode & S_IFDIR) {
			p+=sprintf(p,"<tr><td width=35%%><a href='%s/'>%s</a></td><td width=15%%>&lt;dir&gt;</td><td width=15%%>",
				cFileName,cFileName);
		} else {
			p+=sprintf(p,"<tr><td width=35%%><a href='%s'>%s</a></td><td width=15%%>%d bytes</td><td width=15%%>",
				cFileName,cFileName,st.st_size);
			s=strrchr(cFileName,'.');
			if (s) {
				int filetype=_mwGetContentType(++s);
				if (filetype!=HTTPFILETYPE_OCTET)
					p+=_mwStrCopy(p,contentTypeTable[filetype]);
				else
					p+=sprintf(p,"%s file",s);
			}
		}
		p+=_mwStrCopy(p,"</td><td>");
		p+=mwGetHttpDateTime(st.st_mtime,p);
		p+=_mwStrCopy(p,"</td></tr>");
	}
	p+=sprintf(p,"</table><hr><i>Directory content generated by MiniWeb</i></body></html>");
	ReadDir(NULL,NULL);
	phsSocket->response.iContentLength=(phsSocket->iDataLength=p-pagebuf);
	phsSocket->response.fileType=HTTPFILETYPE_HTML;
	if (ISFLAGSET(phsSocket,FLAG_TO_FREE)) {
		phsSocket->pucData=pagebuf;
		phsSocket->ptr=pagebuf;
	}
	return 0;
}

void _mwSend404Page(HttpSocket* phsSocket)
{
	int bytes,offset=0;
	char *p=HTTP404_HEADER;

	SYSLOG(LOG_INFO,"[%d] Http file not found\n",phsSocket->socket);
	// send file not found header
	do {
		bytes=send(phsSocket->socket, p+offset,sizeof(HTTP404_HEADER)-1-offset,0);
		if (bytes<=0) break;
		offset+=bytes;
	} while (offset<sizeof(HTTP404_HEADER)-1);
}

#ifdef WIN32
#define OPEN_FLAG O_RDONLY|0x8000
#else
#define OPEN_FLAG O_RDONLY
#endif

////////////////////////////////////////////////////////////////////////////
// _mwStartSendFile
// Setup for sending of a file
////////////////////////////////////////////////////////////////////////////
int _mwStartSendFile(HttpParam* hp, HttpSocket* phsSocket)
{
	struct stat st;
	HttpFilePath hfp;

#ifdef HTTPAUTH
	// check if authenticated
	if (FALSE == _mwCheckAuthentication(phsSocket)) {
		// Not authenticated
		if (phsSocket->response.fileType==HTTPFILETYPE_HTML) {
		// send password page only
			pchFilename=(char*)g_chPasswordPage;
		}
	}
#endif

	hfp.pchRootPath=hp->pchWebPath;
	// check type of file requested
	if (!(phsSocket->flags & FLAG_DATA_FD)) {
		hfp.pchHttpPath=phsSocket->request.pucPath;
		mwGetLocalFileName(&hfp);
		// open file
		phsSocket->fd=open(hfp.cFilePath,OPEN_FLAG);
	} else {
		strcpy(hfp.cFilePath, phsSocket->request.pucPath);
		hfp.pchExt = strrchr(hfp.cFilePath, '.');
		if (hfp.pchExt) hfp.pchExt++;
	}

	if (phsSocket->fd <= 0) {
		char *p;
		int i;
		if (stat(hfp.cFilePath,&st) < 0 || !(st.st_mode & S_IFDIR)) {
			// file/dir not found
			_mwSend404Page(phsSocket);
			return -1;
		}

⌨️ 快捷键说明

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