Can libcurl be used to make multiple concurrent requests? - c

I am using libcurl for one of my projects. I know that curl is not used to make multiple concurrent requests but does libcurl support it?
I know there are other tools like ab but that there are many features that libcurl provides.
Again I know I can use curl within script to run multiple requests,but that's not what I am looking for.
I could not find a satisfactory answer for this expect this one. Although, It's not conclusive.
I should be able to use multiple handles for multiple connections.
Has anyone tried this? Are there any gotchas I need to look out for?
I should be able to do something like this:
my_app --total_connections 1000 --concurrency 100 <Other libcurl options> url

To test what you are looking for, i wrote a little C program. It executes 10 http-get requests using libcurl in a loop. The loop is parallelized using openmp (if available).
To run it, just save it in a file called for example parallel_curl_test.cpp and compile it two times. First using g++ parallel_curl_test.cpp -fopenmp $(pkg-config --libs --cflags libcurl) -o parallel_curl for the parallel version and a second time using g++ parallel_curl_test.cpp $(pkg-config --libs --cflags libcurl) -o sequential_curl without openmp for the sequential version.
Here is the code:
#include <cmath>
#include <stdio.h>
#include <curl/curl.h>
#include <time.h>
void curl_request();
size_t write_data(void *, size_t, size_t, void *);
static struct timeval tm1;
static int num_requests = 10;
static inline void start()
{
gettimeofday(&tm1, NULL);
}
static inline void stop()
{
struct timeval tm2;
gettimeofday(&tm2, NULL);
unsigned long long t = 1000 * (tm2.tv_sec - tm1.tv_sec) + (tm2.tv_usec - tm1.tv_usec) / 1000;
printf("%d requests in %llu ms\n",num_requests , t);
}
int main()
{
start();
#pragma omp parallel for
for(int n=0; n<num_requests; ++n){
curl_request();
}
stop();
return 0;
}
void curl_request()
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_request() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
}
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
return size * nmemb;
}
The output for ./parallel_curl will look like this:
10 requests in 657 ms
the output for ./sequential_curl will look something like:
10 requests in 13794 ms
As you can see, the parallel_curl which uses concurrency finished significantly faster than sequential_curl which ran sequential.
Thus the answer to your question is : Yes!
Of course, sequential execution could be done much more efficient using pipelining, keep-alives and reusage of resources. But this is another question.

Related

compiling c language program with libcurl on windows

