curlpp

C++ wrapper library for libcurl. Utilizes C++ standard library structures providing exception safety and type safety. Design based on RAII principles with automated memory management. Achieves more C++-like interface while providing access to all libcurl features.

HTTP ClientC++RAIIlibcurlObject-Oriented

GitHub Overview

jpbarrette/curlpp

C++ wrapper around libcURL

Stars1,767
Watchers62
Forks372
Created:February 26, 2015
Language:C++
License:-

Topics

c-plus-pluscurlpplibcurltransfer

Star History

jpbarrette/curlpp Star History
Data as of: 10/22/2025, 09:55 AM

Library

curlpp

Overview

curlpp is "a C++ wrapper library for libcurl" developed as an object-oriented HTTP client library. With the concept of "performing HTTP communication in a C++ way," it provides libcurl's powerful functionality through C++ design patterns. Achieving RAII (Resource Acquisition Is Initialization) support, type safety, exception safety, and integration with the C++ standard library, it realizes a natural and safe HTTP communication environment for C++ developers.

Details

curlpp 2025 edition is a mature wrapper library that enables the utilization of libcurl's proven performance and reliability through modern C++ design patterns. With over 15 years of development experience, it provides high stability and maintainability for HTTP/HTTPS communication in C++ projects. Object-oriented design automates resource management and significantly reduces runtime errors through type-safe APIs. Exception-based error handling and RAII support prevent memory leaks and resource leaks, optimized for enterprise-level C++ application development.

Key Features

  • RAII Support: Automatic resource management and cleanup functionality
  • Type Safety: Compile-time type checking for error prevention
  • Exception Safety: C++-style exception-based error handling
  • Object-Oriented: Abstraction of libcurl functionality through C++ classes
  • STL Integration: Seamless integration with std::string and std::list
  • Option Retrieval: Ability to retrieve previously set option values

Pros and Cons

Pros

  • Natural and maintainable code writing following C++ design patterns
  • Improved memory safety and automatic resource management through RAII support
  • Significant reduction in runtime errors through type-safe APIs
  • Robust error handling through exception-based error handling
  • C++-style coding while utilizing all libcurl functionality
  • Easy build environment setup with CMake support and vcpkg compatibility

Cons

  • Increased binary size due to libcurl dependency
  • C++-specific learning costs and complex configuration requirements in some cases
  • Excessive functionality and overhead for small-scale projects
  • Wrapper layer may become a barrier during debugging
  • Latest HTTP/3 and QUIC support depends on libcurl's support status
  • More complex Windows build configuration compared to other platforms

Reference Pages

Code Examples

Installation and Build Configuration

# Installation using vcpkg
vcpkg install curlpp

# Get source code from GitHub
git clone https://github.com/jpbarrette/curlpp.git
cd curlpp

# Build using CMake
mkdir build && cd build
cmake ..
make

# Get compiler flags with pkg-config
pkg-config --cflags --libs curlpp

# Or use curlpp-config
curlpp-config --cflags --libs

Basic Initialization and Cleanup (RAII)

#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
#include <iostream>
#include <sstream>

// RAII-compatible basic pattern
int main() {
    try {
        // RAII: Automatic initialization and cleanup
        curlpp::Cleanup cleanup;
        
        // Safe to use curlpp within this scope
        std::cout << "curlpp initialization completed" << std::endl;
        
    } catch(const curlpp::RuntimeError& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
        return 1;
    } catch(const curlpp::LogicError& e) {
        std::cerr << "Logic error: " << e.what() << std::endl;
        return 1;
    }
    
    std::cout << "curlpp automatic cleanup completed" << std::endl;
    return 0;
}

// Manual initialization/cleanup pattern (not recommended)
void manual_cleanup_example() {
    try {
        // Manual initialization (only once per application)
        curlpp::initialize();
        
        // HTTP communication processing
        // ...
        
        // Manual cleanup (on application exit)
        curlpp::terminate();
        
    } catch(const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        curlpp::terminate(); // Always cleanup on error
    }
}

