libcurl: The Swiss Army Knife for C++ Programmers

1. What is libcurl?

In a nutshell: libcurl is a cross-platform client-side networking library written in C that supports over 200 protocols, used internally by countless projects such as Chrome, PHP, Python-requests, Node.js, and more.

Features Description
Protocols HTTP/1.1, HTTP/2, HTTP/3, HTTPS, FTP, FTPS, SMTP, IMAP, WebSocket, MQTT…
Transport Layer TCP, UDP, Unix Domain Socket, TLS (OpenSSL/LibreSSL/BoringSSL/wolfSSL)
Platforms Windows, Linux, macOS, iOS, Android, Embedded
License MIT/X Derivative (business-friendly)
Latest Version 8.11.0 (October 2025) supports HTTP/3, Rustls, Zstd

Why is libcurl the first choice for C++ projects?

  • No dependencies: only requires a dynamic/static library
  • Stable API: unchanged for 20 years
  • Documentation + Community: official documentation at curl.se + millions of answers on StackOverflow

2. Environment Setup (Cross-Platform)

2.1 Windows (VS 2022)

# Using vcpkg (recommended)
vcpkg install curl[x64-windows]      # Dynamic linking
vcpkg install curl[x64-windows-static]  # Static linking
vcpkg integrate install   # Automatically integrate into VS

2.2 Linux (Ubuntu/Debian)

sudo apt update
sudo apt install libcurl4-openssl-dev   # Development headers + dynamic library
# Static library
sudo apt install libcurl4-openssl-dev libssl-dev zlib1g-dev

2.3 macOS (Homebrew)

brew install curl
brew install openssl
# Specify path during CMake linking
export OPENSSL_ROOT_DIR=$(brew --prefix openssl)

Tip: If you are using CMake, here is a CMakeLists.txt template that you can copy and paste directly.

cmake_minimum_required(VERSION 3.16)
project(CurlDemo LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
find_package(CURL REQUIRED)

add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE CURL::libcurl)

3. Overview of Core Concepts

Concept Function Key Functions
easy handle Single request, blocking/synchronous <span>curl_easy_init</span>, <span>curl_easy_perform</span>
multi handle Multiple requests concurrently, non-blocking <span>curl_multi_init</span>, <span>curl_multi_perform</span>
Callback Functions Receive data, upload data, debug logs <span>CURLOPT_WRITEFUNCTION</span>, <span>CURLOPT_READFUNCTION</span>

4. Practical Code: 7 Major Scenarios

All examples use C++17 and are compatible with C++11. The code has been compiled successfully (VS2022 / GCC 14 / Clang 18).

4.1 Scenario 1: The Simplest GET Request

#include <curl/curl.h>
#include <string>
#include <iostream>

size_t WriteCallback(void* ptr, size_t size, size_t nmemb, std::string* data) {
    data->append((char*)ptr, size * nmemb);
    return size * nmemb;
}

int main() {
    CURL* curl = curl_easy_init();
    if (!curl) return -1;

    std::string response;
    curl_easy_setopt(curl, CURLOPT_URL, "https://httpbin.org/get");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);  // 302 redirect
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-demo/1.0");

    CURLcode res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
    } else {
        std::cout << "Response length: " << response.size() << " bytes\n";
        std::cout << response.substr(0, 500) << "\n";  // Print the first 500 characters
    }

    curl_easy_cleanup(curl);
    return 0;
}

Example Output:

Response length: 678 bytes
{
  "args": {},
  "headers": {
    "User-Agent": "libcurl-demo/1.0",
    ...

4.2 Scenario 2: POST JSON and Parse Response

static size_t WriteString(void* ptr, size_t size, size_t nmemb, std::string* s) {
    s->append((char*)ptr, size * nmemb);
    return size * nmemb;
}

int post_json() {
    CURL* curl = curl_easy_init();
    std::string readBuffer;

    const char* json = R"({"name":"A Yuan","lang":"C++"})";
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "Content-Type: application/json");

    curl_easy_setopt(curl, CURLOPT_URL, "https://httpbin.org/post");
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteString);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);

    CURLcode res = curl_easy_perform(curl);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (res == CURLE_OK) {
        std::cout << "POST response:\n" << readBuffer << std::endl;
    }
    return 0;
}

4.3 Scenario 3: File Upload (multipart/form-data)

int upload_file(const std::string& filepath) {
    CURL* curl = curl_easy_init();
    curl_mime* mime = curl_mime_init(curl);
    curl_mimepart* part = curl_mime_addpart(mime);

    curl_mime_name(part, "file");
    curl_mime_filedata(part, filepath.c_str());   // Local file path

    curl_easy_setopt(curl, CURLOPT_URL, "https://httpbin.org/post");
    curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);

    CURLcode res = curl_easy_perform(curl);

    curl_mime_free(mime);
    curl_easy_cleanup(curl);
    return res == CURLE_OK ? 0 : -1;
}

4.4 Scenario 4: Large File Resumable Download (Range + Progress Bar)

