-
Notifications
You must be signed in to change notification settings - Fork 0
HTTP2 Guide
github-actions[bot] edited this page Jan 28, 2026
·
1 revision
Elio provides full HTTP/2 client support via the nghttp2 library. This guide covers HTTP/2 usage, configuration, and best practices.
HTTP/2 offers several advantages over HTTP/1.1:
- Multiplexing: Multiple requests/responses over a single connection
- Header compression: HPACK compression reduces overhead
- Binary framing: More efficient parsing
- Stream prioritization: Clients can hint at request importance
HTTP/2 support requires:
- OpenSSL with ALPN support
- nghttp2 library (fetched automatically by CMake)
- CMake option:
-DELIO_ENABLE_HTTP2=ON
Note: HTTP/2 requires HTTPS (h2 over TLS). For plaintext HTTP, use the HTTP/1.1 client.
#include <elio/elio.hpp>
#include <elio/http/http2.hpp>
using namespace elio;
using namespace elio::http;
coro::task<void> fetch_example() {
h2_client client;
// Simple GET request
auto resp = co_await client.get("https://example.com/");
if (resp) {
std::cout << "Status: " << resp->status_code() << std::endl;
std::cout << "Body: " << resp->body() << std::endl;
}
}coro::task<void> post_example() {
h2_client client;
auto resp = co_await client.post(
"https://api.example.com/users",
R"({"name": "John", "email": "john@example.com"})",
mime::application_json
);
if (resp) {
std::cout << "Created: " << resp->body() << std::endl;
}
}struct h2_client_config {
std::chrono::seconds connect_timeout{10};
std::chrono::seconds read_timeout{30};
size_t max_concurrent_streams = 100;
size_t initial_window_size = 65535;
bool verify_certificate = true;
std::string user_agent = "elio-http2/1.0";
};
// Usage
h2_client_config config;
config.verify_certificate = false; // For testing only!
config.max_concurrent_streams = 50;
h2_client client(config);h2_client client;
// Access the underlying TLS context
auto& tls_ctx = client.tls_context();
// Use custom CA certificate
tls_ctx.load_verify_file("/path/to/ca-bundle.crt");
// Use client certificate (for mutual TLS)
tls_ctx.use_certificate_file("/path/to/client.crt");
tls_ctx.use_private_key_file("/path/to/client.key");HTTP/2 excels at handling multiple concurrent requests:
coro::task<void> parallel_requests() {
h2_client client;
// Spawn multiple requests concurrently
auto h1 = client.get("https://api.example.com/users/1").spawn();
auto h2 = client.get("https://api.example.com/users/2").spawn();
auto h3 = client.get("https://api.example.com/users/3").spawn();
// Wait for all responses
auto r1 = co_await h1;
auto r2 = co_await h2;
auto r3 = co_await h3;
// All requests used the same TCP connection!
}coro::task<void> handle_response() {
h2_client client(ctx);
auto resp = co_await client.get("https://example.com/api/data");
if (!resp) {
std::cerr << "Request failed: " << strerror(errno) << std::endl;
co_return;
}
// Check status
if (!resp->is_success()) {
std::cerr << "HTTP error: " << resp->status_code() << std::endl;
co_return;
}
// Access headers
auto content_type = resp->header("Content-Type");
auto cache_control = resp->header("Cache-Control");
// Get body
std::string body = resp->body();
// Or take ownership (avoids copy)
std::string body_moved = resp->take_body();
}coro::task<void> error_handling() {
h2_client client(ctx);
auto resp = co_await client.get("https://example.com/");
if (!resp) {
switch (errno) {
case ECONNREFUSED:
std::cerr << "Connection refused" << std::endl;
break;
case ETIMEDOUT:
std::cerr << "Connection timeout" << std::endl;
break;
case ECONNRESET:
std::cerr << "Connection reset" << std::endl;
break;
default:
std::cerr << "Error: " << strerror(errno) << std::endl;
}
co_return;
}
// Handle HTTP-level errors
if (resp->status_code() >= 400) {
std::cerr << "HTTP " << resp->status_code() << ": "
<< resp->body() << std::endl;
}
}coro::task<void> cancellable_request() {
h2_client client(ctx);
coro::cancel_source cancel_source;
// Start cancellable request
auto request_task = client.get("https://slow-api.example.com/",
cancel_source.token());
// In another coroutine or after timeout
cancel_source.cancel();
auto resp = co_await request_task;
if (!resp && errno == ECANCELED) {
std::cout << "Request was cancelled" << std::endl;
}
}The h2_client maintains a connection pool internally:
coro::task<void> connection_lifecycle() {
h2_client client(ctx);
// First request establishes connection
co_await client.get("https://api.example.com/ping");
// Subsequent requests reuse the same connection
for (int i = 0; i < 100; i++) {
co_await client.get("https://api.example.com/data/" +
std::to_string(i));
}
// Connection is automatically closed when client is destroyed
}| Feature | HTTP/2 | HTTP/1.1 |
|---|---|---|
| Multiplexing | Yes | No (pipelining limited) |
| Header compression | HPACK | None |
| Binary protocol | Yes | Text-based |
| Server push | Supported | No |
| TLS required | Yes (in practice) | No |
| Connection reuse | Single connection | Connection pool |
Use HTTP/2 when:
- Making many concurrent requests to the same host
- Bandwidth is limited (header compression helps)
- Low latency is critical
- Server supports HTTP/2
Use HTTP/1.1 when:
- Server doesn't support HTTP/2
- Making single requests
- HTTP (non-TLS) is required
- Compatibility with older infrastructure
Enable debug logging to see HTTP/2 frame details:
// Set log level before creating client
elio::log::set_level(elio::log::level::debug);
h2_client client(ctx);
auto resp = co_await client.get("https://example.com/");
// Output shows frame exchanges:
// [DEBUG] Sending SETTINGS frame
// [DEBUG] Received SETTINGS frame
// [DEBUG] Sending HEADERS frame (stream 1)
// [DEBUG] Received HEADERS frame (stream 1)
// [DEBUG] Received DATA frame (stream 1, 1234 bytes)- Reuse clients: Create one h2_client per host and reuse it
- Use concurrency: Take advantage of multiplexing with parallel requests
- Handle errors: Always check response validity
- Set timeouts: Configure appropriate timeouts for your use case
- Verify certificates: Only disable certificate verification for testing