https://github.com/siddiqsoft/restcl
Focused REST client for modern C++
https://github.com/siddiqsoft/restcl
async cpp20 json-api nlohmann-json nuget rest-client windows winhttp
Last synced: about 1 month ago
JSON representation
Focused REST client for modern C++
- Host: GitHub
- URL: https://github.com/siddiqsoft/restcl
- Owner: SiddiqSoft
- License: bsd-3-clause
- Created: 2021-07-31T01:22:07.000Z (almost 5 years ago)
- Default Branch: main
- Last Pushed: 2025-02-01T06:02:28.000Z (over 1 year ago)
- Last Synced: 2025-04-14T23:42:03.142Z (about 1 year ago)
- Topics: async, cpp20, json-api, nlohmann-json, nuget, rest-client, windows, winhttp
- Language: C++
- Homepage: https://siddiqsoft.github.io/restcl/
- Size: 672 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# restcl: A Focused REST Client for Modern C++
[](https://dev.azure.com/siddiqsoft/siddiqsoft/_build/latest?definitionId=13&branchName=main)



## Overview
**restcl** is a header-only REST client library for modern C++23 that provides a clean, JSON-first API for interacting with RESTful servers. It abstracts platform-specific HTTP implementations (WinHTTP on Windows, libcurl on Unix/Linux/macOS) behind a unified, modern C++ interface. The library prioritizes simplicity and instructional clarity over performance, making it ideal for applications that need straightforward REST communication without the complexity of lower-level HTTP libraries.
### Key Features
- **JSON-First API**: JSON is a first-class citizen in the API design
- **Modern C++23**: Leverages C++23 features including `std::expected`, `std::format`, and user-defined literals
- **Header-Only**: Easy integration with no compilation overhead
- **Cross-Platform**: Native implementations for Windows (WinHTTP) and Unix/Linux/macOS (libcurl)
- **User-Defined Literals**: Convenient syntax like `"https://api.example.com"_GET`
- **Async Support**: Non-blocking async operations with callback-based responses
- **Error Handling**: Uses `std::expected` for robust error handling without exceptions for IO operations
- **Comprehensive Testing**: Extensive test suite with unit, integration, and stress tests
- **Type-Safe**: Template-based design with support for custom character types
## Table of Contents
- [Quick Start](#quick-start)
- [Installation](#installation)
- [Usage Examples](#usage-examples)
- [Architecture](#architecture)
- [Dependencies](#dependencies)
- [Building](#building)
- [Testing](#testing)
- [Best Practices](#best-practices)
- [Contributing](#contributing)
- [License](#license)
## Quick Start
### Basic GET Request
```cpp
#include
using namespace siddiqsoft::restcl_literals;
int main() {
auto client = siddiqsoft::GetRESTClient();
auto request = "https://api.example.com/users"_GET;
auto response = client->send(request);
if (response) {
std::cout << "Status: " << response->statusCode() << "\n";
std::cout << "Body: " << response->body() << "\n";
} else {
std::cerr << "Error: " << response.error() << "\n";
}
return 0;
}
```
### POST Request with JSON
```cpp
#include
#include
using json = nlohmann::json;
using namespace siddiqsoft::restcl_literals;
int main() {
auto client = siddiqsoft::GetRESTClient();
auto request = "https://api.example.com/users"_POST;
json payload = {
{"name", "John Doe"},
{"email", "john@example.com"}
};
request.setBody(payload.dump());
request.setHeader("Content-Type", "application/json");
auto response = client->send(request);
if (response) {
auto result = json::parse(response->body());
std::cout << "Created user: " << result["id"] << "\n";
}
return 0;
}
```
### Async Request with Callback
```cpp
#include
#include
using namespace siddiqsoft::restcl_literals;
int main() {
auto client = siddiqsoft::GetRESTClient();
std::atomic done{false};
auto request = "https://api.example.com/data"_GET;
client->sendAsync(request, [&done](const auto& req, auto response) {
if (response) {
std::cout << "Async response: " << response->statusCode() << "\n";
} else {
std::cerr << "Async error: " << response.error() << "\n";
}
done = true;
});
// Wait for async operation to complete
while (!done) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
```
## Installation
### Via NuGet (Windows)
```bash
nuget install SiddiqSoft.restcl
```
### Via CMake (Recommended)
Add to your `CMakeLists.txt`:
```cmake
include(FetchContent)
FetchContent_Declare(restcl
GIT_REPOSITORY https://github.com/SiddiqSoft/restcl.git
GIT_TAG main
)
FetchContent_MakeAvailable(restcl)
target_link_libraries(your_target PRIVATE restcl::restcl)
```
### Manual Integration
1. Clone the repository
2. Copy the `include/siddiqsoft` directory to your project
3. Ensure your compiler supports C++23
4. Link against platform-specific libraries:
- **Windows**: WinHTTP (included in Windows SDK)
- **Unix/Linux/macOS**: libcurl
## Usage Examples
### Supported HTTP Methods
The library provides user-defined literals for all standard HTTP methods:
```cpp
using namespace siddiqsoft::restcl_literals;
auto get_req = "https://api.example.com/resource"_GET;
auto post_req = "https://api.example.com/resource"_POST;
auto put_req = "https://api.example.com/resource/123"_PUT;
auto delete_req = "https://api.example.com/resource/123"_DELETE;
auto patch_req = "https://api.example.com/resource/123"_PATCH;
auto head_req = "https://api.example.com/resource"_HEAD;
auto options_req = "https://api.example.com/resource"_OPTIONS;
```
### Working with Headers
```cpp
auto request = "https://api.example.com/data"_GET;
request.setHeader("Authorization", "Bearer token123");
request.setHeader("Accept", "application/json");
request.setHeader("User-Agent", "MyApp/1.0");
// Access headers
auto auth = request.getHeader("Authorization");
```
### Request Configuration
```cpp
auto client = siddiqsoft::GetRESTClient({
{"connectTimeout", 3000}, // Connection timeout in ms
{"timeout", 5000}, // Overall timeout in ms
{"userAgent", "MyApp/1.0"}, // Custom user agent
{"trace", false} // Enable/disable tracing
});
```
### Error Handling
The library uses `std::expected` for error handling:
```cpp
auto response = client->send(request);
if (response) {
// Success path
std::cout << "Status: " << response->statusCode() << "\n";
} else {
// Error path
int error_code = response.error();
std::cerr << "Request failed with error: " << error_code << "\n";
}
```
### JSON Integration
```cpp
#include
using json = nlohmann::json;
// Parse response as JSON
auto response = client->send(request);
if (response && response->statusCode() == 200) {
auto data = json::parse(response->body());
// Access JSON data
std::string name = data["name"];
int age = data["age"];
}
```
## API Reference
For comprehensive API documentation, see [API.md](API.md).
## Architecture
### Project Structure
```
restcl/
├── include/siddiqsoft/
│ ├── restcl.hpp # Main public header (platform dispatcher)
│ └── private/
│ ├── basic_restclient.hpp # Abstract base class for REST clients
│ ├── http_frame.hpp # Base class for HTTP requests/responses
│ ├── rest_request.hpp # REST request model with literals support
│ ├── rest_response.hpp # REST response model with parsing
│ ├── restcl_unix.hpp # libcurl-based implementation
│ └── restcl_win.hpp # WinHTTP-based implementation
├── tests/ # Comprehensive test suite
│ ├── test_validation.cpp # Validation and error handling tests
│ ├── test_restcl.cpp # Core functionality tests
│ ├── test_serializers.cpp # JSON serialization tests
│ ├── test_postbin.cpp # Integration tests with external services
│ ├── test_libcurl_helpers.cpp # Unix/Linux-specific tests
│ └── test_mock_and_coverage.cpp # Mock and coverage tests
├── docs/ # Doxygen documentation
├── pack/ # NuGet packaging and build helpers
├── CMakeLists.txt # Main CMake configuration
├── CMakePresets.json # CMake presets for builds
├── .clang-format # Code formatting rules
├── .clang-tidy # Static analysis configuration
├── best_practices.md # Comprehensive development guidelines
└── azure-pipelines.yml # CI/CD pipeline
```
### Core Components
#### `basic_restclient`
Abstract interface defining the REST client contract with `send()` and `sendAsync()` methods.
#### `http_frame`
Base class providing common HTTP functionality including headers, content, and protocol version management.
#### `rest_request`
Extends `http_frame` with request-specific encoding and HTTP method support.
#### `rest_response`
Extends `http_frame` with response parsing, status codes, and reason phrases.
#### Platform-Specific Implementations
- **`HttpRESTClient`**: Unix/Linux/macOS implementation using libcurl
- **`WinHttpRESTClient`**: Windows implementation using WinHTTP with HTTP/2 support
## Dependencies
### Core Dependencies
| Dependency | Version | Purpose |
|-----------|---------|---------|
| nlohmann/json | v3.12.0 | JSON parsing and serialization |
| SplitUri | v3.0.0 | URI parsing and manipulation |
| AzureCppUtils | v3.2.5 | Azure-specific utilities |
| string2map | v2.5.0 | String-to-map conversion utilities |
| RunOnEnd | v1.4.2 | RAII-based cleanup utilities |
| asynchrony | v2.1.1 | Asynchronous operation helpers |
| acw32h | v2.7.4 | Windows-specific C++ wrapper for WinHTTP (Windows only) |
| CURL | v8.7+ | libcurl for Unix/Linux/macOS |
### Build Tools
| Tool | Version | Purpose |
|------|---------|---------|
| CMake | v3.29+ | Build system |
| Clang-Format | Latest | Code formatting |
| Clang-Tidy | Latest | Static analysis |
| Google Test | v1.17.0 | Testing framework |
| Doxygen | Latest | Documentation generation |
### Compiler Requirements
- **C++23 Standard**: Required
- **Visual Studio 2022**: For Windows builds (MSVC)
- **Clang 18+** or **GCC 13+**: For Unix/Linux builds
- **Clang on macOS**: With `-fexperimental-library` flag for `std::stop_token` and `std::jthread`
## Building
### Prerequisites
- CMake 3.29 or later
- C++23 compatible compiler
- Platform-specific dependencies:
- **Windows**: Windows SDK (includes WinHTTP)
- **Linux**: libcurl development libraries (`libcurl-devel` or `libcurl4-openssl-dev`)
- **macOS**: libcurl (usually pre-installed)
### Build Steps
```bash
# Clone the repository
git clone https://github.com/SiddiqSoft/restcl.git
cd restcl
# Configure with CMake
cmake --preset default -DRESTCL_BUILD_TESTS=ON
# Build the project
cmake --build --preset default
# Run tests (optional)
ctest --preset default
```
### CMake Options
- `RESTCL_BUILD_TESTS`: Enable/disable test suite (default: OFF)
- `CMAKE_BUILD_TYPE`: Debug or Release (default: Release)
## Testing
The library includes a comprehensive test suite covering:
### Test Categories
- **Unit Tests** (`test_validation.cpp`): Request/response validation, error handling, edge cases
- **Core Tests** (`test_restcl.cpp`): Core functionality, async operations, multiple HTTP verbs, stress tests
- **Serialization Tests** (`test_serializers.cpp`): JSON serialization and deserialization
- **Integration Tests** (`test_postbin.cpp`): Real HTTP calls to external services
- **Platform-Specific Tests** (`test_libcurl_helpers.cpp`): Unix/Linux-specific libcurl tests
- **Mock Tests** (`test_mock_and_coverage.cpp`): Mock objects and code coverage validation
### Running Tests
```bash
# Build with tests enabled
cmake --preset default -DRESTCL_BUILD_TESTS=ON
cmake --build --preset default
# Run all tests
ctest --preset default
# Run specific test
ctest --preset default -R test_restcl
```
### Test Coverage
- ASAN (Address Sanitizer) and leak detection enabled for Debug builds on Linux/Clang
- Code coverage instrumentation enabled for Debug builds
- Tests pass on both Windows (MSVC) and Unix/Linux (Clang/GCC)
## Best Practices
For comprehensive guidance on code style, testing strategies, common patterns, and best practices when working with or extending this library, see [best_practices.md](best_practices.md).
### Key Principles
#### Error Handling
- Use `std::expected` for IO operations instead of exceptions
- Throw `std::invalid_argument` for validation errors
- Different platforms return different error codes:
- Windows: WinHTTP error codes (12001, 12002, 12029, etc.)
- Unix/Linux: POSIX error codes (ECONNRESET, etc.)
#### Modern C++ Features
- Leverage C++23 features: `std::expected`, `std::format`, `std::atomic`, concepts
- Use move semantics extensively
- Prefer `std::shared_ptr` for shared ownership
- Use `[[nodiscard]]` on functions returning important values
#### Async Operations
- Use callbacks for async operations: `std::function&, std::expected, int>)>`
- Callbacks receive both request and response (or error code)
- Register global callbacks via `configure()` and override per-request
- Use `std::atomic` with `.wait()` and `.notify_all()` for test synchronization
#### Code Style
- Classes: PascalCase (e.g., `rest_request`, `HttpRESTClient`)
- Functions: camelCase (e.g., `setMethod()`, `getUri()`)
- Constants: UPPER_SNAKE_CASE (e.g., `HTTP_NEWLINE`)
- Private members: Prefix with underscore (e.g., `_statusCode`)
- Namespaces: lowercase (e.g., `siddiqsoft`, `restcl_literals`)
#### Documentation
- Use Doxygen comments (`///` or `/**`) for public APIs
- Include `@brief`, `@param`, `@return`, `@throw` tags
- Comment non-obvious algorithms or platform-specific code
- Guard debug output with `#if defined(DEBUG)`
## Design Philosophy
### Motivation
Design a library where JSON is a first-class API metaphor for interacting with RESTful servers.
### Core Principles
1. **Focused Scope**: REST interactions with JSON only. This limitation allows us to simplify usage and make it feel very C++ instead of the C-like API of Win32 or LibCURL.
2. **Modern C++**: C++23 is required, enabling:
- Visual Studio 2022 on Windows
- WinHTTP library with HTTP/2 support
- Modern language features and idioms
3. **Header-Only**: Easy integration with no compilation overhead for the library itself.
4. **Native Implementations**: Use platform-specific libraries for actual IO:
- Windows: WinHTTP library
- Unix/Linux/macOS: libcurl
5. **User-Defined Literals**: Support for convenient syntax like `_GET`, `_DELETE`, etc. via the `siddiqsoft::restcl_literals` namespace.
6. **Instructional**: Use as little code as necessary and be clear about intent.
7. **Interface-Focused**: The focus is on the interface to the end user, not internal complexity.
8. **Simplicity Over Performance**: Hide the underlying implementation and prioritize clarity.
## Roadmap
- [ ] Performance optimizations
- [ ] Additional test coverage
- [ ] Extended documentation with more examples
- [ ] Support for additional content types
- [ ] Enhanced error diagnostics
## Contributing
Contributions are welcome! Please ensure:
1. Code follows the style guidelines in [best_practices.md](best_practices.md)
2. All tests pass: `ctest --preset default`
3. New features include appropriate tests
4. Documentation is updated accordingly
5. Code is formatted with clang-format: `clang-format -i `
## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
## Support
For issues, questions, or suggestions:
1. Check the [best_practices.md](best_practices.md) for comprehensive guidance
2. Review existing [GitHub Issues](https://github.com/SiddiqSoft/restcl/issues)
3. Create a new issue with detailed information
4. For security concerns, please email privately
## Acknowledgments
- Built with modern C++23 features
- Uses [nlohmann/json](https://github.com/nlohmann/json) for JSON handling
- Cross-platform support via WinHTTP and libcurl
- Comprehensive testing with Google Test framework