Basic HTTP Requests (GET/POST)

#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
#include <curlpp/Exception.hpp>
#include <iostream>
#include <sstream>
#include <string>

// Basic GET request
void simple_get_request() {
    try {
        curlpp::Cleanup cleanup;
        curlpp::Easy request;
        
        // URL configuration
        request.setOpt(new curlpp::Options::Url("https://api.example.com/users"));
        
        // Header configuration
        std::list<std::string> headers;
        headers.push_back("Accept: application/json");
        headers.push_back("User-Agent: curlpp/1.0");
        request.setOpt(new curlpp::Options::HttpHeader(headers));
        
        // Write response to std::stringstream
        std::ostringstream response_stream;
        request.setOpt(new curlpp::Options::WriteStream(&response_stream));
        
        // Execute request
        request.perform();
        
        // Get response
        std::string response = response_stream.str();
        std::cout << "Response: " << response << std::endl;
        
        // Get HTTP status code
        long response_code = curlpp::Infos::ResponseCode::get(request);
        std::cout << "Status code: " << response_code << std::endl;
        
    } catch(const curlpp::RuntimeError& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
    } catch(const curlpp::LogicError& e) {
        std::cerr << "Logic error: " << e.what() << std::endl;
    }
}

// POST request (sending JSON)
void post_json_request() {
    try {
        curlpp::Cleanup cleanup;
        curlpp::Easy request;
        
        // URL configuration
        request.setOpt(new curlpp::Options::Url("https://api.example.com/users"));
        
        // POST data (JSON)
        std::string json_data = R"({
            "name": "John Doe",
            "email": "[email protected]",
            "age": 30
        })";
        
        // POST option configuration
        request.setOpt(new curlpp::Options::Post(true));
        request.setOpt(new curlpp::Options::PostFields(json_data));
        request.setOpt(new curlpp::Options::PostFieldSize(json_data.length()));
        
        // Header configuration
        std::list<std::string> headers;
        headers.push_back("Content-Type: application/json");
        headers.push_back("Accept: application/json");
        headers.push_back("Authorization: Bearer your-token");
        request.setOpt(new curlpp::Options::HttpHeader(headers));
        
        // Response stream
        std::ostringstream response_stream;
        request.setOpt(new curlpp::Options::WriteStream(&response_stream));
        
        // Execute request
        request.perform();
        
        // Check result
        long response_code = curlpp::Infos::ResponseCode::get(request);
        std::string response = response_stream.str();
        
        if (response_code == 201) {
            std::cout << "User creation successful: " << response << std::endl;
        } else {
            std::cerr << "Error " << response_code << ": " << response << std::endl;
        }
        
    } catch(const curlpp::RuntimeError& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
    }
}

// Form data POST
void post_form_data() {
    try {
        curlpp::Cleanup cleanup;
        curlpp::Easy request;
        
        request.setOpt(new curlpp::Options::Url("https://api.example.com/login"));
        
        // Form data configuration
        std::string form_data = "username=testuser&password=secret123";
        request.setOpt(new curlpp::Options::Post(true));
        request.setOpt(new curlpp::Options::PostFields(form_data));
        
        // Content-Type header
        std::list<std::string> headers;
        headers.push_back("Content-Type: application/x-www-form-urlencoded");
        request.setOpt(new curlpp::Options::HttpHeader(headers));
        
        // Get response
        std::ostringstream response_stream;
        request.setOpt(new curlpp::Options::WriteStream(&response_stream));
        
        request.perform();
        
        std::cout << "Login result: " << response_stream.str() << std::endl;
        
    } catch(const curlpp::RuntimeError& e) {
        std::cerr << "Login error: " << e.what() << std::endl;
    }
}

Advanced Configuration (Authentication, SSL, Proxy, etc.)

