Skip to content

Instantly share code, notes, and snippets.

@aaronhurt
Last active November 26, 2023 10:29
Show Gist options
  • Star 77 You must be signed in to star a gist
  • Fork 26 You must be signed in to fork a gist
  • Save aaronhurt/e6b8fef41a153218e1f4 to your computer and use it in GitHub Desktop.
Save aaronhurt/e6b8fef41a153218e1f4 to your computer and use it in GitHub Desktop.
example code using libcurl and json-c to post and parse a return from http://jsonplaceholder.typicode.com
/**
* example C code using libcurl and json-c
* to post and return a payload using
* http://jsonplaceholder.typicode.com
*
* License:
*
* This code is licensed under MIT license
* https://opensource.org/licenses/MIT
*
* 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 using a temporary pointer to avoid memory leaks */
char * temp = realloc(p->payload, p->size + realsize + 1);
/* check allocation */
if (temp == NULL) {
/* this isn't good */
fprintf(stderr, "ERROR: Failed to expand buffer in curl_callback");
/* free buffer */
free(p->payload);
/* return */
return 1;
}
/* assign payload */
p->payload = temp;
/* 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, 15);
/* 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 1;
}
/* 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 1;
}
/* 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 1;
}
/* debugging */
printf("Parsed JSON: %s\n", json_object_to_json_string(json));
/* free json object before return */
json_object_put(json);
/* exit */
return 0;
}
@mauri870
Copy link

mauri870 commented May 1, 2017

First of all thanks for sharing this gist. Your code has a memory leak because you aren't freeing the json_object *json at the end of the program. This is confirmed by running valgrind --leak-check=full ./program:

==8196== LEAK SUMMARY:
==8196==    definitely lost: 72 bytes in 1 blocks
==8196==    indirectly lost: 1,101 bytes in 14 blocks
==8196==      possibly lost: 0 bytes in 0 blocks
==8196==    still reachable: 97,892 bytes in 685 blocks
==8196==         suppressed: 0 bytes in 0 blocks

Please update your code and add json_object_put(json) at the very end before the return statement

@adiog
Copy link

adiog commented Oct 23, 2017

I would not return -1 as an error code in function returning unsigned int ;)

@xy0058
Copy link

xy0058 commented Dec 16, 2017

mark

@gabrieledarrigo
Copy link

Hi!
Why do you use &(p->payload[p->size]) in memcpy before assigning the real size to the payload:
p->size += realsize; ?

@xogutu
Copy link

xogutu commented May 19, 2020

Thanks. Can I use this in my application? i.e license.

@aaronhurt
Copy link
Author

Thanks. Can I use this in my application? i.e license.

Let me apply some updates and give it a license in the comment block.

@xogutu
Copy link

xogutu commented May 20, 2020

Ok.

@med8bra
Copy link

med8bra commented Nov 5, 2020

Hi
In line 57, you're basically freeing a NULL pointer. I suggest checking the realloc() result before storing in it in p->payload (line 50).

@aaronhurt
Copy link
Author

Thanks @med8bra, in general I prefer to improve readability by reducing unnecessary checks. Freeing a NULL pointer is always safe.

@med8bra
Copy link

med8bra commented Nov 5, 2020

You right, freeing a NULL pointer is safe and useless. But there will be a memory leak anyway since p->payload is lost.

@aaronhurt
Copy link
Author

I see, that's a valid point.

@aaronhurt
Copy link
Author

Thanks @med8bra, I pushed an update to fix that issue.

@mahedi13
Copy link

mahedi13 commented Jan 6, 2021

Thanks, this helps.
I have added the json object like this.
The requirement for header is
Content-Encoding : gzip.

If I append this into header list, should I perform anything for json object or need to compress json object ?
Please suggest how can I do that?

@aaronhurt
Copy link
Author

@mahedi13 cURL does not (to my knowledge) provide a way to automatically compress payloads. You will need to do you own compression of the payload using zlib. You should probably also make sure that the server accepts gzip before sending it gzipped data.

See usage examples for zlib here: http://www.zlib.net/zlib_how.html

@mahedi13
Copy link

mahedi13 commented Jan 6, 2021

@leprechau According to your Json Object how will you handle, if you append Content-Encoding:gzip in header?
Can you share please?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment