Skip to content

Latest commit

 

History

History
233 lines (233 loc) · 7.79 KB

HTTP框架指南.md

File metadata and controls

233 lines (233 loc) · 7.79 KB

HTTP框架指南

概览

http_server 是 sflib 中的http框架,底层基于 tcp_server,其框架结构如下: http_framework 从浏览器(客户端)发起的连接由 tcp_server 处理, tcp_server 将接收到的数据转发给 http_base_server, http_base_server会解析http请求,封装为http_request 调用回调函数进行处理,而这些回调函数是由 http_server 使用router机制提供。http_server 可以设置多种router:

  • http_router 服务请求路由。通常一些查询类的请求可以通过此router处理。
  • static_router 静态资源路由。该路由处理静态资源请求。
  • websocket_router websocket 路由。该路由用于相应websocket请求。
  • part_router 分层路由。该路由可以拦截或者分发请求到下一层路由,分层路由使得每个服务可以模块化组织。 在相应的路由处理完请求后,会有一个响应(http_response)被构建,该响应返回到http_base_server, 经http_base_server调用tcp_server返回浏览器(客户端)。

入门

现在我们从头搭建一个http服务器。(Linux环境下) http_server依赖zlib和openssl,编译器版本要求gcc 9.1及以上。 编写服务器代码:

// main.cpp
#include <sflib>
using namespace std;
using namespace skyfire;
int main(){
    http_server_config conf;
    conf.host = "0.0.0.0";
    conf.port = 8080;
    auto server = http_server::make_instance(conf);
    server->add_router(http_router::make_instance("/"s, 
        function([](const http_request& req, 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/http_base_server.hpp (614) 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

使用 http_part_router 进行拦截与分发

http_part_router 用于对路由进行分层,同时有拦截的功能。

// main.cpp
#include <sflib>
using namespace std;
using namespace skyfire;
int main()
{
    http_server_config conf;
    conf.host = "0.0.0.0";
    conf.port = 8080;
    auto server = http_server::make_instance(conf);
    int n = 0;
    auto info_router = http_part_router::make_instance("/info"s,
        function([&n](const http_request& req, http_response& res) {
            if (n++ < 3) {
                return true;
            } else {
                res.set_status(403);
                return false;
            }
        }));
    server->add_router(info_router);
    info_router->add_router(http_router::make_instance("/"s,
        function([&n](const http_request& req, http_response& res) {
            res.set_text("you got message: " + to_string(n));
        })));
    server->start();
}

上述代码使用了 http_part_router 对 /info 的访问进行了次数限制。注意 http_part_router::make_instance 的第二个参数是一个返回bool值的可调用对象,如果返回 true,则该请求会向下层路由做匹配,否则会在本层直接返回。 http_part_router 和 http_server 一样有 add_router 接口,且参数保持一致,所以可以将路由组织为树状结构。

静态路由

使用静态路由可以将本地路径下的文件供浏览器(客户端)访问:

// main.cpp
#include <sflib>
using namespace std;
using namespace skyfire;
int main()
{
    http_server_config conf;
    conf.host = "0.0.0.0";
    conf.port = 8080;
    auto server = http_server::make_instance(conf);
    server->add_router(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 "http_router.hpp"
#include "http_server.hpp"
#include "http_static_router.hpp"
using namespace std;
using namespace skyfire;
int main()
{
    http_server_config conf;
    conf.host = "0.0.0.0";
    conf.port = 8080;
    auto server = http_server::make_instance(conf);
    server->add_router(static_router::make_instance("./"s));
    server->start();
}
* Closing connection 0

静态路由通常与分层路由结合使用。

websocket

http_server 支持 websocket 协议,如下:

// main.cpp
#include <sflib>
using namespace std;
using namespace skyfire;
int main()
{
    http_server_config conf;
    conf.host = "0.0.0.0";
    conf.port = 8080;
    auto server = http_server::make_instance(conf);
    server->add_router(websocket_router::make_instance("/ws"s, function([](const 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文档。

使用session

示例代码如下:

// main.cpp
#include <sflib>
using namespace std;
using namespace skyfire;
int main()
{
    http_server_config conf;
    conf.host = "0.0.0.0";
    conf.port = 8080;
    auto server = http_server::make_instance(conf);
    server->add_router(http_router::make_instance("/test_session"s,
        function([&server](const http_request& req, 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进行测试。