Sortix main manual
This manual documents Sortix main. You can instead view this document in the latest official manual.
libcurl-tutorial(3) | libcurl programming | 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 will focus mainly on the C interface but might apply fairly well on other interfaces as well as they usually follow the C one pretty closely.Building
There are many different ways to build C programs. This chapter will assume 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:
- 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:
- 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 will be built with SSL support. To figure out if an installed libcurl has been built with SSL support enabled, use 'curl-config' like this:
- 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()
- CURL_GLOBAL_WIN32
- which only does anything on Windows machines. When used on a Windows machine, it will make 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 will make 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.easyhandle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_URL, "http://domain.com/");
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &internal_struct);
success = curl_easy_perform(easyhandle);
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 will always be 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);
curl_easy_setopt(easyhandle, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(easyhandle, CURLOPT_READDATA, &filedata);
curl_easy_setopt(easyhandle, CURLOPT_UPLOAD, 1L);
/* in this example, file_size must be an curl_off_t variable */
curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE_LARGE, file_size);
Passwords
Many protocols use or even require that user name 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/
curl_easy_setopt(easyhandle, CURLOPT_USERPWD, "myname:thesecret");
curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, "myname:thesecret");
curl_easy_setopt(easyhandle, CURLOPT_NETRC, 1L);
machine myhost.mydomain.com
login userlogin
password secretword
curl_easy_setopt(easyhandle, CURLOPT_KEYPASSWD, "keypassword");
HTTP Authentication
The previous chapter showed how to set user name 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 will (attempt to) use 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(easyhandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_easy_setopt(easyhandle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
curl_easy_setopt(easyhandle, 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 will thus include examples using both different versions of HTTP POST that libcurl supports.char *data="name=daniel&project=curl";
curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, data);
curl_easy_setopt(easyhandle, CURLOPT_URL, "http://posthere.com/");
curl_easy_perform(easyhandle); /* post away! */
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Content-Type: text/xml");
/* post binary data */
curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDS, binaryptr);
/* set the size of the postfields data */
curl_easy_setopt(easyhandle, CURLOPT_POSTFIELDSIZE, 23L);
/* pass our list of custom made headers */
curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);
curl_easy_perform(easyhandle); /* post away! */
curl_slist_free_all(headers); /* free the header list */
curl_mime *multipart = curl_mime_init(easyhandle);
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(easyhandle, CURLOPT_MIMEPOST, multipart);
curl_easy_perform(easyhandle); /* 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(easyhandle, CURLOPT_HTTPPOST, post);
curl_easy_perform(easyhandle); /* 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(easyhandle); /* post away! */
curl_formfree(post); /* free post */
curl_slist_free_all(headers); /* free custom header list */
curl_easy_setopt(easyhandle, CURLOPT_HTTPGET, 1L);
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);
becomes:
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);
becomes:
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);
becomes:
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(easyhandle, 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);
becomes:
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);
becomes:
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);
becomes:
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's 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
-
curl_easy_setopt(easyhandle, CURLOPT_PROXY, "proxy-host.com:8080");
curl_easy_setopt(easyhandle, CURLOPT_PROXYUSERPWD, "user:password");
curl_easy_setopt(easyhandle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
- Environment Variables
-
- SSL and Proxies
-
- 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(easyhandle, CURLOPT_HTTPPROXYTUNNEL, 1L);
- Proxy Auto-Config
-
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 will pass 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 will 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.- 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(easyhandle, 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(easyhandle, CURLOPT_HTTPHEADER, headers);
curl_easy_perform(easyhandle); /* 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 will 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
-
- HTTP Version
-
curl_easy_setopt(easyhandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
- FTP Custom Commands
-
headers = curl_slist_append(headers, "DELE file-to-remove");
/* pass the list of custom commands to the handle */
curl_easy_setopt(easyhandle, CURLOPT_QUOTE, headers);
curl_easy_perform(easyhandle); /* transfer ftp data! */
curl_slist_free_all(headers); /* free the header list */
- FTP Custom CUSTOMREQUEST
-
If you do want to list the contents of an FTP directory using your own defined FTP command, CURLOPT_CUSTOMREQUEST(3) will do 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(easyhandle, 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 fact will come 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(easyhandle);
/* The inline part is an alternative proposing the html and the text
versions of the email. */
curl_mime *alt = curl_mime_init(easyhandle);
/* 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(easyhandle, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(easyhandle, 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.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-errors(3), libcurl-multi(3), libcurl-easy(3)May 17, 2022 | libcurl 7.84.0 |