-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttp2.cpp
More file actions
143 lines (126 loc) · 4.76 KB
/
Copy pathhttp2.cpp
File metadata and controls
143 lines (126 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/// @file http2.cpp
/// @brief Minimal HTTP/2 server example demonstrating ALPN (h2) and h2c upgrade.
///
/// Build with HTTP/2 support:
/// cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DAERONET_ENABLE_HTTP2=ON
/// cmake --build build -j
///
/// Run:
/// ./build/examples/aeronet-http2 # Cleartext h2c on ephemeral port
/// ./build/examples/aeronet-http2 8080 # Cleartext h2c on port 8080
/// ./build/examples/aeronet-http2 8443 --tls # HTTPS with ALPN "h2" (requires certs)
///
/// Test:
/// curl --http2-prior-knowledge http://localhost:8080/hello # h2c prior knowledge
/// curl --http2 http://localhost:8080/hello # h2c upgrade
/// curl -k --http2 https://localhost:8443/hello # ALPN h2
#include <aeronet/aeronet-server.hpp>
#include <charconv>
#include <csignal>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <iostream>
#include <string>
#include <string_view>
#include <system_error>
#include <utility>
using namespace aeronet;
int main(int argc, char** argv) {
uint16_t port = 0;
bool useTls = false;
std::string certPath;
std::string keyPath;
// Parse arguments: [cert] [key] [port] [--tls]
// or: [port] [--tls]
if (argc > 1) {
// Check if first arg is a cert file (exists and is readable)
std::string firstArg = argv[1];
bool firstArgIsFile = (firstArg.contains('.') && firstArg.contains('/'));
if (firstArgIsFile && argc >= 3) {
// Cert/key path format: cert key [port] [--tls]
certPath = argv[1];
keyPath = argv[2];
useTls = true;
if (argc > 3) {
const auto [ptr, errc] = std::from_chars(argv[3], argv[3] + std::strlen(argv[3]), port);
if (errc != std::errc{} || ptr != argv[3] + std::strlen(argv[3])) {
std::cerr << "Invalid port number: " << argv[3] << "\n";
return EXIT_FAILURE;
}
}
} else {
// Port-first format: [port] [--tls]
const auto [ptr, errc] = std::from_chars(argv[1], argv[1] + std::strlen(argv[1]), port);
if (errc != std::errc{} || ptr != argv[1] + std::strlen(argv[1])) {
std::cerr << "Invalid port number: " << argv[1] << "\n";
return EXIT_FAILURE;
}
if (argc > 2) {
useTls = (std::string_view(argv[2]) == "--tls");
}
}
}
// Enable signal handler for graceful shutdown on Ctrl+C
SignalHandler::Enable();
Router router;
try {
// Unified handler for both HTTP/1.1 and HTTP/2
// The handler receives an HttpRequestView which has isHttp2() method to detect protocol
router.setDefault([](const HttpRequestView& req) {
HttpResponse resp(200);
if (req.isHttp2()) {
resp.bodyAppend("Hello from aeronet HTTP/2!\n");
resp.bodyAppend("Stream ID: ");
resp.bodyAppend(std::to_string(req.streamId()));
resp.bodyAppend("\n");
} else {
resp.bodyAppend("Hello from aeronet HTTP/1.1!\n");
}
resp.bodyAppend("Path: ");
resp.bodyAppend(req.path());
resp.bodyAppend("\nMethod: ");
resp.bodyAppend(http::MethodToStr(req.method()));
resp.bodyAppend("\n");
return resp;
});
// Configure HTTP/2
Http2Config http2Config;
http2Config.enable = true;
http2Config.maxConcurrentStreams = 100;
http2Config.initialWindowSize = 65535;
http2Config.enableH2c = true; // Allow cleartext HTTP/2 (prior knowledge)
http2Config.enableH2cUpgrade = true; // Allow HTTP/1.1 -> HTTP/2 upgrade
// Configure server with HTTP/2 settings
HttpServerConfig config;
config.withPort(port).withHttp2(http2Config);
// Configure TLS if requested
if (useTls) {
// Use provided cert/key paths, or defaults
if (certPath.empty()) {
certPath = "server.crt";
}
if (keyPath.empty()) {
keyPath = "server.key";
}
config.withTlsCertKey(certPath, keyPath).withTlsAlpnProtocols({"h2", "http/1.1"}); // Prefer HTTP/2 via ALPN
}
SingleHttpServer server(std::move(config), std::move(router));
std::cout << "HTTP/2 server listening on port " << server.port();
if (useTls) {
std::cout << " (TLS with ALPN h2)\n";
std::cout << "Test with: curl -k --http2 https://localhost:" << server.port() << "/hello\n";
} else {
std::cout << " (cleartext h2c)\n";
std::cout << "Test with h2c prior knowledge: curl --http2-prior-knowledge http://localhost:" << server.port()
<< "/hello\n";
std::cout << "Test with h2c upgrade: curl --http2 http://localhost:" << server.port() << "/hello\n";
}
server.run(); // blocking run, until Ctrl+C
} catch (const std::exception& e) {
std::cerr << "Server encountered error: " << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}