HTTP Client¶
The Glaze HTTP client provides a simple and efficient way to make HTTP requests with connection pooling and asynchronous operations.
Basic Usage¶
#include "glaze/net/http_client.hpp"
int main() {
glz::http_client client;
auto response = client.get("https://example.com");
if (response) {
std::cout << "Status: " << response->status_code << std::endl;
std::cout << "Body: " << response->response_body << std::endl;
// Access response headers
for (const auto& [name, value] : response->response_headers) {
std::cout << name << ": " << value << std::endl;
}
} else {
std::cerr << "Error: " << response.error().message() << std::endl;
}
return 0;
}
Features¶
- Connection Pooling: Automatically reuses connections for better performance
- Asynchronous Operations: Non-blocking requests with futures or completion handlers
- JSON Support: Built-in JSON serialization for POST requests
- Thread-Safe: Multiple threads can safely use the same client instance
- Error Handling: Uses
std::expected
for clean error handling
Synchronous Methods¶
GET Request¶
std::expected<response, std::error_code> get(
std::string_view url,
const std::unordered_map<std::string, std::string>& headers = {}
);
POST Request¶
std::expected<response, std::error_code> post(
std::string_view url,
std::string_view body,
const std::unordered_map<std::string, std::string>& headers = {}
);
JSON POST Request¶
template<class T>
std::expected<response, std::error_code> post_json(
std::string_view url,
const T& data,
const std::unordered_map<std::string, std::string>& headers = {}
);
Asynchronous Methods¶
All asynchronous methods come in two variants:
- Future-based: Returns a
std::future
for the response - Callback-based: Takes a completion handler that's called when the operation completes
Async GET Request¶
Future-based:
std::future<std::expected<response, std::error_code>> get_async(
std::string_view url,
const std::unordered_map<std::string, std::string>& headers = {}
);
Callback-based:
template<typename CompletionHandler>
void get_async(
std::string_view url,
const std::unordered_map<std::string, std::string>& headers,
CompletionHandler&& handler
);
Async POST Request¶
Future-based:
std::future<std::expected<response, std::error_code>> post_async(
std::string_view url,
std::string_view body,
const std::unordered_map<std::string, std::string>& headers = {}
);
Callback-based:
template<typename CompletionHandler>
void post_async(
std::string_view url,
std::string_view body,
const std::unordered_map<std::string, std::string>& headers,
CompletionHandler&& handler
);
Async JSON POST Request¶
Future-based:
template<class T>
std::future<std::expected<response, std::error_code>> post_json_async(
std::string_view url,
const T& data,
const std::unordered_map<std::string, std::string>& headers = {}
);
Callback-based:
template<class T, typename CompletionHandler>
void post_json_async(
std::string_view url,
const T& data,
const std::unordered_map<std::string, std::string>& headers,
CompletionHandler&& handler
);
Streaming Requests¶
The HTTP client supports streaming requests, which allow you to receive data in chunks.
template<typename OnData, typename OnError, typename OnConnect, typename OnDisconnect>
stream_connection::pointer stream_request(stream_options options);
The stream_options
struct contains the following fields:
struct stream_options {
std::string url;
std::string method = "GET";
std::unordered_map<std::string, std::string> headers;
OnData on_data;
OnError on_error;
OnConnect on_connect;
OnDisconnect on_disconnect;
};
url
: The URL to request.method
: The HTTP method to use (default: "GET").headers
: The HTTP headers to send.on_data
: A callback that's called when data is received.on_error
: A callback that's called when an error occurs.on_connect
: A callback that's called when the connection is established and the headers are received.on_disconnect
: A callback that's called when the connection is closed.
The stream_connection::pointer
object contains a disconnect()
method that can be used to close the connection.
Response Structure¶
The response
object contains:
struct response {
uint16_t status_code; // HTTP status code
std::unordered_map<std::string, std::string> response_headers; // Response headers
std::string response_body; // Response body
};
Error Handling¶
The HTTP client returns a std::expected
object for synchronous and asynchronous requests, which contains either the response or an error code. You can check for errors using the has_value()
method or by accessing the error()
method.
auto response = client.get("https://example.com");
if (response) {
// Request was successful
std::cout << "Status: " << response->status_code << std::endl;
} else {
std::error_code ec = response.error();
std::cerr << "Error: " << ec.message() << std::endl;
}
For streaming requests, errors are reported via the on_error
callback in the stream_options
struct. The client translates HTTP error statuses (4xx/5xx) into std::errc::connection_refused
errors.
Examples¶
Simple GET Request¶
#include "glaze/net/http_client.hpp"
int main() {
glz::http_client client;
auto response = client.get("https://api.github.com/users/octocat");
if (response) {
std::cout << "Status: " << response->status_code << std::endl;
std::cout << "Content-Type: " << response->response_headers["Content-Type"] << std::endl;
std::cout << "Body: " << response->response_body << std::endl;
} else {
std::cerr << "Error: " << response.error().message() << std::endl;
}
return 0;
}
POST Request with Custom Headers¶
#include "glaze/net/http_client.hpp"
int main() {
glz::http_client client;
std::unordered_map<std::string, std::string> headers = {
{"Content-Type", "text/plain"},
{"Authorization", "Bearer your-token"}
};
auto response = client.post("https://api.example.com/data", "Hello, World!", headers);
if (response) {
std::cout << "Status: " << response->status_code << std::endl;
std::cout << "Response: " << response->response_body << std::endl;
} else {
std::cerr << "Error: " << response.error().message() << std::endl;
}
return 0;
}
JSON POST Request¶
#include "glaze/net/http_client.hpp"
#include "glaze/glaze.hpp"
struct User {
int id;
std::string name;
std::string email;
};
int main() {
glz::http_client client;
User user{123, "John Doe", "john@example.com"};
// Using the convenient post_json method
auto response = client.post_json("https://api.example.com/users", user);
if (response) {
std::cout << "User created! Status: " << response->status_code << std::endl;
std::cout << "Response: " << response->response_body << std::endl;
} else {
std::cerr << "Error: " << response.error().message() << std::endl;
}
return 0;
}
Asynchronous Requests with Futures¶
#include "glaze/net/http_client.hpp"
#include <future>
#include <vector>
int main() {
glz::http_client client;
// Launch multiple async requests
std::vector<std::future<std::expected<glz::response, std::error_code>>> futures;
futures.push_back(client.get_async("https://api.github.com/users/octocat"));
futures.push_back(client.get_async("https://api.github.com/users/defunkt"));
futures.push_back(client.get_async("https://api.github.com/users/pjhyett"));
// Wait for all requests to complete
for (auto& future : futures) {
auto response = future.get();
if (response) {
std::cout << "Status: " << response->status_code << std::endl;
} else {
std::cerr << "Error: " << response.error().message() << std::endl;
}
}
return 0;
}
Asynchronous Requests with Callbacks¶
#include "glaze/net/http_client.hpp"
#include <iostream>
int main() {
glz::http_client client;
// Async GET with callback
client.get_async("https://api.github.com/users/octocat", {},
[](std::expected<glz::response, std::error_code> result) {
if (result) {
std::cout << "Async GET completed! Status: " << result->status_code << std::endl;
} else {
std::cerr << "Async GET failed: " << result.error().message() << std::endl;
}
});
// Async JSON POST with callback
struct Data { int value = 42; };
Data data;
client.post_json_async("https://httpbin.org/post", data, {},
[](std::expected<glz::response, std::error_code> result) {
if (result) {
std::cout << "Async JSON POST completed! Status: " << result->status_code << std::endl;
} else {
std::cerr << "Async JSON POST failed: " << result.error().message() << std::endl;
}
});
// Keep the main thread alive long enough for async operations to complete
std::this_thread::sleep_for(std::chrono::seconds(2));
return 0;
}
URL Parsing¶
The client includes a URL parsing utility:
#include "glaze/net/http_client.hpp"
auto url_parts = glz::parse_url("https://api.example.com:8080/v1/users");
if (url_parts) {
std::cout << "Protocol: " << url_parts->protocol << std::endl; // "https"
std::cout << "Host: " << url_parts->host << std::endl; // "api.example.com"
std::cout << "Port: " << url_parts->port << std::endl; // 8080
std::cout << "Path: " << url_parts->path << std::endl; // "/v1/users"
}
Performance Notes¶
- The client automatically pools connections for better performance when making multiple requests to the same host
- Multiple worker threads are used internally to handle concurrent requests
- The connection pool has a configurable limit (default: 10 connections per host), which can be adjusted using the
http_client::options::max_connections
option. - Connections are automatically returned to the pool when requests complete successfully