/** .============.* // M A K E / \* // C++ DEV / \* // E A S Y / \/ \* ++ ----------. \/\ .* \\ \ \ /\ /* \\ \ \ /* \\ \ \ /* -============'** Copyright (c) 2018 Hevake and contributors, all rights reserved.** This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox)* Use of this source code is governed by MIT license that can be found* in the LICENSE file in the root of the source tree. All contributing* project authors may be found in the CONTRIBUTORS.md file in the root* of the source tree.*/#include "server_imp.h"#include <algorithm>#include <tbox/base/log.h>#include <tbox/base/assert.h>#include <tbox/base/wrapped_recorder.h>#include <tbox/util/buffer.h>#include <tbox/network/tcp_connection.h>#include "middleware.h"namespace tbox {namespace http {namespace server {using namespace std;using namespace std::placeholders;using namespace event;using namespace network;Server::Impl::Impl(Server *wp_parent, Loop *wp_loop): wp_parent_(wp_parent), wp_loop_(wp_loop), tcp_server_(wp_loop){ }Server::Impl::~Impl(){TBOX_ASSERT(cb_level_ == 0);cleanup();}bool Server::Impl::initialize(const network::SockAddr &bind_addr, int listen_backlog){if (!tcp_server_.initialize(bind_addr, listen_backlog))return false;tcp_server_.setConnectedCallback(bind(&Impl::onTcpConnected, this, _1));tcp_server_.setReceiveCallback(bind(&Impl::onTcpReceived, this, _1, _2), 0);tcp_server_.setSendCompleteCallback(bind(&Impl::onTcpSendCompleted, this, _1));state_ = State::kInited;return true;}bool Server::Impl::setTlsConfig(const network::TlsConfig &config){return tcp_server_.setTlsConfig(config);}bool Server::Impl::start(){if (tcp_server_.start()) {state_ = State::kRunning;return true;}return false;}void Server::Impl::stop(){if (state_ == State::kRunning) {tcp_server_.stop();state_ = State::kInited;}}void Server::Impl::cleanup(){if (state_ != State::kNone) {stop();mw_cabinet_.foreach([](RequestHandler *ptr) { delete ptr; });mw_cabinet_.clear();mw_order_.clear();tcp_server_.cleanup();state_ = State::kNone;}}MiddlewareToken Server::Impl::use(RequestHandler &&handler){if (cb_level_ > 0) {LogWarn("不能在 next 链中调用 use(),请使用 Loop 的 runNext() 来处理");return MiddlewareToken();}auto token = mw_cabinet_.alloc(new RequestHandler(std::move(handler)));mw_order_.push_back(token);return token;}MiddlewareToken Server::Impl::use(Middleware *wp_middleware){if (cb_level_ > 0) {LogWarn("不能在 next 链中调用 use(),请使用 Loop 的 runNext() 来处理");return MiddlewareToken();}auto token = mw_cabinet_.alloc(new RequestHandler(bind(&Middleware::handle, wp_middleware, _1, _2)));mw_order_.push_back(token);return token;}bool Server::Impl::unuse(const MiddlewareToken &token){if (cb_level_ > 0) {LogWarn("不能在 next 链中调用 unuse(),请使用 Loop 的 runNext() 来处理");return false;}//! 从 Cabinet 中释放RequestHandler *handler = mw_cabinet_.free(token);if (handler == nullptr)return false; //! token 无效或已释放delete handler;//! 从 order 中删除该 token(低频 O(n) 操作)auto iter = find(mw_order_.begin(), mw_order_.end(), token);if (iter != mw_order_.end())mw_order_.erase(iter);return true;}void Server::Impl::onTcpConnected(const TcpServer::ConnToken &ct){RECORD_SCOPE();auto conn = new Connection;tcp_server_.setContext(ct, conn,[](void* ptr) {auto conn = static_cast<Connection*>(ptr);CHECK_DELETE_OBJ(conn);});}namespace {bool IsLastRequest(const Request *req){auto iter = req->headers.find("Connection");if (req->http_ver == HttpVer::k1_0) {//! 1.0 版本默认为一连接一请求,需要特定的 Connection: Keep-Alive 才能持处if (iter == req->headers.end()) {return true;} else {return iter->second.find("keep-alive") == std::string::npos;}} else {//! 否则为 1.1 及以上的版本,默认为持久连接;除非出现 Connection: closeif (iter == req->headers.end()) {return false;} else {return iter->second.find("close") != std::string::npos;}}}}void Server::Impl::onTcpReceived(const TcpServer::ConnToken &ct, Buffer &buff){RECORD_SCOPE();Connection *conn = static_cast<Connection*>(tcp_server_.getContext(ct));TBOX_ASSERT(conn != nullptr);//! 如果已被标记为最后的请求,就不应该再有请求来if (conn->close_index != numeric_limits<int>::max()) {buff.hasReadAll();LogWarn("should not recv any data");return;}//! 如果已被标记为升级请求,停止解析 HTTP 数据if (conn->is_upgrade) {//! 不消费剩余数据,留给升级后的协议处理return;}while (buff.readableSize() > 0) {size_t rsize = conn->req_parser.parse(buff.readableBegin(), buff.readableSize());buff.hasRead(rsize);if (conn->req_parser.state() == RequestParser::State::kFinishedAll) {Request *req = conn->req_parser.getRequest();if (context_log_enable_)LogDbg("REQ: [%s]", req->toString().c_str());auto sp_ctx = make_shared<Context>(wp_parent_, ct, conn->req_index++, req);handle(sp_ctx, 0);//! 检查是否有协议升级回调(WebSocket、SSE 等)//! 如果有,标记连接为升级模式,停止继续解析 HTTP 数据if (sp_ctx->res().upgrade_cb) {conn->is_upgrade = true;//! 升级请求:保留 buffer 中未消费的数据,留给升级后的协议break;}//! 非升级请求:检查是否为最后一个请求if (IsLastRequest(req)) {conn->close_index = conn->req_index;LogDbg("mark close at %d", conn->close_index);tcp_server_.shutdown(ct, SHUT_RD);}} else if (conn->req_parser.state() == RequestParser::State::kFail) {LogNotice("parse http from %s fail", tcp_server_.getClientAddress(ct).toString().c_str());tcp_server_.disconnect(ct);break;} else {break;}}}void Server::Impl::onTcpSendCompleted(const TcpServer::ConnToken &ct){RECORD_SCOPE();if (!tcp_server_.isClientValid(ct))return;Connection *conn = static_cast<Connection*>(tcp_server_.getContext(ct));TBOX_ASSERT(conn != nullptr);//! 如果最后一个已完成发送,则断开连接if (conn->res_index > conn->close_index)tcp_server_.disconnect(ct);}/*** 为了保证管道化连接中Respond与Request的顺序一致性,要做特殊处理。* 如果所提交的index不是当前需要回复的res_index,那么就先暂存起来,等前面的发送完成后再发送;* 如果是,则可以直接回复。然后再将暂存中的未发送的其它数据也一同发送。** 对于协议升级请求(WebSocket 101、SSE 200 等),发送完响应后,* 将从HTTP服务器分离TcpConnection,并通过Respond::upgrade_cb回调交给升级协议。*/void Server::Impl::commitRespond(const TcpServer::ConnToken &ct, int index, Respond *res){RECORD_SCOPE();if (!tcp_server_.isClientValid(ct)) {delete res;return;}//! 处理协议升级请求(WebSocket 101、SSE 200 等)//! 触发条件:res->upgrade_cb 已设置(中间件负责设置)if (res->upgrade_cb) {//! 注意:必须在 std::move(upgrade_cb) 之前调用 toString()//! 否则 move 后 upgrade_cb 为空,toString() 会误判为普通响应而添加 Content-Length//! SSE 响应添加 Content-Length:0 会导致浏览器认为响应已完成并断开重连const string content = res->toString();//! 发送响应后,将 TcpConnection 从 HTTP 服务器分离,交给升级协议auto upgrade_cb = std::move(res->upgrade_cb);delete res;tcp_server_.send(ct, content.data(), content.size());if (context_log_enable_)LogDbg("RES: [%s]", content.c_str());//! 当前回调结束后立即执行 detach(使用 runNext,更高效)//! 因为 commitRespond() 是在 Loop 线程中执行的wp_loop_->runNext([this, ct, upgrade_cb] {TcpConnection *tcp_conn = tcp_server_.detachConnection(ct);if (tcp_conn != nullptr)upgrade_cb(tcp_conn);elseLogWarn("tcp_conn == nullptr");}, "HttpUpgrade: detach connection");return;}Connection *conn = static_cast<Connection*>(tcp_server_.getContext(ct));TBOX_ASSERT(conn != nullptr);if (index == conn->res_index) {//! 将当前的数据直接发送出去{const string &content = res->toString();tcp_server_.send(ct, content.data(), content.size());delete res;if (context_log_enable_)LogDbg("RES: [%s]", content.c_str());}++conn->res_index;//! 如果当前这个回复是最后一个,则不需要发送缓存中的数据if (conn->res_index > conn->close_index)return;//! 尝试发送 conn->res_buff 缓冲中的数据auto &res_buff = conn->res_buff;auto iter = res_buff.find(conn->res_index);while (iter != res_buff.end()) {Respond *res = iter->second;{const string &content = res->toString();tcp_server_.send(ct, content.data(), content.size());delete res;if (context_log_enable_)LogDbg("RES: [%s]", content.c_str());}res_buff.erase(iter);++conn->res_index;//! 如果当前这个回复是最后一个,则不需要再发送缓存中的数据if (conn->res_index > conn->close_index)return;iter = res_buff.find(conn->res_index);}} else {//! 放入到 conn.res_buff 中暂存conn->res_buff[index] = res;}}void Server::Impl::handle(ContextSptr sp_ctx, size_t index){RECORD_SCOPE();if (index >= mw_order_.size())return;auto token = mw_order_.at(index);RequestHandler *handler_ptr = mw_cabinet_.at(token);if (handler_ptr && *handler_ptr) {++cb_level_;(*handler_ptr)(sp_ctx, std::bind(&Impl::handle, this, sp_ctx, index + 1));--cb_level_;} else {//! handler 已被移除或为空,跳过,执行下一个handle(sp_ctx, index + 1);}}Server::Impl::Connection::~Connection(){//! 一定要记得清除非发送的Respondfor (auto & item : res_buff)delete item.second;}}}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. Open source ecosystem
2. Collaboration, People, Software
3. Evaluation model