Warning: session_start(): Session cannot be started after headers have already been sent in /home/tvrreohg/public_html/manga.php on line 13
.\" generated by cd2nroff 0.1 from libcurl-tutorial.md
.TH libcurl-tutorial 3 "2026-09-02" libcurl
.SH NAME
libcurl\-tutorial \- libcurl programming tutorial
.SH Objective
This document attempts to describe the general principles and some basic
approaches to consider when programming with libcurl. The text focuses on the
C interface but should apply fairly well on other language bindings as well as
they usually follow the C API pretty closely.
This document refers to \(aqthe user\(aq as the person writing the source code that
uses libcurl. That would probably be you or someone in your position. What is
generally referred to as \(aqthe program\(aq is the collected source code that you
write that is using libcurl for transfers. The program is outside libcurl and
libcurl is outside of the program.
To get more details on all options and functions described herein, please
refer to their respective man pages.
.SH Building
There are many different ways to build C programs. This chapter assumes a Unix
style build process. If you use a different build system, you can still read
this to get general information that may apply to your environment as well.
.IP "Compiling the Program"
Your compiler needs to know where the libcurl headers are located. Therefore
you must set your compiler\(aqs include path to point to the directory where you
installed them. The \(aqcurl\-config\(aq[3] tool can be used to get this information:
.nf
$ curl-config --cflags
.fi
.IP "Linking the Program with libcurl"
When having compiled the program, you need to link your object files to create
a single executable. For that to succeed, you need to link with libcurl and
possibly also with other libraries that libcurl itself depends on. Like the
OpenSSL libraries, but even some standard OS libraries may be needed on the
command line. To figure out which flags to use, once again the \(aqcurl\-config\(aq
tool comes to the rescue:
.nf
$ curl-config --libs
.fi
.IP "SSL or Not"
libcurl can be built and customized in many ways. One of the things that
varies from different libraries and builds is the support for SSL\-based
transfers, like HTTPS and FTPS. If a supported SSL library was detected
properly at build\-time, libcurl is built with SSL support. To figure out if an
installed libcurl has been built with SSL support enabled, use \fIcurl\-config\fP
like this:
.nf
$ curl-config --feature
.fi
If SSL is supported, the keyword \fISSL\fP is written to stdout, possibly together
with a other features that could be either on or off on for different
libcurls.
See also the "Features libcurl Provides" further down.
.IP "autoconf macro"
When you write your configure script to detect libcurl and setup variables
accordingly, we offer a macro that probably does everything you need in this
area. See docs/libcurl/libcurl.m4 file \- it includes docs on how to use it.
.SH Portable Code in a Portable World
The people behind libcurl have put a considerable effort to make libcurl work
on a large amount of different operating systems and environments.
You program libcurl the same way on all platforms that libcurl runs on. There
are only a few minor details that differ. If you make sure to write your code
portable enough, you can create a portable program. libcurl should not stop
you from that.
.SH Global Preparation
The program must initialize some of the libcurl functionality globally. That
means it should be done exactly once, no matter how many times you intend to
use the library. Once for your program\(aqs entire life time. This is done using
.nf
curl_global_init()
.fi
and it takes one parameter which is a bit pattern that tells libcurl what to
initialize. Using \fICURL_GLOBAL_ALL\fP makes it initialize all known internal
sub modules, and might be a good default option. The current two bits that are
specified are:
.IP CURL_GLOBAL_WIN32
which only does anything on Windows machines. When used on a Windows machine,
it makes libcurl initialize the Win32 socket stuff. Without having that
initialized properly, your program cannot use sockets properly. You should
only do this once for each application, so if your program already does this
or of another library in use does it, you should not tell libcurl to do this
as well.
.IP CURL_GLOBAL_SSL
which only does anything on libcurls compiled and built SSL\-enabled. On these
systems, this makes libcurl initialize the SSL library properly for this
application. This only needs to be done once for each application so if your
program or another library already does this, this bit should not be needed.
libcurl has a default protection mechanism that detects if
\fIcurl_global_init(3)\fP has not been called by the time
\fIcurl_easy_perform(3)\fP is called and if that is the case, libcurl runs the
function itself with a guessed bit pattern. Please note that depending solely
on this is not considered nice nor good.
When the program no longer uses libcurl, it should call
\fIcurl_global_cleanup(3)\fP, which is the opposite of the init call. It
performs the reversed operations to cleanup the resources the
\fIcurl_global_init(3)\fP call initialized.
Repeated calls to \fIcurl_global_init(3)\fP and \fIcurl_global_cleanup(3)\fP
should be avoided. They should only be called once each.
.SH Features libcurl Provides
It is considered best\-practice to determine libcurl features at runtime rather
than at build\-time (if possible of course). By calling
\fIcurl_version_info(3)\fP and checking out the details of the returned
struct, your program can figure out exactly what the currently running libcurl
supports.
.SH Two Interfaces
libcurl first introduced the so called easy interface. All operations in the
easy interface are prefixed with \(aqcurl_easy\(aq. The easy interface lets you do
single transfers with a synchronous and blocking function call.
libcurl also offers another interface that allows multiple simultaneous
transfers in a single thread, the so called multi interface. More about that
interface is detailed in a separate chapter further down. You still need to
understand the easy interface first, so please continue reading for better
understanding.
.SH Handle the Easy libcurl
To use the easy interface, you must first create yourself an easy handle. You
need one handle for each easy session you want to perform. You should use one
handle for every thread you plan to use for transferring. You must never share
the same handle in multiple threads.
Get an easy handle with
.nf
handle = curl_easy_init();
.fi
It returns an easy handle. Using that you proceed to the next step: setting
up your preferred actions. A handle is a logic entity for the upcoming
transfer or series of transfers.
You set properties and options for this handle using
\fIcurl_easy_setopt(3)\fP. They control how the subsequent transfer or
transfers using this handle are made. Options remain set in the handle until
set again to something different. They are sticky. Multiple requests using the
same handle use the same options.
If you at any point would like to blank all previously set options for a
single easy handle, you can call \fIcurl_easy_reset(3)\fP and you can also
make a clone of an easy handle (with all its set options) using
\fIcurl_easy_duphandle(3)\fP.
Many of the options you set in libcurl are "strings", pointers to data
terminated with a zero byte. When you set strings with
\fIcurl_easy_setopt(3)\fP, libcurl makes its own copy so that they do not need
to be kept around in your application after being set[4].
One of the most basic properties to set in the handle is the URL. You set your
preferred URL to transfer with \fICURLOPT_URL(3)\fP in a manner similar to:
.nf
curl_easy_setopt(handle, CURLOPT_URL, "https://example.com/");
.fi
Let\(aqs assume for a while that you want to receive data as the URL identifies a
remote resource you want to get here. Since you write a sort of application
that needs this transfer, I assume that you would like to get the data passed
to you directly instead of getting it passed to stdout. You write your
own function that matches this prototype:
.nf
size_t write_data(char *buffer, size_t size, size_t nmemb, void *userp);
.fi
You tell libcurl to pass all data to this function by issuing a function
similar to this:
.nf
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data);
.fi
You can control what data your callback function gets in the fourth argument
by setting another property:
.nf
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &internal_struct);
.fi
Using that property, you can easily pass local data between your application
and the function that gets invoked by libcurl. libcurl itself does not touch
the data you pass with \fICURLOPT_WRITEDATA(3)\fP.
libcurl offers its own default internal callback that takes care of the data
if you do not set the callback with \fICURLOPT_WRITEFUNCTION(3)\fP. It
outputs the received data to stdout. You can have the default callback write
the data to a different file handle by passing a \(aqFILE *\(aq to a file opened for
writing with the \fICURLOPT_WRITEDATA(3)\fP option.
Now, we need to take a step back and take a deep breath. Here is one of those
rare platform\-dependent nitpicks. Did you spot it? On some platforms[2],
libcurl is not able to operate on file handles opened by the
program. Therefore, if you use the default callback and pass in an open file
handle with \fICURLOPT_WRITEDATA(3)\fP, libcurl crashes. You should avoid this
to make your program run fine virtually everywhere.
(\fICURLOPT_WRITEDATA(3)\fP was formerly known as \fICURLOPT_FILE\fP. Both names still
work and do the same thing).
If you are using libcurl as a Windows DLL, you MUST use the
\fICURLOPT_WRITEFUNCTION(3)\fP if you set \fICURLOPT_WRITEDATA(3)\fP \- or experience
crashes.
There are of course many more options you can set, and we get back to a few of
them later. Let\(aqs instead continue to the actual transfer:
.nf
success = curl_easy_perform(handle);
.fi
\fIcurl_easy_perform(3)\fP connects to the remote site, does the necessary commands
and performs the transfer. Whenever it receives data, it calls the callback
function we previously set. The function may get one byte at a time, or it may
get many kilobytes at once. libcurl delivers as much as possible as often as
possible. Your callback function should return the number of bytes it "took
care of". If that is not the same amount of bytes that was passed to it,
libcurl aborts the operation and returns with an error code.
When the transfer is complete, the function returns a return code that informs
you if it succeeded in its mission or not. If a return code is not enough for
you, you can use the \fICURLOPT_ERRORBUFFER(3)\fP to point libcurl to a buffer of
yours where it stores a human readable error message as well.
If you then want to transfer another file, the handle is ready to be used
again. It is even preferred and encouraged that you reuse an existing handle
if you intend to make another transfer. libcurl then attempts to reuse a
previous connection.
For some protocols, downloading a file can involve a complicated process of
logging in, setting the transfer mode, changing the current directory and
finally transferring the file data. libcurl takes care of all that
complication for you. Given the URL to a file, libcurl takes care of
all the details needed to get the file moved from one machine to another.
.SH Multi-threading Issues
libcurl is thread\-safe but there are a few exceptions. Refer to
\fIlibcurl\-thread(3)\fP for more information.
.SH When It does not Work
There are times when the transfer fails for some reason. You might have set
the wrong libcurl option or misunderstood what the libcurl option actually
does, or the remote server might return non\-standard replies that confuse the
library which then confuses your program.
There is one golden rule when these things occur: set the
\fICURLOPT_VERBOSE(3)\fP option to 1. it causes the library to spew out the
entire protocol details it sends, some internal info and some received
protocol data as well (especially when using FTP). If you are using HTTP,
adding the headers in the received output to study is also a clever way to get
a better understanding why the server behaves the way it does. Include headers
in the normal body output with \fICURLOPT_HEADER(3)\fP set 1.
Of course, there are bugs left. We need to know about them to be able to fix
them, so we are dependent on your bug reports. When you do report suspected
bugs in libcurl, please include as many details as you possibly can: a protocol
dump that \fICURLOPT_VERBOSE(3)\fP produces, library version, as much as possible of
your code that uses libcurl, operating system name and version, compiler name
and version etc.
If \fICURLOPT_VERBOSE(3)\fP is not enough, you increase the level of debug
data your application receive by using the \fICURLOPT_DEBUGFUNCTION(3)\fP.
Getting some in\-depth knowledge about the protocols involved is never wrong,
and if you are trying to do funny things, you might understand libcurl and how
to use it better if you study the appropriate RFC documents at least briefly.
.SH Upload Data to a Remote Site
libcurl tries to keep a protocol independent approach to most transfers, thus
uploading to a remote FTP site is similar to uploading data to an HTTP server
with a PUT request.
Of course, first you either create an easy handle or you reuse one existing
one. Then you set the URL to operate on like before. This is the remote URL,
that we now upload.
Since we write an application, we most likely want libcurl to get the upload
data by asking us for it. To make it do that, we set the read callback and the
custom pointer libcurl passes to our read callback. The read callback should
have a prototype similar to:
.nf
size_t function(char *bufptr, size_t size, size_t nitems, void *userp);
.fi
Where \fIbufptr\fP is the pointer to a buffer we fill in with data to upload
and \fInitems\fP is the size of the buffer and therefore also the maximum
amount of data we can return to libcurl in this call. The \fIuserp\fP pointer
is the custom pointer we set to point to a struct of ours to pass private data
between the application and the callback.
.nf
curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(handle, CURLOPT_READDATA, &filedata);
.fi
Tell libcurl that we want to upload:
.nf
curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
.fi
A few protocols do not behave properly when uploads are done without any prior
knowledge of the expected file size. Set the upload file size using the
\fICURLOPT_INFILESIZE_LARGE(3)\fP for all known file sizes like this[1]:
.nf
/* in this example, file_size must be an curl_off_t variable */
curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, file_size);
.fi
When you call \fIcurl_easy_perform(3)\fP this time, it performs all the
necessary operations and when it has invoked the upload it calls your supplied
callback to get the data to upload. The program should return as much data as
possible in every invoke, as that is likely to make the upload perform as fast
as possible. The callback should return the number of bytes it wrote in the
buffer. Returning 0 signals the end of the upload.
.SH Passwords
Many protocols use or even require that username and password are provided
to be able to download or upload the data of your choice. libcurl offers
several ways to specify them.
Most protocols support that you specify the name and password in the URL
itself. libcurl detects this and use them accordingly. This is written like
this:
.nf
protocol://user:password@example.com/path/
.fi
If you need any odd letters in your username or password, you should enter
them URL encoded, as %XX where XX is a two\-digit hexadecimal number.
libcurl also provides options to set various passwords. The username and
password as shown embedded in the URL can instead get set with the
\fICURLOPT_USERPWD(3)\fP option. The argument passed to libcurl should be a
\fIchar *\fP to a string in the format \fI"user:password"\fP. In a manner like this:
.nf
curl_easy_setopt(handle, CURLOPT_USERPWD, "myname:thesecret");
.fi
Another case where name and password might be needed at times, is for those
users who need to authenticate themselves to a proxy they use. libcurl offers
another option for this, the \fICURLOPT_PROXYUSERPWD(3)\fP. Its use is similar to the
\fICURLOPT_USERPWD(3)\fP option, like this:
.nf
curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, "myname:thesecret");
.fi
There is a long time Unix "standard" way of storing FTP usernames and
passwords, namely in the $HOME/.netrc file (on Windows, libcurl also checks
the *%USERPROFILE% environment* variable if *%HOME%* is unset, and tries
\fI_netrc\fP as name). The file should be made private so that only the user may
read it (see also the "Security Considerations" chapter), as it might contain
the password in plain text. libcurl has the ability to use this file to figure
out what set of username and password to use for a particular host. As an
extension to the normal functionality, libcurl also supports this file for
non\-FTP protocols such as HTTP. To make curl use this file, use the
\fICURLOPT_NETRC(3)\fP option:
.nf
curl_easy_setopt(handle, CURLOPT_NETRC, 1L);
.fi
A basic example of how such a .netrc file may look like:
.nf
machine myhost.mydomain.com
login userlogin
password secretword
.fi
All these examples have been cases where the password has been optional, or
at least you could leave it out and have libcurl attempt to do its job
without it. There are times when the password is not optional, like when
you are using an SSL private key for secure transfers.
To pass the known private key password to libcurl:
.nf
curl_easy_setopt(handle, CURLOPT_KEYPASSWD, "keypassword");
.fi
.SH HTTP Authentication
The previous chapter showed how to set username and password for getting URLs
that require authentication. When using the HTTP protocol, there are many
different ways a client can provide those credentials to the server and you
can control which way libcurl uses them. The default HTTP authentication
method is called \(aqBasic\(aq, which is sending the name and password in clear\-text
in the HTTP request, base64\-encoded. This is insecure.
At the time of this writing, libcurl can be built to use: Basic, Digest, NTLM,
Negotiate (SPNEGO). You can tell libcurl which one to use with
\fICURLOPT_HTTPAUTH(3)\fP as in:
.nf
curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
.fi
When you send authentication to a proxy, you can also set authentication type
the same way but instead with \fICURLOPT_PROXYAUTH(3)\fP:
.nf
curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
.fi
Both these options allow you to set multiple types (by ORing them together),
to make libcurl pick the most secure one out of the types the server/proxy
claims to support. This method does however add a round\-trip since libcurl
must first ask the server what it supports:
.nf
curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_BASIC);
.fi
For convenience, you can use the \fICURLAUTH_ANY\fP define (instead of a list with
specific types) which allows libcurl to use whatever method it wants.
When asking for multiple types, libcurl picks the available one it considers
\&"best" in its own internal order of preference.
.SH HTTP POSTing
We get many questions regarding how to issue HTTP POSTs with libcurl the
proper way. This chapter thus includes examples using both different versions
of HTTP POST that libcurl supports.
The first version is the simple POST, the most common version, that most HTML
pages using the