http参数 libcurl_基于libcurl封装的HTTP客户端库

2023-05-16

@[toc]

libcurl安装

HttpClient

我们的目标是封装一个HttpClient类,支持GET、POST或者自定义方法,支持发送和接收文本、json、xml、form-data、x-www-form-urlencoded数据,支持自定义头部Headers等

// HttpRequest.h

#ifndef HTTP_REQUEST_H_

#define HTTP_REQUEST_H_

#include

#include

#include

using std::string;

using std::vector;

using std::map;

typedef std::map<:string std::string> KeyValue;

// F(id, str)

#define FOREACH_CONTENT_TYPE(F) \

F(TEXT_PLAIN, "text/plain") \

F(TEXT_HTML, "text/html") \

F(TEXT_XML, "text/xml") \

F(APPLICATION_JSON, "application/json") \

F(APPLICATION_XML, "application/xml") \

F(APPLICATION_JAVASCRIPT, "application/javascript") \

\

F(FORM_DATA, "multipart/form-data") \

\

F(X_WWW_FORM_URLENCODED, "application/x-www-form-urlencoded") \

F(QUERY_STRING, "text/plain")

#define ENUM_CONTENT_TYPE(id, _) id,

enum ContentType {

FOREACH_CONTENT_TYPE(ENUM_CONTENT_TYPE)

};

struct FormData {

enum FormDataType {

CONTENT,

FILENAME

} type;

string data;

FormData() {

type = CONTENT;

}

FormData(const char* data, FormDataType type = CONTENT) {

this->type = type;

this->data = data;

}

FormData(const string& str, FormDataType type = CONTENT) {

this->type = type;

this->data = str;

}

FormData(int n) {

this->type = CONTENT;

this->data = std::to_string(n);

}

FormData(long long n) {

this->type = CONTENT;

this->data = std::to_string(n);

}

FormData(float f) {

this->type = CONTENT;

this->data = std::to_string(f);

}

FormData(double lf) {

this->type = CONTENT;

this->data = std::to_string(lf);

}

};

typedef std::multimap<:string formdata> Form;

struct HttpRequest {

// request line

string method;

string url;

string version;

// headers

KeyValue headers;

// body

ContentType content_type;

string text;

KeyValue kvs; // QUERY_STRING,X_WWW_FORM_URLENCODED

Form form; // FORM_DATA

};

struct HttpResponse {

// status line

string version;

int status_code;

string status_message;

// headers

KeyValue headers;

// body

string body;

};

#endif // HTTP_REQUEST_H_

HttpRequest.h头文件中定义了ContentType,FormData,HttpRequest,HttpResponse等数据结构;

// HttpClient.h

#ifndef HTTP_CLIENT_H_

#define HTTP_CLIENT_H_

/***************************************************************

HttpClient based libcurl

***************************************************************/

#include

#include "HttpRequest.h"

class HttpClient {

public:

HttpClient();

~HttpClient();

int Send(const HttpRequest& req, HttpResponse* res);

static const char* strerror(int errcode);

void SetTimeout(int sec) {m_timeout = sec;}

void AddHeader(string key, string value) {

m_headers[key] = value;

}

void DelHeader(string key) {

auto iter = m_headers.find(key);

if (iter != m_headers.end()) {

m_headers.erase(iter);

}

}

void ClearHeader() {

m_headers.clear();

}

protected:

int curl(const HttpRequest& req, HttpResponse* res);

private:

int m_timeout; // unit:s default:10s

KeyValue m_headers;

};

#endif // HTTP_CLIENT_H_

HttpClient.h头文件声明了基于libcurl的HTTP客户端类;

// HttpClient.cpp

#include "HttpClient.h"

#include

#include

using std::string;

#define SPACE_CHARS " \t\r\n"

static string trim(const string& str) {

string::size_type pos1 = str.find_first_not_of(SPACE_CHARS);

if (pos1 == string::npos) return "";

string::size_type pos2 = str.find_last_not_of(SPACE_CHARS);

return str.substr(pos1, pos2-pos1+1);

}

