Http模块
提供Http服务
1. 模块设计
采用Ragel(有限状态机,性能媲美汇编),实现了HTTP/1.1的简单协议实现和uri的解析。基于SocketStream实现了HttpConnection(HTTP的客户端)和HttpSession(HTTP服务器端的链接)。基于TcpServer实现了HttpServer。提供了完整的HTTP的客户端API请求功能,HTTP基础API服务器功能。
主要包含以下几个模块:
- HTTP常量定义,包括HTTP方法HttpMethod与HTTP状态HttpStatus。
- HTTP请求与响应结构,对应HttpRequest和HttpResponse。
- HTTP解析器,包含HTTP请求解析器与HTTP响应解析器,对应HttpRequestParser和HttpResponseParser。
- HTTP会话结构,对应HttpSession。
- HTTP服务器。
- HTTP Servlet。
- HTTP客户端HttpConnection,用于发起GET/POST等请求,支持连接池。
2. 模块实现
2.1 Http常量定义
2.1.1 http_parser
/* 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) \
...
2.1.2 http
/**
* @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
};
2.2 Http请求与响应结构
包括HttpRequest和HttpResponse两个结构,用于封装HTTP请求与响应。
对于HTTP请求,需要关注HTTP方法,请求路径和参数,HTTP版本,HTTP头部的key-value结构,Cookies,以及HTTP Body内容。
对于HTTP响应,需要关注HTTP版本,响应状态码,响应字符串,响应头部的key-value结构,以及响应的Body内容。
2.3 HttpParser
Http解析器
输入字节流,解析HTTP消息,包括HttpRequestParser和HttpResponseParser两个结构。
HTTP解析器基于nodejs/http-parser实现,通过套接字读到HTTP消息后将消息内容传递给解析器,解析器通过回调的形式通知调用方HTTP解析的内容。
2.3.1 HTTP请求解析类
class HttpRequestParser {
public:
/// HTTP解析类的智能指针
typedef std::shared_ptr<HttpRequestParser> ptr;
...
private:
/// http_parser
http_parser m_parser;
/// HttpRequest
HttpRequest::ptr m_data;
/// 错误码,参考http_errno
int m_error;
/// 是否解析结束
bool m_finished;
/// 当前的HTTP头部field,http-parser解析HTTP头部是field和value分两次返回
std::string m_field;
};
2.3.2 Http响应解析结构体
class HttpResponseParser {
public:
/// 智能指针类型
typedef std::shared_ptr<HttpResponseParser> ptr;
...
private:
/// HTTP响应解析器
http_parser m_parser;
/// HTTP响应对象
HttpResponse::ptr m_data;
/// 错误码
int m_error;
/// 是否解析结束
bool m_finished;
/// 当前的HTTP头部field
std::string m_field;
};
2.4 HttpSession
Http会话结构
继承自SocketStream,实现了在套接字流上读取HTTP请求与发送HTTP响应的功能,在读取HTTP请求时需要借助HTTP解析器,以便于将套接字流上的内容解析成HTTP请求。
class HttpSession : public SocketStream {
public:
typedef std::shared_ptr<HttpSession> ptr;
HttpSession(Socket::ptr sock, bool owner = true);
HttpRequest::ptr recvRequest();
int sendResponse(HttpResponse::ptr rsp);
};
2.5 HttpServer
Http服务器
继承自TcpServer,重载handleClient方法,将accept后得到的客户端套接字封装成HttpSession结构,以便于接收和发送HTTP消息。
class HttpServer : public TcpServer {
public:
typedef std::shared_ptr<HttpServer> ptr;
HttpServer(bool keepalive = false
,jujimeizuo::IOManager* worker = jujimeizuo::IOManager::GetThis()
,jujimeizuo::IOManager* io_worker = jujimeizuo::IOManager::GetThis()
,jujimeizuo::IOManager* accept_worker = jujimeizuo::IOManager::GetThis());
ServletDispatch::ptr getServletDispatch() const { return m_dispatch;}
void setServletDispatch(ServletDispatch::ptr v) { m_dispatch = v;}
virtual void setName(const std::string& v) override;
protected:
virtual void handleClient(Socket::ptr client) override;
private:
/// 是否支持长连接
bool m_isKeepalive;
/// Servlet分发器
ServletDispatch::ptr m_dispatch;
};
2.6 Http Servlet
Http请求
提供HTTP请求路径到处理类的映射,用于规范化的HTTP消息处理流程。
HTTP Servlet包括两部分,第一部分是Servlet对象,每个Servlet对象表示一种处理HTTP消息的方法,第二部分是ServletDispatch,它包含一个请求路径到Servlet对象的映射,用于指定一个请求路径该用哪个Servlet来处理。
2.6.1 Servlet
Servlet类
class Servlet {
public:
typedef std::shared_ptr<Servlet> ptr;
Servlet(const std::string& name)
:m_name(name) {}
virtual ~Servlet() {}
virtual int32_t handle(jujimeizuo::http::HttpRequest::ptr request
, jujimeizuo::http::HttpResponse::ptr response
, jujimeizuo::http::HttpSession::ptr session) = 0;
const std::string& getName() const { return m_name;}
protected:
std::string m_name;
};
2.6.2 ServletDispatch
Servlet分发器
class ServletDispatch : public Servlet {
public:
typedef std::shared_ptr<ServletDispatch> ptr;
typedef RWMutex RWMutexType;
...
private:
/// 读写互斥量
RWMutexType m_mutex;
/// 精准匹配servlet MAP
/// uri(/jujimeizuo/xxx) -> servlet
std::unordered_map<std::string, IServletCreator::ptr> m_datas;
/// 模糊匹配servlet 数组
/// uri(/jujimeizuo/*) -> servlet
std::vector<std::pair<std::string, IServletCreator::ptr> > m_globs;
/// 默认servlet,所有路径都没匹配到时使用
Servlet::ptr m_default;
};
2.7 HttpConnection
Http客户端
用于发起GET/POST等请求并获取响应,支持设置超时,keep-alive,支持连接池。
HTTP服务端的业务模型是接收请求→ 发送响应,而HTTP客户端的业务模型是发送请求→ 接收响应。
关于连接池,是指提前预备好一系列已接建立连接的socket,这样,在发起请求时,可以直接从中选择一个进行通信,而不用重复创建套接字→ 发起connect→ 发起请求 的流程。
连接池与发起请求时的keep-alive参数有关,如果使用连接池来发起GET/POST请求,在未设置keep-alive时,连接池并没有什么卵用。
class HttpConnection : public SocketStream {
friend class HttpConnectionPool;
public:
typedef std::shared_ptr<HttpConnection> ptr;
...
private:
/// 创建时间
uint64_t m_createTime = 0;
/// 该连接已使用的次数,只在使用连接池的情况下有用
uint64_t m_request = 0;
};
3. 总结
Http的各个模块相互联系、不可分割,从请求和响应,到解析,包括会话,再到服务端和客户端。
HTTP模块依赖nodejs/http-parser提供的HTTP解析器,并且直接复用了nodejs/http-parser中定义的HTTP方法与状态枚举。