Networking
DNS Resolution
#include <coroio/dns/resolver.hpp>
#include <coroio/all.hpp>
using namespace NNet;
TFuture<void> lookup(TDefaultPoller& poller) {
TResolver resolver(poller); // reads /etc/resolv.conf
auto addrs = co_await resolver.Resolve("example.com"); // A record
auto v6 = co_await resolver.Resolve("example.com", EDNSType::AAAA);
// THostPort: literal IPs bypass DNS; hostnames go through TResolver
TAddress addr = co_await THostPort("example.com", 443).Resolve(resolver);
co_return;
}
TResolver caches results and deduplicates in-flight queries — concurrent Resolve() calls for the same name share a single UDP packet.
Resolve multiple names in parallel:
TFuture<void> bulkResolve(TDefaultPoller& poller) {
TResolver resolver(poller);
std::vector<std::string> names = {"a.example.com", "b.example.com", "c.example.com"};
// All queries are sent concurrently; results arrive in any order
std::vector<TFuture<std::vector<TAddress>>> futures;
for (auto& name : names)
futures.push_back(resolver.Resolve(name));
auto results = co_await All(std::move(futures));
co_return;
}
HTTP Server
#include <coroio/http/httpd.hpp>
using namespace NNet;
struct TMyRouter : public IRouter {
TFuture<void> HandleRequest(TRequest& req, TResponse& resp) override {
if (req.Method() == "GET" && req.Uri().Path() == "/hello") {
resp.SetStatus(200);
resp.SetHeader("Content-Type", "text/plain");
co_await resp.WriteBodyFull("Hello, World!\n");
} else if (req.Method() == "POST" && req.Uri().Path() == "/echo") {
auto body = co_await req.ReadBodyFull();
resp.SetStatus(200);
resp.SetHeader("Content-Type", "application/octet-stream");
co_await resp.WriteBodyFull(body);
} else {
resp.SetStatus(404);
co_await resp.WriteBodyFull("Not found\n");
}
co_return;
}
};
int main() {
TLoop<TDefaultPoller> loop;
TMyRouter router;
using TSocket = TDefaultPoller::TSocket;
TSocket sock(loop.Poller(), AF_INET6);
sock.Bind(TAddress{"::", 8080});
sock.Listen();
TWebServer<TSocket> server(std::move(sock), router);
server.Start();
loop.Loop();
}
Request API
| Method | Returns | Description |
|---|---|---|
req.Method() |
string_view |
HTTP method ("GET", "POST", …) |
req.Uri().Path() |
string_view |
URL path |
req.Uri().Query() |
string_view |
Query string (without ?) |
req.Headers() |
map<sv,sv> |
All request headers |
req.HasBody() |
bool |
Non-zero Content-Length or chunked |
co_await req.ReadBodyFull() |
string |
Read entire body |
co_await req.ReadBodySome(buf, n) |
ssize_t |
Read body chunk |
req.BodyConsumed() |
bool |
True once body is fully read |
Response API
| Method | Description |
|---|---|
resp.SetStatus(code) |
HTTP status code (default 200) |
resp.SetHeader(name, value) |
Add response header |
co_await resp.SendHeaders() |
Flush status + headers |
co_await resp.WriteBodyFull(data) |
Set Content-Length, send headers + body |
co_await resp.WriteBodyChunk(data, n) |
Transfer-Encoding: chunked |
WriteBodyFull implies SendHeaders. SetStatus/SetHeader must be called before either.
Streaming Response
TFuture<void> HandleRequest(TRequest& req, TResponse& resp) override {
resp.SetStatus(200);
resp.SetHeader("Content-Type", "text/event-stream");
co_await resp.SendHeaders(); // headers only, no Content-Length
for (int i = 0; i < 10; ++i) {
std::string chunk = "data: " + std::to_string(i) + "\n\n";
co_await resp.WriteBodyChunk(chunk.data(), chunk.size());
}
co_return;
}
WebSocket Client
#include <coroio/ws/ws.hpp>
#include <coroio/dns/resolver.hpp>
using namespace NNet;
TFuture<void> wsClient(TDefaultPoller& poller) {
TResolver resolver(poller);
TAddress addr = co_await THostPort("ws.kraken.com", 443).Resolve(resolver);
TDefaultPoller::TSocket tcp(poller, addr.Domain());
// wss:// — wrap with TLS
TSslContext ctx = TSslContext::Client();
TSslSocket ssl(std::move(tcp), ctx);
ssl.SslSetTlsExtHostName("ws.kraken.com"); // SNI, must be before Connect
co_await ssl.Connect(addr);
TWebSocket ws(ssl);
co_await ws.Connect("ws.kraken.com", "/v2"); // HTTP Upgrade
// Subscribe to BTC/USD ticker
co_await ws.SendText(
R"({"method":"subscribe","params":{"channel":"ticker","symbol":["BTC/USD"]}})");
while (true) {
auto msg = co_await ws.ReceiveText(); // string_view, valid until next call
std::cout << msg << "\n";
}
co_return;
}
TWebSocket API
| Method | Description |
|---|---|
co_await ws.Connect(host, path) |
HTTP Upgrade handshake |
co_await ws.SendText(sv) |
Send a text frame (client masking per RFC 6455) |
co_await ws.ReceiveText() |
Receive next text frame |
Client-side only. Binary frames not supported. See market data example for a working wss:// demo.
SSL/TLS
#include <coroio/ssl.hpp>
using namespace NNet;
// ── Client ───────────────────────────────────────────────────────────────────
TSslContext ctx = TSslContext::Client();
TSslSocket ssl(std::move(tcpSocket), ctx);
ssl.SslSetTlsExtHostName("example.com"); // SNI — before Connect
co_await ssl.Connect(addr);
ssize_t n = co_await ssl.ReadSome(buf, sizeof buf);
co_await ssl.WriteSome(buf, n);
// ── Server (PEM files) ───────────────────────────────────────────────────────
TSslContext ctx = TSslContext::Server("server.crt", "server.key");
// ── Server (in-memory PEM) ───────────────────────────────────────────────────
TSslContext ctx = TSslContext::ServerFromMem(certPemPtr, keyPemPtr);
// In the accept loop:
auto raw = co_await listener.Accept();
TSslSocket<decltype(raw)> ssl(std::move(raw), ctx);
co_await ssl.AcceptHandshake();
// ssl is now ready for ReadSome / WriteSome
TSslSocket<T> has exactly the same ReadSome/WriteSome interface as a plain socket. Swap it in without touching the rest of your I/O logic.
Full example: sslechoserver.cpp · sslechoclient.cpp
Subprocess / Pipe
Available on Linux and macOS.
#include <coroio/pipe/pipe.hpp>
using namespace NNet;
TFuture<void> compress(TDefaultPoller& poller) {
TPipe pipe(poller, {"gzip", "-c"}); // spawn gzip
std::string input = "hello world\n";
co_await pipe.WriteSome(input.data(), input.size());
pipe.CloseWrite(); // send EOF to child's stdin
char buf[4096]; ssize_t n;
while ((n = co_await pipe.ReadSome(buf, sizeof buf)) > 0)
std::cout.write(buf, n); // compressed bytes
int exit_code = co_await pipe.Wait();
std::cout << "gzip exited: " << exit_code << "\n";
co_return;
}
TPipe API
| Method | Description |
|---|---|
co_await pipe.WriteSome(buf, n) |
Write to child's stdin |
co_await pipe.ReadSome(buf, n) |
Read from child's stdout |
co_await pipe.ReadSomeErr(buf, n) |
Read from child's stderr |
pipe.CloseWrite() |
Close stdin — sends EOF to child |
co_await pipe.Wait() |
waitpid() — returns exit status |
pipe.Pid() |
Child PID |
Pass stderrToStdout = true to the constructor to merge stderr into stdout (like 2>&1).
Full example: test_pipe.cpp