HTTP

include: co/so/http.h.

#http::Client

http::Client 是基于协程的 http 客户端,它基于 libcurl 实现。

#Client::Client

explicit Client(const char* serv_url);
  • 构造函数,参数 serv_url 是服务器的 url 地址,它的形式是 protocol://host:port,下面的 server url 都是合理的:
    • “github.com”
    • https://github.com
    • “http://127.0.0.1:7788”
    • “http://[::1]:8888”
  • http::Client 对象创建时,并没有建立连接。

#Client::~Client

Client::~Client();
  • 析构函数,关闭连接,释放 libcurl 相关资源。

#Client::add_header

void add_header(const char* key, const char* val);
void add_header(const char* key, int val);
  • 添加 HTTP 头部,用户在进行 HTTP 请求前,可以用此方法添加头部,这些头部会自动添加到后续所有请求中。
  • 第 2 个版本中,参数 val 是整数,内部自动转换成字符串。

#Client::body

const char* body() const;
  • 获取当前 HTTP 请求的响应体,返回一个指针,用户需要用 body_size() 方法获取它的长度。

#Client::body_size

size_t body_size() const;
  • 返回当前 HTTP 请求的响应体长度。

#Client::close

void close();
  • 关闭 HTTP 连接,需要在协程中调用此方法。
  • 调用此方法后,http::Client 对象就不能再用了

#Client::del

void del(const char* url, const char* s, size_t n);
void del(const char* url, const char* s);
void del(const char* url);
  • HTTP **DELETE **请求,必须在协程中调用。
  • 参数 url 必须是 '/' 开头的字符串。
  • 前两个版本,适用于带 body 部分的 DELETE 请求,参数 s 是 body,n 是 s 的长度,第 2 个版本 s 以 ‘\0’ 结尾。
  • 第 3 个版本适用于不带 body 的 DELETE 请求。

#Client::easy_handle

void* easy_handle() const;
  • 返回 libcurl 的 easy handle。

#Client::get

void get(const char* url);
  • HTTP **GET **请求,必须在协程中调用。
  • 参数 url 必须是 '/' 开头的字符串。

#Client::head

void head(const char* url);
  • HTTP **HEAD **请求,必须在协程中调用。
  • 参数 url 必须是 '/' 开头的字符串。

#Client::header

const char* header(const char* key);
const char* header() const;
  • 第 1 个版本获取当前 HTTP 响应中 header 的值,header 不存在时,返回一个空字符串。

  • 第 2 个版本获取当前 HTTP 响应的整个 header 部分。

  • 示例

http::Client c("xx.com");
c.get("/");
auto s = c.header("Content-Length");

#Client::perform

void perform();
  • 执行 HTTP 请求,get, post 等方法实际上都是基于此方法实现。

  • 用户一般不需要调用此方法,只有当 http::Client 提供的 get, post 等方法满足不了需要时,才考虑用此方法自定义 HTTP 请求。

  • 示例

void Client::post(const char* url, const char* data, size_t size) {
    curl_easy_setopt(_ctx->easy, CURLOPT_POST, 1L);
    curl_easy_setopt(_ctx->easy, CURLOPT_URL, make_url(url));
    curl_easy_setopt(_ctx->easy, CURLOPT_POSTFIELDS, data);
    curl_easy_setopt(_ctx->easy, CURLOPT_POSTFIELDSIZE, (long)size);
    this->perform();
}

#Client::post

void post(const char* url, const char* s, size_t n);
void post(const char* url, const char* s);
  • HTTP **POST **请求,必须在协程中调用。
  • 参数 url 必须是 '/' 开头的字符串。
  • 参数 s 是 HTTP 请求的 body 数据,n 是 s 的长度,第 2 个版本 s 以 ‘\0’ 结尾。

#Client::put

void put(const char* url, const char* s, size_t n);
void put(const char* url, const char* s);
  • HTTP **PUT **请求,必须在协程中调用。
  • 参数 url 必须是 '/' 开头的字符串。
  • 参数 s 是 HTTP 请求的 body 数据,n 是 s 的长度,第 2 个版本 s 以 ‘\0’ 结尾。

#Client::remove_header

