Skip to content

BlackMATov/curly.hpp

main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 

curly.hpp

Simple cURL C++17 wrapper

linux darwin windows language license

Features

  • Custom headers
  • Asynchronous requests
  • Different types of timeouts
  • URL encoded query parameters
  • Completion and progress callbacks
  • Custom uploading and downloading streams
  • PUT, GET, HEAD, POST, PATCH, DELETE, OPTIONS methods

Requirements

Installation

Just add the root repository directory to your cmake project:

add_subdirectory(external/curly.hpp)
target_link_libraries(your_project_target PUBLIC curly.hpp::curly.hpp)

CMake options

  • USE_STATIC_CRT Use static C runtime library. Default: OFF
  • USE_SYSTEM_CURL Build with cURL from system paths. Default: OFF
  • USE_EMBEDDED_CURL Build with embedded cURL library. Default: ON

Examples

#include <curly.hpp/curly.hpp>
namespace net = curly_hpp;

// creates and hold a separate thread for automatically update async requests
net::performer performer;

// also, you can update requests manually from your favorite thread
net::perform();

GET Requests

// makes a GET request and async send it
auto request = net::request_builder()
    .method(net::http_method::GET)
    .url("http://www.httpbin.org/get")
    .send();

// synchronous waits and take a response
auto response = request.take();

// prints results
std::cout << "Status code: " << response.http_code() << std::endl;
std::cout << "Content type: " << response.headers["content-type"] << std::endl;
std::cout << "Body content: " << response.content.as_string_view() << std::endl;

// Status code: 200
// Content type: application/json
// Body content: {
//     "args": {},
//     "headers": {
//         "Accept": "*/*",
//         "Host": "www.httpbin.org",
//         "User-Agent": "cURL/7.54.0"
//     },
//     "origin": "37.195.66.134, 37.195.66.134",
//     "url": "https://www.httpbin.org/get"
// }

POST Requests

auto request = net::request_builder()
    .method(net::http_method::POST)
    .url("http://www.httpbin.org/post")
    .header("Content-Type", "application/json")
    .content(R"({"hello" : "world"})")
    .send();

auto response = request.take();
std::cout << "Body content: " << response.content.as_string_view() << std::endl;
std::cout << "Content Length: " << response.headers["content-length"] << std::endl;

// Body content: {
//     "args": {},
//     "data": "{\"hello\" : \"world\"}",
//     "files": {},
//     "form": {},
//     "headers": {
//         "Accept": "*/*",
//         "Content-Length": "19",
//         "Content-Type": "application/json",
//         "Host": "www.httpbin.org",
//         "User-Agent": "cURL/7.54.0"
//     },
//     "json": {
//         "hello": "world"
//     },
//     "origin": "37.195.66.134, 37.195.66.134",
//     "url": "https://www.httpbin.org/post"
// }
// Content Length: 389

Query Parameters

auto request = net::request_builder()
    .url("http://httpbin.org/anything")
    .qparam("hello", "world")
    .send();

auto response = request.take();
std::cout << "Last URL: " << response.last_url() << std::endl;

// Last URL: http://httpbin.org/anything?hello=world

Error Handling

auto request = net::request_builder()
    .url("http://unavailable.site.com")
    .send();

request.wait();

if ( request.is_done() ) {
    auto response = request.take();
    std::cout << "Status code: " << response.http_code() << std::endl;
} else {
    // throws net::exception because a response is unavailable
    // auto response = request.take();

    std::cout << "Error message: " << request.get_error() << std::endl;
}

// Error message: Could not resolve host: unavailable.site.com

Request Callbacks

auto request = net::request_builder("http://www.httpbin.org/get")
    .callback([](net::request request){
        if ( request.is_done() ) {
            auto response = request.take();
            std::cout << "Status code: " << response.http_code() << std::endl;
        } else {
            std::cout << "Error message: " << request.get_error() << std::endl;
        }
    }).send();

request.wait_callback();
// Status code: 200

Streamed Requests

Downloading

class file_dowloader : public net::download_handler {
public:
    file_dowloader(const char* filename)
    : stream_(filename, std::ofstream::binary) {}

    std::size_t write(const char* src, std::size_t size) override {
        stream_.write(src, static_cast<std::streamsize>(size));
        return size;
    }
private:
    std::ofstream stream_;
};

net::request_builder()
    .url("https://httpbin.org/image/jpeg")
    .downloader<file_dowloader>("image.jpeg")
    .send().wait();

Uploading

class file_uploader : public net::upload_handler {
public:
    file_uploader(const char* filename)
    : stream_(filename, std::ifstream::binary) {
        stream_.seekg(0, std::ios::end);
        size_ = static_cast<std::size_t>(stream_.tellg());
        stream_.seekg(0, std::ios::beg);
    }

    std::size_t size() const override {
        return size_;
    }

    std::size_t upload(char* dst, std::size_t size) override {
        stream_.read(dst, static_cast<std::streamsize>(size));
        return size;
    }
private:
    std::size_t size_{0u};
    std::ifstream stream_;
};

net::request_builder()
    .method(net::http_method::POST)
    .url("https://httpbin.org/anything")
    .uploader<file_uploader>("image.jpeg")
    .send().wait();

Promised Requests

Also, you can easily integrate promises like a promise.hpp.

#include <curly.hpp/curly.hpp>
namespace net = curly_hpp;

#include <promise.hpp/promise.hpp>
namespace netex = promise_hpp;

netex::promise<net::content_t> download(std::string url) {
    return netex::make_promise<net::content_t>([
        url = std::move(url)
    ](auto resolve, auto reject){
        net::request_builder(std::move(url))
            .callback([resolve,reject](net::request request) mutable {
                if ( !request.is_done() ) {
                    reject(net::exception("network error"));
                    return;
                }
                net::response response = request.take();
                if ( response.is_http_error() ) {
                    reject(net::exception("server error"));
                    return;
                }
                resolve(std::move(response.content));
            }).send();
    });
}

auto promise = download("https://httpbin.org/image/png")
    .then([](const net::content_t& content){
        std::cout << content.size() << " bytes downloaded" << std::endl;
    }).except([](std::exception_ptr e){
        try {
            std::rethrow_exception(e);
        } catch (const std::exception& ee) {
            std::cerr << "Failed to download: " << ee.what() << std::endl;
        }
    });

promise.wait();
// 8090 bytes downloaded