Transports
This page explains the transport layer used by Vix Core sessions.
Use it when you want to understand how the HTTP session reads and writes bytes without depending directly on plain TCP or TLS.
Public header
#include <vix.hpp>You can also include the transport headers directly:
#include <vix/session/Transport.hpp>
#include <vix/session/PlainTransport.hpp>
#include <vix/session/TlsTransport.hpp>What transports provide
A transport is the byte I/O abstraction used by the HTTP session.
It provides a common interface for:
- reading bytes
- writing bytes
- checking whether the connection is open
- closing the connection
This lets the HTTP session work with both plain HTTP and HTTPS without duplicating HTTP parsing logic.
Session
-> Transport
-> PlainTransport
-> TlsTransportWhy transports exist
The HTTP session should not care whether bytes come from a plain TCP stream or an encrypted TLS stream.
The session only needs to know:
read bytes
write bytes
check open
closeSo Vix uses a shared transport interface.
Plain HTTP
-> PlainTransport
-> Session
HTTPS
-> TlsTransport
-> SessionAfter the transport is ready, the same Session logic handles HTTP parsing, routing, and response writing.
Transport interface
The core interface is:
class Transport
{
public:
virtual task<std::size_t> async_read(
std::span<std::byte> buffer,
cancel_token token) = 0;
virtual task<std::size_t> async_write(
std::span<const std::byte> buffer,
cancel_token token) = 0;
virtual bool is_open() const noexcept = 0;
virtual void close() noexcept = 0;
};The interface is asynchronous and uses vix::async::core::task.
Transport methods
| Method | Purpose |
|---|---|
async_read(buffer, token) | Read bytes into a buffer. |
async_write(buffer, token) | Write bytes from a buffer. |
is_open() | Return whether the underlying connection is open. |
close() | Close the transport safely. |
Transport and async
Transports use vix::async.
That means read and write operations return async tasks.
task<std::size_t> async_read(...);
task<std::size_t> async_write(...);The session can then do:
const std::size_t n = co_await transport.async_read(buffer, token);and:
co_await transport.async_write(buffer, token);This keeps I/O non-blocking from the session point of view.
PlainTransport
PlainTransport adapts a native Vix TCP stream to the generic Transport interface.
tcp_stream
-> PlainTransport
-> Transport
-> SessionIt is used for normal HTTP connections without TLS.
Plain transport flow
For a plain HTTP connection:
HTTPServer accepts tcp_stream
-> creates Session
-> Session creates PlainTransport
-> Session reads HTTP bytes
-> Session writes HTTP responseSimplified flow:
client
-> TCP
-> PlainTransport
-> Session
-> Router
-> Handler
-> Response
-> PlainTransport
-> TCP
-> clientPlainTransport read
PlainTransport::async_read(...) delegates to the underlying TCP stream.
PlainTransport::async_read
-> tcp_stream::async_readThe session does not need to know that the transport is backed by TCP.
PlainTransport write
PlainTransport::async_write(...) delegates to the underlying TCP stream.
PlainTransport::async_write
-> tcp_stream::async_writeThe session passes serialized HTTP bytes to the transport.
PlainTransport close
PlainTransport::close() closes the underlying TCP stream.
The close operation is best-effort.
If closing throws internally, the transport ignores the exception.
This makes shutdown paths safer.
TlsTransport
TlsTransport adapts a native Vix TCP stream into an encrypted transport.
tcp_stream
-> TlsTransport
-> TLS handshake
-> Transport
-> SessionIt is used for HTTPS connections.
TLS transport flow
For HTTPS:
HTTPServer accepts tcp_stream
-> creates TlsSession
-> creates TlsTransport
-> performs TLS handshake
-> creates Session with TLS transport
-> Session reads decrypted HTTP bytes
-> Session writes encrypted response bytesAfter the handshake, the HTTP session works the same as it does for plain HTTP.
TLS handshake
TlsTransport must complete the TLS server handshake before the session reads HTTP requests.
co_await transport->async_handshake();Conceptually:
accepted TCP stream
-> TLS context
-> certificate
-> private key
-> SSL_accept
-> encrypted connection readyIf the handshake fails, the connection is closed and the session does not continue.
TLS read
After the handshake, reads return decrypted bytes.
TLS encrypted bytes
-> TlsTransport::async_read
-> decrypted HTTP bytes
-> Session parserFrom the session point of view, this looks like a normal read.
const std::size_t n = co_await transport.async_read(buffer, token);TLS write
Writes accept normal plaintext HTTP response bytes.
Session response bytes
-> TlsTransport::async_write
-> encrypted TLS bytes
-> TCP streamThe session does not need to perform encryption itself.
TLS close
TlsTransport::close() closes both the TLS connection state and the underlying TCP stream.
The close path is best-effort.
If TLS shutdown or socket close fails, the transport still releases resources safely.
Transport and Session
The session owns a transport.
Session
-> std::unique_ptr<Transport>This allows the session to use dynamic dispatch without knowing the concrete transport type.
Session::read_header_block
-> transport.async_read
Session::send_response
-> transport.async_write
Session::close_stream_gracefully
-> transport.closeSession constructors
A session can be constructed from a TCP stream.
Session(tcp_stream)
-> creates PlainTransport internallyOr it can be constructed from a generic transport.
Session(Transport)
-> uses provided transportThis second form is used by TLS.
TlsSession
-> creates TlsTransport
-> creates Session(TlsTransport)Plain vs TLS
The important difference is where encryption happens.
PlainTransport
-> reads and writes raw TCP bytes
TlsTransport
-> reads decrypted bytes
-> writes encrypted bytesThe session sees both as:
TransportSo HTTP parsing stays shared.
Transport and cancellation
Transport reads and writes accept a cancellation token.
cancel_token tokenThis is used for:
- request timeouts
- session shutdown
- server shutdown
- cancelled operations
Example shape:
co_await transport.async_read(buffer, timer_cancel_.token());If cancellation is requested, the operation can stop.
Transport and timeouts
The session starts a timer for request or response work.
If the timeout fires, the session can cancel the token and close the connection.
start timer
-> transport read/write
-> timeout fires
-> cancel token
-> close transportThis keeps slow connections from holding the session forever.
Transport and keep-alive
The transport remains open while the session handles keep-alive requests.
request 1
-> response 1
request 2
-> response 2
request 3
-> response 3When the connection closes, the transport closes.
Connection: close
-> Session closes TransportTransport and errors
Transport errors are handled by the session.
Common normal disconnects include:
EOF
connection reset
broken pipe
operation canceled
timeoutThe session can treat these as normal connection lifecycle events.
Unexpected read or write failures can be logged and cause the connection to close.
Transport architecture
The simplified architecture is:
HTTPServer
-> accepts tcp_stream
Plain HTTP:
-> Session
-> PlainTransport
-> tcp_stream
HTTPS:
-> TlsSession
-> TlsTransport
-> TLS handshake
-> Session
-> TransportThe shared part is:
Session
-> read request
-> router
-> handler
-> write responsePlain HTTP example
Application code does not create the transport directly.
#include <vix.hpp>
int main()
{
vix::App app;
app.get("/", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.text("plain HTTP");
});
app.run(8080);
return 0;
}Flow:
client
-> HTTPServer
-> Session
-> PlainTransport
-> handlerHTTPS example
Application code stays almost the same.
#include <vix.hpp>
int main()
{
vix::config::Config cfg;
cfg.setServerPort(8443);
cfg.set("server.tls.enabled", true);
cfg.set("server.tls.cert_file", "/etc/letsencrypt/live/example.com/fullchain.pem");
cfg.set("server.tls.key_file", "/etc/letsencrypt/live/example.com/privkey.pem");
vix::App app;
app.get("/", [](vix::Request &req, vix::Response &res)
{
(void)req;
res.text("secure HTTP");
});
app.run(cfg);
return 0;
}Flow:
client
-> HTTPServer
-> TlsSession
-> TlsTransport
-> Session
-> handlerTransport abstraction benefit
The transport abstraction keeps the HTTP session simple.
Without transport abstraction:
Session must know plain TCP
Session must know TLS
Session must duplicate read/write logicWith transport abstraction:
Session only knows Transport
PlainTransport handles TCP
TlsTransport handles TLSThis keeps Core easier to maintain and extend.
Future transport extensions
The same interface can support more transports later.
Possible examples:
Unix domain socket transport
in-memory test transport
custom encrypted transport
proxy transport
instrumented transportThe session would not need to change as long as the transport implements:
async_read
async_write
is_open
closeTesting with transports
Because the session depends on Transport, advanced tests can provide a custom transport.
A test transport could simulate:
- request bytes
- partial reads
- write failures
- disconnects
- timeouts
- malformed requests
This makes the session easier to test independently from real sockets.
Transport lifecycle
The lifecycle is:
construct transport
-> read/write while open
-> close on error, timeout, shutdown, or connection close
-> destroy transportFor TLS:
construct TlsTransport
-> TLS handshake
-> read/write while open
-> TLS shutdown and socket close
-> destroy transportComplete architecture flow
App
-> HTTPServer
-> accepts tcp_stream
Plain HTTP:
-> Session
-> PlainTransport
-> tcp_stream
HTTPS:
-> TlsSession
-> TlsTransport
-> TLS handshake
-> Session
-> Transport
Both:
-> read request
-> Router
-> RequestHandler
-> Response
-> write responseAPI summary
| API | Purpose |
|---|---|
Transport::async_read(buffer, token) | Read bytes from the connection. |
Transport::async_write(buffer, token) | Write bytes to the connection. |
Transport::is_open() | Check whether the connection is open. |
Transport::close() | Close the connection. |
PlainTransport(tcp_stream) | Adapt a TCP stream to Transport. |
TlsTransport(tcp_stream, config) | Adapt a TCP stream to encrypted Transport. |
TlsTransport::async_handshake(token) | Perform the TLS server handshake. |
Best practices
Use vix::App for normal application code.
vix::App app;
app.run(8080);Do not create transports manually unless you are extending Core, writing tests, or building advanced integrations.
Use static_dir, routes, handlers, and templates for application behavior.
app.get("/", handler);
app.static_dir("public", "/assets");Use TLS configuration to switch from plain transport to TLS transport.
cfg.set("server.tls.enabled", true);Keep HTTP logic independent from the transport type.
Handler code should not care whether the request came through HTTP or HTTPS.Next steps
Read the next pages: