DOCUMENTATION

ply-network.h
Networking

Plywood provides a portable API for TCP/IP networking supporting both IPv4 and IPv6 addresses, with optional HTTP support.

Before using any networking functions, call Network::initialize(). When finished, call Network::shutdown().

Network

The Network class provides static methods for network initialization and hostname resolution.

static void Network::initialize(IPVersion ipVersion)

Initializes the networking subsystem. Must be called before any other networking functions. Specify IPVersion::V4 or IPVersion::V6.

static void Network::shutdown()

Shuts down the networking subsystem and releases resources.

static IPAddress Network::resolveHostName(StringView hostName, IPVersion ipVersion)

Resolves a hostname (e.g., "example.com") to an IP address using DNS.

static NetResult Network::lastResult()

Returns the result code from the most recent network operation.

NetResult can take on any of the following values:

Value Description
OK The operation completed successfully.
NoSocket A socket could not be created because a system resource limit was reached.
Unreachable The destination network could not be reached.
Refused The remote host refused the connection.
InUse The requested local address or port is already in use.
NotListening TCPListener::stopListening() was called.
AccessDenied The operation was denied by an operating system security policy.
Unknown An unrecognized or unexpected networking error occurred.

IPAddress

Represents an IP address (either IPv4 or IPv6).

u32 netOrdered[4] The raw address bytes in network byte order. For IPv4, only netOrdered[0] is used.
IPVersion IPAddress::version() const

Returns IPVersion::V4 or IPVersion::V6.

bool IPAddress::isNull() const

Returns true if this is a null/uninitialized address.

static constexpr IPAddress IPAddress::localHost(IPVersion ipVersion)

Returns the localhost address (127.0.0.1 for IPv4, ::1 for IPv6).

static constexpr IPAddress IPAddress::fromIPv4(u32 netOrdered)

Creates an IPv4 address from a 32-bit value in network byte order.

String IPAddress::toString() const

Returns a human-readable string representation of the address.

static IPAddress IPAddress::fromString()

Parses an IP address from a string.

TCPConnection

Represents an established TCP connection to a remote host. Use createInStream() and createOutStream() to obtain a Stream interface.

IPAddress remoteAddr The IP address of the remote host.
u16 remotePort The port number of the remote host.
Owned<Pipe> pipe A bidirectional pipe that handles both sending and receiving data.
static Owned<TCPConnection> TCPConnection::connectTo(const IPAddress& address, u16 port)

Establishes a TCP connection to the specified address and port. Returns null on failure.

Stream TCPConnection::createInStream()

Creates a buffered stream for reading data from the connection.

Stream TCPConnection::createOutStream()

Creates a buffered stream for writing data to the connection.

TCPListener

A TCPListener listens for incoming TCP connections on a specific port.

static Owned<TCPListener> TCPListener::create(const IPAddress& bindAddress, u16 port)

Creates a TCP listener bound to the specified address and port. A null bindAddress listens on every local interface.

bool TCPListener::isListening()

Returns true if stopListening() has not been called.

void TCPListener::stopListening()

Immediately stops accepting new connections on the listening port. Existing connections are not closed. Future calls to accept() return null after this. If called while another thread is waiting inside accept(), the waiting thread will immediately return.

Owned<TCPConnection> TCPListener::accept()

Blocks until either a client connects or stopListening() is called. If a client connects, the new connection is returned.

// Simple echo server
Network::initialize(IPVersion::V4);
Owned<TCPListener> listener = TCPListener::create({}, 8080);

while (true) {
    Owned<TCPConnection> conn = listener->accept();
    if (!conn) break;

    Stream in = conn->createInStream();
    Stream out = conn->createOutStream();

    String line = readLine(in);
    out.write(line);
    out.write("\n");
    out.flush();
}

Network::shutdown();

HTTPClient

HTTPClient is an optional class for making HTTP requests to remote servers. To enable it, set the preprocessor definition PLY_WITH_HTTP_CLIENT=1 in your project's settings. HTTPClient's member functions aren't thread-safe and are meant to be called from a single thread except for HTTPClient::wakeUp. All member functions are designed to return as quickly as possible, so they can be used in an application's main loop without causing frame delays. Requires libcurl to be linked and initialized.

#include <ply-network.h>
#include <curl/curl.h>

using namespace ply;

int main() {
    // Initialize libcurl.
    if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
        return 1;

    // Perform an HTTP request.
    Owned<HTTPClient> client = HTTPClient::create();
    HTTPClient::Args args;
    args.url = "https://plywood.dev";
    args.callback = [](const HTTPClient::Event& event) {
        if (auto* headers = event.as<HTTPClient::Headers>()) {
            getStdOut().format("HTTP status: {}\n", headers->statusCode);
        } else if (auto* data = event.as<HTTPClient::Data>()) {
            getStdOut().write(data->bytes);
        } else if (event.is<HTTPClient::End>()) {
            getStdOut().write("Request complete\n");
        } else if (auto* error = event.as<HTTPClient::Error>()) {
            getStdErr().format("Request failed: {}\n", error->message);
        }
    };
    client->beginRequest(std::move(args));
    while (client->receiveResponse()) {
        // Response data is delivered to `callback` inside receiveResponse().
    }
    client.clear();

    // Shut down libcurl.
    curl_global_cleanup();
    return 0;
}
static Owned<HTTPClient> HTTPClient::create()