static inline bool is_unambiguous(char c) {

return (c >= '0' && c <= '9') ||

(c >= 'A' && c <= 'Z') ||

(c >= 'a' && c <= 'z') ||

c == '-' ||

c == '_' ||

c == '.' ||

c == '~';

}

static string escape(const string& istr) {

string ostr;

const char* p = istr.c_str();

int len = istr.size();

char szHex[4] = {0};

for (int i = 0; i < len; ++i) {

if (is_unambiguous(p[i])) {

ostr += p[i];

}

else {

sprintf(szHex, "%%%02X", p[i]);

ostr += szHex;

}

}

return ostr;

}

#define DEFAULT_TIMEOUT 10

HttpClient::HttpClient() {

m_timeout = DEFAULT_TIMEOUT;

}

HttpClient::~HttpClient() {

}

int HttpClient::Send(const HttpRequest& req, HttpResponse* res) {

return curl(req, res);

}

static size_t s_formget_cb(void *arg, const char *buf, size_t len) {

return len;

}

static size_t s_header_cb(char* buf, size_t size, size_t cnt, void* userdata) {

if (buf == NULL || userdata == NULL) return 0;

HttpResponse* res = (HttpResponse*)userdata;

string str(buf);

string::size_type pos = str.find_first_of(':');

if (pos == string::npos) {

if (res->version.empty() && strncmp(buf, "HTTP", 4) == 0) {

// status line

// HTTP/1.1 200 OK\r\n

char* space1 = strchr(buf, ' ');

char* space2 = strchr(space1+1, ' ');

if (space1 && space2) {

*space1 = '\0';

*space2 = '\0';

res->version = buf;

res->status_code = atoi(space1+1);

res->status_message = trim(space2+1);

}

}

}

else {

// headers

string key = trim(str.substr(0, pos));

string value = trim(str.substr(pos+1));

res->headers[key] = value;

}

return size*cnt;

}

static size_t s_body_cb(char *buf, size_t size, size_t cnt, void *userdata) {

if (buf == NULL || userdata == NULL) return 0;

HttpResponse* res = (HttpResponse*)userdata;

res->body.append(buf, size*cnt);

return size*cnt;

}

int HttpClient::curl(const HttpRequest& req, HttpResponse* res) {

CURL* handle = curl_easy_init();

// SSL

curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);

curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0);

// method

curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, req.method.c_str());

// url

curl_easy_setopt(handle, CURLOPT_URL, req.url.c_str());

// header

struct curl_slist *headers = NULL;

if (m_headers.size() != 0) {

for (auto& pair : m_headers) {

string header = pair.first;

header += ": ";

header += pair.second;

headers = curl_slist_append(headers, header.c_str());

}

}

const char* psz = "text/plain";

switch (req.content_type) {

#define CASE_CONTENT_TYPE(id, str) \

case id: psz = str; break;

FOREACH_CONTENT_TYPE(CASE_CONTENT_TYPE)

#undef CASE_CONTENT_TYPE

}

string strContentType("Content-type: ");

strContentType += psz;

headers = curl_slist_append(headers, strContentType.c_str());

curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);

//hlogd("%s %s", req.method.c_str(), req.url.c_str());

//hlogd("%s", strContentType.c_str());

// body or params

struct curl_httppost* httppost = NULL;

struct curl_httppost* lastpost = NULL;

