diff --git a/README.md b/README.md index bb0eb78..5fdd47d 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,20 @@ ## Installation -> coming soon! +Just add the root repository directory to your cmake project: + +```cmake +add_subdirectory(external/curly.hpp) +target_link_libraries(your_project_target curly.hpp) +``` + +## Features + +- Custom headers +- Asynchronous requests +- PUT, GET, HEAD, POST methods +- Custom uploading and downloading streams +- Connection and last server response timeouts ## Examples @@ -84,10 +97,9 @@ auto request = net::request_builder() .send(); auto response = request.get(); -std::cout << "Status code: " << response.code() << std::endl; std::cout << "Body content: " << response.content().as_string_view() << std::endl; +std::cout << "Content Length: " << response.headers()["content-length"] << std::endl; -// Status code: 200 // Body content: { // "args": {}, // "data": "{\"hello\" : \"world\"}", @@ -106,6 +118,85 @@ std::cout << "Body content: " << response.content().as_string_view() << std::end // "origin": "37.195.66.134, 37.195.66.134", // "url": "https://www.httpbin.org/post" // } +// Content Length: 389 +``` + +### Error Handling + +```cpp +auto request = net::request_builder() + .url("http://unavailable.site.com") + .send(); + +if ( request.wait() == net::request::statuses::done ) { + auto response = request.get(); + std::cout << "Status code: " << response.code() << std::endl; +} else { + // throws net::exception because a response is unavailable + // auto response = request.get(); + + std::cout << "Error message: " << request.error() << std::endl; +} + +// Error message: Couldn't resolve host name +``` + +### Streamed Requests + +#### Downloading + +```cpp +class file_dowloader : public net::download_handler { +public: + file_dowloader(const char* filename) + : stream_(filename, std::ofstream::binary) {} + + std::size_t download(const char* src, std::size_t size) override { + stream_.write(src, size); + return size; + } +private: + std::ofstream stream_; +}; + +net::request_builder() + .url("https://httpbin.org/image/jpeg") + .downloader("image.jpeg") + .send().wait(); +``` + +#### Uploading + +```cpp +class file_uploader : public net::upload_handler { +public: + file_uploader(const char* filename) + : stream_(filename, std::ifstream::binary) { + stream_.seekg(0, std::ios::end); + available_ = static_cast(stream_.tellg()); + stream_.seekg(0, std::ios::beg); + } + + std::size_t size() const override { + return available_; + } + + std::size_t upload(char* dst, std::size_t size) override { + std::size_t read_bytes = std::min(size, available_); + stream_.read(dst, read_bytes); + available_ -= read_bytes; + return read_bytes; + } +private: + std::ifstream stream_; + std::size_t available_{0u}; +}; + +net::request_builder() + .method(net::methods::post) + .url("https://httpbin.org/anything") + .uploader("image.jpeg") + .send().wait(); ``` ## API diff --git a/untests/curly_tests.cpp b/untests/curly_tests.cpp index 9357334..c8cb78d 100644 --- a/untests/curly_tests.cpp +++ b/untests/curly_tests.cpp @@ -7,6 +7,7 @@ #define CATCH_CONFIG_FAST_COMPILE #include +#include #include #include @@ -402,10 +403,9 @@ TEST_CASE("curly_examples") { .send(); auto response = request.get(); - std::cout << "Status code: " << response.code() << std::endl; std::cout << "Body content: " << response.content().as_string_view() << std::endl; + std::cout << "Content Length: " << response.headers()["content-length"] << std::endl; - // Status code: 200 // Body content: { // "args": {}, // "data": "{\"hello\" : \"world\"}", @@ -424,5 +424,77 @@ TEST_CASE("curly_examples") { // "origin": "37.195.66.134, 37.195.66.134", // "url": "https://www.httpbin.org/post" // } + // Content Length: 389 + } + + SECTION("Error Handling") { + auto request = net::request_builder() + .url("http://unavailable.site.com") + .send(); + + if ( request.wait() == net::request::statuses::done ) { + auto response = request.get(); + std::cout << "Status code: " << response.code() << std::endl; + } else { + // throws net::exception because a response is unavailable + // auto response = request.get(); + + std::cout << "Error message: " << request.error() << std::endl; + } + + // Error message: Couldn't resolve host name + } + + SECTION("Streamed Requests") { + { + class file_dowloader : public net::download_handler { + public: + file_dowloader(const char* filename) + : stream_(filename, std::ofstream::binary) {} + + std::size_t download(const char* src, std::size_t size) override { + stream_.write(src, size); + return size; + } + private: + std::ofstream stream_; + }; + + net::request_builder() + .url("https://httpbin.org/image/jpeg") + .downloader("image.jpeg") + .send().wait(); + } + { + class file_uploader : public net::upload_handler { + public: + file_uploader(const char* filename) + : stream_(filename, std::ifstream::binary) { + stream_.seekg(0, std::ios::end); + available_ = static_cast(stream_.tellg()); + stream_.seekg(0, std::ios::beg); + } + + std::size_t size() const override { + return available_; + } + + std::size_t upload(char* dst, std::size_t size) override { + std::size_t read_bytes = std::min(size, available_); + stream_.read(dst, read_bytes); + available_ -= read_bytes; + return read_bytes; + } + private: + std::ifstream stream_; + std::size_t available_{0u}; + }; + + net::request_builder() + .method(net::methods::post) + .url("https://httpbin.org/anything") + .uploader("image.jpeg") + .send().wait(); + } } }