Sortix nightly manual
This manual documents Sortix nightly, a development build that has not been officially released. You can instead view this document in the latest official manual.
libcurl-tutorial(3) | Library Functions Manual | libcurl-tutorial(3) |
NAME
libcurl-tutorial - libcurl programming tutorialObjective
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.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.- Compiling the Program
-
Your compiler needs to know where the libcurl headers are located. Therefore you must set your compiler's include path to point to the directory where you installed them. The 'curl-config'[3] tool can be used to get this information:
$ curl-config --cflags
- 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 'curl-config' tool comes to the rescue:
$ curl-config --libs
- 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 curl-config like this:
$ curl-config --feature
- 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.
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.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's entire life time. This is done usingcurl_global_init()
and it takes one parameter which is a bit pattern that tells libcurl what to initialize. Using CURL_GLOBAL_ALL makes it initialize all known internal sub modules, and might be a good default option. The current two bits that are specified are:
- 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.
- 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.
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 curl_version_info(3) and checking out the details of the returned struct, your program can figure out exactly what the currently running libcurl supports.Two Interfaces
libcurl first introduced the so called easy interface. All operations in the easy interface are prefixed with 'curl_easy'. The easy interface lets you do single transfers with a synchronous and blocking function call.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. Basically, you should use one handle for every thread you plan to use for transferring. You must never share the same handle in multiple threads.handle = curl_easy_init();
It returns an easy handle. Using that you proceed to the next step: setting up your preferred actions. A handle is just a logic entity for the upcoming transfer or series of transfers.
curl_easy_setopt(handle, CURLOPT_URL, "http://domain.com/");
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
You tell libcurl to pass all data to this function by issuing a function similar to this:
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data);
You can control what data your callback function gets in the fourth argument by setting another property:
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &internal_struct);
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 CURLOPT_WRITEDATA(3).
success = curl_easy_perform(handle);
Multi-threading Issues
libcurl is thread safe but there are a few exceptions. Refer to libcurl-thread(3) for more information.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.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.size_t function(char *bufptr, size_t size, size_t nitems, void *userp);
Where bufptr is the pointer to a buffer we fill in with data to upload and sizenitems* is the size of the buffer and therefore also the maximum amount of data we can return to libcurl in this call. The userp pointer is the custom pointer we set to point to a struct of ours to pass private data between the application and the callback.
curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(handle, CURLOPT_READDATA, &filedata);
Tell libcurl that we want to upload:
curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
A few protocols do not behave properly when uploads are done without any prior knowledge of the expected file size. So, set the upload file size using the CURLOPT_INFILESIZE_LARGE(3) for all known file sizes like this[1]:
/* in this example, file_size must be an curl_off_t variable */
curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, file_size);
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.protocol://user:password@example.com/path/
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.
curl_easy_setopt(handle, CURLOPT_USERPWD, "myname:thesecret");
curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, "myname:thesecret");
curl_easy_setopt(handle, CURLOPT_NETRC, 1L);
machine myhost.mydomain.com
login userlogin
password secretword
curl_easy_setopt(handle, CURLOPT_KEYPASSWD, "keypassword");
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 'Basic', which is sending the name and password in clear-text in the HTTP request, base64-encoded. This is insecure.curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC);
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.char *data="name=daniel&project=curl";
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, data);
curl_easy_setopt(handle, CURLOPT_URL, "http://posthere.com/");
curl_easy_perform(handle); /* post away! */
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Content-Type: text/xml");
/* post binary data */
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, binaryptr);
/* set the size of the postfields data */
curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, 23L);
/* pass our list of custom made headers */
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
curl_easy_perform(handle); /* post away! */
curl_slist_free_all(headers); /* free the header list */
curl_mime *multipart = curl_mime_init(handle);
curl_mimepart *part = curl_mime_addpart(multipart);
curl_mime_name(part, "name");
curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "project");
curl_mime_data(part, "curl", CURL_ZERO_TERMINATED);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "logotype-image");
curl_mime_filedata(part, "curl.png");
/* Set the form info */
curl_easy_setopt(handle, CURLOPT_MIMEPOST, multipart);
curl_easy_perform(handle); /* post away! */
/* free the post data again */
curl_mime_free(multipart);
curl_mime_data_cb(part, filesize, (curl_read_callback) fread,
(curl_seek_callback) fseek, NULL, filepointer);
struct curl_httppost *post=NULL;
struct curl_httppost *last=NULL;
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "name",
CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "project",
CURLFORM_COPYCONTENTS, "curl", CURLFORM_END);
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "logotype-image",
CURLFORM_FILECONTENT, "curl.png", CURLFORM_END);
/* Set the form info */
curl_easy_setopt(handle, CURLOPT_HTTPPOST, post);
curl_easy_perform(handle); /* post away! */
/* free the post data again */
curl_formfree(post);
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Content-Type: text/xml");
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "logotype-image",
CURLFORM_FILECONTENT, "curl.xml",
CURLFORM_CONTENTHEADER, headers,
CURLFORM_END);
curl_easy_perform(handle); /* post away! */
curl_formfree(post); /* free post */
curl_slist_free_all(headers); /* free custom header list */
curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L);
Just setting CURLOPT_POSTFIELDS(3) to "" or NULL does not stop libcurl from doing a POST. It just makes it POST without any data to send!
Converting from deprecated form API to MIME API
Four rules have to be respected in building the multi-part:curl_formadd(&post, &last,
CURLFORM_COPYNAME, "id",
CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
CURLFORM_CONTENTHEADER, headers,
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "id");
curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED);
curl_mime_headers(part, headers, FALSE);
curl_formadd(&post, &last,
CURLFORM_PTRNAME, "logotype-image",
CURLFORM_FILECONTENT, "-",
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "logotype-image");
curl_mime_data_cb(part, (curl_off_t) -1, fread, fseek, NULL, stdin);
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "datafile[]",
CURLFORM_FILE, "file1",
CURLFORM_FILE, "file2",
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "datafile[]");
curl_mime_filedata(part, "file1");
part = curl_mime_addpart(multipart);
curl_mime_name(part, "datafile[]");
curl_mime_filedata(part, "file2");
curl_easy_setopt(handle, CURLOPT_READFUNCTION, myreadfunc);
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "stream",
CURLFORM_STREAM, arg,
CURLFORM_CONTENTLEN, (curl_off_t) datasize,
CURLFORM_FILENAME, "archive.zip",
CURLFORM_CONTENTTYPE, "application/zip",
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "stream");
curl_mime_data_cb(part, (curl_off_t) datasize,
myreadfunc, NULL, NULL, arg);
curl_mime_filename(part, "archive.zip");
curl_mime_type(part, "application/zip");
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "memfile",
CURLFORM_BUFFER, "memfile.bin",
CURLFORM_BUFFERPTR, databuffer,
CURLFORM_BUFFERLENGTH, (long) sizeof databuffer,
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "memfile");
curl_mime_data(part, databuffer, (curl_off_t) sizeof databuffer);
curl_mime_filename(part, "memfile.bin");
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "message",
CURLFORM_FILECONTENT, "msg.txt",
CURLFORM_END);
part = curl_mime_addpart(multipart);
curl_mime_name(part, "message");
curl_mime_filedata(part, "msg.txt");
curl_mime_filename(part, NULL);
Showing Progress
For historical and traditional reasons, libcurl has a built-in progress meter that can be switched on and then makes it present a progress meter in your terminal.int progress_callback(void *clientp,
double dltotal,
double dlnow,
double ultotal,
double ulnow);
libcurl with C++
There is basically only one thing to keep in mind when using C++ instead of C when interfacing libcurl:class AClass {
static size_t write_data(void *ptr, size_t size, size_t nmemb,
void *ourpointer)
{
/* do what you want with the data */
}
}
Proxies
What "proxy" means according to Merriam-Webster: "a person authorized to act for another" but also "the agency, function, or office of a deputy who acts as a substitute for another".- Proxy Options
-
To tell libcurl to use a proxy at a given port number:
curl_easy_setopt(handle, CURLOPT_PROXY, "proxy-host.com:8080");
Some proxies require user authentication before allowing a request, and you pass that information similar to this:
curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, "user:password");
If you want to, you can specify the hostname only in the CURLOPT_PROXY(3) option, and set the port number separately with CURLOPT_PROXYPORT(3).
curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
- Environment Variables
-
libcurl automatically checks and uses a set of environment variables to know what proxies to use for certain protocols. The names of the variables are following an old tradition and are built up as "[protocol]_proxy" (note the lower casing). Which makes the variable 'http_proxy' checked for a name of a proxy to use when the input URL is HTTP. Following the same rule, the variable named 'ftp_proxy' is checked for FTP URLs. Again, the proxies are always HTTP proxies, the different names of the variables simply allows different HTTP proxies to be used.
- SSL and Proxies
-
SSL is for secure point-to-point connections. This involves strong encryption and similar things, which effectively makes it impossible for a proxy to operate as a "man in between" which the proxy's task is, as previously discussed. Instead, the only way to have SSL work over an HTTP proxy is to ask the proxy to tunnel everything through without being able to check or fiddle with the traffic.
- Tunneling Through Proxy
-
As explained above, tunneling is required for SSL to work and often even restricted to the operation intended for SSL; HTTPS.
curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1L);
In fact, there might even be times when you want to do plain HTTP operations using a tunnel like this, as it then enables you to operate on the remote server instead of asking the proxy to do so. libcurl does not stand in the way for such innovative actions either!
- Proxy Auto-Config
-
Netscape first came up with this. It is basically a webpage (usually using a .pac extension) with a JavaScript that when executed by the browser with the requested URL as input, returns information to the browser on how to connect to the URL. The returned information might be "DIRECT" (which means no proxy should be used), "PROXY host:port" (to tell the browser where the proxy for this particular URL is) or "SOCKS host:port" (to direct the browser to a SOCKS proxy).
Persistence Is The Way to Happiness
Re-cycling the same easy handle several times when doing multiple requests is the way to go.HTTP Headers Used by libcurl
When you use libcurl to do HTTP requests, it passes along a series of headers automatically. It might be good for you to know and understand these. You can replace or remove them by using the CURLOPT_HTTPHEADER(3) option.- Host
- This header is required by HTTP 1.1 and even many 1.0 servers and should be the name of the server we want to talk to. This includes the port number if anything but default.
- Accept
- "/"
- Expect
- When doing POST requests, libcurl sets this header to "100-continue" to ask the server for an "OK" message before it proceeds with sending the data part of the post. If the posted data amount is deemed "small", libcurl does not use this header.
Customizing Operations
There is an ongoing development today where more and more protocols are built upon HTTP for transport. This has obvious benefits as HTTP is a tested and reliable protocol that is widely deployed and has excellent proxy-support.- CURLOPT_CUSTOMREQUEST
-
If just changing the actual HTTP request keyword is what you want, like when GET, HEAD or POST is not good enough for you, CURLOPT_CUSTOMREQUEST(3) is there for you. It is simple to use:
curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MYOWNREQUEST");
- Modify Headers
-
HTTP-like protocols pass a series of headers to the server when doing the request, and you are free to pass any amount of extra headers that you think fit. Adding headers is this easy:
struct curl_slist *headers=NULL; /* init to NULL is important */
headers = curl_slist_append(headers, "Hey-server-hey: how are you?");
headers = curl_slist_append(headers, "X-silly-content: yes");
/* pass our list of custom made headers */
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
curl_easy_perform(handle); /* transfer http */
curl_slist_free_all(headers); /* free the header list */
headers = curl_slist_append(headers, "Accept: Agent-007");
headers = curl_slist_append(headers, "Host: munged.host.line");
- Delete Headers
-
If you replace an existing header with one with no contents, you prevent the header from being sent. For instance, if you want to completely prevent the "Accept:" header from being sent, you can disable it with code similar to this:
headers = curl_slist_append(headers, "Accept:");
- Enforcing chunked transfer-encoding
- By making sure a request uses the custom header "Transfer-Encoding: chunked" when doing a non-GET HTTP operation, libcurl switches over to "chunked" upload, even though the size of the data to upload might be known. By default, libcurl usually switches over to chunked upload automatically if the upload data size is unknown.
- HTTP Version
-
All HTTP requests includes the version number to tell the server which version we support. libcurl speaks HTTP 1.1 by default. Some old servers do not like getting 1.1-requests and when dealing with stubborn old things like that, you can tell libcurl to use 1.0 instead by doing something like this:
curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
- FTP Custom Commands
-
Not all protocols are HTTP-like, and thus the above may not help you when you want to make, for example, your FTP transfers to behave differently.
headers = curl_slist_append(headers, "DELE file-to-remove");
/* pass the list of custom commands to the handle */
curl_easy_setopt(handle, CURLOPT_QUOTE, headers);
curl_easy_perform(handle); /* transfer ftp data! */
curl_slist_free_all(headers); /* free the header list */
- FTP Custom CURLOPT_CUSTOMREQUEST
- If you do want to list the contents of an FTP directory using your own defined FTP command, CURLOPT_CUSTOMREQUEST(3) does just that. "NLST" is the default one for listing directories but you are free to pass in your idea of a good alternative.
Cookies Without Chocolate Chips
In the HTTP sense, a cookie is a name with an associated value. A server sends the name and value to the client, and expects it to get sent back on every subsequent request to the server that matches the particular conditions set. The conditions include that the domain name and path match and that the cookie has not become too old.curl_easy_setopt(handle, CURLOPT_COOKIE, "name1=var1; name2=var2;");
FTP Peculiarities We Need
FTP transfers use a second TCP/IP connection for the data transfer. This is usually a fact you can forget and ignore but at times this detail comes back to haunt you. libcurl offers several different ways to customize how the second connection is being made.MIME API revisited for SMTP and IMAP
In addition to support HTTP multi-part form fields, the MIME API can be used to build structured email messages and send them via SMTP or append such messages to IMAP directories.curl_mime *message = curl_mime_init(handle);
/* The inline part is an alternative proposing the html and the text
versions of the email. */
curl_mime *alt = curl_mime_init(handle);
/* HTML message. */
curl_mimepart *part = curl_mime_addpart(alt);
curl_mime_data(part, "<html><body><p>This is HTML</p></body></html>",
CURL_ZERO_TERMINATED);
curl_mime_type(part, "text/html");
/* Text message. */
part = curl_mime_addpart(alt);
curl_mime_data(part, "This is plain text message",
CURL_ZERO_TERMINATED);
/* Create the inline part. */
part = curl_mime_addpart(message);
curl_mime_subparts(part, alt);
curl_mime_type(part, "multipart/alternative");
struct curl_slist *headers = curl_slist_append(NULL,
"Content-Disposition: inline");
curl_mime_headers(part, headers, TRUE);
/* Add the attachment. */
part = curl_mime_addpart(message);
curl_mime_filedata(part, "manual.pdf");
curl_mime_encoder(part, "base64");
/* Build the mail headers. */
headers = curl_slist_append(NULL, "From: me@example.com");
headers = curl_slist_append(headers, "To: you@example.com");
/* Set these into the easy handle. */
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(handle, CURLOPT_MIMEPOST, mime);
Headers Equal Fun
Some protocols provide "headers", meta-data separated from the normal data. These headers are by default not included in the normal data stream, but you can make them appear in the data stream by setting CURLOPT_HEADER(3) to 1.Post Transfer Information
See curl_easy_getinfo(3).The multi Interface
The easy interface as described in detail in this document is a synchronous interface that transfers one file at a time and does not return until it is done.SSL, Certificates and Other Tricks
[ seeding, passwords, keys, certificates, ENGINE, ca certs ]
Sharing Data Between Easy Handles
You can share some data between easy handles when the easy interface is used, and some data is share automatically when you use the multi interface.Footnotes
- [1]
- libcurl 7.10.3 and later have the ability to switch over to chunked Transfer-Encoding in cases where HTTP uploads are done with data of an unknown size.
- [2]
- This happens on Windows machines when libcurl is built and used as a DLL. However, you can still do this on Windows if you link with a static library.
- [3]
- The curl-config tool is generated at build-time (on Unix-like systems) and should be installed with the 'make install' or similar instruction that installs the library, header files, man pages etc.
- [4]
- This behavior was different in versions before 7.17.0, where strings had to remain valid past the end of the curl_easy_setopt(3) call.
SEE ALSO
libcurl-easy(3), libcurl-errors(3), libcurl-multi(3), libcurl-url(3)2024-11-21 | libcurl |