I am trying to compile a simple c program on windows 10 using gcc from the libcurl website I cloned vcpkg and then ran the .bat file , next I installed curl with the command vcpkg install curl and got this output
Computing installation plan...
The following packages are already installed:
curl[core,non-http,openssl,schannel,ssl,sspi]:x86-windows -> 7.80.0
Package curl:x86-windows is already installed
Restored 0 packages from C:\Users\<me>\AppData\Local\vcpkg\archives in 138.1 us. Use --debug to see more details.
Total elapsed time: 386.9 ms
The package curl provides CMake targets:
find_package(CURL CONFIG REQUIRED)
target_link_libraries(main PRIVATE CURL::libcurl)
#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>
int report()
{
CURL* curl;
CURLcode res;
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
if (curl) {
/* 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://192.168.1.12:8000");
const char* c = const_cast<char*>(output.c_str());
printf ("%s", c);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, c );
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
int main()
{
report();
return 0;
}
Now when I try to compile the code above I get the error
c_example_curl.c:3:23: fatal error: curl/curl.h: No such file or directory
#include <curl/curl.h>
^
compilation terminated.
I am compiling using the command
gcc -lcurl file.c
I also tried using the command
gcc -lcurl -I F:\_C_\vcpkg\installed\x86-windows\include\curl file.c
doesn't seem to change anything
I even tried to copy the curl header file to the working directory of the above code but I was not lucky
How do you install libcurl for windows isn't there any command equivalent to the linux one
download curl for windows here.
unpack the zip and place it anywhere on the system (i personally prefer C:\curl).
then compile your script with: gcc file.c -I<path-to-curl> -L<path-to-curl>\lib -lcurl
NOTE: replace <path-to-curl> with the curl location on the system, for example C:\curl

exe very large when curl is linked

I have downloaded a compiled curl for Windows (the .a files) and linked it to my project. The problem is, the compiled .exe is very large (2,41MB) just for this simple program:
#include <stdio.h>
#include <curl/curl.h>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void)
{
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "https://website/app.exe";
char outfilename[FILENAME_MAX] = "file.exe";
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
Now, I assume that is because curl I have linked to my project has many functions enabled in itself/contains many functions which I don't need. Is there any way to enable only HTTPS (and HTTP by that?) and with that make the program size smaller? I have found something like that on the curl website (https://curl.haxx.se/docs/install.html - Disabling Specific Protocols in Windows builds), but I didn't quite understand what should I do. I tried putting the "HTTP_ONLY" in "defines" in CodeBlocks but it did nothing. Probably curl has to be compiled somehow with that option. What can I do?
Any help is appreciated, thank you!
Compile your application in release mode and turn on all strips, compiling an application in release mode will reduce its size due to removal of debugging symbols
You have to compile libcurl yourself and configure it. configuring a library could be a little tricky, but it worth, you can fully customize it so disabling unused features let you reduce library size, also don't remember to compile curl without debugging informations.
compile all staffs with different optimization flags, say -Os is preferable but you should check with other optimization levels to verify.
use shared libraries as much as possible

How to override fprintf in the C library? How to add GCC option to toplevel CMakeLists.txt?

I know how to overwrite C-lib functions in just simple sample. But what I need is IN A REAL PROJECT which has hundreds of files that call fprintf.
In each file, there's a "#include < stdio.h >", and tens or hundreds of calls of fprintf. I wanna make all these fprintf to do my own job. I cannot remove the "stdio.h" and put a "#include < myprint.h >", where myprint.h defines the real function or macro of fprintf that do my own job. "stdio.h" has many other calls in the project. I want a simple enough solution.
Thanks!
Do I make my question clear enough?...
Update on 2014.03.08:
Salute to all women at first...
Please see my 2nd post below.
If you are using gcc for example, you can invoke it in the following way:
gcc -include myheader.h -Dfprintf=myfprintf file.c
And that will include myheader.h before every other include header in file.c and will redefine fprintf as myfprintf.
You can find more details here
Question update overview: I added GCC option to CMAKE, but it seems not working.
====== SOLUTION TO MY FIRST POST ======
Hi all,
As for the question in my first post, I found a hook solution as follows:
#define _GNU_SOURCE
// for time() and localtime()
#include <time.h>
// for fprintf/fwrite
#include <stdio.h>
// for va_* functions
#include <stdarg.h>
// for dlsym
#include <dlfcn.h>
// compile command line: gcc -shared -Wl,--no-as-needed -ldl -fPIC -Wall hooktest.c -o hooktest.so
// how to use: LD_PRELOAD=hooktest.so ./hooktestprog, where hooktestprog includes stdio.h and calls fprintf
// a helper function
void print_time(FILE *fp, const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(fp, format, args);
va_end(args);
}
// GCC optimizes fprintf to fwrite
// to make original program use fprintf exactly, compile the program (not this library) with -fno-builtin-fprintf
int fprintf(FILE *stream, const char *format, ...)
{
static int (*my_fprintf)(FILE *stream, const char *format, ...) = NULL;
if (NULL == my_fprintf)
{
my_fprintf = (int (*)(FILE *stream, const char *format, ...))dlsym(RTLD_NEXT, "fprintf");
}
va_list args;
va_start(args, format);
time_t tNow = time(NULL);
struct tm *tmNow = localtime(&tNow);
print_time(stream, "%04d/%02d/%02d %02d:%02d:%02d === ", tmNow->tm_year+1900, tmNow->tm_mon+1, tmNow->tm_mday, tmNow->tm_hour, tmNow->tm_min, tmNow->tm_sec);
my_fprintf(stream, format, args);
va_end(args);
return 0;
}
// GCC optimizes fprintf to fwrite
// to make original program use fprintf exactly, compile the program (not this library) with -fno-builtin-fprintf
/* size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
static size_t (*my_fwrite)(const void *, size_t, size_t, FILE *) = NULL;
if (NULL == my_fwrite)
{
my_fwrite = (size_t (*)(const void *, size_t, size_t, FILE *))dlsym(RTLD_NEXT, "fwrite");
}
time_t tNow = time(NULL);
struct tm *tmNow = localtime(&tNow);
print_time(stream, "%04d/%02d/%02d %02d:%02d:%02d === ", tmNow->tm_year+1900, tmNow->tm_mon+1, tmNow->tm_mday, tmNow->tm_hour, tmNow->tm_min, tmNow->tm_sec);
my_fwrite(ptr, size, nmemb, stream);
FILE *fp = fopen("/home/idesclient/log_freerdp.log", "a");
if (NULL != fp)
{
print_time(fp, "%04d/%02d/%02d %02d:%02d:%02d === ", tmNow->tm_year+1900, tmNow->tm_mon+1, tmNow->tm_mday, tmNow->tm_hour, tmNow->tm_min, tmNow->tm_sec);
my_fwrite(ptr, size, nmemb, fp);
fclose(fp);
}
return nmemb;
} */
I wanna override fprintf, but GCC optimizes fprintf to fwrite. You can check this by writing a sample that calls fprintf, compile it, and use the command "nm yoursample | grep #GLIBC_2.0". you can see something like "fwrite#GLIBC_2.0", and there is NO "fprintf#GLIBC_2.0".
But I cannot override fwrite because there are other fwrite calls in my project. As you can see, I commented out fwrite().
I found another solution: add "-fno-builtin-fprintf" while compiling a sample program, and now the sample works. NOTE: only the sample works, not my project works... The command line is as follows:
>gcc -shared -Wl,--no-as-needed -ldl -fPIC -Wall hooktest.c -o hooktest.so
>gcc -fno-builtin-fprintf sample.c -o sample # where sample.c calls fprintf
>LD_PRELOAD=./hooktest.so ./sample
Now I can see fprintf adds time information - fprintf is hooked.
====== NEW QUESTION STARTS HERE ======
By now, it seems I can add "-fno-builtin-fprintf" to my toplevel CMakeLists.txt (the project has many subdirectories, it's quite a complex project), and everythind SHOULD work. But in real practice, it does not.
I have the following lines in my toplevel CMakeLists.txt:
if(CMAKE_COMPILER_IS_GNUCC)
xxxxxx (this is the original definitions in my project)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin-fprintf")
endif()
But after I compile the project, use "LD_PRELOAD=/xxx/hooktest.so /xxx/project/bin/somebinary", fprintf is not hooked.
I do another test. In my C code above, I comment fprintf and uncomment fwrite (GCC optimizes fprintf to fwrite, see expaination above), and remove the "-fno-builtin-fprintf" in the toplevel CMakeLists.txt. I can see fprintf hooked to fwrite in my code, but only fprintf(stderr, "xxx") hooked, but fprintf(stderr, "xxx %s %d xxx", somestring, someinteger) not hooked. It seems that fprintf with % and more than 2 arguments cannot be hooked.
Please help to debug this. Thanks!

Saving a file using libcurl in C

I'm expanding from perl to C and I'm trying to use curl's library to simply save a file from a remote url but I'm having a hard time finding a good example to work from.
Also, I'm not sure if I should be using curl_easy_recv or curl_easy_perform
I find this resource very developer friendly.
I compiled the source code below with:
gcc demo.c -o demo -I/usr/local/include -L/usr/local/lib -lcurl
Basically, it will download a file and save it on your hard disk.
File demo.c
#include <curl/curl.h>
#include <stdio.h>
void get_page(const char* url, const char* file_name)
{
CURL* easyhandle = curl_easy_init();
curl_easy_setopt( easyhandle, CURLOPT_URL, url ) ;
FILE* file = fopen( file_name, "w");
curl_easy_setopt( easyhandle, CURLOPT_WRITEDATA, file) ;
curl_easy_perform( easyhandle );
curl_easy_cleanup( easyhandle );
fclose(file);
}
int main()
{
get_page( "http://blog.stackoverflow.com/wp-content/themes/zimpleza/style.css", "style.css" ) ;
return 0;
}
Also, I believe your question is similar to this one:
Download file using libcurl in C/C++

What's the most efficient way to get source code of web page in C?

In PHP I can do it as simple as :
file_get_contents('http://stackoverflow.com/questions/ask');
What's the shortest code to do the same in C?
UPDATE
When I compile the sample with curl, got errors like this:
unresolved external symbol __imp__curl_easy_cleanup referenced in function _main
Use libcurl, refer to their example C snippets
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}
Try the libcurl C interface
I should have commented the Richard Harrison good answer, but I have not 50 reputations points yet, so I put here as an answer my hint to ieplugin for compiling the code:
On Ubuntu 10.04 (and supposing you named the source file getpage.c):
sudo apt-get install libcurl4-dev
gcc getpage.c -lcurl -o getpage

Resources