如何在Asio HTTPS服务器中处理HTTP连接并返回308重定向?
解决方案
当SSL握手因HTTP请求失败时,SSL流已处于错误状态,无法继续通过它发送数据。必须直接操作底层的TCP套接字来返回308响应,之后关闭连接。
核心思路
- 准确识别握手失败的原因是HTTP请求(
SSL_R_HTTP_REQUEST)。 - 获取SSL流底层的原始TCP套接字,绕过失效的SSL层。
- 构造符合HTTP规范的308永久重定向响应并发送。
- 发送完成后正确关闭套接字,释放资源。
修改后的代码示例
同步发送版本(适用于简单场景)
void HttpSession::onHandshake(boost::beast::error_code errorCode) { if (!errorCode) { readHttps(); return; } // 校验是否为HTTP请求导致的SSL握手失败 if (errorCode.category() == boost::asio::error::get_ssl_category() && ERR_GET_REASON(errorCode.value()) == SSL_R_HTTP_REQUEST) { // 获取底层TCP套接字(根据你的SSL流类型调整next_layer调用次数) auto& socket = ssl_stream_.next_layer().next_layer(); // 构造308响应,替换getHost()为你获取请求主机名的逻辑 std::string response = "HTTP/1.1 308 Permanent Redirect\r\n" "Location: https://" + getHost() + "\r\n" "Connection: close\r\n" "\r\n"; // 同步发送响应 boost::asio::write(socket, boost::asio::buffer(response), errorCode); // 关闭套接字 socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, errorCode); socket.close(errorCode); return; } // 处理其他握手错误 closeSession(); }
异步发送版本(适用于异步IO模型)
如果你的服务器基于异步IO设计,避免同步阻塞,改用异步发送:
void HttpSession::send308Response() { auto& socket = ssl_stream_.next_layer().next_layer(); std::string response = "HTTP/1.1 308 Permanent Redirect\r\n" "Location: https://" + getHost() + "\r\n" "Connection: close\r\n" "\r\n"; // 异步发送响应,绑定完成回调 boost::asio::async_write(socket, boost::asio::buffer(response), std::bind(&HttpSession::onSend308Complete, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void HttpSession::onSend308Complete(boost::beast::error_code errorCode, std::size_t bytesTransferred) { auto& socket = ssl_stream_.next_layer().next_layer(); // 关闭发送端,然后关闭套接字 socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, errorCode); socket.close(errorCode); } void HttpSession::onHandshake(boost::beast::error_code errorCode) { if (!errorCode) { readHttps(); return; } if (errorCode.category() == boost::asio::error::get_ssl_category() && ERR_GET_REASON(errorCode.value()) == SSL_R_HTTP_REQUEST) { send308Response(); return; } closeSession(); }
关键注意事项
- 错误码判断:用
ERR_GET_REASON配合SSL类别校验,比直接ERR_PACK更可靠,避免版本差异导致的匹配失败。 - 套接字获取:根据你的SSL流类型调整
next_layer()的调用次数——如果是ssl_stream<tcp_stream>,需要两次next_layer()才能拿到tcp::socket。 - 响应规范:必须包含
Connection: close头,明确告知客户端响应发送完毕后关闭连接;Location头要填写正确的HTTPS地址。
内容的提问来源于stack exchange,提问作者Arthaiir
相关产品推荐
相关产品推荐

