1、http.h
- 定义了HttpMethod和HttpStatus
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
...
/* Status Codes */
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
...
/**
* @brief HTTP方法枚举
*/
enum class HttpMethod {
#define XX(num, name, string) name = num,
HTTP_METHOD_MAP(XX)
#undef XX
INVALID_METHOD
};
/**
* @brief HTTP状态枚举
*/
enum class HttpStatus {
#define XX(code, name, desc) name = code,
HTTP_STATUS_MAP(XX)
#undef XX
};
- HttpRequest和HttpResponse
Http请求和响应的格式可以参考https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Messages
HttpRequest和HttpResponse其实可以看成是容器,将请求消息和响应消息那一坨字符串,拆解开放在里面,这样就可以让机器去按照这些信息去做操作。
对于HTTP请求,需要关注HTTP方法,请求路径和参数,HTTP版本,HTTP头部的key-value结构,Cookies,以及HTTP Body内容。
对于HTTP响应,需要关注HTTP版本,响应状态码,响应字符串,响应头部的key-value结构,以及响应的Body内容。
2、http_parser.h
这个模块的作用就是将输入的字节流信息解析到HttpRequest和HttpResponse结构体中。这里是基于https://github.com/nodejs/http-parser实现的,采用了有限状态机算法,效率非常快。 - HttpRequestParser
当解析完相关项时就会执行相应的回调,将数据放到指定位置
void on_request_method(void *data, const char *at, size_t length) {
HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
HttpMethod m = CharsToHttpMethod(at);
if(m == HttpMethod::INVALID_METHOD) {
SYLAR_LOG_WARN(g_logger) << "invalid http request method: "
<< std::string(at, length);
parser->setError(1000);
return;
}
parser->getData()->setMethod(m);
}
void on_request_uri(void *data, const char *at, size_t length) {
}
void on_request_fragment(void *data, const char *at, size_t length) {
//SYLAR_LOG_INFO(g_logger) << "on_request_fragment:" << std::string(at, length);
HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
parser->getData()->setFragment(std::string(at, length));
}
void on_request_path(void *data, const char *at, size_t length) {
HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
parser->getData()->setPath(std::string(at, length));
}
void on_request_query(void *data, const char *at, size_t length) {
HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
parser->getData()->setQuery(std::string(at, length));
}
void on_request_version(void *data, const char *at, size_t length) {
HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
uint8_t v = 0;
if(strncmp(at, "HTTP/1.1", length) == 0) {
v = 0x11;
} else if(strncmp(at, "HTTP/1.0", length) == 0) {
v = 0x10;
} else {
SYLAR_LOG_WARN(g_logger) << "invalid http request version: "
<< std::string(at, length);
parser->setError(1001);
return;
}
parser->getData()->setVersion(v);
}
void on_request_header_done(void *data, const char *at, size_t length) {
//HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
}
void on_request_http_field(void *data, const char *field, size_t flen
,const char *value, size_t vlen) {
HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
if(flen == 0) {
SYLAR_LOG_WARN(g_logger) << "invalid http request field length == 0";
//parser->setError(1002);
return;
}
parser->getData()->setHeader(std::string(field, flen)
,std::string(value, vlen));
}
- HttpResponseParser
void on_response_reason(void *data, const char *at, size_t length) {
HttpResponseParser* parser = static_cast<HttpResponseParser*>(data);
parser->getData()->setReason(std::string(at, length));
}
void on_response_status(void *data, const char *at, size_t length) {
HttpResponseParser* parser = static_cast<HttpResponseParser*>(data);
HttpStatus status = (HttpStatus)(atoi(at));
parser->getData()->setStatus(status);
}
void on_response_chunk(void *data, const char *at, size_t length) {
}
void on_response_version(void *data, const char *at, size_t length) {
HttpResponseParser* parser = static_cast<HttpResponseParser*>(data);
uint8_t v = 0;
if(strncmp(at, "HTTP/1.1", length) == 0) {
v = 0x11;
} else if(strncmp(at, "HTTP/1.0", length) == 0) {
v = 0x10;
} else {
SYLAR_LOG_WARN(g_logger) << "invalid http response version: "
<< std::string(at, length);
parser->setError(1001);
return;
}
parser->getData()->setVersion(v);
}
void on_response_header_done(void *data, const char *at, size_t length) {
}
void on_response_last_chunk(void *data, const char *at, size_t length) {
}
void on_response_http_field(void *data, const char *field, size_t flen
,const char *value, size_t vlen) {
HttpResponseParser* parser = static_cast<HttpResponseParser*>(data);
if(flen == 0) {
SYLAR_LOG_WARN(g_logger) << "invalid http response field length == 0";
//parser->setError(1002);
return;
}
parser->getData()->setHeader(std::string(field, flen)
,std::string(value, vlen));
}
3、http_session.h
- 继承自SocketStream,实现了在套接字流上读取HTTP请求与发送HTTP响应的功能,在读取HTTP请求时需要借助HTTP解析器,以便于将套接字流上的内容解析成HTTP请求。 从每个请求的通信套接字读到请求数据,利用parser将流数据转换成自己定义的结构体由该模块完成。解析完后,会由servlet执行相关操作,把信息封装在HttpResponse,由该模块通过通信套接字传给客户端。
4、servlet.h - 提供HTTP请求路径到处理类的映射,用于规范化的HTTP消息处理流程。HTTP Servlet包括两部分,第一部分是Servlet对象,每个Servlet对象表示一种处理HTTP消息的方法,第二部分是ServletDispatch,它包含一个请求路径到Servlet对象的映射,用于指定一个请求路径该用哪个Servlet来处理。
/**
* @brief 处理请求
* @param[in] request HTTP请求
* @param[in] response HTTP响应
* @param[in] session HTTP连接
* @return 是否处理成功
*/
virtual int32_t handle(sylar::http::HttpRequest::ptr request
, sylar::http::HttpResponse::ptr response
, sylar::http::HttpSession::ptr session) = 0;
5、http_server.h
- 继承自TcpServer,重载handleClient方法,将accept后得到的客户端套接字封装成HttpSession结构,以便于接收和发送HTTP消息。
void HttpServer::handleClient(Socket::ptr client) {
SYLAR_LOG_DEBUG(g_logger) << "handleClient " << *client;
HttpSession::ptr session(new HttpSession(client));
do {
auto req = session->recvRequest();
if(!req) {
SYLAR_LOG_DEBUG(g_logger) << "recv http request fail, errno="
<< errno << " errstr=" << strerror(errno)
<< " cliet:" << *client << " keep_alive=" << m_isKeepalive;
break;
}
HttpResponse::ptr rsp(new HttpResponse(req->getVersion()
,req->isClose() || !m_isKeepalive));
rsp->setHeader("Server", getName());
m_dispatch->handle(req, rsp, session);
session->sendResponse(rsp);
if(!m_isKeepalive || req->isClose()) {
break;
}
} while(true);
session->close();
}
6、http_connection
- 用于发起GET/POST等请求并获取响应,支持设置超时,keep-alive,支持连接池。HTTP服务端的业务模型是接收请求→ 发送响应,而HTTP客户端的业务模型是发送请求→ 接收响应。
- 关于连接池,是指提前预备好一系列已接建立连接的socket,这样,在发起请求时,可以直接从中选择一个进行通信,而不用重复创建套接字→ 发起connect→ 发起请求 的流程。
- 连接池与发起请求时的keep-alive参数有关,如果使用连接池来发起GET/POST请求,在未设置keep-alive时,连接池并没有什么卵用。