static size_t WriteFile(void* ptr, size_t size, size_t nmemb, FILE* stream) {
    return fwrite(ptr, size, nmemb, stream);
}

static int ProgressCallback(void* clientp,
                            curl_off_t dltotal,
                            curl_off_t dlnow,
                            curl_off_t ultotal,
                            curl_off_t ulnow) {
    if (dltotal > 0) {
        double percent = 100.0 * dlnow / dltotal;
        printf("\rDownloading: %.2f%%", percent);
        fflush(stdout);
    }
    return 0;
}

int download_with_resume(const std::string& url, const std::string& filename) {
    FILE* fp = fopen(filename.c_str(), "ab");  // Append mode
    if (!fp) return -1;

    fseek(fp, 0, SEEK_END);
    long already = ftell(fp);
    fclose(fp);

    CURL* curl = curl_easy_init();
    fp = fopen(filename.c_str(), "ab");

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteFile);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    curl_easy_setopt(curl, CURLOPT_RESUME_FROM, already);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback);

    CURLcode res = curl_easy_perform(curl);
    fclose(fp);
    curl_easy_cleanup(curl);
    if (res == CURLE_OK) printf("\nDownload completed!\n");
    return res;
}

Resumable Download:<span>CURLOPT_RESUME_FROM</span> + <span>ab</span> mode, automatically implemented with the <span>Range</span> header.

4.5 Scenario 5: HTTPS + Self-Signed Certificate Verification

curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);   // Verify CA
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem"); // Mozilla root certificate bundle

Self-Signed/Intranet Certificates:

curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);   // Disable verification (for development only)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);

4.6 Scenario 6: Automatic Cookie Saving/Loading

curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "cookies.txt");   // Write file on exit
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookies.txt");  // Read file on startup

4.7 Scenario 7: WebSocket (libcurl 7.86+)

#include <curl/curl.h>

static int ws_callback(CURL* easy, curl_ws_frametype type,
                       const void* data, size_t size, void* userp) {
    if (type == CURLWS_TEXT || type == CURLWS_BINARY) {
        std::string* out = (std::string*)userp;
        out->append((char*)data, size);
        printf("WebSocket recv: %.*s\n", (int)size, (char*)data);
    }
    return 0;
}

int websocket_demo() {
    CURL* curl = curl_easy_init();
    std::string recv;

    curl_easy_setopt(curl, CURLOPT_URL, "wss://echo.websocket.org");
    curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L);  // Only establish WebSocket
    curl_easy_setopt(curl, CURLOPT_WS_OPTIONS, CURLWS_RAW_MODE);

    CURLcode res = curl_easy_perform(curl);
    if (res != CURLE_OK) return -1;

    // Send text frame
    const char* text = "Hello libcurl!";
    size_t sent;
    curl_ws_send(curl, text, strlen(text), &sent, 0, CURLWS_TEXT);

    // Receive
    const struct curl_ws_frame* meta;
    size_t rlen;
    char buffer[4096];
    while ((res = curl_ws_recv(curl, buffer, sizeof(buffer), &rlen, &meta)) == CURLE_AGAIN) {
        // Wait
    }
    if (res == CURLE_OK) {
        printf("Recv: %.*s\n", (int)rlen, buffer);
    }

    curl_easy_cleanup(curl);
    return 0;
}

5. Advanced Techniques & Common Pitfalls

Scenario Technique
High Concurrency Use multi handle + <span>select()</span> / <span>epoll</span>, single-threaded can achieve 10k+ concurrency
Timeout <span>CURLOPT_TIMEOUT_MS</span> (overall) + <span>CURLOPT_CONNECTTIMEOUT_MS</span> (connection)
Redirection <span>CURLOPT_FOLLOWLOCATION</span> + <span>CURLOPT_MAXREDIRS</span>
DNS Cache <span>CURLOPT_DNS_CACHE_TIMEOUT</span> default 60s
HTTP/2 Compile with nghttp2, automatically negotiate at runtime
Memory Leak Each <span>curl_easy_init</span> must correspond to <span>curl_easy_cleanup</span>
Chinese URL Use <span>curl_escape</span> before concatenation

Simple Template for Multi Handle:

CURLM* multi = curl_multi_init();
curl_multi_add_handle(multi, easy1);
curl_multi_add_handle(multi, easy2);

int still_running = 0;
curl_multi_perform(multi, &still_running);

while (still_running) {
    CURLMcode mc = curl_multi_poll(multi, nullptr, 0, 1000, nullptr);
    if (mc) break;
    curl_multi_perform(multi, &still_running);
}

6. Conclusion

libcurl is like the “Swiss Army Knife” in C++:

  • GET/POST done in one line of code
  • File upload/download with progress bar and resumable download
  • WebSocket, HTTP/3 all supported
  • Cross-platform, No dependencies, High performance

Add it to your toolbox, and you won’t have to write sockets by hand next time!

Action Items:

  1. Give a Star to the template repository above
  2. Copy the code in this article to run the first GET
  3. Leave a comment telling me what feature you most want to implement with libcurl (web scraping? cloud API? IoT?)

Leave a Comment