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目前有三個接口用於創建任務
- 使用
url創建簡單的Http Get任務 - 使用
url、method、header、body創建任務 - 通過
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代理又有兩種模式
- 當被請求的url scheme為http時,使用普通代理模式,將Http消息的
request uri部分改寫為被請求的url,發送到代理服務器 - 當被請求的url scheme為https時,先向代理服務器發起CONNECT請求建立隧道,再繼續發起正常的請求
對於模式1,因為不是由原生的C++ Workflow的HttpTask支持的功能,所以重定向功能會被禁用,且根據上一篇文章的討論,使用coke可以很便捷地實現重定向的功能。
實際上,coke提供的coke::HttpClient是封裝了workflow的Http任務的語法糖,它本身並不維護任務的狀態等信息。即使不使用coke::HttpClient,熟悉C++ Workflow的同學也可以手動創建原生的HttpTask並結合coke提供的組件以協程的方式發起任務,但由於會帶來關於生命週期的一系列問題,此處不展開討論,後續會補充到coke的項目文檔當中去。
本系列文章同步發佈於個人網站和知乎專欄,轉載請註明來源,以便讀者及時獲取最新內容及勘誤。