上一篇文章《仿nginx Http服务器的设计与实现(一)——多进程和多路IO的实现》中实现了一个仿照nginx的支持高并发的服务器,但只是实现了端口监听和数据接收,并没有实现对http协议的解析,下面就对如何解析http协议进行说明。
我们可以通过浏览器访问之前所搭建的http服务器,可以看到终端输出如下:
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
在明确了这些内容之后,我们就可以开始对接收到的http请求进行解析了。本文将使用两个类,CHttpRequest和CHttpResponse来实现这一功能。下面首先修改上一篇文章中的
handleRequest方法:
//处理http请求
bool handleRequest(int connFd) {
if (connFd<=0) return false;
//读取缓存
char buff[4096];
//读取http header
int len = (int)recv(connFd, buff, sizeof(buff), 0);
if (len<=0) {
return false;
}
buff[len] = '\0';
std::cout<handleRequest(buff);
CHttpResponse *httpResponse = new CHttpResponse(httpRequest);
bool result = httpResponse->response(connFd);
//返回是否需要中断连接
std::string transformConnection(httpRequest->connection);
std::transform(transformConnection.begin(), transformConnection.end(), transformConnection.begin(), ::tolower);
return transformConnection == "Keep-Alive" && result;
}
#include "CHttpRequest.h"
#include "define.h"
using namespace std;
CHttpRequest::CHttpRequest() {
connection = "Close";
modifiedTime = "";
fileStart = 0;
fileEnd = 0;
fieldMap[TS_HTTP_HEADER_CONNECTION] = &CHttpRequest::handleConnection;
fieldMap[TS_HTTP_HEADER_AUTHORIZATION] = &CHttpRequest::handleAuthorization;
fieldMap[TS_HTTP_HEADER_RANGE] = &CHttpRequest::handleRange;
fieldMap[TS_HTTP_HEADER_IF_MOD_SINCE] = &CHttpRequest::handleIfModSince;
}
void CHttpRequest::handleRequest(char *header) {
stringstream stream;
stream<>method;
lineStream>>path;
lineStream>>version;
}else {
string fieldName;
lineStream>>fieldName;
//remove \r
line[strlen(line)-1] = '\0';
void(CHttpRequest::*func)(char*) = fieldMap[fieldName];
if (func!=NULL) {
(this->*func)(line+fieldName.length()+1);
}
}
count++;
}
}
void CHttpRequest::handleConnection(char *field) {
if (ENABLE_KEEP_ALIVE) {
connection = string(field);
}
}
void CHttpRequest::handleAuthorization(char *field) {
char authName[10], authInfo[256];
sscanf(field, "%s %s", authName, authInfo);
authorize = string(authInfo);
}
void CHttpRequest::handleRange(char *field) {
if (strstr(field, "bytes=")==field) {
char *start = strtok(field+strlen("bytes="), "-");
fileStart = start==NULL?0:atol(start);
char *end = strtok(NULL, "-");
fileEnd = end==NULL?0:atol(end);
}
}
void CHttpRequest::handleIfModSince(char *field) {
modifiedTime = string(field);
}
#include "CHttpResponse.h"
#include "CHttpRequest.h"
#include
#include "define.h"
#include
#define HTTP_RESPONSE_404 "404 Not Found Not Found
The requested URL was not found on this server.
"
std::string getStringFromTime(time_t time) {
char timeBuff[64];
struct tm tm = *gmtime(&time);
strftime(timeBuff, sizeof timeBuff, "%a, %d %b %Y %H:%M:%S %Z", &tm);
return std::string(timeBuff);
}
CHttpResponse::CHttpResponse(CHttpRequest *request) {
m_request = request;
if (m_request->method.compare(TS_HTTP_METHOD_GET_S)==0 || m_request->method.compare(TS_HTTP_METHOD_HEAD_S)==0) {
std::string path = ROOT_PATH;
if (m_request->path.compare("/")==0) {
path += ROOT_HTML;
}else {
path += m_request->path;
}
m_statusCode = 0;
//if file exist
if (isFileExist(path.c_str())) {
//if receive modified time
if (!m_request->modifiedTime.empty()) {
time_t time = fileModifiedTime(path.c_str());
if (getStringFromTime(time) == m_request->modifiedTime) {
m_statusCode = TS_HTTP_STATUS_NOT_MODIFIED;
m_statusMsg = TS_HTTP_STATUS_NOT_MODIFIED_S;
}
}
//if file modified
if (m_statusCode == 0) {
if (m_request->fileStart || m_request->fileEnd) {
long long fileSize = getFileSize(path.c_str());
//if request range satisfied
if (m_request->fileStartfileEndversion<<" "<connection<<"\r\n";
//content length
long long contentLength = 0;
//if file exist
if (!m_sendFilePath.empty()) {
//if define file end
if (m_request->fileEnd) {
contentLength = m_request->fileEnd - m_request->fileStart + 1;
}
//if define file start
else if (m_request->fileStart) {
contentLength = getFileSize(m_sendFilePath.c_str()) - m_request->fileStart + 1;
}
//if undefine start or end
else {
contentLength = getFileSize(m_sendFilePath.c_str());
}
} else if (!m_sendStr.empty()) {
contentLength = m_sendStr.length();
}
if (contentLength) {
responseStream<<"Content-Length: "<fileStart<<"-"<<(m_request->fileEnd==0?contentLength:m_request->fileEnd)<<"/"<method.compare(TS_HTTP_METHOD_HEAD_S)!=0) {
if (!m_sendFilePath.empty()) {
std::ifstream file(m_sendFilePath);
file.seekg(m_request->fileStart, std::ifstream::beg);
while(file.tellg() != -1)
{
char *p = new char[1024];
bzero(p, 1024);
file.read(p, 1024);
int n = (int)send(connFd, p, 1024, 0);
if (n < 0) {
std::cout<<"ERROR writing to socket"<
该Http服务器的代码已经上传到GitHub上,大家可以直接下载。