#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
#include <curlpp/Infos.hpp>

// Basic authentication and SSL configuration
void advanced_authentication() {
    try {
        curlpp::Cleanup cleanup;
        curlpp::Easy request;
        
        // URL configuration
        request.setOpt(new curlpp::Options::Url("https://secure-api.example.com/data"));
        
        // Basic authentication
        request.setOpt(new curlpp::Options::HttpAuth(CURLAUTH_BASIC));
        request.setOpt(new curlpp::Options::UserPwd("username:password"));
        
        // SSL/TLS configuration
        request.setOpt(new curlpp::Options::SslVerifyPeer(true));
        request.setOpt(new curlpp::Options::SslVerifyHost(2));
        request.setOpt(new curlpp::Options::CaInfo("/etc/ssl/certs/ca-certificates.crt"));
        
        // Client certificate
        request.setOpt(new curlpp::Options::SslCert("/path/to/client.crt"));
        request.setOpt(new curlpp::Options::SslKey("/path/to/client.key"));
        request.setOpt(new curlpp::Options::SslKeyPasswd("cert-password"));
        
        // Timeout configuration
        request.setOpt(new curlpp::Options::Timeout(30)); // 30 seconds
        request.setOpt(new curlpp::Options::ConnectTimeout(10)); // 10 seconds connection
        
        // User-Agent and custom headers
        request.setOpt(new curlpp::Options::UserAgent("MyApp/1.0 (curlpp)"));
        
        std::list<std::string> headers;
        headers.push_back("Accept: application/json");
        headers.push_back("X-API-Version: v2");
        headers.push_back("X-Request-ID: req-12345");
        request.setOpt(new curlpp::Options::HttpHeader(headers));
        
        // Get response
        std::ostringstream response_stream;
        request.setOpt(new curlpp::Options::WriteStream(&response_stream));
        
        request.perform();
        
        // Get connection information
        std::string effective_url = curlpp::Infos::EffectiveUrl::get(request);
        long response_code = curlpp::Infos::ResponseCode::get(request);
        double total_time = curlpp::Infos::TotalTime::get(request);
        
        std::cout << "Effective URL: " << effective_url << std::endl;
        std::cout << "Status: " << response_code << std::endl;
        std::cout << "Total time: " << total_time << " seconds" << std::endl;
        std::cout << "Response: " << response_stream.str() << std::endl;
        
    } catch(const curlpp::RuntimeError& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
    }
}

// Proxy configuration and cookie management
void proxy_and_cookies() {
    try {
        curlpp::Cleanup cleanup;
        curlpp::Easy request;
        
        request.setOpt(new curlpp::Options::Url("https://api.example.com/user-data"));
        
        // Proxy configuration
        request.setOpt(new curlpp::Options::Proxy("http://proxy.example.com:8080"));
        request.setOpt(new curlpp::Options::ProxyUserPwd("proxy_user:proxy_pass"));
        request.setOpt(new curlpp::Options::ProxyType(CURLPROXY_HTTP));
        
        // Cookie configuration
        request.setOpt(new curlpp::Options::Cookie("session_id=abc123; user_pref=dark_mode"));
        request.setOpt(new curlpp::Options::CookieJar("/tmp/cookies.txt")); // Save cookies
        request.setOpt(new curlpp::Options::CookieFile("/tmp/cookies.txt")); // Load cookies
        
        // Redirect configuration
        request.setOpt(new curlpp::Options::FollowLocation(true));
        request.setOpt(new curlpp::Options::MaxRedirs(5));
        
        // Enable debug information
        request.setOpt(new curlpp::Options::Verbose(true));
        
        // Get response
        std::ostringstream response_stream;
        request.setOpt(new curlpp::Options::WriteStream(&response_stream));
        
        request.perform();
        
        std::cout << "Data retrieved via proxy" << std::endl;
        std::cout << response_stream.str() << std::endl;
        
    } catch(const curlpp::RuntimeError& e) {
        std::cerr << "Proxy error: " << e.what() << std::endl;
    }
}