switch (req.content_type) {

case FORM_DATA:

{

for (auto& pair : req.form) {

CURLformoption opt = pair.second.type == FormData::FILENAME ? CURLFORM_FILE : CURLFORM_COPYCONTENTS;

curl_formadd(&httppost, &lastpost,

CURLFORM_COPYNAME, pair.first.c_str(),

opt, pair.second.data.c_str(),

CURLFORM_END);

}

if (httppost) {

curl_easy_setopt(handle, CURLOPT_HTTPPOST, httppost);

curl_formget(httppost, NULL, s_formget_cb);

}

}

break;

case QUERY_STRING:

case X_WWW_FORM_URLENCODED:

{

string params;

auto iter = req.kvs.begin();

while (iter != req.kvs.end()) {

if (iter != req.kvs.begin()) {

params += '&';

}

params += escape(iter->first);

params += '=';

params += escape(iter->second);

iter++;

}

if (req.content_type == QUERY_STRING) {

string url_with_params(req.url);

url_with_params += '?';

url_with_params += params;

curl_easy_setopt(handle, CURLOPT_URL, url_with_params.c_str());

//hlogd("%s", url_with_params.c_str());

}

else {

curl_easy_setopt(handle, CURLOPT_POSTFIELDS, params.c_str());

//hlogd("%s", params.c_str());

}

}

break;

default:

{

if (req.text.size() != 0) {

curl_easy_setopt(handle, CURLOPT_POSTFIELDS, req.text.c_str());

//hlogd("%s", req.text.c_str());

}

}

break;

}

if (m_timeout != 0) {

curl_easy_setopt(handle, CURLOPT_TIMEOUT, m_timeout);

}

curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, s_body_cb);

curl_easy_setopt(handle, CURLOPT_WRITEDATA, res);

curl_easy_setopt(handle, CURLOPT_HEADER, 0);

curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, s_header_cb);

curl_easy_setopt(handle, CURLOPT_HEADERDATA, res);

int ret = curl_easy_perform(handle);

if (ret != 0) {

//hloge("%d: %s", ret, curl_easy_strerror((CURLcode)ret));

}

//if (res->body.length() != 0) {

//hlogd("Response:%s", res->body.c_str());

//}

//double total_time, name_time, conn_time, pre_time;

//curl_easy_getinfo(handle, CURLINFO_TOTAL_TIME, &total_time);

//curl_easy_getinfo(handle, CURLINFO_NAMELOOKUP_TIME, &name_time);

//curl_easy_getinfo(handle, CURLINFO_CONNECT_TIME, &conn_time);

//curl_easy_getinfo(handle, CURLINFO_PRETRANSFER_TIME, &pre_time);

//hlogd("TIME_INFO: %lf,%lf,%lf,%lf", total_time, name_time, conn_time, pre_time);

if (headers) {

curl_slist_free_all(headers);

}

if (httppost) {

curl_formfree(httppost);

}

curl_easy_cleanup(handle);

return ret;

}

const char* HttpClient::strerror(int errcode) {

return curl_easy_strerror((CURLcode)errcode);

}

HttpClient.cpp源文件根据HttpRequest利用libcurl完成请求,在回调函数中解析填充HttpResponse

// test.cpp

#include "HttpClient.h"

#include

int main(int argc, char* argv[]) {

HttpClient session;

HttpRequest req;

req.method = "GET";

req.url = "www.baidu.com";

HttpResponse res;

int ret = session.Send(req, &res);

if (ret != 0) {

printf("%s %s failed => %d:%s\n", req.method.c_str(), req.url.c_str(), ret, HttpClient::strerror(ret));

}

else {

printf("%s %d %s\r\n", res.version.c_str(), res.status_code, res.status_message.c_str());

for (auto& header : res.headers) {

printf("%s: %s\r\n", header.first.c_str(), header.second.c_str());

}

printf("\r\n");

printf("%s", res.body.c_str());

printf("\n");

}

return ret;

}

g++ -std=c++11 HttpClient.cpp test.cpp -o test -lcurl

封装过后使用起来相当方便吧,一点不输python的requests

完整http客户端

git clone https://github.com/ithewei/hw.git

# 使用libcurl

make curl DEFINES="WITH_CURL CURL_STATICLIB"

bin/curl -h

bin/curl -v www.baidu.com

# 不使用libcurl,自实现的http客户端

make curl

# 使用openssl支持https

make curl DEFINES="WITH_OPENSSL"

bin/curl -v https:///www.baidu.com

# 使用nghhtp2支持http2

make curl DEFINES="WITH_NGHTTP2"

bin/curl -v www.nghttp2.org --http2

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

http参数 libcurl_基于libcurl封装的HTTP客户端库 的相关文章

随机推荐