20160112_jeffrey - silenceuncrio/diary GitHub Wiki
- 0935 - CURL - study
- 1020 - CURL - curl_easy_setopt - set options for a curl easy handle
- 1035 - CURL - CURLOPT_URL explained
- 1045 - CURL - CURLOPT_POSTFIELDS explained
- 1055 - CURL - CURLOPT_HTTPHEADER explained
- 1325 - CURL - example - curltest.c
- 1455 - CURL - example - get_common.c
- 1505 - SQLite - study
- 1625 - SQLite - C tutorial
研究一下 CURL
base on http-post.c 去了解每個 function
- curl_global_init - global libcurl initialisation
- curl_easy_init - create an easy handle
- curl_easy_setopt - set options for a curl easy handle
curl_easy_setopt - set options for a curl easy handle
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
注意到 option
的支援分為以下幾個大類
- BEHAVIOR OPTIONS
- CALLBACK OPTIONS
- ERROR OPTIONS
- NETWORK OPTIONS
- NAMES and PASSWORDS OPTIONS (Authentication)
- HTTP OPTIONS
- SMTP OPTIONS
- TFTP OPTIONS
- FTP OPTIONS
- RTSP OPTIONS
- PROTOCOL OPTIONS
- CONNECTION OPTIONS
- SSL and SECURITY OPTIONS
- SSH OPTIONS
- OTHER OPTIONS
- TELNET OPTIONS
注意 http-post.c 用到 option
的部分CURLOPT_POSTFIELDS
/* First set the URL that is about to receive our POST. This URL can
just as well be a https:// URL if that is what should receive the
data. */
curl_easy_setopt(curl, CURLOPT_URL, "http://postit.example.com/moo.cgi");
/* Now specify the POST data */
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=daniel&project=curl");
http-post.c 用到的 option
有 CURLOPT_URL
和 CURLOPT_POSTFIELDS
CURLOPT_URL
屬於 NETWORK OPTIONS 大類
CURLOPT_POSTFIELDS
屬於 HTTP OPTIONS 大類
cURL / libcurl / API / curl_easy_setopt / CURLOPT_URL
CURLOPT_URL - provide the URL to use in the request
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_URL, char *URL);
Pass in a pointer to the URL to work with. The parameter should be a char * to a zero terminated string which must be URL-encoded in the following format:
scheme://host:port/path
...
功能相當強悍
cURL / libcurl / API / curl_easy_setopt / CURLOPT_POSTFIELDS
CURLOPT_POSTFIELDS - specify data to POST to server
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_POSTFIELDS, char *postdata);
Pass a char * as parameter, pointing to the full data to send in a HTTP POST operation. You must make sure that the data is formatted the way you want the server to receive it. libcurl will not convert or encode it for you in any way. For example, the web server may assume that this data is url-encoded.
The data pointed to is NOT copied by the library: as a consequence, it must be preserved by the calling application until the associated transfer finishes. This behaviour can be changed (so libcurl does copy the data) by setting the CURLOPT_COPYPOSTFIELDS option.
This POST is a normal application/x-www-form-urlencoded kind (and libcurl will set that Content-Type by default when this option is used), which is commonly used by HTML forms. Change Content-Type with CURLOPT_HTTPHEADER.
Using CURLOPT_POSTFIELDS implies CURLOPT_POST.
You can use curl_easy_escape to url-encode your data, if necessary. It returns a pointer to an encoded string that can be passed as postdata.
If you want to do a zero-byte POST, you need to set CURLOPT_POSTFIELDSIZE explicitly to zero, as simply setting CURLOPT_POSTFIELDS to NULL or "" just effectively disables the sending of the specified string. libcurl will instead assume that you'll send the POST data using the read callback!
Using POST with HTTP 1.1 implies the use of a "Expect: 100-continue" header. You can disable this header with CURLOPT_HTTPHEADER as usual.
To make multipart/formdata posts (aka RFC 2388-posts), check out the CURLOPT_HTTPPOST option combined with curl_formadd.
...
看來把 curl 搞熟就省了一堆工了
之後也會用到 curl 來做 POST 的動作
而且內容物是 json
應該可以從 CURLOPT_HTTPHEADER
這個 option 來著手
cURL / libcurl / API / curl_easy_setopt / CURLOPT_HTTPHEADER
CURLOPT_HTTPHEADER - set custom HTTP headers
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTPHEADER, struct curl_slist *headers);
Pass a pointer to a linked list of HTTP headers to pass to the server and/or proxy in your HTTP request. The same list can be used for both host and proxy requests!
The linked list should be a fully valid list of struct curl_slist
structs properly filled in.
Use curl_slist_append
to create the list and curl_slist_free_all
to clean up an entire list.
If you add a header that is otherwise generated and used by libcurl internally, your added one will be used instead.
If you add a header with no content as in 'Accept:' (no data on the right side of the colon), the internally used header will get disabled.
With this option you can add new headers, replace internal headers and remove internal headers.
To add a header with no content (nothing to the right side of the colon), use the form 'MyHeader;' (note the ending semicolon).
The headers included in the linked list must not be CRLF-terminated, because libcurl adds CRLF after each header item. Failure to comply with this will result in strange bugs because the server will most likely ignore part of the headers you specified.
The first line in a request (containing the method, usually a GET or POST) is not a header and cannot be replaced using this option. Only the lines following the request-line are headers. Adding this method line in this list of headers will only cause your request to send an invalid header. Use CURLOPT_CUSTOMREQUEST to change the method.
When this option is passed to curl_easy_setopt, libcurl will not copy the entire list so you must keep it around until you no longer use this handle for a transfer before you call curl_slist_free_all on the list.
Pass a NULL to this option to reset back to no custom headers.
The most commonly replaced headers have "shortcuts" in the options CURLOPT_COOKIE, CURLOPT_USERAGENT and CURLOPT_REFERER. We recommend using those.
There's an alternative option that sets or replaces headers only for requests that are sent with CONNECT to a proxy: CURLOPT_PROXYHEADER. Use CURLOPT_HEADEROPT to control the behavior.
文謅謅的難以理解
不過之前發現的範例 leprechau/curltest.c 倒是越來越知道它的價值
example code using libcurl and json-c to post and parse a return from http://jsonplaceholder.typicode.com
試著在 JS9331 上執行 leprechau/curltest.c
/**
* example C code using libcurl and json-c
* to post and return a payload using
* http://jsonplaceholder.typicode.com
*
* Requirements:
*
* json-c - https://github.com/json-c/json-c
* libcurl - http://curl.haxx.se/libcurl/c
*
* Build:
*
* cc curltest.c -lcurl -ljson-c -o curltest
*
* Run:
*
* ./curltest
*
*/
/* standard includes */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
/* json-c (https://github.com/json-c/json-c) */
#include <json-c/json.h>
/* libcurl (http://curl.haxx.se/libcurl/c) */
#include <curl/curl.h>
/* holder for curl fetch */
struct curl_fetch_st {
char *payload;
size_t size;
};
/* callback for curl fetch */
size_t curl_callback (void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb; /* calculate buffer size */
struct curl_fetch_st *p = (struct curl_fetch_st *) userp; /* cast pointer to fetch struct */
/* expand buffer */
p->payload = (char *) realloc(p->payload, p->size + realsize + 1);
/* check buffer */
if (p->payload == NULL) {
/* this isn't good */
fprintf(stderr, "ERROR: Failed to expand buffer in curl_callback");
/* free buffer */
free(p->payload);
/* return */
return -1;
}
/* copy contents to buffer */
memcpy(&(p->payload[p->size]), contents, realsize);
/* set new buffer size */
p->size += realsize;
/* ensure null termination */
p->payload[p->size] = 0;
/* return size */
return realsize;
}
/* fetch and return url body via curl */
CURLcode curl_fetch_url(CURL *ch, const char *url, struct curl_fetch_st *fetch) {
CURLcode rcode; /* curl result code */
/* init payload */
fetch->payload = (char *) calloc(1, sizeof(fetch->payload));
/* check payload */
if (fetch->payload == NULL) {
/* log error */
fprintf(stderr, "ERROR: Failed to allocate payload in curl_fetch_url");
/* return error */
return CURLE_FAILED_INIT;
}
/* init size */
fetch->size = 0;
/* set url to fetch */
curl_easy_setopt(ch, CURLOPT_URL, url);
/* set calback function */
curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, curl_callback);
/* pass fetch struct pointer */
curl_easy_setopt(ch, CURLOPT_WRITEDATA, (void *) fetch);
/* set default user agent */
curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl-agent/1.0");
/* set timeout */
curl_easy_setopt(ch, CURLOPT_TIMEOUT, 5);
/* enable location redirects */
curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, 1);
/* set maximum allowed redirects */
curl_easy_setopt(ch, CURLOPT_MAXREDIRS, 1);
/* fetch the url */
rcode = curl_easy_perform(ch);
/* return */
return rcode;
}
int main(int argc, char *argv[]) {
CURL *ch; /* curl handle */
CURLcode rcode; /* curl result code */
json_object *json; /* json post body */
enum json_tokener_error jerr = json_tokener_success; /* json parse error */
struct curl_fetch_st curl_fetch; /* curl fetch struct */
struct curl_fetch_st *cf = &curl_fetch; /* pointer to fetch struct */
struct curl_slist *headers = NULL; /* http headers to send with request */
/* url to test site */
char *url = "http://jsonplaceholder.typicode.com/posts/";
/* init curl handle */
if ((ch = curl_easy_init()) == NULL) {
/* log error */
fprintf(stderr, "ERROR: Failed to create curl handle in fetch_session");
/* return error */
return 1;
}
/* set content type */
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
/* create json object for post */
json = json_object_new_object();
/* build post data */
json_object_object_add(json, "title", json_object_new_string("testies"));
json_object_object_add(json, "body", json_object_new_string("testies ... testies ... 1,2,3"));
json_object_object_add(json, "userId", json_object_new_int(133));
/* set curl options */
curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(ch, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(ch, CURLOPT_POSTFIELDS, json_object_to_json_string(json));
/* fetch page and capture return code */
rcode = curl_fetch_url(ch, url, cf);
/* cleanup curl handle */
curl_easy_cleanup(ch);
/* free headers */
curl_slist_free_all(headers);
/* free json object */
json_object_put(json);
/* check return code */
if (rcode != CURLE_OK || cf->size < 1) {
/* log error */
fprintf(stderr, "ERROR: Failed to fetch url (%s) - curl said: %s",
url, curl_easy_strerror(rcode));
/* return error */
return 2;
}
/* check payload */
if (cf->payload != NULL) {
/* print result */
printf("CURL Returned: \n%s\n", cf->payload);
/* parse return */
json = json_tokener_parse_verbose(cf->payload, &jerr);
/* free payload */
free(cf->payload);
} else {
/* error */
fprintf(stderr, "ERROR: Failed to populate payload");
/* free payload */
free(cf->payload);
/* return */
return 3;
}
/* check error */
if (jerr != json_tokener_success) {
/* error */
fprintf(stderr, "ERROR: Failed to parse json string");
/* free json object */
json_object_put(json);
/* return */
return 4;
}
/* debugging */
printf("Parsed JSON: %s\n", json_object_to_json_string(json));
/* exit */
return 0;
}
Makefile
CROSS_COMPILE = mips-openwrt-linux-uclibc-
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
CC_I = -I/home/jeffrey/openwrt/staging_dir/target-mips_34kc_uClibc-0.9.33.2/usr/include/
CC_I += -I/home/jeffrey/openwrt/staging_dir/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2/include/
CC_L = -L/home/jeffrey/openwrt/staging_dir/target-mips_34kc_uClibc-0.9.33.2/usr/lib/
CC_L += -L/home/jeffrey/openwrt/staging_dir/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2/lib
LIBS = -lm -ldl -lpthread -lfcgi -ljson-c -lpolarssl -lcurl
curltest: curltest.c
$(CC) $(CC_I) $(CC_L) $(LIBS) -o $@ $<
JS9331 執行結果 - 需要接上 WAN 端 - eth0
root@JoySince:/tmp# ./curltest
CURL Returned:
{
"title": "testies",
"body": "testies ... testies ... 1,2,3",
"userId": 133,
"id": 101
}
Parsed JSON: { "title": "testies", "body": "testies ... testies ... 1,2,3", "userId": 133, "id": 101 }
另存新檔 get_common.c
準備改裝以符合
- 解析出 response 內的
{
"name": "ward",
"location": "room",
"config": {
"mode": "on"
},
"thingManifest": {
"doorOpen" : 1,
"lightDetector": 1
}
}
- 更新 database
這一陣子前進的步伐都很扎實ㄚ
不過伴隨著安全感而來的卻是感到無聊...
get_common.c
改裝完畢
只有動到 main()
...
int main(int argc, char *argv[]) {
CURL *ch; /* curl handle */
CURLcode rcode; /* curl result code */
json_object *json; /* json post body */
enum json_tokener_error jerr = json_tokener_success; /* json parse error */
struct curl_fetch_st curl_fetch; /* curl fetch struct */
struct curl_fetch_st *cf = &curl_fetch; /* pointer to fetch struct */
/* url to test site */
char *url = "http://192.168.1.113:8888/common";
/* init curl handle */
if ((ch = curl_easy_init()) == NULL) {
/* log error */
fprintf(stderr, "ERROR: Failed to create curl handle in fetch_session");
/* return error */
return 1;
}
/* fetch page and capture return code */
rcode = curl_fetch_url(ch, url, cf);
/* cleanup curl handle */
curl_easy_cleanup(ch);
/* check return code */
if (rcode != CURLE_OK || cf->size < 1) {
/* log error */
fprintf(stderr, "ERROR: Failed to fetch url (%s) - curl said: %s",
url, curl_easy_strerror(rcode));
/* return error */
return 2;
}
/* check payload */
if (cf->payload != NULL) {
/* print result */
printf("CURL Returned: \n%s\n", cf->payload);
/* parse return */
json = json_tokener_parse_verbose(cf->payload, &jerr);
/* free payload */
free(cf->payload);
} else {
/* error */
fprintf(stderr, "ERROR: Failed to populate payload");
/* free payload */
free(cf->payload);
/* return */
return 3;
}
/* check error */
if (jerr != json_tokener_success) {
/* error */
fprintf(stderr, "ERROR: Failed to parse json string");
/* free json object */
json_object_put(json);
/* return */
return 4;
}
/* debugging */
printf("Parsed JSON: %s\n", json_object_to_json_string(json));
/* exit */
return 0;
}
JS9331 執行結果如下
root@JoySince:/tmp# ./get_common
CURL Returned:
{"name":"ward","location":"room","config":{"mode":"on"},"thingManifest":{"doorOpen":1,"lightDetector":1}}
Parsed JSON: { "name": "ward", "location": "room", "config": { "mode": "on" }, "thingManifest": { "doorOpen": 1, "lightDetector": 1 } }
再來就是更新 database 了
找個幾篇 SQLite 的教學備用
就它了 - SQLite C tutorial
第一步
sqlite_version.c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
printf("%s\n", sqlite3_libversion());
return 0;
}
JS9331 結果
root@JoySince:/tmp# ./sqlite_version
3.8.7.4
一片蛋糕
第二步
sqlite_version_1.c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
sqlite3 *db;
sqlite3_stmt *res;
int rc = sqlite3_open(":memory:", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
rc = sqlite3_prepare_v2(db, "SELECT SQLITE_VERSION()", -1, &res, 0);
if (rc != SQLITE_OK) {
fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
rc = sqlite3_step(res);
if (rc == SQLITE_ROW) {
printf("%s\n", sqlite3_column_text(res, 0));
}
sqlite3_finalize(res);
sqlite3_close(db);
return 0;
}
兩片蛋糕
第三步
sqlite_insert_data.c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "DROP TABLE IF EXISTS Cars;"
"CREATE TABLE Cars(Id INT, Name TEXT, Price INT);"
"INSERT INTO Cars VALUES(1, 'Audi', 52642);"
"INSERT INTO Cars VALUES(2, 'Mercedes', 57127);"
"INSERT INTO Cars VALUES(3, 'Skoda', 9000);"
"INSERT INTO Cars VALUES(4, 'Volvo', 29000);"
"INSERT INTO Cars VALUES(5, 'Bentley', 350000);"
"INSERT INTO Cars VALUES(6, 'Citroen', 21000);"
"INSERT INTO Cars VALUES(7, 'Hummer', 41400);"
"INSERT INTO Cars VALUES(8, 'Volkswagen', 21600);";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
JS9331 output
root@JoySince:/tmp# ./sqlite_insert_data
root@JoySince:/tmp# sqlite3 test.db
SQLite version 3.8.7.4 2014-12-09 01:34:36
Enter ".help" for usage hints.
sqlite> .mode column
sqlite> .headers on
sqlite> select * from Cars;
Id Name Price
---------- ---------- ----------
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000
6 Citroen 21000
7 Hummer 41400
8 Volkswagen 21600
sqlite>
三片蛋糕
也慢慢喚起了當初在 C 叫用 MySQL 的回憶
SQLite with IoT... 威力驚人