// File upload
void file_upload() {
    try {
        curlpp::Cleanup cleanup;
        curlpp::Easy request;
        
        request.setOpt(new curlpp::Options::Url("https://api.example.com/upload"));
        
        // Upload file configuration
        std::ifstream file("/path/to/upload/file.txt", std::ios::binary);
        if (!file.is_open()) {
            throw std::runtime_error("Cannot open file");
        }
        
        // Get file size
        file.seekg(0, std::ios::end);
        long file_size = file.tellg();
        file.seekg(0, std::ios::beg);
        
        // PUT request configuration
        request.setOpt(new curlpp::Options::Upload(true));
        request.setOpt(new curlpp::Options::ReadStream(&file));
        request.setOpt(new curlpp::Options::InFileSize(file_size));
        
        // Header configuration
        std::list<std::string> headers;
        headers.push_back("Content-Type: application/octet-stream");
        headers.push_back("Authorization: Bearer your-token");
        request.setOpt(new curlpp::Options::HttpHeader(headers));
        
        // Progress display configuration
        request.setOpt(new curlpp::Options::NoProgress(false));
        
        request.perform();
        
        long response_code = curlpp::Infos::ResponseCode::get(request);
        std::cout << "Upload completed: " << response_code << std::endl;
        
    } catch(const curlpp::RuntimeError& e) {
        std::cerr << "Upload error: " << e.what() << std::endl;
    } catch(const std::exception& e) {
        std::cerr << "File error: " << e.what() << std::endl;
    }
}

Error Handling and Debugging

#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
#include <curlpp/Exception.hpp>

// Comprehensive error handling
class HttpClient {
private:
    curlpp::Cleanup cleanup_;
    
public:
    struct Response {
        long status_code;
        std::string body;
        std::string error_message;
        bool success;
        double total_time;
    };
    
    Response safe_request(const std::string& url, 
                         const std::string& method = "GET",
                         const std::string& data = "",
                         const std::list<std::string>& headers = {}) {
        Response result;
        result.success = false;
        
        try {
            curlpp::Easy request;
            
            // Basic configuration
            request.setOpt(new curlpp::Options::Url(url));
            request.setOpt(new curlpp::Options::Timeout(30));
            request.setOpt(new curlpp::Options::ConnectTimeout(10));
            request.setOpt(new curlpp::Options::FollowLocation(true));
            request.setOpt(new curlpp::Options::MaxRedirs(3));
            
            // SSL configuration
            request.setOpt(new curlpp::Options::SslVerifyPeer(true));
            request.setOpt(new curlpp::Options::SslVerifyHost(2));
            
            // Method configuration
            if (method == "POST") {
                request.setOpt(new curlpp::Options::Post(true));
                if (!data.empty()) {
                    request.setOpt(new curlpp::Options::PostFields(data));
                }
            } else if (method == "PUT") {
                request.setOpt(new curlpp::Options::CustomRequest("PUT"));
                if (!data.empty()) {
                    request.setOpt(new curlpp::Options::PostFields(data));
                }
            } else if (method == "DELETE") {
                request.setOpt(new curlpp::Options::CustomRequest("DELETE"));
            }
            
            // Header configuration
            if (!headers.empty()) {
                request.setOpt(new curlpp::Options::HttpHeader(headers));
            }
            
            // Response configuration
            std::ostringstream response_stream;
            request.setOpt(new curlpp::Options::WriteStream(&response_stream));
            
            // Execute request
            request.perform();
            
            // Get result
            result.status_code = curlpp::Infos::ResponseCode::get(request);
            result.body = response_stream.str();
            result.total_time = curlpp::Infos::TotalTime::get(request);
            result.success = (result.status_code >= 200 && result.status_code < 300);
            
            if (!result.success) {
                result.error_message = "HTTP error: " + std::to_string(result.status_code);
            }
            
        } catch(const curlpp::LibcurlRuntimeError& e) {
            result.error_message = "libcurl runtime error: " + std::string(e.what());
            std::cerr << "libcurl error code: " << e.whatCode() << std::endl;
        } catch(const curlpp::RuntimeError& e) {
            result.error_message = "curlpp runtime error: " + std::string(e.what());
        } catch(const curlpp::LogicError& e) {
            result.error_message = "curlpp logic error: " + std::string(e.what());
        } catch(const std::exception& e) {
            result.error_message = "Unexpected error: " + std::string(e.what());
        }
        
        return result;
    }
    
