workflow系列教程(5-1)HTTP Server

往期教程

如果觉得写的可以,请给一个点赞+关注支持一下
观看之前请先看,往期的博客教程,否则这篇博客没办法看懂

创建与启动http server

本示例里,我们采用http server的默认参数。创建和启动过程非常简单。

  • process为设置的异步回调函数
  • server.start(port):中port参数表示监听的端口号
cpp 复制代码
WFHttpServer server(process);
if (server.start(port) == 0)
{
    pause();
    server.stop();
}

start()接口有好几个重载函数,在WFServer.h里,可以看到如下一些接口:

cpp 复制代码
class WFServerBase
{
public:
    /* To start TCP server. */
    int start(unsigned short port);
    int start(int family, unsigned short port);
    int start(const char *host, unsigned short port);
    int start(int family, const char *host, unsigned short port);
    int start(const struct sockaddr *bind_addr, socklen_t addrlen);

    /* To start an SSL server */
    int start(unsigned short port, const char *cert_file, const char *key_file);
    int start(int family, unsigned short port,
              const char *cert_file, const char *key_file);
    int start(const char *host, unsigned short port,
              const char *cert_file, const char *key_file);
    int start(int family, const char *host, unsigned short port,
              const char *cert_file, const char *key_file);
    int start(const struct sockaddr *bind_addr, socklen_t addrlen,
              const char *cert_file, const char *key_file);

    /* For graceful restart or multi-process server. */
    int serve(int listen_fd);
    int serve(int listen_fd, const char *cert_file, const char *key_file);

    /* Get the listening address. Used when started a server on a random port. */
    int get_listen_addr(struct sockaddr *addr, socklen_t *addrlen) const;
};

这些接口都比较好理解。任何一个start函数,当端口号为0时,将使用随机端口。此时用户可能需要在server启动完成之后通过get_listen_addr获得实际监听地址。

http echo server的业务逻辑

我们看到在构造http server的时候,传入了一个process参数,这也是一个std::function,定义如下:

cpp 复制代码
using http_process_t = std::function<void (WFHttpTask *)>;
using WFHttpServer = WFServer<protocol::HttpRequest, protocol::HttpResponse>;

template<>
WFHttpServer::WFServer(http_process_t proc) :
    WFServerBase(&HTTP_SERVER_PARAMS_DEFAULT),
    process(std::move(proc))
{
}

其实这个http_proccess_t和的http_callback_t类型是完全一样的。都是处理一个WFHttpTask。

对server来讲,我们的目标就是根据request,填写好response。

同样我们用一个普通函数实现process。逐条读出request的http header写入html页面。

cpp 复制代码
void process(WFHttpTask *server_task)
{
    protocol::HttpRequest *req = server_task->get_req();
    protocol::HttpResponse *resp = server_task->get_resp();
    long seq = server_task->get_task_seq();
    protocol::HttpHeaderCursor cursor(req);
    std::string name;
    std::string value;
    char buf[8192];
    int len;

    /* Set response message body. */
    resp->append_output_body_nocopy("<html>", 6);
    len = snprintf(buf, 8192, "<p>%s %s %s</p>", req->get_method(),
                   req->get_request_uri(), req->get_http_version());
    resp->append_output_body(buf, len);

    while (cursor.next(name, value))
    {
        len = snprintf(buf, 8192, "<p>%s: %s</p>", name.c_str(), value.c_str());
        resp->append_output_body(buf, len);
    }

    resp->append_output_body_nocopy("</html>", 7);

    /* Set status line if you like. */
    resp->set_http_version("HTTP/1.1");
    resp->set_status_code("200");
    resp->set_reason_phrase("OK");

    resp->add_header_pair("Content-Type", "text/html");
    resp->add_header_pair("Server", "Sogou WFHttpServer");
    if (seq == 9) /* no more than 10 requests on the same connection. */
        resp->add_header_pair("Connection", "close");

    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    serverTask->get_peer_addr((sockaddr *)&addr,&len);
    if(addr.sin_family == AF_INET){
        fprintf(stderr,"sin_family:AF_INET\n");
        fprintf(stderr,"ip:%s\n", inet_ntoa(addr.sin_addr));
        fprintf(stderr,"port:%d\n",ntohs(addr.sin_port));
    }
}
  • set_http_version设置http版本
  • set_status_code设置状态码
  • set_reason_phrase设置响应dui'x

大多数HttpMessage相关操作之前已经介绍过了,在这里唯一的一个新操作是append_output_body()。

显然让用户生成完整的http body再传给我们并不太高效。用户只需要调用append接口,把离散的数据一块块扩展到message里就可以了。

append_output_body()操作会把数据复制走,另一个带_nocopy后缀的接口会直接引用指针,使用时需要注意不可以指向局部变量。

相关几个调用在HttpMessage.h可以看到其声明:

cpp 复制代码
class HttpMessage
{
public:
    bool append_output_body(const void *buf, size_t size);
    bool append_output_body_nocopy(const void *buf, size_t size);
    ...
    bool append_output_body(const std::string& buf);
};

再次强调,使用append_output_body_nocopy()接口时,buf指向的数据的生命周期至少需要延续到task的callback。

函数中另外一个变量seq,通过server_task->get_task_seq()得到,表示该请求是当前连接上的第几次请求,从0开始计。

程序中,完成10次请求之后就强行关闭连接,于是:

cpp 复制代码
    if (seq == 9) /* no more than 10 requests on the same connection. */
        resp->add_header_pair("Connection", "close");

关闭连接还可以通过task->set_keep_alive()接口来完成,但对于http协议,还是推荐使用设置header的方式。

这个示例中,因为返回的页面很小,我们没有关注回复成功与否。

相关推荐
l1x1n01 小时前
网络安全概述:从认知到实践
网络
鄃鳕1 小时前
HTTP【网络】
网络·网络协议·http
蜡笔小新星1 小时前
Python Kivy库学习路线
开发语言·网络·经验分享·python·学习
读心悦3 小时前
如何在 Axios 中封装事件中心EventEmitter
javascript·http
小白爱电脑3 小时前
WIFI网速不够是不是光猫的“路由模式”和“桥接模式”配置错了?
网络·智能路由器·桥接模式
CXDNW3 小时前
【网络篇】计算机网络——应用层详述(笔记)
服务器·笔记·计算机网络·http·web·cdn·dns
qxqxa4 小时前
cfg80211是怎么配置无线设备的AP的?
网络·驱动开发
秋夫人5 小时前
http cache-control
网络·网络协议·http
叶北辰CHINA5 小时前
nginx反向代理,负载均衡,HTTP配置简述(说人话)
linux·运维·nginx·http·云原生·https·负载均衡
不灭锦鲤7 小时前
ssrf学习(ctfhub靶场)
网络·学习·安全