1️⃣ 概述

sylar 框架中,HttpServerTcpServer 的核心派生类,也是整个框架在 Web 开发领域的直接入口。如果说 TcpServer 负责的是 底层“连接管理”,那么 HttpServer 负责的就是 “业务协议生命周期的全链路治理”

HttpServer 不仅继承了 TcpServer 高并发协程驱动 的优良基因,还引入了现代 Web 框架必备的 SSL 安全通信中间件插件化、**Session 状态管理** 以及 Servlet 路由分发 等高级特性。

2️⃣ 设计核心

HttpServer核心逻辑 高度凝练在重写的 handleClient 方法中。

handleClientsocket 接收到的 Http 请求 转化成最终的 HTTP 响应,这其中经历了一套标准化的 “流水线” 操作:

  • 安全加固 (SSL/TLS): 识别连接属性。若是 HTTPS 请求,则在最前端完成 非对称加密握手 逻辑(doHandShake),确保后续数据在加密流(SslSocketStream)中传输。
  • 状态化封装 Session 将原始 socket 包装为 HttpSession,通过 流式接口 屏蔽底层读写细节,实现对 HTTP 请求报文的自动化解析。
  • 拦截与增强 (Middleware): 通过 MiddleChain 机制,在业务逻辑执行前后嵌入中间件。无论是跨域处理(CORS)、日志审计还是身份验证,都能以 非侵入 的方式接入。
  • 业务路由 (Servlet Dispatch): 根据请求的 URI,通过 ServletDispatch 精确匹配对应的业务处理器(Servlet),实现 “一对一” 的逻辑分发。

3️⃣ 相关组件

1️⃣ httpSession

HttpSession 类可以理解成 “一次 HTTP 连接的会话封装”

  • 把底层 socket 流(SocketStream) 包装起来:提供 收请求发响应关闭连接 这三个最核心的 HTTP 服务端操作,让上层业务不需要直接跟 socket 打交道。
  • HttpSession 不关心底层是 TCP 还是 SSL,它只依赖 “流” 的读写能力。

整体框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class HttpSession
{
public:
using ptr = std::shared_ptr<HttpSession>;

/**
* @brief 构造函数
*/
HttpSession(SocketStream::ptr stream, bool owner = true);


/**
* @brief 用来接收Http请求
*/
HttpRequest::ptr recvRequest();

/**
* @brief 用来发送Http响应
* @param[in] rsp HTTP响应
* @return >0 发送成功
* =0 对方关闭
* <0 Socket异常
*/
int sendResponse(HttpResponse::ptr rsp);

/**
* @brief 关闭套接字
*/
void close();

private:
SocketStream::ptr m_stream;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
HttpSession::HttpSession(SocketStream::ptr stream, bool owner)
: m_stream(stream)
{}

HttpRequest::ptr HttpSession::recvRequest()
{
// 定义HttpRequest解析器
HttpRequestParser::ptr parser(new HttpRequestParser);
// 获得最大可解析的长度
uint64_t buff_size = HttpRequestParser::GetHttpRequestBufferSize();
// 定义一个字符串数组
std::shared_ptr<char> buffer(new char[buff_size], [](char* ptr) { delete[] ptr; });
// 获得原始数组的指针
char* data = buffer.get();
// 定义读取数据的偏移量
int offset = 0;
// 通过do-while循环来解析请求
do
{
// read函数的调用过程: 此处的read -> SocketStream的read -> Socket的recv -> 此处的Socket是accept返回的socket -> 用来接受对方发送的请求
int len = m_stream->read(data + offset, buff_size - offset);
// 如果没有读取到数据就关闭连接
if (len <= 0)
{
m_stream->close();
return nullptr;
}
SYLAR_LOG_DEBUG(g_logger) << "read bytes: " << len;
SYLAR_LOG_DEBUG(g_logger) << "raw data: " << std::string(data, len);
// 此处代表读取到了数据
len += offset;
// 调用解析器来进行解析: nparse表示解析了的长度
size_t nparse = parser->execute(data, len);
// 判断解析结果
if (parser->hasError())
{
m_stream->close();
return nullptr;
}
// 此时的offset 表示未解析的长度
offset = len - nparse;
// 这表示解析长度超过了最大解析长度
if (offset == (int)buff_size)
{
m_stream->close();
return nullptr;
}
// 这表示解析终止
if (parser->isFinished()) {
break;
}
} while (true);
// 设置连接状态
parser->getHttpRequest()->init();
return parser->getHttpRequest();
}

int HttpSession::sendResponse(HttpResponse::ptr rsp)
{
std::stringstream ss;
ss << *rsp;
std::string data = ss.str();
return m_stream->writeFixSize(data.c_str(), data.size());
}

void HttpSession::close()
{
m_stream->close();
}
  • m_stream:保存 socket 连接通。SocketStream 类保存了从 socket 读/写能力。
  • recvRequest():从字节流 SocketStream读出一个完整的 Http 请求。
  • sendResponse():它会把 HttpResponse 变成标准 HTTP 响应文本并写入 socket,并将其发送给客户端。
  • close():关闭 socket 的读写能力

2️⃣ SSL 相关成员

SslConfig

HttpServer 迈向生产环境的过程中,安全通信 HTTPS 是无法绕过的门槛。SslConfig 类是简单的配置集合,但它实际上是服务器 “安全策略的蓝图”

ssl_config 主要提供各个成员变量的 set/get 方法。

成员变量

  • m_certfile 存放服务器的公钥证书。通常是 .crt.pem 格式

    在握手阶段发送给客户端,包含服务器的名称、公钥以及 CA 的签名。它是客户端验证服务器合法性的“身份证”。

  • m_keyfile 存放服务器的私钥。通常是 .key.pem 格式)。

    用于解密客户端发送的对称密钥,或在握手时进行数字签名。

  • m_chainfile 存放中间 CA(证书颁发机构)的证书。

    如果 服务器证书 不是直接由 顶级根证书 签发的,客户端需要通过 证书链 追溯到它信任的根证书

  • m_version 指定使用的 TLS 版本。

    主要用于 控制安全上下限。例如,TLS 1.3 移除了许多不安全的加密算法并优化了握手时间。通常建议使用 TLS_1_2 或更高。

  • m_cipherList 定义服务器支持的一系列 加密算法

    规定双方如何交换密钥、如何加密数据、如何校验完整性。

  • m_verifyClient 是否开启双向认证 (mTLS) 开关。

    开启后,服务器在握手时会强制要求客户端提供证书。常用于银行、API 网关或微服务间内部通信。

  • m_verifyDepth 证书链校验的最大深度。

    例如设为 4,表示服务器在验证客户端证书时,最多往上追溯 4 级 CA。超过此深度则视为无效,防止通过过长的证书链进行伪造攻击。

  • m_sessionTimeout SSL Session 缓存的有效期(单位:秒)。

    客户端在有效期内重连时,可以跳过繁琐的非对称加密握手,直接恢复之前的会话,大幅降低 CPU 负载。

  • m_sessionCacheSize 服务器内存中存储 SSL 会话信息的最大条数。

    限制缓存占用的内存。对于超大规模并发的服务器,需要增大此值以提高会话复用率,减少频繁的完整握手。

