動態

詳情 返回 返回

Coke(三):使用HttpClient的更多功能 - 動態 詳情

Coke項目Github主頁。

上一篇文章通過幾個示例介紹瞭如何使用Coke便捷地發起Http請求,本文延續上一個話題,將coke::HttpClient的功能詳細地介紹一下。

在C++ Workflow中,Http任務通常通過工廠函數創建,並且可以指定重試次數等參數。而在Coke中可以通過coke::HttpClient來創建Http任務。首先介紹一下與任務相關的參數

struct HttpClientParams {
    // 當請求失敗時,支持自動進行重試操作,默認不重試
    int retry_max           = 0;
    // 連接創建完成後,將請求完全發出的超時時間,默認不超時,單位毫秒
    int send_timeout        = -1;
    // 請求發出後,客户端接收到完整回覆的超時時間,默認不超時
    int receive_timeout     = -1;
    // 連接保活時間,默認60秒
    int keep_alive_timeout  = 60 * 1000;

    // Http任務在框架內自動重定向的最大次數,默認為0
    int redirect_max        = 0;
    // 通過Http代理髮送請求,若不使用代理則指定為空串
    // 格式為 http://proxy.host:port/ 或 http://user:pass@proxy.host:port/
    std::string proxy;
};

coke::HttpClient目前有三個接口用於創建任務

  1. 使用url創建簡單的Http Get任務
  2. 使用urlmethodheaderbody創建任務
  3. 通過coke::HttpRequest準備好請求,再創建任務

具體使用方式參考下述示例代碼

#include <iostream>
#include <vector>
#include <string>
#include <utility>

#include "coke/coke.h"
#include "coke/http_client.h"
#include "coke/http_utils.h"

void show_response(const coke::HttpResponse &resp) {
    std::cout << resp.get_http_version() << ' '
              << resp.get_status_code() << ' '
              << resp.get_reason_phrase() << std::endl;

    // 使用 coke::HttpHeaderCursor 方便地遍歷所有header
    for (const coke::HttpHeaderView &header : coke::HttpHeaderCursor(resp))
        std::cout << header.name << ": " << header.value << std::endl;

    std::cout << "\n";

    // 為了簡潔起見,省略了Http body內容的輸出

    if (resp.is_chunked()) {
        // 對於Chunk編碼的Http body,可使用coke::HttpChunkCursor來遍歷
        for (std::string_view chunk : coke::HttpChunkCursor(resp))
            std::cout << "chunk body length " << chunk.size() << std::endl;
    }
    else {
        // 對於非Chunk編碼的Http body,可使用coke::http_body_view函數獲取
        std::string_view body_view = coke::http_body_view(resp);
        std::cout << "\nbody length " << body_view.size() << std::endl;
    }
}

coke::Task<> simple_get(coke::HttpClient &cli, const std::string &url) {
    coke::HttpResult result;
    result = co_await cli.request(url);

    if (result.state != coke::STATE_SUCCESS)
        std::cerr << coke::get_error_string(result.state, result.error) << std::endl;
    else
        show_response(result.resp);
}

coke::Task<> with_header_body(coke::HttpClient &cli, const std::string &url) {
    coke::HttpResult result;
    std::vector<std::pair<std::string, std::string>> header;
    std::string body;
    header.emplace_back("User-Agent", "coke-http-client");

    result = co_await cli.request(url, "HEAD", header, body);

    if (result.state != coke::STATE_SUCCESS)
        std::cerr << coke::get_error_string(result.state, result.error) << std::endl;
    else
        show_response(result.resp);
}

coke::Task<> with_req(coke::HttpClient &cli, const std::string &url) {
    coke::HttpResult result;
    coke::HttpRequest req;

    req.set_method("GET");
    req.set_request_uri("/");
    req.set_http_version("HTTP/1.1");
    req.set_header_pair("User-Agent", "coke-http-client");
    req.append_output_body("");

    result = co_await cli.request(url, std::move(req));

    if (result.state != coke::STATE_SUCCESS)
        std::cerr << coke::get_error_string(result.state, result.error) << std::endl;
    else
        show_response(result.resp);
}

void http_cli(const std::string &url, const std::string &proxy) {
    coke::HttpClientParams params;
    params.retry_max = 2;
    params.redirect_max = 2;
    params.proxy = proxy;

    // 使用指定的參數創建客户端
    coke::HttpClient cli(params);

    // 依次發起Http任務
    coke::sync_wait(simple_get(cli, url));
    coke::sync_wait(with_header_body(cli, url));
    coke::sync_wait(with_req(cli, url));

    // 也可以同時發起多個任務,但展示結果的函數沒有互斥鎖保護,結果可能錯亂
    //coke::sync_wait(
    //    simple_get(cli),
    //    with_header_body(cli),
    //    with_req(cli)
    //);
}

// ./http_client url [proxy]
int  main(int argc, char *argv[]) {
    std::string url{"https://sogou.com/"};
    std::string proxy;

    if (argc > 1 && argv[1])
        url.assign(argv[1]);
    if (argc > 2 && argv[2])
        proxy.assign(argv[2]);

    http_cli(url, proxy);

    return  0;
}

目前的代理僅支持http代理,而http代理又有兩種模式

  1. 當被請求的url scheme為http時,使用普通代理模式,將Http消息的request uri部分改寫為被請求的url,發送到代理服務器
  2. 當被請求的url scheme為https時,先向代理服務器發起CONNECT請求建立隧道,再繼續發起正常的請求

對於模式1,因為不是由原生的C++ WorkflowHttpTask支持的功能,所以重定向功能會被禁用,且根據上一篇文章的討論,使用coke可以很便捷地實現重定向的功能。

實際上,coke提供的coke::HttpClient是封裝了workflow的Http任務的語法糖,它本身並不維護任務的狀態等信息。即使不使用coke::HttpClient,熟悉C++ Workflow的同學也可以手動創建原生的HttpTask並結合coke提供的組件以協程的方式發起任務,但由於會帶來關於生命週期的一系列問題,此處不展開討論,後續會補充到coke的項目文檔當中去。

本系列文章同步發佈於個人網站和知乎專欄,轉載請註明來源,以便讀者及時獲取最新內容及勘誤。

user avatar hlinleanring 頭像 Yzi321 頭像 puxiaoke6 頭像 headofhouchang 頭像 artificer 頭像 tekin_cn 頭像 fanqiemao 頭像 qiyuxuanangdelvdou 頭像 wanmuc 頭像 goodcitizen 頭像 wei-boke 頭像 zhongyq-with-yuanqiu 頭像
點贊 13 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.