    // Request with retry functionality
    Response request_with_retry(const std::string& url,
                               int max_retries = 3,
                               int backoff_ms = 1000) {
        Response result;
        
        for (int attempt = 0; attempt <= max_retries; ++attempt) {
            result = safe_request(url);
            
            if (result.success) {
                if (attempt > 0) {
                    std::cout << "Successful on attempt " << (attempt + 1) << std::endl;
                }
                break;
            }
            
            if (attempt < max_retries) {
                std::cerr << "Attempt " << (attempt + 1) << " failed: " 
                         << result.error_message << std::endl;
                std::cerr << "Retrying in " << backoff_ms * (attempt + 1) << "ms..." << std::endl;
                
                std::this_thread::sleep_for(
                    std::chrono::milliseconds(backoff_ms * (attempt + 1))
                );
            }
        }
        
        return result;
    }
};

// Usage example
void error_handling_example() {
    HttpClient client;
    
    // Basic request
    auto response = client.safe_request("https://api.example.com/users");
    
    if (response.success) {
        std::cout << "Success (" << response.status_code << "): " 
                 << response.body << std::endl;
        std::cout << "Execution time: " << response.total_time << " seconds" << std::endl;
    } else {
        std::cerr << "Failed: " << response.error_message << std::endl;
    }
    
    // Request with retry
    auto retry_response = client.request_with_retry(
        "https://unstable-api.example.com/data", 
        3,   // Maximum 3 attempts
        500  // 500ms interval
    );
    
    if (retry_response.success) {
        std::cout << "Successful with retry" << std::endl;
    } else {
        std::cerr << "Finally failed: " << retry_response.error_message << std::endl;
    }
}

// Debug detailed logging
void debug_request() {
    try {
        curlpp::Cleanup cleanup;
        curlpp::Easy request;
        
        request.setOpt(new curlpp::Options::Url("https://httpbin.org/json"));
        
        // Enable detailed logging
        request.setOpt(new curlpp::Options::Verbose(true));
        
        // Also get header information
        std::ostringstream header_stream;
        request.setOpt(new curlpp::Options::HeaderFunction(
            curlpp::Types::WriteFunctionFunctor(&header_stream)
        ));
        
        std::ostringstream response_stream;
        request.setOpt(new curlpp::Options::WriteStream(&response_stream));
        
        request.perform();
        
        // Display detailed information
        std::cout << "=== Header Information ===" << std::endl;
        std::cout << header_stream.str() << std::endl;
        std::cout << "=== Response ===" << std::endl;
        std::cout << response_stream.str() << std::endl;
        
        // Performance information
        double name_lookup = curlpp::Infos::NameLookupTime::get(request);
        double connect_time = curlpp::Infos::ConnectTime::get(request);
        double total_time = curlpp::Infos::TotalTime::get(request);
        
        std::cout << "=== Performance ===" << std::endl;
        std::cout << "DNS resolution time: " << name_lookup << " seconds" << std::endl;
        std::cout << "Connection time: " << connect_time << " seconds" << std::endl;
        std::cout << "Total time: " << total_time << " seconds" << std::endl;
        
    } catch(const curlpp::RuntimeError& e) {
        std::cerr << "Debug execution error: " << e.what() << std::endl;
    }
}