Creates a new HTTPClient.

void HTTPClient::destroy(HTTPClient* httpClient)

Destroys an HTTPClient. Any in-progress request is immediately canceled.

void HTTPClient::beginRequest(Args&& args)

Starts a new request. Must not be called while a request is already in progress. HTTPClient::Args has the following data members. All members are moved from when the function is called, leaving the original args in an empty state.

String url The URL to request.
Map<String, String> headers HTTP headers to send with the request.
String body The request body.
Functor<void(const Event&)> callback Receives response events.
bool useBundledCaCert If true, verifies the peer using the bundled CA certificates. If false, disables TLS verification and should only be used for trusted localhost endpoints. Default is true.
void HTTPClient::cancelRequest()

Cancels any request in progress. After this returns, isRequestInProgress() returns false. It's safe to call beginRequest() on the same HTTPClient again after this.

bool HTTPClient::isRequestInProgress() const

Returns true while a request is in progress.

bool HTTPClient::receiveResponse(u32 timeOutMillis)

Drives the request, delivering response events to the request's callback. If an event is available, it invokes the callback and returns immediately. If no data is available, it waits up to timeOutMillis for data to arrive, but can be interrupted by other threads calling wakeUp(). When timeOutMillis is 0, returns as soon as all available data is processed. Returns false if no request is in progress; true otherwise.

void HTTPClient::wakeUp()

Can be called from any thread. If receiveResponse() is currently blocked in another thread, it returns immediately; otherwise the next receiveResponse() call returns immediately.

HTTPClient::Event

The HTTPClient::Event object received by the response callback is a variant with the following subtypes:

HTTPClient::Headers Contains the HTTP status code and response headers. Header keys are stored in lowercase.
HTTPClient::Data Contains the next response body chunk. Its bytes member is only valid for the duration of the callback.
HTTPClient::End Indicates that the HTTP transfer completed successfully. HTTP error status codes are still successful transfers.
HTTPClient::Error Contains a libcurl error message and terminates the event stream. It is never followed by End.

A successful request produces Headers, zero or more Data events and one End event. A transport failure produces Error, possibly after Headers and Data events were already delivered. cancelRequest() does not generate an event.

HTTPServer

HTTPServer is an optional class for running a local HTTP server. To enable it, set the preprocessor definition PLY_WITH_HTTP_SERVER=1 in your projects's settings. No encryption capabilities are provided.

#include <ply-network.h>

using namespace ply;

void serverCallback(HTTPServer::Request& request) {
    HTTPServer::Response response{HTTPServer::Response::OK};
    *response.headers.insert("content-type").value = "text/plain";
    String body = String::format("Request URI: {}\n", request.uri);
    request.sendFullResponse(std::move(response), body);
}

int main() {
    // Initialize the network.
    Network::initialize(IPv4);

    // Run a webserver.
    HTTPServer::run({}, 8080, serverCallback);

    // Shut down the network.
    Network::shutdown();
    return 0;
}
static void HTTPServer::run(const IPAddress& bindAddress, u16 port, const Functor<void(Request& request)>& requestHandler)

When this function is called, the calling thread is blocked for as long as the server keeps running. Network::initialize() must be called first. requestHandler is a user-provided callback function that handles individual HTTP requests. A null bindAddress listens on every local interface.

HTTPServer::Request

For each incoming HTTP request, the requestHandler is called with an HTTPServer::Request object, which exposes the following public data members and member functions:

IPAddress clientAddr The remote TCP peer address.
u16 clientPort The remote TCP peer port.
String method The request method, such as GET, HEAD, POST, PUT or PATCH.
String uri The raw request URI.
String httpVersion The HTTP version token, such as HTTP/1.1.
Map<String, String> headers Request headers indexed by lower-case header name. For example, keys contain "content-type" rather than "Content-Type".
String body The complete request body. This string can contain arbitrary binary data and is not guaranteed to be null-terminated.
void HTTPServer::Request::sendFullResponse(HTTPServer::Response&& response, StringView body)

Sends a complete response. The response argument is moved from, leaving the original argument in an empty state. The underlying connection can be reused for additional requests when HTTP rules permit it.

Stream HTTPServer::Request::beginStreamingResponse(HTTPServer::Response&& response)

Sends response headers only and returns a TCP stream for writing the body. The connection is closed when the caller destroys the stream.

void HTTPServer::Request::sendGenericResponse(u32 responseCode)

Writes a generic HTML error page with the given status code.

HTTPServer::Response

HTTPServer::Response is used as an argument to HTTPServer::Request member functions and has the following public data members:

u32 code The HTTP status code to emit. Default is 500 (InternalError).
Map<String, String> headers Response headers to emit.

HTTPServer::Response exposes constants for the following status codes:

Response::OK 200
Response::PermanentRedirect 301
Response::TemporaryRedirect 302
Response::BadRequest 400
Response::Unauthorized 401
Response::Forbidden 403
Response::NotFound 404
Response::MethodNotAllowed 405
Response::RequestTimeout 408
Response::Conflict 409
Response::UnprocessableContent 422
Response::TooManyRequests 429
Response::InternalError 500
Response::BadGateway 502
Response::ServiceUnavailable 503
Response::GatewayTimeout 504