Appearance
Networking & WebSockets
Vulpis features a fully asynchronous, non-blocking networking stack. This ensures that making HTTP requests or listening to real-time WebSocket streams will never freeze or stutter your user interface.
The engine provides three layers of networking APIs:
- HTTP Client: For standard REST requests (GET, POST, etc.).
- WebSocket Wrapper (
ws.lua): A high-level, developer-friendly Lua module for real-time data. - Native WebSockets: The raw C++ bindings powering the wrapper.
1. HTTP Client (vulpis.fetch)
The vulpis.fetch API allows you to make asynchronous HTTP requests. It supports all standard HTTP methods, custom headers, and timeouts.
Because the request runs on a background C++ thread, you must provide a callback function to handle the response once it completes.
Function Signature
lua
vulpis.fetch(url, [options], callback)Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The destination URL (must include http:// or https://). |
options | table | No | Configuration table (method, headers, body, timeout). |
callback | function | Yes | Function called when the request finishes or fails. |
The options Table
| Field | Type | Default | Description |
|---|---|---|---|
method | string | "GET" | "GET", "POST", "PUT", "DELETE", or "PATCH". |
timeout | integer | 10000 | Max time to wait in milliseconds (default is 10 seconds). |
body | string | "" | The payload for POST/PUT requests (usually a JSON string). |
headers | table | {} | Key-value pairs for HTTP headers. |
The Response Object
The callback receives a single res table containing:
res.status(integer): The HTTP status code (e.g.,200for OK,404for Not Found).res.body(string): The raw string response from the server.res.error(string): An error message if the request failed at the network level (e.g., "Connection Refused").
Example: Fetching & Posting Data
This example demonstrates how to fetch data and how to send a JSON payload.
lua
local el = require("utils.core.elements")
local json = require("utils.core.json")
function App()
local apiStatus = useState("api_status", "Idle")
local apiData = useState("api_data", "")
return el.VBox({
style = { padding = 20, gap = 15, BGColor = "#1e1e1e" },
children = {
el.Text("Network Status: " .. apiStatus, { color = "#4CAF50" }, { allowSelection = true }),
el.Text(apiData, { color = "#A1A1AA" }),
el.HBox({
style = { gap = 10 },
children = {
-- Example 1: Standard GET Request
el.Button({
text = "GET Request",
onClick = function()
setState("api_status", "Fetching...")
vulpis.fetch("https://jsonplaceholder.typicode.com/todos/1", function(res)
if res.status == 200 then
setState("api_status", "Success")
setState("api_data", res.body)
else
setState("api_status", "Error: " .. res.error)
end
end)
end,
}),
-- Example 2: POST Request with JSON
el.Button({
text = "POST Request",
style = { BGColor = "#2196F3" },
onClick = function()
setState("api_status", "Sending...")
local payload = json.encode({ title = "Vulpis Post", userId = 1 })
vulpis.fetch("https://jsonplaceholder.typicode.com/posts", {
method = "POST",
headers = { ["Content-Type"] = "application/json" },
body = payload,
}, function(res)
setState("api_status", "POST returned Status: " .. tostring(res.status))
setState("api_data", res.body)
end)
end,
}),
},
}),
},
})
end2. WebSocket Wrapper (ws.lua)
For real-time, bi-directional communication (like chat apps or live game servers), you should use the utils.core.ws wrapper. It provides an object-oriented interface over the raw C++ bindings and automatically encodes Lua tables into JSON when sending messages.
Setup
lua
local ws = require("utils.core.ws")ws.connect(url, onEvent)
Creates a new WebSocket connection.
url: The WebSocket endpoint (e.g.,"wss://echo.websocket.events").onEvent: A callback function that triggers whenever the connection opens, receives a message, encounters an error, or closes.
Returns: A Connection object.
The Connection Object
conn:send(message): Sends data to the server. Ifmessageis a Lua table, it is automatically converted to a JSON string.conn:close(): Safely terminates the connection.
The Event Object
The onEvent callback receives a table with the following structure:
event.type: A string representing the event state ("open","message","error","close").event.data: The payload or error message string.
Example: Live Chat / Echo Client
lua
local el = require("utils.core.elements")
local ws = require("utils.core.ws")
local myConnection = nil
function App()
local serverStatus = useState("ws_status", "Disconnected")
return el.VBox({
style = { gap = 20, padding = 20 },
children = {
el.Text("Server says: " .. serverStatus),
el.Button({
text = "1. Connect",
onClick = function()
setState("ws_status", "Connecting...")
-- Open the connection
myConnection = ws.connect("wss://echo.websocket.org", function(event)
-- The server will trigger this function whenever something happens
if event.type == "open" then
setState("ws_status", "Connected! Ready to send.")
elseif event.type == "message" then
-- We got a reply from the server!
setState("ws_status", "Message received: " .. event.data)
end
end)
end,
}),
el.Button({
text = "2. Send 'Hello'",
onClick = function()
-- Make sure we are actually connected first!
if myConnection then
myConnection:send("Hello from Vulpis!")
setState("ws_status", "Message sent, waiting for reply...")
else
setState("ws_status", "You need to connect first!")
end
end,
}),
},
})
end3. Native WebSockets (C++ Bindings)
If you are building your own custom networking architecture and want to bypass the ws.lua wrapper, you can interface directly with the native C++ endpoints exposed in the vulpis namespace.
These functions manage connections using raw integer IDs.
| Function | Description |
|---|---|
vulpis.wsConnect(url, callback) | Opens a connection and returns an integer ID. The callback receives a table with type and data. |
vulpis.wsSend(id, string_msg) | Sends a raw string to the specified connection ID. Returns true on success. |
vulpis.wsClose(id) | Terminates the specified connection ID. |
Example: Raw Binding Usage
lua
-- 1. Connect and store the raw ID
local connectionId = vulpis.wsConnect("wss://[example.com/socket](https://example.com/socket)", function(ev)
if ev.type == "message" then
print("Raw payload received: " .. ev.data)
end
end)
-- 2. Send a raw string (You must handle JSON encoding manually)
local success = vulpis.wsSend(connectionId, '{"auth": "token_123"}')
-- 3. Close the socket
vulpis.wsClose(connectionId)