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 XXINVALID_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时,连接池并没有什么卵用。