OpenHttps
OpenHttps是跨全平台的Actor模式、组件设计的高性能、高并发的超轻量、超迷你的Https框架。
使用OpenServer开源库开发,小巧迷你,支持IPv6,让C++开发Https如此简单,易如反掌。
由于时间关系,暂时没有实现状态机设计,不过使用OpenFSM库可以轻松实现状态机设计。
OpenHttps也超容易实现Websocket,由于时间关系,暂时不实现。
作为一款C++的Http框架需要满足几点:
- 高性能、高并发和跨全平台;
- Actor模式、组件设计和状态机设计;
- 任意组装,实现各种超难度的网络通信。
由于C++后端开发不适合协程,协程不在考虑范围。
https://github.com/OpenMiniServer
跨平台支持
Linux和安卓使用epoll,Windows使用IOCP(wepoll),iOS和Mac使用kqueue,其他系统使用select。
编译和执行
请安装cmake工具,用cmake可以构建出VS或者XCode工程,就可以在vs或者xcode上编译运行。
源代码:https://github.com/OpenMiniServer/openhttps
https://github.com/OpenMiniServer/openhttps
https需要openssl支持。如果本地没有opessl库,需要先安装openssl库。如果不安装,关闭USE_OPEN_SSL宏定义。
#克隆项目
git clone https://github.com/openlinyou/openhttps
cd ./openhttps
#创建build工程目录
mkdir build
cd build
cmake ..
#如果是win32,在该目录出现openhttps.sln,点击它就可以启动vs写代码调试
make
./helloworld
技术特点
OpenHttps的技术特点:
- 跨全平台设计,此服务器框架可以运行在安卓和iOS上。
- Linux和安卓使用epoll,Windows使用IOCP(wepoll),iOS和Mac使用kqueue,其他系统使用select。
- 支持IPv6,小巧迷你,采用Actor模式和组件设计,通过组件去组装业务。
- Actor模式和组件设计,可以非常容易实现高并发和分布式。也可以通过配置文件去定制业务和启动服务。
- 一条线程一个actor,一个actor由多个组件组装。用玩积木的方式去做服务器开发。
设计一个使用Https的科学上网工具
先声明:本例子只是展示OpenHttps的功能。由于科学上网涉及法律问题,由此造成的后果自己承担。
OpenHttps只是OpenServer技术的应用。具体用法见OpenServer。
OpenHttps只实现了三个组件OpenComHttpServer,OpenComHttpAgent和OpenComHttpClient。
OpenComHttpServer负责监听socket,与OpenComHttpAgent配合使用,OpenComHttpAgent处理具体的客户端通信,它们两组成一个web服务器。
OpenComHttpClient只是一个http客户端组件。
接下来就是组装出各自的server。
OpenHttpServer负责listen客户端连接,然后分发给OpenComHttpAgent,它的业务量不多只需要一个。
OpenHttpAgent1负责具体的客户端连接,需要处理大量的客户端连接。需要创建多个server,实现多核处理。
OpenHttpClient负责http客户端请求,可以用它来实现web服务器压力测试。
OpenHttps采用actor模式设计,启动它们只要向它们发送消息即可。
本展示的例子科学上网的原理。以访问https://www.bing.com 为例子。
假设服务器运行在本地电脑127.0.0.1上,类似海龟服务器。
在浏览器端,输入http://127.0.0.1/xx 。 使用浏览器插件,往http头加入client字段,client:https://www.bing.com.
浏览器就会请求127.0.0.1的服务器。
OpenHttpServer就会监听到浏览器的请求连接,把socket的fd发给OpenHttpAgent。
OpenHttpAgent解析http报文,发现http请求头含有client,进行移除,把url请求改成client指定的域名。然后把修改好的报文转发给OpenHttpClient。
OpenHttpClient再用修改的http报文向https://www.bing.com 请求。获得的数据立刻转发给OpenHttpAgent。OpenHttpAgent直接把接收到的数据立刻发给浏览器。
当然实际中,是没法实现科学上网的。因为科学上网很不稳定,容易掉包,网络连接超时等,都可能导致请求失败。
需要采用UDP方案,而Http协议天生适合UDP通信,由于涉及违法行为,不会提供任何代码。
#include <assert.h>
#include <iostream>
#include <stdio.h>
#include "openserver.h"
#include "opencomhttpclient.h"
#include "opencomhttpserver.h"
using namespace open;
class HttpApp :public OpenApp
{
int balance_;
OpenServer* server_;
std::vector<OpenServer*> accepts_;
std::vector<OpenServer*> clients_;
static HttpApp OpenApp_;
public:
static inline HttpApp& Instance() { return OpenApp_; }
virtual void start()
{
OpenApp::start();
OpenTimer::Run();
OpenServer::RegisterCom<OpenComHttpServer>("OpenComHttpServer");
OpenServer::RegisterCom<OpenComHttpAgent>("OpenComHttpAgent");
OpenServer::RegisterCom<OpenComHttpClient>("OpenComHttpClient");
accepts_ = {
OpenServer::StartServer("OpenHttpAgent1", { "OpenComHttpAgent" }),
OpenServer::StartServer("OpenHttpAgent2", { "OpenComHttpAgent" }),
OpenServer::StartServer("OpenHttpAgent3", { "OpenComHttpAgent" }),
OpenServer::StartServer("OpenHttpAgent4", { "OpenComHttpAgent" })
};
server_ = OpenServer::StartServer("OpenHttpServer", { "OpenComHttpServer" });
assert(server_);
balance_ = 0;
clients_ = {
OpenServer::StartServer("OpenHttpClient1", { "OpenComHttpClient" }),
OpenServer::StartServer("OpenHttpClient2", { "OpenComHttpClient" }),
OpenServer::StartServer("OpenHttpClient3", { "OpenComHttpClient" }),
OpenServer::StartServer("OpenHttpClient4", { "OpenComHttpClient" })
};
}
bool httpServer(std::shared_ptr<OpenHttpServerMsg>& protoMsg)
{
for (size_t i = 0; i < accepts_.size(); i++)
protoMsg->vectAccepts_.push_back(accepts_[i]->pid());
for (size_t i = 0; i < clients_.size(); i++)
protoMsg->vectClients_.push_back(clients_[i]->pid());
auto proto = std::shared_ptr<OpenMsgProto>(new OpenMsgProto);
proto->msg_ = protoMsg;
bool ret = OpenThread::Send(server_->pid(), proto);
assert(ret);
return ret;
}
bool httpClient(std::shared_ptr<OpenMsgProtoMsg>& protoMsg, bool isAll)
{
if (clients_.empty())
{
assert(false);
return false;
}
//isAll
auto proto = std::shared_ptr<OpenMsgProto>(new OpenMsgProto);
proto->msg_ = protoMsg;
bool ret = false;
if (isAll)
{
for (size_t i = 0; i < clients_.size(); i++)
{
ret = OpenThread::Send(clients_[i]->pid(), proto);
assert(ret);
}
}
else
{
if (balance_ >= clients_.size()) balance_ = 0;
OpenServer* client = clients_[balance_++];
ret = OpenThread::Send(client->pid(), proto);
assert(ret);
}
return ret;
}
bool httpClient(std::shared_ptr<OpenHttpRequest>& request)
{
if (clients_.empty())
{
assert(false);
return false;
}
auto protoMsg = std::shared_ptr<OpenHttpClientSyncMsg>(new OpenHttpClientSyncMsg);
protoMsg->request_ = request;
if (balance_ >= clients_.size())
{
balance_ = 0;
}
OpenServer* client = clients_[balance_++];
auto proto = std::shared_ptr<OpenMsgProto>(new OpenMsgProto);
proto->msg_ = protoMsg;
bool ret = OpenThread::Send(client->pid(), proto);
assert(ret);
protoMsg->openSync_.await();
return ret;
}
};
HttpApp HttpApp::OpenApp_;
void OnOpenHttpHandle(OpenHttpRequest& req, OpenHttpResponse& resp)
{
#ifdef USE_OPEN_SSL
//80 redirect to 443
if (req.listenPort_ == 80)
{
resp.code_ = 302;
resp["location"] = "https://" + req.host_ + ":" + std::to_string(req.port_) + "/" + req.url_;
return;
}
#endif
resp.response(".html", "<html><body><h1>HelloWorld</h1>"
"<p>Welcome to OpenLinyou</p></body></html>");
}
int main()
{
HttpApp::Instance().start();
printf("start HttpServer\n");
auto msg = std::shared_ptr<OpenHttpServerMsg>(new OpenHttpServerMsg);
msg->ip_ = "0.0.0.0";
#ifdef USE_OPEN_SSL
msg->port_ = 443;
msg->port1_ = 80;
msg->isHttps_ = 1;
msg->keyFile_ = "www.xx.com.key";
msg->certFile_ = "www.xx.com.crt";
#else
msg->port_ = 80;
msg->port1_ = 0;
msg->isHttps_ = 0;
#endif
msg->handle_ = &OnOpenHttpHandle;
HttpApp::Instance().httpServer(msg);
//httpclient
OpenThread::Sleep(500);
auto request = std::shared_ptr<OpenHttpRequest>(new OpenHttpRequest);
request->method_ = "GET";
#ifdef USE_OPEN_SSL
request->url_ = "https://www.xx.com/";
(*request)["client"] = "https://www.bing.com/";
#else
request->url_ = "http://127.0.0.1/";
(*request)["client"] = "http://www.bing.com/";
#endif
HttpApp::Instance().httpClient(request);
auto& response = request->response_;
std::string head;
response.getHead(head);
printf("[http client]code:%d, header:%s\n", response.code_, head.data());
std::string body;
response.getBody(body);
printf("[http client]body:%s\n", body.data());
HttpApp::Instance().wait();
return getchar();
}