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.
GitHub Overview
jpbarrette/curlpp
C++ wrapper around libcURL
Topics
Star History
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;
}