Practical Class Design and Multi-threading Support

#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
#include <thread>
#include <future>
#include <vector>
#include <mutex>

// Thread-safe HTTP client
class ThreadSafeHttpClient {
private:
    static std::once_flag init_flag_;
    static bool initialized_;
    
    // Global initialization (once only)
    static void initialize_once() {
        std::call_once(init_flag_, []() {
            curlpp::initialize();
            initialized_ = true;
            std::atexit(cleanup_global);
        });
    }
    
    static void cleanup_global() {
        if (initialized_) {
            curlpp::terminate();
            initialized_ = false;
        }
    }
    
public:
    struct RequestConfig {
        std::string url;
        std::string method = "GET";
        std::string body;
        std::list<std::string> headers;
        int timeout = 30;
        bool follow_redirects = true;
    };
    
    struct Response {
        long status_code = 0;
        std::string body;
        std::map<std::string, std::string> headers;
        double total_time = 0.0;
        bool success = false;
        std::string error;
    };
    
    ThreadSafeHttpClient() {
        initialize_once();
    }
    
    // Synchronous request
    Response execute(const RequestConfig& config) {
        Response response;
        
        try {
            curlpp::Easy request;
            
            // Basic configuration
            request.setOpt(new curlpp::Options::Url(config.url));
            request.setOpt(new curlpp::Options::Timeout(config.timeout));
            request.setOpt(new curlpp::Options::FollowLocation(config.follow_redirects));
            
            // Method configuration
            if (config.method == "POST") {
                request.setOpt(new curlpp::Options::Post(true));
                if (!config.body.empty()) {
                    request.setOpt(new curlpp::Options::PostFields(config.body));
                }
            } else if (config.method == "PUT") {
                request.setOpt(new curlpp::Options::CustomRequest("PUT"));
                if (!config.body.empty()) {
                    request.setOpt(new curlpp::Options::PostFields(config.body));
                }
            } else if (config.method == "DELETE") {
                request.setOpt(new curlpp::Options::CustomRequest("DELETE"));
            }
            
            // Header configuration
            if (!config.headers.empty()) {
                request.setOpt(new curlpp::Options::HttpHeader(config.headers));
            }
            
            // Get response
            std::ostringstream body_stream;
            request.setOpt(new curlpp::Options::WriteStream(&body_stream));
            
            std::ostringstream header_stream;
            request.setOpt(new curlpp::Options::HeaderFunction(
                curlpp::Types::WriteFunctionFunctor(&header_stream)
            ));
            
            // Execute request
            request.perform();
            
            // Set result
            response.status_code = curlpp::Infos::ResponseCode::get(request);
            response.body = body_stream.str();
            response.total_time = curlpp::Infos::TotalTime::get(request);
            response.success = (response.status_code >= 200 && response.status_code < 300);
            
            // Parse headers (simplified version)
            std::string header_text = header_stream.str();
            std::istringstream header_lines(header_text);
            std::string line;
            while (std::getline(header_lines, line)) {
                auto colon_pos = line.find(':');
                if (colon_pos != std::string::npos) {
                    std::string key = line.substr(0, colon_pos);
                    std::string value = line.substr(colon_pos + 1);
                    // Remove whitespace
                    value.erase(0, value.find_first_not_of(" \t"));
                    value.erase(value.find_last_not_of(" \t\r\n") + 1);
                    response.headers[key] = value;
                }
            }
            
        } catch(const curlpp::RuntimeError& e) {
            response.error = "curlpp runtime error: " + std::string(e.what());
        } catch(const std::exception& e) {
            response.error = "error: " + std::string(e.what());
        }
        
        return response;
    }
    
    // Asynchronous request
    std::future<Response> execute_async(const RequestConfig& config) {
        return std::async(std::launch::async, [this, config]() {
            return this->execute(config);
        });
    }
    
