この記事は最終更新日から1年以上が経過しています。
@programming
投稿日 2021/2/1
更新日 2021/2/1 ✏
C++でlibcurlを利用したHTTPクライアント
C++でlibcurl
モジュールを利用したHTTPクライアントの簡単なサンプルです。
目次:
前提
- Ubuntu 16.04 LTS
- g++ 5.4.0
- curl 7.47.0 (libcurl 7.47.0)
サンプルコード
libcurlを使ったHTTPクライアント
libcurlを使ったシンプルなHTTPクライアントの自作クラスです。
Curl.h
#ifndef CURL_H
#define CURL_H
#include <curl/curl.h>
using namespace std;
/** A definition of the function pointer for http callback */
typedef void (*CurlCallback)(string err, string body);
/**
* Curl HTTP client class
*
* Usage:
* ```c++
* void callback(string err, string body)
* {
* if (err != "") {
* std::cerr << "Error:" << err << std::endl;
* } else {
* std::cout << body << std::endl;
* }
* }
*
* Curl* curl = new Curl();
* curl.get("https://www.google.com/", callback);
* ```
*
* or use lambda function:
*
* ```c++
* Curl* curl = new Curl();
* curl->get(url, [](string err, string body) {
* if (err != "") {
* cerr << "ERROR: " << err << endl;
* } else {
* cout << body << endl;
* }
* });
* ```
*/
class Curl {
private:
/** response body */
string body;
// TIP: CURLOPT_WRITEFUNCTION では 引数となる関数に static しか受け付けないので強引に static cast しています:
// see: https://curl.se/docs/faq.html#Using_C_non_static_functions_f
static size_t invoke_write_data(char *buffer, size_t size, size_t nmemb, void *f) {
// Call non-static member function.
return static_cast<Curl*>(f)->write_data(buffer, size, nmemb, f);
}
/** a callback function for libcurl request */
size_t write_data(char *buffer, size_t size, size_t nmemb, void *f) {
int dataLength = size * nmemb;
this->body.append(buffer, dataLength);
return dataLength;
}
public:
/** user-agent */
string useragent = "libcurl-agent/1.0";
/** timeout */
int timeout = 30L; // timeout 30 seconds
/**
* Constructor
*/
Curl() {
//
}
/**
* HTTP GET
*/
void get(const string url, const CurlCallback cb) {
CURL* curl;
CURLcode ret;
this->body = ""; // init result body.
string err = "";
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl == NULL) {
err = "curl_easy_init() failed on " + url;
return cb(err, "");
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, this->invoke_write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
curl_easy_setopt(curl, CURLOPT_USERAGENT, this->useragent.c_str()); // UA
curl_easy_setopt(curl, CURLOPT_TIMEOUT, this->timeout); // timeout
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // verbose
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_global_cleanup();
if (ret != CURLE_OK) {
err = "curl_easy_perform() failed on " + url + " (ret:" + to_string(ret) + ")";
return cb(err, "");
}
return cb(err, this->body);
}
/**
* HTTP POST
*/
void post(const string url, const string data, const CurlCallback cb) {
CURL* curl;
CURLcode ret;
this->body = ""; // init result body.
string err = "";
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl == NULL) {
err = "curl_easy_init() failed on " + url;
return cb(err, "");
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, this->invoke_write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
curl_easy_setopt(curl, CURLOPT_USERAGENT, this->useragent.c_str()); // UA
curl_easy_setopt(curl, CURLOPT_TIMEOUT, this->timeout); // timeout
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // verbose
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_global_cleanup();
if (ret != CURLE_OK) {
err = "curl_easy_perform() failed on " + url + " (ret:" + to_string(ret) + ")";
return cb(err, "");
}
return cb(err, this->body);
}
};
#endif // CURL_H
libcurl
モジュールのcurl_*
なAPIの使い方はなんとなく理解できるかと思います。(少々汚いコードですがお許しを🙇)
mainコード
先ほどのCurlクラス(Curl.h)を使うmainコードです。
curl-test.cpp
#include <iostream>
#include "Curl.h"
using namespace std;
int main(int argc, char* argv[]) {
string url = "http://127.0.0.1:3000/";
Curl* curl = new Curl();
// HTTP GET
curl->get(url, [](string err, string body) {
if (err != "") {
cerr << "ERROR: " << err << endl;
} else {
cout << "GET Response body:" << endl;
cout << body << endl;
}
});
// HTTP POST
string data = "a=123&b=456"; // posting data
curl->post(url, data, [](string err, string body) {
if (err != "") {
cerr << "ERROR: " << err << endl;
} else {
cout << "POST Response body:" << endl;
cout << body << endl;
}
});
}
テスト用Webサーバ (Node.js)
Node.jsで書かれた簡単なテスト用Webサーバです。GET
されるとクライアントへ "Hello, World!" を返し、POST
されるとPOSTされたデータを返すだけの簡単なサーバです。(テスト用なのでテキトーです。)
server.js
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
console.log("req.method:", req.method);
if (req.method === "GET") {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, World!\n');
} else if (req.method === "POST") {
let data = '';
req.on('data', chunk => {
data += chunk;
});
req.on('end', () => {
console.log("post body:", data);
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('post received: ' + data);
});
}
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
上記Webサーバを起動:
$ node server.js
Server running at http://127.0.0.1:3000/
サンプルコードのコンパイル&実行
mainコードのコンパイル&実行: (libcurlを使うために-lcurl
オプション、もしくは$(pkg-config --libs libcurl)
を付けてコンパイルします)
## コンパイル
$ g++ -o a.out curl-test.cpp $(pkg-config --libs libcurl) -std=c++11
## 実行:
$ ./a.out
GET Response body:
Hello, World!
POST Response body:
post received: a=123&b=456
Webサーバ側のログ:
Server running at http://127.0.0.1:3000/
req.method: GET
req.method: POST
post body: a=123&b=456
以上です。