sf_http_server 是 sflib 中的http框架,底层基于 sf_tcp_server,其框架结构如下:
从浏览器(客户端)发起的连接由 sf_tcp_server 处理, sf_tcp_server 将接收到的数据转发给 sf_http_base_server, sf_http_base_server会解析http请求,封装为sf_http_request 调用回调函数进行处理,而这些回调函数是由 sf_http_server 使用router机制提供。sf_http_server 可以设置多种router:
在相应的路由处理完请求后,会有一个响应(sf_http_response)被构建,该响应返回到sf_http_base_server, 经sf_http_base_server调用sf_tcp_server返回浏览器(客户端)。
现在我们从头搭建一个http服务器。(Linux环境下)
sf_http_server依赖zlib和openssl,编译器版本要求gcc 9.1及以上。
编写服务器代码:
// main.cpp
#include "network/sf_http_server.hpp"
#include "network/sf_http_router.hpp"
using namespace std;
using namespace skyfire;
int main(){
sf_http_server_config conf;
conf.host = "0.0.0.0";
conf.port = 8080;
auto server = sf_http_server::make_instance(conf);
server->add_router(sf_http_router::make_instance("/"s,
function([](const sf_http_request& req, sf_http_response &res){
res.set_text("hello world");
})
));
server->start();
}
使用g++编译:
g++ main.cpp -I ../sflib/ -lpthread -lcrypto -lssl -lstdc++fs -lz -std=c++2a -o main
其中../sflib为sflib库所在目录。
运行:
./main
打印出server配置并停顿说明运行成功,http服务已启动:
[INFO ][2019-09-28 00:35:30][139725116159872][../sflib/network/sf_http_base_server.hpp (614) sf_http_base_server] --> [server config:][{"tmp_file_path":".","max_cache_file_size":1048576,"debu
g":false,"host":"0.0.0.0","port":8080,"session_timeout":1800,"max_cache_count":1024}]
另起一个终端使用curl测试,或者直接使用浏览器访问http://localhost:8080
curl http://localhost:8080
可以看到打印的hello world
,同时server会打印出请求日志。
或者使用:
curl -v http://localhost:8080
查看更详细的请求信息。
返回信息大致如下:
* Trying ::1:8080...
* TCP_NODELAY set
* connect to ::1 port 8080 failed: Connection refused
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Connection:Close
< Content-Type:text/plain
< Content-Length:11
< Server:SkyFire HTTP Server
< Set-Cookie:_SESSION_ID=188E0AA8-EA4F-A317-83F7-DC139D70568D;Path=/;HttpOnly;
<
* Closing connection 0
hello world
sf_http_part_router 用于对路由进行分层,同时有拦截的功能。
// main.cpp
#include "network/sf_http_part_router.hpp"
#include "network/sf_http_router.hpp"
#include "network/sf_http_server.hpp"
using namespace std;
using namespace skyfire;
int main()
{
sf_http_server_config conf;
conf.host = "0.0.0.0";
conf.port = 8080;
auto server = sf_http_server::make_instance(conf);
int n = 0;
auto info_router = sf_http_part_router::make_instance("/info"s,
function([&n](const sf_http_request& req, sf_http_response& res) {
if (n++ < 3) {
return true;
} else {
res.set_status(403);
return false;
}
}));
server->add_router(info_router);
info_router->add_router(sf_http_router::make_instance("/"s,
function([&n](const sf_http_request& req, sf_http_response& res) {
res.set_text("you got message: " + to_string(n));
})));
server->start();
}
上述代码使用了 sf_http_part_router 对 /info 的访问进行了次数限制。注意 sf_http_part_router::make_instance 的第二个参数是一个返回bool值的可调用对象,如果返回 true,则该请求会向下层路由做匹配,否则会在本层直接返回。
sf_http_part_router 和 sf_http_server 一样有 add_router 接口,且参数保持一致,所以可以将路由组织为树状结构。
使用静态路由可以将本地路径下的文件供浏览器(客户端)访问:
// main.cpp
#include "network/sf_http_router.hpp"
#include "network/sf_http_server.hpp"
#include "network/sf_http_static_router.hpp"
using namespace std;
using namespace skyfire;
int main()
{
sf_http_server_config conf;
conf.host = "0.0.0.0";
conf.port = 8080;
auto server = sf_http_server::make_instance(conf);
server->add_router(sf_static_router::make_instance("./"s));
server->start();
}
此代码将当前工作目录供外部访问。使用 curl 获取本源文件内容:
curl -v http://localhost:8080/main.cpp
输出:
* Trying ::1:8080...
* TCP_NODELAY set
* connect to ::1 port 8080 failed: Connection refused
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /main.cpp HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type:application/octet-stream
< Connection:Close
< Content-Length:420
< Date:Sat, 28 Sep 2019 01:40:32 GMT
< Server:SkyFire HTTP Server
< Set-Cookie:_SESSION_ID=E4B04384-554C-9938-FA5D-F50871964385;Path=/;HttpOnly;
<
// main.cpp
#include "network/sf_http_router.hpp"
#include "network/sf_http_server.hpp"
#include "network/sf_http_static_router.hpp"
using namespace std;
using namespace skyfire;
int main()
{
sf_http_server_config conf;
conf.host = "0.0.0.0";
conf.port = 8080;
auto server = sf_http_server::make_instance(conf);
server->add_router(sf_static_router::make_instance("./"s));
server->start();
}
* Closing connection 0
静态路由通常与分层路由结合使用。
sf_http_server 支持 websocket 协议,如下:
// main.cpp
#include "network/sf_http_server.hpp"
#include "network/sf_websocket_router.hpp"
using namespace std;
using namespace skyfire;
int main()
{
sf_http_server_config conf;
conf.host = "0.0.0.0";
conf.port = 8080;
auto server = sf_http_server::make_instance(conf);
server->add_router(sf_websocket_router::make_instance("/ws"s, function([](const sf_websocket_param_t& p) {
if (p.type == websocket_data_type::TextData) {
p.p_server->send_websocket_data(p.sock, "hello:" + p.text_msg);
}
})));
server->start();
}
可使用 wscat 工具进行测试:
skyfire@skyfire-pc ~/D/test_sflib> wscat -c ws://127.0.0.1:8080/ws
connected (press CTRL+C to quit)
> skyfire
< hello:skyfire
> test
< hello:test
> ⏎
详情请参阅API文档。
示例代码如下:
// main.cpp
#include "network/sf_http_server.hpp"
#include "network/sf_websocket_router.hpp"
using namespace std;
using namespace skyfire;
int main()
{
sf_http_server_config conf;
conf.host = "0.0.0.0";
conf.port = 8080;
auto server = sf_http_server::make_instance(conf);
server->add_router(sf_http_router::make_instance("/test_session"s,
function([&server](const sf_http_request& req, sf_http_response& res) {
auto ss = server->session(req.session_id());
if (!ss.has("my_session")) {
res.set_text("no session");
ss["my_session"] = "my session content";
} else {
res.set_text(string(ss["my_session"]));
}
})));
server->start();
}
使用浏览器访问 http://localhost:8080/test_session进行测试。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。