记录一下最近这学期学习的sylar服务器框架项目,输出整理一下项目的结构,用到的知识和自己的体会
项目仓库地址
https://github.com/sylar-yin/sylar/
整理博客过程中参考的大佬资料链接:
============================================
基础介绍
本模块封装了HttpRequest类以及HttpResponse类,提供http方法,状态,版本,请求路径,参数,锚点,消息体,请求头以及cookie等部分的设置,解析以及读取方法
本模块还提供了HttpRequestParser类以及HttpResponseParser类,用于解析http请求/响应报文
方法以及状态码的枚举定义
使用xx宏批量定义了http请求方法以及http响应状态码的枚举类
/* 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) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
// ...
/* 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) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
// ...
// 使用宏将所有请求方法转为枚举类成员
enum class HttpMethod {
#define XX(num, name, string) name = num,
HTTP_METHOD_MAP(XX)
#undef XX
INVALID_METHOD
};
// 使用宏将所有响应码转为枚举类成员
enum class HttpStatus {
#define XX(code, name, desc) name = code,
HTTP_STATUS_MAP(XX)
#undef XX
};
HttpMethod StringToHttpMethod(const std::string& m); // 字符串转成HTTP方法枚举
HttpMethod CharsToHttpMethod(const char* m); // 字符串转成HTTP方法枚举
const char* HttpMethodToString(const HttpMethod& m); // HTTP方法枚举转成字符串
const char* HttpStatusToString(const HttpStatus& s); // HTTP状态枚举转成字符串
HttpMethod StringToHttpMethod(const std::string& m) {
#define XX(num, name, string) \
if(strcmp(#string, m.c_str()) == 0) { \
return HttpMethod::name; \
}
HTTP_METHOD_MAP(XX)
#undef XX
return HttpMethod::INVALID_METHOD;
}
HttpMethod CharsToHttpMethod(const char* m) {
#define XX(num, name, string) \
if(strncmp(#string, m, strlen(#string)) == 0) { \
return HttpMethod::name; \
}
HTTP_METHOD_MAP(XX)
#undef XX
return HttpMethod::INVALID_METHOD;
}
static const char* s_method_string[] = {
#define XX(num, name, string) #string,
HTTP_METHOD_MAP(XX)
#undef XX
};
const char* HttpMethodToString(const HttpMethod& m) {
uint32_t idx = (uint32_t)m;
if((idx >= (sizeof(s_method_string)) / sizeof(s_method_string[0]))) {
return "
<unknown>";
}
return s_method_string[idx];
}
const char* HttpStatusToString(const HttpStatus& s) {
switch(s) {
#define XX(num, name, msg) \
case HttpStatus::name: \
return #msg;
HTTP_STATUS_MAP(XX);
#undef XX
default:
return "
<unknown>";
}
}
HttpRequeset
// HTTP请求类
class HttpRequest {
public:
typedef std::shared_ptr
<HttpRequest> ptr;
typedef std::map<std::string, std::string, CaseInsensitiveLess> MapType;
HttpRequest(uint8_t version = 0x11, bool close = true);
std::shared_ptr
<HttpResponse> createResponse();
HttpMethod getMethod() const { return m_method; }
uint8_t getVersion() const { return m_version; }
HttpStatus getStatus() const { return m_status; }
const std::string& getPath() const { return m_path; }
const std::string& getQuery() const { return m_query; }
const std::string& getBody() const { return m_body; }
const MapType& getHeaders() const { return m_headers; }
const MapType& getParams() const { return m_params; }
const MapType& getCookies() const { return m_cookies; }
void setMethod(HttpMethod v) { m_method = v; }
void setStatus(HttpStatus v) { m_status = v; }
void setVersion(uint8_t v) { m_version = v; }
void setPath(const std::string& v) { m_path = v; }
void setQuery(const std::string& v) { m_query = v; }
void setFragment(const std::string& v) { m_fragment = v; }
void setBody(const std::string& v) { m_body = v; }
bool isClose() const { return m_close; }
void setClose(bool v) { m_close = v; }
bool isWebsocket() const { return m_websocket;}
void setWebsocket(bool v) { m_websocket = v;}
void setHeaders(const MapType& v) { m_headers = v; }
void setParams(const MapType& v) { m_params = v; }
void setCookies(const MapType& v) { m_cookies = v; }
std::string getHeader(const std::string& key, const std::string& def = "") const;
std::string getParam(const std::string& key, const std::string& def = "") const;
std::string getCookie(const std::string& key, const std::string& def = "") const;
void setHeader(const std::string& key, const std::string& val);
void setParam(const std::string& key, const std::string& val);
void setCookie(const std::string& key, const std::string& val);
void delHeader(const std::string& key);
void delParam(const std::string& key);
void delCookie(const std::string& key);
bool hasHeader(const std::string& key, std::string* val = nullptr);
bool hasParam(const std::string& key, std::string* val = nullptr);
bool hasCookie(const std::string& key, std::string* val = nullptr);
// Header参数获取(返回bool检查是否存在)
template<class T>
bool checkGetHeaderAs(const std::string& key, T& val, const T& def = T()) {
return checkGetAs(m_headers, key, val, def);
}
// Header参数获取
template<class T>
T getHeaderAs(const std::string& key, const T& def = T()) {
return getAs(m_headers, key, def);
}
// 解析参数获取(返回bool检查是否存在)
template<class T>
bool checkGetParamAs(const std::string& key, T& val, const T& def = T()) {
return checkGetAs(m_params, key, val, def);
}
// 解析参数获取
template<class T>
T getParamAs(const std::string& key, const T& def = T()) {
return getAs(m_params, key, def);
}
// Cookie参数获取(返回bool检查是否存在)
template<class T>
bool checkGetCookieAs(const std::string& key, T& val, const T& def = T()) {
return checkGetAs(m_cookies, key, val, def);
}
// Cookie参数获取
template<class T>
T getCookieAs(const std::string& key, const T& def = T()) {
return getAs(m_cookies, key, def);
}
std::ostream& dump(std::ostream& os) const; // 序列化为字符串
std::string toString() const; // 序列化为字符串
private:
HttpMethod m_method; // HTTP方法
HttpStatus m_status; // HTTP状态
uint8_t m_version; // HTTP版本(http/1.1支持长连接)
bool m_close; // 是否自动关闭
bool m_websocket; // 是否是websocket
std::string m_path; // 请求路径(/)
std::string m_query; // 请求参数(?)
std::string m_fragment; // 请求锚点(#)
std::string m_body; // 请求消息体
MapType m_headers; // 请求头Map(解析后)
MapType m_params; // 请求参数Map(解析后)
MapType m_cookies; // 请求CookieMap(解析后)
};
除了基础的解析设置等方法,还提供了字符串方法
std::ostream& HttpRequest::dump(std::ostream& os) const {
//GET /uri HTTP/1.1
//Host: wwww.sylar.top
//
//
os << HttpMethodToString(m_method) << " "
<< m_path
<< (m_query.empty() ? "" : "?")
<< m_query
<< (m_fragment.empty() ? "" : "#")
<< m_fragment
<< " HTTP/"
<< ((uint32_t)(m_version >> 4))
<< "."
<< ((uint32_t)(m_version & 0x0F))
<< "\r\n";
if(!m_websocket) {
os << "connection: " << (m_close ? "close" : "keep-alive") << "\r\n";
}
for(auto& i : m_headers) {
if(!m_websocket && strcasecmp(i.first.c_str(), "connection") == 0) {
continue;
}
os << i.first << ": " << i.second << "\r\n";
}
if(!m_body.empty()) {
os << "content-length: " << m_body.size() << "\r\n\r\n"
<< m_body;
} else {
os << "\r\n";
}
return os;
}
std::string HttpRequest::toString() const {
std::stringstream ss;
dump(ss);
return ss.str();
}
std::ostream& operator<<(std::ostream& os, const HttpRequest& req) {
return req.dump(os);
}
HttpResponse
// HTTP响应类
class HttpResponse {
public:
typedef std::shared_ptr
<HttpResponse> ptr;
typedef std::map<std::string, std::string, CaseInsensitiveLess> MapType;
HttpResponse(uint8_t version = 0x11, bool close = true);
HttpStatus getStatus() const { return m_status; }
uint8_t getVersion() const { return m_version; }
const std::string& getBody() const { return m_body; }
const std::string& getReason() const { return m_reason; }
const MapType& getHeaders() const { return m_headers; }
void setStatus(HttpStatus v) { m_status = v; }
void setVersion(uint8_t v) { m_version = v; }
void setBody(const std::string& v) { m_body = v; }
void setReason(const std::string& v) { m_reason = v; }
void setHeaders(const MapType& v) { m_headers = v; }
bool isClose() const { return m_close; }
void setClose(bool v) { m_close = v; }
bool isWebsocket() const { return m_websocket;}
void setWebsocket(bool v) { m_websocket = v;}
std::string getHeader(const std::string& key, const std::string& def = "") const;
void setHeader(const std::string& key, const std::string& val);
void delHeader(const std::string& key);
// 获取Header参数(返回bool检查是否存在)
template<class T>
bool checkGetHeaderAs(const std::string& key, T& val, const T& def = T()) {
return checkGetAs(m_headers, key, val, def);
}
// 获取Header参数
template<class T>
T getHeaderAs(const std::string& key, const T& def = T()) {
return getAs(m_headers, key, def);
}
std::ostream& dump(std::ostream& os) const; // 序列化为字符串
std::string toString() const; // 序列化为字符串
void setCookie(const std::string& key, const std::string& val,
time_t expired = 0, const std::string& path = "",
const std::string& domain = "", bool secure = false);
private:
HttpStatus m_status; // 响应状态
uint8_t m_version; // HTTP版本
bool m_close; // 是否自动关闭
bool m_websocket; // 是否是websocket
std::string m_body; // 响应消息体
std::string m_reason; // 响应原因
MapType m_headers; // 响应头Map
std::vector<std::string> m_cookies;
};
除了基础的解析设置等方法,还提供了字符串方法
std::ostream& HttpResponse::dump(std::ostream& os) const {
os << "HTTP/"
<< ((uint32_t)(m_version >> 4))
<< "."
<< ((uint32_t)(m_version & 0x0F))
<< " "
<< (uint32_t)m_status
<< " "
<< (m_reason.empty() ? HttpStatusToString(m_status) : m_reason)
<< "\r\n";
for(auto& i : m_headers) {
if(!m_websocket && strcasecmp(i.first.c_str(), "connection") == 0) {
continue;
}
os << i.first << ": " << i.second << "\r\n";
}
for(auto& i : m_cookies) {
os << "Set-Cookie: " << i << "\r\n";
}
if(!m_websocket) {
os << "connection: " << (m_close ? "close" : "keep-alive") << "\r\n";
}
if(!m_body.empty()) {
os << "content-length: " << m_body.size() << "\r\n\r\n"
<< m_body;
} else {
os << "\r\n";
}
return os;
}
std::string HttpResponse::toString() const {
std::stringstream ss;
dump(ss);
return ss.str();
}
std::ostream& operator<<(std::ostream& os, const HttpResponse& rsp){
return rsp.dump(os);
}
HttpResponseParser
基于nodejs/http-parser实现,使用ragel状态机语言生成的代码,效率极高
// HTTP请求解析器类
class HttpRequestParser {
public:
typedef std::shared_ptr
<HttpRequestParser> ptr;
HttpRequestParser(); // 构造函数
size_t execute(char* data, size_t len); // 执行解析
int isFinished(); // 解析是否结束
int hasError(); // 是否出错
HttpRequest::ptr getData() const { return m_data; }
void setError(int v) { m_error = v; }
uint64_t getContentLength(); // 获取content-length
public:
static uint64_t GetHttpRequestBufferSize();
static uint64_t GetHttpRequestMaxBodySize();
const http_parser& getParser() const { return m_parser; }
private:
http_parser m_parser; // http_parser
HttpRequest::ptr m_data; // 请求报文
int m_error; // 错误代码
// 1000: invalid method
// 1001: invalid version
// 1002: invalid field
};
// request
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) {
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) {
}
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 lenth == 0";
// parser->setError(1002);
return;
}
parser->getData()->setHeader(std::string(field, flen), std::string(value, vlen));
}
HttpRequestParser::HttpRequestParser()
:m_error(0) {
m_data.reset(new sylar::http::HttpRequest);
// 初始化http_parser
http_parser_init(&m_parser);
// 设置回调函数,状态机解析到时调用保存数据
m_parser.request_method = on_request_method;
m_parser.request_uri = on_request_uri;
m_parser.fragment = on_request_fragment;
m_parser.request_path = on_request_path;
m_parser.query_string = on_request_query;
m_parser.http_version = on_request_version;
m_parser.header_done = on_request_header_done;
m_parser.http_field = on_request_http_field;
m_parser.data = this;
}
uint64_t HttpRequestParser::getContentLength() {
return m_data->getHeaderAs
<uint64_t>("content-length", 0);
}
// 1: 成功
// 2: 有错误
// >0:已处理的字节数, 且data有效数据长度为len - v
size_t HttpRequestParser::execute(char* data, size_t len) {
size_t offset = http_parser_execute(&m_parser, data, len, 0);
memmove(data, data + offset, (len - offset));
return offset;
}
int HttpRequestParser::isFinished() {
return http_parser_finish(&m_parser);
}
int HttpRequestParser::hasError() {
return m_error || http_parser_has_error(&m_parser);
}
HttpResponseParser
基于nodejs/http-parser实现,使用ragel状态机语言生成的代码,效率极高
// HTTP响应解析器类
class HttpResponseParser {
public:
typedef std::shared_ptr
<HttpResponseParser> ptr;
HttpResponseParser(); // 构造函数
size_t execute(char* data, size_t len, bool chunk); // 执行解析
int isFinished(); // 解析是否结束
int hasError(); // 是否出错
HttpResponse::ptr getData() const { return m_data; }
void setError(int v) { m_error = v; }
uint64_t getContentLength();
public:
static uint64_t GetHttpResponseBufferSize();
static uint64_t GetHttpResponseMaxBodySize();
const httpclient_parser& getParser() const { return m_parser; }
private:
httpclient_parser m_parser; // httpclient_parser
HttpResponse::ptr m_data; // 响应报文
int m_error; // 错误代码
// 1001: invalid version
// 1002: invalid field
};
// reponse
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 lenth == 0";
// parser->setError(1002);
return;
}
parser->getData()->setHeader(std::string(field, flen), std::string(value, vlen));
}
HttpResponseParser::HttpResponseParser()
:m_error(0) {
m_data.reset(new sylar::http::HttpResponse);
// 初始化httpclient_parser
httpclient_parser_init(&m_parser);
// 设置回调函数,状态机解析到时调用保存数据
m_parser.reason_phrase = on_response_reason;
m_parser.status_code = on_response_status;
m_parser.chunk_size = on_response_chunk;
m_parser.http_version = on_response_version;
m_parser.header_done= on_response_header_done;
m_parser.last_chunk = on_response_last_chunk;
m_parser.http_field = on_response_http_field;
m_parser.data = this;
}
size_t HttpResponseParser::execute(char* data, size_t len, bool chunk) {
if(chunk) {
httpclient_parser_init(&m_parser);
}
size_t offset = httpclient_parser_execute(&m_parser, data, len, 0);
memmove(data, data + offset, (len - offset));
return offset;
}
int HttpResponseParser::isFinished() {
return httpclient_parser_finish(&m_parser);
}
int HttpResponseParser::hasError() {
return m_error || httpclient_parser_has_error(&m_parser);
}
uint64_t HttpResponseParser::getContentLength() {
return m_data->getHeaderAs
<uint64_t>("content-length", 0);
}
测试
test_http
#include "sylar/http/http.h"
#include "sylar/log.h"
void test_request() {
sylar::http::HttpRequest::ptr req(new sylar::http::HttpRequest);
req->setHeader("host", "www.sylar.top");
req->setBody("hello sylar");
req->dump(std::cout) << std::endl;
}
void test_response() {
sylar::http::HttpResponse::ptr rsp(new sylar::http::HttpResponse);
rsp->setHeader("X_X", "sylar");
rsp->setBody("hello sylar");
rsp->setStatus((sylar::http::HttpStatus)400);
rsp->setClose(false);
rsp->dump(std::cout) << std::endl;
}
int main(int argc, char** argv) {
test_request();
test_response();
return 0;
}
可以看到解析和字符串化都没问题

test_http_parser
#include "sylar/http/http_parser.h"
#include "sylar/log.h"
static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();
const char test_request_data[] = "POST / HTTP/1.1\r\n"
"Host: www.sylar.top\r\n"
"Content-Length: 10\r\n\r\n"
"1234567890";
void test_request() {
sylar::http::HttpRequestParser parser;
std::string tmp = test_request_data;
size_t s = parser.execute(&tmp[0], tmp.size());
SYLAR_LOG_INFO(g_logger) << "execute rt=" << s
<< " has_error=" << parser.hasError()
<< " is_finished=" << parser.isFinished()
<< " total=" << tmp.size()
<< " content_length=" << parser.getContentLength();
tmp.resize(tmp.size() - s);
SYLAR_LOG_INFO(g_logger) << parser.getData()->toString();
SYLAR_LOG_INFO(g_logger) << tmp;
}
const char test_response_data[] = "HTTP/1.1 200 OK\r\n"
"Date: Tue, 04 Jun 2019 15:43:56 GMT\r\n"
"Server: Apache\r\n"
"Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT\r\n"
"ETag: \"51-47cf7e6ee8400\"\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 81\r\n"
"Cache-Control: max-age=86400\r\n"
"Expires: Wed, 05 Jun 2019 15:43:56 GMT\r\n"
"Connection: Close\r\n"
"Content-Type: text/html\r\n\r\n"
"
<html>\r\n"
"<meta http-equiv=\"refresh\" content=\"0;url=http://www.baidu.com/\">\r\n"
"</html>\r\n";
void test_response() {
sylar::http::HttpResponseParser parser;
std::string tmp = test_response_data;
size_t s = parser.execute(&tmp[0], tmp.size(), false);
SYLAR_LOG_INFO(g_logger) << "execute rt=" << s
<< " has_error=" << parser.hasError()
<< " is_finished=" << parser.isFinished()
<< " total=" << tmp.size()
<< " content_length=" << parser.getContentLength()
<< " tmp[s]=" << tmp[s];
tmp.resize(tmp.size() - s);
SYLAR_LOG_INFO(g_logger) << parser.getData()->toString();
SYLAR_LOG_INFO(g_logger) << tmp;
}
int main(int argc, char** argv) {
test_request();
SYLAR_LOG_INFO(g_logger) << "--------------------------------";
test_response();
return 0;
}
可以看到成功解析http报文

总结
封装了http请求/响应以及http请求/响应解析器




严肃思考中……∑(;°Д°)