I have the following libcurl program. When I run the program I get the following error.
Operation was aborted by an application callback
Process finished with exit code 42
My complete program
#include <stdio.h>
#include <curl/curl.h>
#include <cstdint>
#include <malloc.h>
#include <cstring>
#define TIMETYPE double
#define TIMEOPT CURLINFO_TOTAL_TIME
#define STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES 6000
struct myprogress {
TIMETYPE lastruntime;
CURL *curl;
};
struct memoryStruct {
uint8_t* memory;
size_t size;
};
size_t handleData(void* contents, size_t size, size_t nmemb, void* stream) {
size_t realSize = size * nmemb;
struct memoryStruct* mem = static_cast<struct memoryStruct*>(stream);
mem->memory = static_cast<uint8_t*>(realloc(mem->memory, (mem->size + realSize + 1)));
if (mem->memory == nullptr) {
return 0;
}
memcpy(&(mem->memory[mem->size]), contents, realSize);
mem->size += realSize;
mem->memory[mem->size] = 0;
return realSize;
}
static int xferinfo(void *p,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
struct myprogress *myp = (struct myprogress *)p;
CURL *curl = myp->curl;
TIMETYPE curtime = 0;
curl_easy_getinfo(curl, TIMEOPT, &curtime);
myp->lastruntime = curtime;
fprintf(stderr, "TOTAL TIME: %f \r\n", curtime);
fprintf(stderr," DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T
"\r\n", dlnow, dltotal);
if(dlnow > STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES)
return 1;
return 0;
}
int main(void)
{
CURL *curl;
CURLcode res = CURLE_OK;
struct myprogress prog;
struct memoryStruct m_chunk;
curl = curl_easy_init();
if(curl) {
prog.lastruntime = 0;
prog.curl = curl;
m_chunk.memory = static_cast<uint8_t*>(malloc(1));
if (nullptr == m_chunk.memory) {
return 1;
}
m_chunk.size = 0;
curl_easy_setopt(curl, CURLOPT_URL, "http://172.16.132.84:5000/download/file.out");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handleData);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &m_chunk);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "%s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
return (int)res;
}
Your file is larger then 6000 bytes:
STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES 6000
In this case you return 1 in your xferinfo function, which aborts the download.
Documentation
If your callback function returns CURL_PROGRESSFUNC_CONTINUE it will cause libcurl to continue executing the default progress function.
Returning any other non-zero value from this callback will cause libcurl to abort the transfer and return CURLE_ABORTED_BY_CALLBACK.
see https://curl.haxx.se/libcurl/c/CURLOPT_XFERINFOFUNCTION.html
Test
If I download a file with your code with 4431 bytes I get in the debug console:
...
TOTAL TIME: 0.088631
DOWN: 4431 of 4431
Process finished with exit code 0
If I change STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES to 2000 then I get:
...
Operation was aborted by an application callback
Process finished with exit code 42
Related
I'm trying to get the content length info and the http status code in the CURLOPT_WRITEFUNCTION callback function. I'm using curl_getinfo on the curl handle to query those fields but curl_getinfo always seems to be returning the default values.
Here is the code
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
struct url_data {
size_t size;
char* data;
CURL *curl;
};
size_t _response_header_callback(void *received_data, size_t size, size_t count, void *response_header) {
//printf("%s ", (char *)received_data);
}
size_t write_data(void *ptr, size_t size, size_t nmemb, struct url_data *data) {
size_t index = data->size;
size_t n = (size * nmemb);
char* tmp;
long responseCode = 0;
curl_easy_setopt(data->curl, CURLINFO_RESPONSE_CODE, &responseCode);
curl_off_t cl;
curl_easy_getinfo(data->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl);
printf("\n whats the response Code %d %d", responseCode, cl);
data->size += (size * nmemb);
#ifdef DEBUG
fprintf(stderr, "data at %p size=%ld nmemb=%ld\n", ptr, size, nmemb);
#endif
tmp = realloc(data->data, data->size + 1); /* +1 for '\0' */
if(tmp) {
data->data = tmp;
} else {
if(data->data) {
free(data->data);
}
fprintf(stderr, "Failed to allocate memory.\n");
return 0;
}
memcpy((data->data + index), ptr, n);
data->data[data->size] = '\0';
return size * nmemb;
}
char *handle_url(char* url) {
CURL *curl;
struct url_data data;
data.size = 0;
data.data = malloc(4096); /* reasonable size initial buffer */
if(NULL == data.data) {
fprintf(stderr, "Failed to allocate memory.\n");
return NULL;
}
data.data[0] = '\0';
CURLcode res;
curl = curl_easy_init();
if (curl) {
data.curl = curl;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_RANGE, "0-50");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
curl_easy_setopt(curl, CURLOPT_RANGE, "51-100");
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
// need to detect that this range isn't satisfiable
curl_easy_setopt(curl, CURLOPT_RANGE, "1551-1600");
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
return data.data;
}
int main(int argc, char* argv[]) {
char* data;
if(argc < 2) {
fprintf(stderr, "Must provide URL to fetch.\n");
return 1;
}
data = handle_url(argv[1]);
if(data) {
printf("%s\n", data);
free(data);
}
return 0;
}
And this is the output
./fetch http://<url>/test/test_key
curl_easy_perform() failed: Couldn't resolve host name
the response Code 0 content length 0
the response Code 0 content length 0
the response Code 0 content length 0
<< download text >
I'm on centos 7 and the libcurl version 7.29.0
Appreciate if someone can let me know what I may be doing wrong. Thank you .
I am trying to send a POST type message with HTTPS communication using the curl.h library but I have some doubts:
It is necessary to have the certificate in a file (possibly inside the project folder) or it can be taken dynamically (as PostMan does for example).
How should the format of the sending of data be.
How to receive the data is equal to an HTTP?
This is my program:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <curl/curl.h>
BYTE str[20];
struct string
{
char *ptr;
size_t len;
};
void init_string(struct string *s)
{
s -> len = 0;
s -> ptr = malloc(s -> len + 1);
if (s -> ptr == NULL)
{
fprintf(stderr, "malloc() failed\n");
exit(EXIT_FAILURE);
}
s -> ptr[0] = '\0';
}
size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s)
{
size_t new_len = s -> len + size * nmemb;
s -> ptr = realloc(s -> ptr, new_len + 1);
if (s -> ptr == NULL)
{
fprintf(stderr, "realloc() failed\n");
exit(EXIT_FAILURE);
}
memcpy(s -> ptr + s -> len, ptr, size * nmemb);
s -> ptr[new_len] = '\0';
s -> len = new_len;
return size * nmemb;
}
int post_function()
{
CURL *post;
CURLcode ret;
struct string s;
init_string(&s);
curl_global_init(CURL_GLOBAL_DEFAULT);
post = curl_easy_init();
curl_easy_setopt(post, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(post, CURLOPT_URL, "https://example.com/post.php");
curl_easy_setopt(post, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(post, CURLOPT_WRITEDATA, &s);
curl_easy_setopt(post, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(post, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(post, CURLOPT_POSTFIELDS, "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"data\"\r\n\r\nName\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--");
ret = curl_easy_perform(post);
if(ret == CURLE_OK)
{
sprintf(str, "Msj: %s\n", s.ptr);
}
else
{
//ERROR
}
curl_easy_cleanup(post);
curl_global_cleanup();
}
int main(int argc, char const *argv[])
{
post_function();
return 0;
}
I did this in PHP, and it was relatively easy. However, Its trickier in C, mostly because the cURL library is more tricky, and C isn't object oriented. Here is the snippet of code I have so far:
#include <curl/curl.h>
/* Make first curl call */
curl_global_init();
//initialize first curl instance
CURL *handle;
CURLcode response;
handle = curl_easy_init();
//craft url
char *url = "https://example.com/";
//set url
curl_easy_setopt(handle, CURLOPT_URL, url);
//get resonse
response = curl_easy_perform(handle);
//clean up
curl_easy_cleanup(handle);
The curl should be using get, and also shouldn't I specify Accept: application/json in one of the curlopts?
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
size_t len;
void* buf;
} Body;
typedef size_t (*WriteFunction)(void*, size_t, size_t, void*);
static size_t write_body(const void* ptr, size_t size, size_t nmemb, Body* body_ptr) {
size_t new_len = body_ptr->len + ( size * nmemb );
if (new_len == 0)
return 0;
char* new_buf = realloc(body_ptr->buf, new_len);
if (new_buf == NULL)
return 0;
memcpy(((char*)body_ptr) + new_len, ptr, ( size * nmemb ));
body_ptr->len = new_len;
body_ptr->buf = new_buf;
}
static int write_all(int fd, void* buf, size_t remaining) {
while (remaining > 0) {
ssize_t written = write(STDOUT, buf, remaining);
if (written == -1)
return 0;
remaning -= written;
buf = ((char*)buf) + written;
}
return 1;
}
int main(void) {
CURL* curl = NULL;
struct curl_slist* header_list = NULL;
Body body = { 0, NULL };
int error = 1;
curl = curl_easy_init();
if (curl != NULL) {
fprintf(stderr, "curl_easy_init() failed\n");
goto ERROR;
}
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
header_list = curl_slist_append(header_list, "Accept: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (WriteFunction)write_body);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &body);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
goto ERROR;
}
long http_code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code < 200 || http_code >= 300) {
fprintf(stderr, "Request not successful: HTTP error code %ld\n", http_code);
goto ERROR;
}
if (body.len == 0) {
printf("Empty response\n");
} else {
printf("Received %zu bytes\n", body.len);
fflush(stdout);
if (!write_all(STDOUT, body->buf, body->len)) {
perror("write");
goto ERROR;
}
}
error = 0;
ERROR:
if ( body.buf != NULL ) free(body.buf);
if ( header_list != NULL ) curl_slist_free_all(header_list);
if ( curl != NULL ) curl_easy_cleanup(curl);
return error;
}
Not tested.
I have homework where I need somehow to compare two HTTP responses. I am writing it on C and I use libcurl to make things easier. I am calling the function that uses libcurl to do a HTTP request and response from another function, and I want to return the HTTP response as a char *. Here is my code so far (it crashes):
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
char *handle_url(void) {
CURL *curl;
char *fp;
CURLcode res;
char *url = "http://www.yahoo.com";
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
//printf("\n%s", fp);
}
return fp;
}
This solution C libcurl get output into a string works, but not in my case because I just want to return the string to the calling function.
Any ideas?
Fixed it for you. You need to handle the case where the write_data() function is called multiple times, and pass it the right kind of parameter. You also need to keep track of how big a structure you've got, so you can allocate enough memory.
I left in a debug printf in the write_data function to help you understand how it works.
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
struct url_data {
size_t size;
char* data;
};
size_t write_data(void *ptr, size_t size, size_t nmemb, struct url_data *data) {
size_t index = data->size;
size_t n = (size * nmemb);
char* tmp;
data->size += (size * nmemb);
#ifdef DEBUG
fprintf(stderr, "data at %p size=%ld nmemb=%ld\n", ptr, size, nmemb);
#endif
tmp = realloc(data->data, data->size + 1); /* +1 for '\0' */
if(tmp) {
data->data = tmp;
} else {
if(data->data) {
free(data->data);
}
fprintf(stderr, "Failed to allocate memory.\n");
return 0;
}
memcpy((data->data + index), ptr, n);
data->data[data->size] = '\0';
return size * nmemb;
}
char *handle_url(char* url) {
CURL *curl;
struct url_data data;
data.size = 0;
data.data = malloc(4096); /* reasonable size initial buffer */
if(NULL == data.data) {
fprintf(stderr, "Failed to allocate memory.\n");
return NULL;
}
data.data[0] = '\0';
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
return data.data;
}
int main(int argc, char* argv[]) {
char* data;
if(argc < 2) {
fprintf(stderr, "Must provide URL to fetch.\n");
return 1;
}
data = handle_url(argv[1]);
if(data) {
printf("%s\n", data);
free(data);
}
return 0;
}
Note: compile with gcc -o test test.c -lcurl (assuming you pasted into test.c). Use gcc -o test test.c -lcurl -DDEBUG to see the test printf() calls.
Disclaimer: this is ugly, quick-and-dirty code. There may be bugs. Please see the more robust, better commented example here.
I got the code from C libcurl get output into a string. I modified it and I want to use it to access the Twitter Stream. I added curl_easy_setopt(curl, CURLOPT_URL, "http://stream.twitter.com/1/statuses/sample.json"); and curl_easy_setopt(curl, CURLOPT_USERPWD, "neilmarion:my_password"); in the code. But the problem is whenever I execute it, there is no output. What must be the problem? Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
struct string {
char *ptr;
size_t len;
};
void init_string(struct string *s) {
s->len = 0;
s->ptr = malloc(s->len+1);
if (s->ptr == NULL) {
fprintf(stderr, "malloc() failed\n");
exit(EXIT_FAILURE);
}
s->ptr[0] = '\0';
}
size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s)
{
size_t new_len = s->len + size*nmemb;
s->ptr = realloc(s->ptr, new_len+1);
if (s->ptr == NULL) {
fprintf(stderr, "realloc() failed\n");
exit(EXIT_FAILURE);
}
memcpy(s->ptr+s->len, ptr, size*nmemb);
s->ptr[new_len] = '\0';
s->len = new_len;
return size*nmemb;
}
int main(void)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
struct string s;
init_string(&s);
curl_easy_setopt(curl, CURLOPT_URL, "http://stream.twitter.com/1/statuses/sample.json");
curl_easy_setopt(curl, CURLOPT_USERPWD, "neilmarion:my_password");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
res = curl_easy_perform(curl);
printf("%s\n", s.ptr);
free(s.ptr);
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}
The quickest next step is probably to set CURLOPT_HEADER, to include headers in the body output. Most likely, I would guess it is failing on security, and you'll see the details in the headers.