void remove_header(const char* key);
  • 删除之前添加的 HTTP header。调用 add_header() 方法添加的头部会存在于后续所有 HTTP 请求中,如果用户不想在后续请求中带上某些 header,可以用此方法显示删除。

#Client::response_code

int response_code() const;
  • 获取当前 HTTP 请求的响应码。
  • 正常情况下返回值是 100 到 511 之间的值。
  • 若 HTTP 请求没有发送出去,或者没有收到服务端的响应,此方法返回 0,用户可以用 strerror() 方法获取错误信息。

#Client::strerror

const char* strerror() const;

  • 获取当前 HTTP 请求的错误信息。

#代码示例

void f() {
    http::Client c("https://github.com");
    int r;
    
    c.get("/");
    r = c.response_code();
    LOG << "response code: " << r;
    LOG_IF(r == 0) << "error: " << c.strerror();
    LOG << "body size: " << c.body_size();
    LOG << c.header();

    c.get("/idealvin/co");
    LOG << "body size: " << c.body_size();
    LOG << "Content-Length: " << c.header("Content-Length");
    LOG << c.header();

    c.close();
}

go(f);

#http::Req

http::Req 类是对 HTTP 请求的封装,用于实现 http::Server。

#Req::Req

Req() = default;
  • 默认构造函数。

#Req::body

const char* body() const;
  • 获取 HTTP 请求中的 body 数据,返回一个指针,用户需要调用 body_size() 方法获取它的长度。

#Req::body_size

size_t body_size() const;
  • 返回 HTTP 请求 body 的长度。

#Req::clear

void clear();
  • 清空 http::Req 对象,清空后用户可以继续使用。

#Req::header

const char* header(const char* key) const;
  • 获取 HTTP header 的值,header 不存在时,返回一个空字符串。

#Req::is_method_xxx

bool is_method_get() const;
bool is_method_head() const;
bool is_method_post() const;
bool is_method_put() const;
bool is_method_delete() const;
bool is_method_options() const;
  • 判断 HTTP 请求的方法类型。

#Req::url

const fastring& url() const;
  • 返回 HTTP 请求中的 url 的引用,这个值是 HTTP 请求 start line 中的一部分。

#Req::version

Version version() const;
  • 返回 HTTP 请求中的 HTTP 版本,返回值是 http::kHTTP10 或 http::kHTTP11 中的一种,目前不支持 HTTP/2.0。

#http::Res

http::Res 类是对 HTTP 响应的封装,用于实现 http::Server。

#Res::Res

Res();
  • 默认构造函数。

#Res::add_header

void add_header(const char* key, const char* val);
  • 添加 HTTP header。

#Res::body_size

size_t body_size() const
  • 返回 HTTP 响应 body 的长度。

#Res::clear

void clear();
  • 清空 http::Res 对象,清空后用户可以继续使用。

#Res::set_body

void set_body(const void* s, size_t n);
void set_body(const char* s);
  • 设置 HTTP 响应的 body 部分。
  • 参数 s 是 body 数据,参数 n 是 s 的长度,第 2 个版本中 s 以 '\0' 结尾。

#Res::set_status

void set_status(int status);
  • 设置 HTTP 响应码,这个值一般是 100 到 511 之间的值。

#Res::set_version

void set_version(Version v);
  • 设置 HTTP 响应中的 HTTP 版本,默认使用 HTTP/1.1。
  • 用户一般不需要调用此方法。

#Res::str

fastring str() const;
  • 将 HTTP 响应转化成字符串形式,该字符串可以发送到网络上。

#http::Server

http::Server 是基于协程的 HTTP 服务器,它支持 HTTPS,使用 HTTPS 需要安装 openssl。

#Server::Server

Server();
  • 默认构造函数,用户无需关心。

#Server::on_req

void on_req(std::function<void(const Req&, Res&)>&& f);
template<typename T> void on_req(void (T::*f)(const Req&, Res&), T* o);
  • 设置处理客户端 HTTP 请求的回调函数。
  • 第 2 个版本中,参数 f 是类中的方法,参数 o 是 T 类型的指针。
  • 服务端接收到 HTTP 请求时,会调用此方法设置的回调函数,处理该请求。

#Server::start

void start(const char* ip="0.0.0.0", int port=80);
void start(const char* ip, int port, const char* key, const char* ca);
  • 启动 HTTP server,此方法不会阻塞当前线程。
  • 参数 key 是存放 SSL private key 的 PEM 文件路径,参数 ca 是存放 SSL 证书的 PEM 文件路径,默认 key 和 ca 是 NULL,不启用 SSL。

#Server::exit

void exit();
  • v2.0.3 新增。
  • 退出 HTTP server,关闭 listening socket,不再接收新的连接。
  • 此方法不会关闭之前已经建立的连接。

#代码示例

http::Server s;

s.on_req(
    [](const http::Req& req, http::Res& res) {
        if (req.is_method_get()) {
            if (req.url() == "/hello") {
                res.set_status(200);
                res.set_body("hello world");
            } else {
                res.set_status(404);
            }
        } else {
            res.set_status(405);
        }
    }
);

// start as a http server
s.start("0.0.0.0", 7777);

// start as a https server
s.start("0.0.0.0", 7777, "privkey.pem", "certificate.pem");

co/test 提供了一个简单的 demo,用户可以按下述方式编译运行:

xmake -b http_serv
xmake r http_serv

启动 http server 后,可以在浏览器的地址栏中输入 127.0.0.1/hello 看结果。

#静态 web server (so::easy)

void easy(const char* root_dir=".", const char* ip="0.0.0.0", int port=80);
void easy(const char* root_dir, const char* ip, int port, const char* key, const char* ca);
  • 启动一个静态 web server,参数 root_dir 是 web server 的根目录。

  • 参数 ip 可以是 IPv4 或 IPv6 地址。

  • 参数 key 是存放 SSL private key 的 PEM 文件路径,参数 ca 是存放 SSL 证书的 PEM 文件路径,默认 key 和 ca 是 NULL,不启用 SSL。

  • 此方法会阻塞当前线程。

  • 示例

#include "co/flag.h"
#include "co/log.h"
#include "co/so.h"

DEF_string(d, ".", "root dir"); // root dir of web server

int main(int argc, char** argv) {
    flag::init(argc, argv);
    log::init();

    so::easy(FLG_d.c_str()); // mum never have to worry again

    return 0;
}

#配置项

#http_conn_timeout

DEF_uint32(http_conn_timeout, 3000, "#2 connect timeout in ms for http client");
  • http::Client 的连接超时时间,单位为毫秒。

#http_timeout

DEF_uint32(http_timeout, 3000, "#2 send or recv timeout in ms for http client");
  • http::Client 接收、发送数据的超时时间 (libcurl 内部并不区分接收与发送超时时间),单位为毫秒。

#http_recv_timeout

DEF_uint32(http_recv_timeout, 3000, "#2 recv timeout in ms for http server");
  • http::Server 接收超时时间,单位为毫秒。

#http_send_timeout

DEF_uint32(http_send_timeout, 3000, "#2 send timeout in ms for http server");
  • http::Server 发送超时时间,单位为毫秒。

#http_conn_idle_sec

DEF_uint32(http_conn_idle_sec, 180, "#2 http server may close the con...");
  • http::Server 保持空闲连接的超时时间,单位为秒,server 在此时间内,若未收到客户端的数据,则可能会关闭该连接。

#http_log

DEF_bool(http_log, true, "#2 enable http server log if true");
  • http::Server 是否打印日志,默认为 true。
  • http::Server 中的日志会打印 HTTP 请求或响应的头部。

#http_max_idle_conn

DEF_uint32(http_max_idle_conn, 128, "#2 max idle connections for http server");
  • http::Server 最大空闲连接数,超过此数量时,会关闭部分空闲连接。

#http_max_body_size

DEF_uint32(http_max_body_size, 8 << 20, "#2 max size of http body, default: 8M");
  • http::Server 支持的最大 body 长度,默认为 8M。

#http_max_header_size

DEF_uint32(http_max_header_size, 4096, "#2 max size of http header");
  • http::Server 支持的最大 HTTP 头部(整个头部)长度,默认为 4k。