SslContext

sylar 框架中,SslContext 扮演的是 OpenSSL 资源的掌门人。

它的核心意义在于将 “复杂的 C API 过程式初始化” 封装为 “简洁的 C++ 面向对象调用”。在整个系统中,它的作用可以概括为以下三点:

  • 资源生命周期管理: 通过 Noncopyable 和析构函数中的 SSL_CTX_free,确保底层的安全上下文资源永不泄漏,且不被非法拷贝。
  • 安全策略落地的“处理器”: 读取 SslConfig 中的静态配置(证书路径协议版本加密套件),并将其 “翻译”OpenSSL 内部的状态机参数。
  • 握手模板的“母体”:HttpServer 中,每当有一个新的 TCP 连接进来,系统都会引用这个唯一的 SslContext 句柄来产生具体的 SSL 握手对象。

重要函数

  • 构造函数
      保存 SSL 配置以及初始化 SSL_CTX* m_ctx 为空

    1
    2
    3
    4
    SslContext::SslContext(const SslConfig& config)
    : m_config(config)
    , m_ctx(nullptr)
    {}
  • initilaize()
       作用:把服务器端 TLS/SSL 所需要的 OpenSSL 环境和 SSL_CTX(上下文)一次性初始化好,让后续每个连接都能基于这个上下文去创建 SSL* 并完成握手、加密通信。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    bool SslContext::initilaize()
    {
    // 初始化OpenSSL
    OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, nullptr);
    // 创建 SSL 上下文
    const SSL_METHOD* method = TLS_server_method();
    m_ctx = SSL_CTX_new(method);
    if (!m_ctx)
    {
    handleSslError("Failed to create SSL context");
    return false;
    }
    // 设置Ssl选项
    long option = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |SSL_OP_CIPHER_SERVER_PREFERENCE;
    SSL_CTX_set_options(m_ctx, option);
    // 加载证书和密钥
    if (!loadCertificates())
    {
    return false;
    }
    // 设置协议版本
    if (!setupProtocol())
    {
    return false;
    }
    // 设置会话缓存
    setupSessionCache();
    // 表示成功输出
    SYLAR_LOG_INFO(g_logger) << "SSL context initialized successfully";
    return true;
    }
  • loadCertificates() :主要用来加载公钥证书加载私钥验证公钥证书和私钥匹配构建完整的“证书链” 以供客户端验证。

  • setupProtocol():设置支持的 TLS 的版本,以及加密套件。

  • setupSessionCache设置会话超时时间,确保客户端在会话期间断开重连可以无需重复握手。

SslSocketStream

SslSocketStream 继承自 SocketStream,它的核心使命是:在不改变上层逻辑的前提下,透明地为数据流进行 加密传输

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class SslSocketStream : public SocketStream
{
public:
using ptr = std::shared_ptr<SslSocketStream>;

/**
* @brief 构造函数
* @param[in] sock 用来和客户端通信的socket
* @param[in] ctx 保存Ssl相关的信息
*/
SslSocketStream(sylar::Socket::ptr sock, sylar::ssl::SslContext::ptr ctx);

/**
* @brief 析构函数
*/
~SslSocketStream();

/**
* @brief 判断是否建立了连接
*/
bool isConnected() const;

/**
* @brief 进行SSL 握手
*/
bool doHandShake();

/**
* @brief 关闭socket
*/
virtual void close() override;

/**
* @brief 读取数据
* @param[out] buffer 待接收数据的内存
* @param[in] length 待接收数据的内存长度
* @return
* @retval >0 返回实际接收到的数据长度
* @retval =0 socket被远端关闭
* @retval <0 socket错误
*/
virtual int read(void* buffer, size_t length) override;

/**
* @brief 写入数据
* @param[in] buffer 待发送数据的内存
* @param[in] length 待发送数据的内存长度
* @return
* @retval >0 返回实际接收到的数据长度
* @retval =0 socket被远端关闭
* @retval <0 socket错误
*/
virtual int write(const void* buffer, size_t length) override;

private:
SSL* m_ssl; // SSL 连接
SslContext::ptr m_ctx; // SSL 上下文
bool m_handshake = false; // 标志位,判断是否握手成功
};