libcurl - HTTP usage

libcurl 是處理收送網路 request、response 的 library,可用來收送 HTTP 的 request 及 response。

libcurl 收到 response 時會 call callback function,由 callback function 處理收到的資料。callback function 可自行撰寫,其 prototype 如下:

1
size_t func(void *ptr, size_t size, size_t nmemb, void *userdata)
  • ptr 指向收到的資料
  • nmemb 為收到資料的大小
  • userdata 可自己定義,我拿它當 return value。

libcurl 的 callback function 分成處理 header 跟 data,用 curl_easy_setopt() 指定處理的 callback function。如果沒有指定 callback function,預設上 data 會被印出來,header 則不處理。

Usage

1. Initialize

1
2
3
#include <curl/curl.h>

CURL* curl = curl_easy_init();

2. Set HTTP header data

設 HTTP header,如指定 Content-type:

1
2
3
struct curl_slist* headers;
memset(header, 0, sizeof(headers));
headers = curl_slist_append(headers, "Content-type: application/json");

call curl_slist_append() 會不斷加資訊到 headers 這個資料結構中。

3. Set options

1
2
3
4
5
6
7
8
9
10
curl_easy_setopt(curl, CURLOPT_POST, 1);                // 以 POST 傳送資料
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // 指定 header
curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // 指定 url
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata); // 指定 data,將 postdata 當作 POST data 傳給 server

curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parseHeader); // 指定處理 HTTP header 的 callback function 為 parseHeader
curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &rescode); // 指定 parseHeader 的 userdata

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, getResponse); // 指定處理 data 的 callback function 為 getResponse
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // 指定 getResponse 的 userdata

4. 傳送 request

1
curl_easy_perform(curl);

以 blocking 的方式傳送 request。

5. clean up

1
2
curl_slist_free_all(headers);	// 清掉 headers 裡的東西
curl_easy_cleanup(curl);

callback function

1
size_t parseHeader(void *ptr, size_t size, size_t nmemb, string *userdata)

指定以 parseHeader() 處理 header:

1
2
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parseHeader);
curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &data); // 指定 data 的 address 給 parseHeader() 的 userdata

userdata 是 void*,可以使用任何 type 或 structure 的 pointer。

處理 data 的 option 是 CURLOPT_WRITEFUNCTIONCURLOPT_WRITEDATA

傳送 POST 資料

1
2
3
char postdata[] = "{\"reboot\": {\"type\" : \"SOFT\"}}";
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata);

在 postdata 放要傳的資料,用 set option 指定。不太確定 postdata 的 type 是不是一定要用 char?

curl-config --libs 指令找到需要的 link option。

Example

要求 Openstack reboot 某台 VM:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
bool reboot(string token, int vmid) {
CURL *curl;
struct curl_slist *headers = NULL;
string url = string(APIURL) + "/servers/" + int2string(vmid) + "/action";
char postdata[] = "{\"reboot\": {\"type\" : \"SOFT\"}}";
string rescode;

curl = curl_easy_init();
if ( curl ) {
headers = curl_slist_append(headers, string("X-Auth-Token: " + token).c_str());
headers = curl_slist_append(headers, "Content-type: application/json");

curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata); // put POST data here
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parseHeader);
curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &rescode); // set userdata in callback function

curl_easy_perform(curl);

curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}

if ( rescode == "202" ) return true;
else {
cerr << "reboot failed, response code is " << rescode << endl;
return false;
}
}

// parse http header
size_t parseHeader(void *ptr, size_t size, size_t nmemb, string *userdata) {
if ( strncmp((char *)ptr, "X-Auth-Token:", 13) == 0 ) { // get Token
strtok((char *)ptr, " ");
*userdata = string(strtok(NULL, " \n")); // token will be stored in userdata
}
else if ( strncmp((char *)ptr, "HTTP/1.1", 8) == 0 ) { // get http response code
strtok((char *)ptr, " ");
*userdata = string(strtok(NULL, " \n")); // http response code
}
return nmemb;
}

Ref