    // Parallel requests
    std::vector<Response> execute_parallel(const std::vector<RequestConfig>& configs) {
        std::vector<std::future<Response>> futures;
        
        // Start all requests asynchronously
        for (const auto& config : configs) {
            futures.push_back(execute_async(config));
        }
        
        // Collect results
        std::vector<Response> responses;
        for (auto& future : futures) {
            responses.push_back(future.get());
        }
        
        return responses;
    }
};

// Static member initialization
std::once_flag ThreadSafeHttpClient::init_flag_;
bool ThreadSafeHttpClient::initialized_ = false;

// RESTful API client
class RestApiClient {
private:
    ThreadSafeHttpClient http_client_;
    std::string base_url_;
    std::list<std::string> default_headers_;
    
public:
    RestApiClient(const std::string& base_url, const std::string& auth_token = "") 
        : base_url_(base_url) {
        // Default header configuration
        default_headers_.push_back("Content-Type: application/json");
        default_headers_.push_back("Accept: application/json");
        if (!auth_token.empty()) {
            default_headers_.push_back("Authorization: Bearer " + auth_token);
        }
    }
    
    ThreadSafeHttpClient::Response get(const std::string& endpoint) {
        ThreadSafeHttpClient::RequestConfig config;
        config.url = base_url_ + endpoint;
        config.method = "GET";
        config.headers = default_headers_;
        return http_client_.execute(config);
    }
    
    ThreadSafeHttpClient::Response post(const std::string& endpoint, const std::string& json_data) {
        ThreadSafeHttpClient::RequestConfig config;
        config.url = base_url_ + endpoint;
        config.method = "POST";
        config.body = json_data;
        config.headers = default_headers_;
        return http_client_.execute(config);
    }
    
    ThreadSafeHttpClient::Response put(const std::string& endpoint, const std::string& json_data) {
        ThreadSafeHttpClient::RequestConfig config;
        config.url = base_url_ + endpoint;
        config.method = "PUT";
        config.body = json_data;
        config.headers = default_headers_;
        return http_client_.execute(config);
    }
    
    ThreadSafeHttpClient::Response del(const std::string& endpoint) {
        ThreadSafeHttpClient::RequestConfig config;
        config.url = base_url_ + endpoint;
        config.method = "DELETE";
        config.headers = default_headers_;
        return http_client_.execute(config);
    }
};

// Usage example
void practical_usage_example() {
    try {
        // Create API client
        RestApiClient api("https://api.example.com/v1", "your-jwt-token");
        
        // Get user list
        auto users_response = api.get("/users");
        if (users_response.success) {
            std::cout << "User list: " << users_response.body << std::endl;
        }
        
        // Create new user
        std::string user_json = R"({
            "name": "John Doe",
            "email": "[email protected]",
            "department": "engineering"
        })";
        
        auto create_response = api.post("/users", user_json);
        if (create_response.success) {
            std::cout << "User creation successful: " << create_response.body << std::endl;
        } else {
            std::cerr << "Creation failed: " << create_response.error << std::endl;
        }
        
        // Parallel request example
        ThreadSafeHttpClient client;
        std::vector<ThreadSafeHttpClient::RequestConfig> configs = {
            {"https://api.example.com/users", "GET"},
            {"https://api.example.com/posts", "GET"},
            {"https://api.example.com/comments", "GET"}
        };
        
        auto responses = client.execute_parallel(configs);
        
        for (size_t i = 0; i < responses.size(); ++i) {
            std::cout << "Request " << (i+1) << ": ";
            if (responses[i].success) {
                std::cout << "Success (" << responses[i].status_code << ")" << std::endl;
            } else {
                std::cout << "Failed - " << responses[i].error << std::endl;
            }
        }
        
    } catch(const std::exception& e) {
        std::cerr << "Unexpected error: " << e.what() << std::endl;
    }
}

int main() {
    practical_usage_example();
    return 0;
}