On this page
tecs.io.http
Cooperative HTTP and HTTPS.
local client <const> = tecs.io.http.newClient({userAgent = "mygame/1.0"})
local response <const> = client:send({
url = tecs.io.URI.new("https://example.com/manifest.json"),
})
print(response.status)
print(assert(response.body:readAll(1024 * 1024)))send returns when status and headers exist. Inside a system it suspends only until that boundary; outside an update it blocks its caller. The body is a one-shot streaming Reader backed by bounded native chunks, so a slow consumer applies transport backpressure instead of accumulating the full response. Arbitrary streaming request bodies run in client-owned cooperative work, so their Reader may itself wait on a socket, process, transform, or HTTP response without blocking the SDL pump. On the SDL storage backend, a file stream is opened and read directly by Tokio instead. It stays bounded without retaining the complete file or crossing Lua for every chunk.
An HTTP error status still returns a response. DNS, connection, TLS, timeout, or streaming failures raise at the suspended call.
Raw tecs.io sockets use the process-wide mio readiness reactor. HTTP deliberately uses Reqwest and Tokio for its connection pool, TLS, redirects, and protocol work. Both implementations resume the same direct Lua call, and neither parks one worker thread per waiting socket.
A request holds one of its client's maxConnections slots until its response body ends, so close or discard a body you do not intend to read.
Call Client:close when you no longer need its connection pool. Losing the last Lua reference does not cancel its requests. Application shutdown closes any remaining clients and drains their internal upload tasks to settlement, within a bounded number of scheduler steps that it logs whenever it has to abandon work. An Application owns progress; a direct headless call drives the client while it blocks.
Module contents
Constructors
| Constructor | Description |
|---|---|
newClient |
Builds a client. |
Types
| Type | Kind | Description |
|---|---|---|
Client |
interface | A Client owns one connection pool and its active requests. |
ClientOptions |
record | Settings copied when an HTTP client is built. |
plugin |
record | Read-only. plugin exposes requests and responses as ECS components. |
Request |
record | Request describes one request and requires only url. |
Response |
record | Response describes status, headers, and a progressive response body. |
Functions
| Function | Kind | Description |
|---|---|---|
getOpenClientCount |
Static | Returns the open-client count. |
Constructors
tecs.io.http.newClient Static
Builds a client.
function tecs.io.http.newClient(options: ClientOptions): ClientArguments
| Name | Type | Description |
|---|---|---|
options |
ClientOptions |
The caller supplies client defaults or omits this record to use every default. Each insecureHosts entry logs a warning. |
Returns
| Type | Description |
|---|---|
Client |
Returns an open client that remains active until close. |
Examples
local http <const> = tecs.io.http
local endpoint, reason = tecs.io.URI.new("https://example.com/status")
if endpoint == nil then
error(reason)
end
local client <const> = http.newClient({
userAgent = "mygame/1.0",
timeoutMs = 10000,
maxBytes = 1024 * 1024,
})
local response <const> = client:send({url = endpoint})
print(response.status, response.url:host())
response.body:close()
client:close()Types
tecs.io.http.Client interface
A Client owns one connection pool and its active requests.
Closing the client cancels every pending request, releases the pool, and remains safe to repeat. The Application drives active requests.
interface tecs.io.http.Client is Closeable
pending: function(self): integer
send: function(self, request: Request): Response
endInterfaces
| Interface |
|---|
Closeable |
tecs.io.http.Client:pending Instance
Returns the number of unsettled requests.
function tecs.io.http.Client.pending(self): integerArguments
| Name | Type | Description |
|---|---|---|
self |
Client |
The client to inspect. |
Returns
| Type | Description |
|---|---|
integer |
Returns zero after every request settles or is canceled. |
tecs.io.http.Client:send Instance
Sends one HTTP request and returns its response.
A transport failure raises. An HTTP error status returns normally with that status in the response. Inside a system, the call suspends the world update only while the transfer is pending.
function tecs.io.http.Client.send(self, request: Request): ResponseArguments
| Name | Type | Description |
|---|---|---|
self |
Client |
The open client that owns the request. |
request |
Request |
The caller supplies the URL and optional method, headers, upload body, timeouts, and byte limit. |
Returns
| Type | Description |
|---|---|
Response |
Returns status, headers, and a one-shot streaming body. |
tecs.io.http.ClientOptions record
Settings copied when an HTTP client is built.
userAgent and headers set request defaults. timeoutMs defaults to 30000, connectTimeoutMs to 10000, and stallTimeoutMs to zero, which disables the no-progress timeout. maxRedirects defaults to five, maxConnections to 16, and maxConnectionsPerHost to six; a request holds one slot of each until its response body ends. maxBytes defaults to zero for no response limit. compressed defaults to true.
insecureHosts disables certificate verification for named hosts and logs a warning for each. proxy accepts the textual spelling of an absolute URI, follows environment variables when nil, and forces a direct connection when empty. noProxy uses Reqwest's exclusion syntax, and proxyCredentials uses user:password.
record tecs.io.http.ClientOptions
userAgent: string
headers: {string: string}
timeoutMs: number
connectTimeoutMs: number
stallTimeoutMs: number
maxRedirects: integer
maxConnections: integer
maxConnectionsPerHost: integer
maxBytes: integer
compressed: boolean
insecureHosts: {string}
proxy: string
noProxy: string
proxyCredentials: string
endtecs.io.http.ClientOptions.userAgent field
Caller-writable. Sets User-Agent on every request.
tecs.io.http.ClientOptions.userAgent: stringtecs.io.http.ClientOptions.headers field
Caller-writable. Sets headers sent on every request. Per-request headers override these without regard to case.
tecs.io.http.ClientOptions.headers: {string: string}tecs.io.http.ClientOptions.timeoutMs field
Caller-writable. Sets positive milliseconds allowed for a whole transfer. Defaults to 30000.
tecs.io.http.ClientOptions.timeoutMs: numbertecs.io.http.ClientOptions.connectTimeoutMs field
Caller-writable. Sets positive milliseconds allowed to establish a connection. Defaults to 10000.
tecs.io.http.ClientOptions.connectTimeoutMs: numbertecs.io.http.ClientOptions.stallTimeoutMs field
Caller-writable. Sets milliseconds a response may make no progress. Zero disables this timeout.
tecs.io.http.ClientOptions.stallTimeoutMs: numbertecs.io.http.ClientOptions.maxRedirects field
Caller-writable. Sets redirects followed. Defaults to five; zero follows none.
tecs.io.http.ClientOptions.maxRedirects: integertecs.io.http.ClientOptions.maxConnections field
Caller-writable. Sets requests allowed to use sockets at once. Defaults to 16.
A request holds its slot from the moment it acquires one until its response body ends, because the socket stays open for that whole time and this limit is what bounds open sockets. A caller that reads a body slowly therefore keeps its slot, and a caller that never reads one keeps it until timeoutMs expires. Close or discard a body you do not intend to read, and raise this limit rather than expecting an unread body to release its slot early.
tecs.io.http.ClientOptions.maxConnections: integertecs.io.http.ClientOptions.maxConnectionsPerHost field
Caller-writable. Sets requests allowed to use sockets to one host. Defaults to six. A request holds its host slot for as long as it holds a maxConnections slot.
tecs.io.http.ClientOptions.maxConnectionsPerHost: integertecs.io.http.ClientOptions.maxBytes field
Caller-writable. Sets maximum response-body bytes. Zero is unbounded.
tecs.io.http.ClientOptions.maxBytes: integertecs.io.http.ClientOptions.compressed field
Caller-writable. Accepts gzip and deflate response compression when true. Defaults to true.
tecs.io.http.ClientOptions.compressed: booleantecs.io.http.ClientOptions.insecureHosts field
Caller-writable. Lists host names whose TLS certificates are not verified.
tecs.io.http.ClientOptions.insecureHosts: {string}tecs.io.http.ClientOptions.proxy field
Caller-writable. Sets the textual spelling of an absolute proxy URI. Nil follows the environment; an empty string forces a direct connection. This option accepts a string, not a URI object.
tecs.io.http.ClientOptions.proxy: stringtecs.io.http.ClientOptions.noProxy field
Caller-writable. Lists hosts excluded from an explicitly configured proxy.
tecs.io.http.ClientOptions.noProxy: stringtecs.io.http.ClientOptions.proxyCredentials field
Caller-writable. Sets proxy credentials in user:password form.
tecs.io.http.ClientOptions.proxyCredentials: stringtecs.io.http.plugin record
Read-only. plugin exposes requests and responses as ECS components.
record tecs.io.http.plugin
record Request is Component
url: URI
method: string
headers: {string: string}
body: string | ReadableStream
timeoutMs: number
stallTimeoutMs: number
maxBytes: integer
end
record Response is Component
status: integer
headers: {string: string}
body: Stream
url: URI
error: string
end
record Pending is Component
end
clientOf: function(World): Client
close: function(World)
install: function(World, Options)
endtecs.io.http.plugin.Request record
What a game spawns to make a request. The fields of a Request, because there is no second vocabulary for the same thing.
record tecs.io.http.plugin.Request is Component
url: URI
method: string
headers: {string: string}
body: string | ReadableStream
timeoutMs: number
stallTimeoutMs: number
maxBytes: integer
endInterfaces
| Interface |
|---|
Component |
tecs.io.http.plugin.Request.url field
Caller-writable. Sets an absolute HTTP or HTTPS URI.
tecs.io.http.plugin.Request.method field
Caller-writable. Sets the method. Defaults to "GET".
tecs.io.http.plugin.Request.headers field
Caller-writable. Sets headers merged over the client's defaults without regard to case. A Content-Length must contain decimal digits and must match a body whose length is known.
tecs.io.http.plugin.Request.body field
Caller-writable. Sets what to send. A snapshot rejects a tecs.io.newHandleStream here because its live handle cannot be reconstructed.
tecs.io.http.plugin.Request.body: string | ReadableStreamtecs.io.http.plugin.Request.timeoutMs field
Caller-writable. Sets milliseconds for this transfer, overriding the client's value.
tecs.io.http.plugin.Request.stallTimeoutMs field
Caller-writable. Sets tolerated milliseconds without progress, overriding the client's value.
tecs.io.http.plugin.Request.stallTimeoutMs: numbertecs.io.http.plugin.Request.maxBytes field
Caller-writable. Sets the body byte limit, overriding the client's value.
tecs.io.http.plugin.Response record
What replaces a Request once the transfer settles.
Present whatever happened, because "the request finished" is the event a system waits for and a failure is one of the ways it can finish. error is what says which.
record tecs.io.http.plugin.Response is Component
status: integer
headers: {string: string}
body: Stream
url: URI
error: string
endInterfaces
| Interface |
|---|
Component |
tecs.io.http.plugin.Response.status field
Engine-owned. Reports the HTTP status code, or zero when the transfer never received one.
tecs.io.http.plugin.Response.headers field
Engine-owned. Reports response headers with lower-cased names, in the joined form Response.headers uses. A component is one value per name, so a repeated set-cookie keeps only its first value here; a system that needs every cookie sends its request through a client and reads Response:getAll.
tecs.io.http.plugin.Response.body field
Engine-owned. Provides a one-shot progressive response body. Despawning this entity closes a body that remains unread.
tecs.io.http.plugin.Response.url field
Engine-owned. Reports the URI that actually answered, or nil when the request failed before it supplied a valid URI.
tecs.io.http.plugin.Response.error field
Engine-owned. Reports why the transfer did not complete, or nil when it did. A 404 is not one of these: it is a status of 404 and no error.
tecs.io.http.plugin.Pending record
On an entity whose request is in flight. Internal, and the reason a request is sent once rather than every frame.
A marker, while the callback and transfer state remain in the plugin. It is transient because runtime transport state cannot be saved. A save taken mid-request keeps the Request, drops this marker, and sends the request again after loading.
record tecs.io.http.plugin.Pending is Component
endInterfaces
| Interface |
|---|
Component |
tecs.io.http.plugin.clientOf Static
The world's client, or nil when the plugin is not installed.
Arguments
| Name | Type | Description |
|---|---|---|
#1 |
World |
Returns
| Type | Description |
|---|---|
Client |
tecs.io.http.plugin.close Static
Closes the world's client and forgets it.
function tecs.io.http.plugin.close(World)Arguments
| Name | Type | Description |
|---|---|---|
#1 |
World |
Returns
None.
tecs.io.http.plugin.install Static
Installs the plugin: world:addPlugin(tecs.io.http.plugin.install).
Takes options because the plugin builds the world's client, and a game that wants a userAgent on it has nowhere else to say so: world:addPlugin(function(w) http.plugin.install(w, {...}) end).
function tecs.io.http.plugin.install(World, Options)Arguments
| Name | Type | Description |
|---|---|---|
#1 |
World |
|
#2 |
Options |
Returns
None.
tecs.io.http.Request record
Request describes one request and requires only url.
record tecs.io.http.Request
url: URI
method: string
headers: {string: string}
body: string | iotypes.ReadableStream
timeoutMs: number
stallTimeoutMs: number
maxBytes: integer
endtecs.io.http.Request.url field
Caller-writable. Sets an absolute URL with an http or https scheme.
tecs.io.http.Request.method field
Caller-writable. Sets the method. Defaults to GET.
tecs.io.http.Request.headers field
Caller-writable. Sets headers merged over the client's defaults. A Content-Length must contain decimal digits and must match a body whose length is known.
tecs.io.http.Request.body field
Caller-writable. Sets bytes to send. A general stream opens one reader and feeds a bounded upload queue. On the SDL storage backend, a file stream is opened and read directly by the native HTTP lane. The client owns cooperative work that waits on a composed reader.
tecs.io.http.Request.body: string | iotypes.ReadableStreamtecs.io.http.Request.timeoutMs field
Caller-writable. Overrides the client's positive whole-transfer timeout.
tecs.io.http.Request.stallTimeoutMs field
Caller-writable. Overrides the client's no-progress timeout.
tecs.io.http.Request.stallTimeoutMs: numbertecs.io.http.Request.maxBytes field
Caller-writable. Overrides the client's body limit. Zero is unbounded.
tecs.io.http.Response record
Response describes status, headers, and a progressive response body. headers joins a repeated header's values, and Response:getAll reports them one by one, which is what set-cookie needs.
record tecs.io.http.Response
status: integer
headers: {string: string}
body: iotypes.Stream
url: URI
getAll: function(self, name: string): {string}
ok: function(self): boolean
endtecs.io.http.Response.status field
Read-only. Reports the status after redirects.
tecs.io.http.Response.headers field
Read-only. Reports final response headers with lower-cased names. A name the server sent more than once holds its values joined with ", ", which RFC 9110 permits for every field except Set-Cookie. A repeated set-cookie holds only the first value here, because joining cookies changes what they mean. Response:getAll reports every value of any name.
tecs.io.http.Response.body field
Read-only. Provides a one-shot progressive response body. Reading may suspend a system or block a direct caller until bytes arrive. The caller must consume, discard, or close this owned stream.
tecs.io.http.Response.url field
Read-only. Reports the effective URL after redirects.
tecs.io.http.Response:getAll Instance
Returns every value the server sent for one header name.
This is the accessor for a repeated header, and the only correct way to read set-cookie, which cannot be joined.
function tecs.io.http.Response.getAll(self, name: string): {string}Arguments
| Name | Type | Description |
|---|---|---|
self |
Response |
The response to read. |
name |
string |
The caller supplies a header name, and the lookup ignores case. |
Returns
| Type | Description |
|---|---|
{string} |
Returns a new array in the order the server sent the values, or an empty array when the response has no such header. The array belongs to the caller. |
tecs.io.http.Response:ok Instance
Whether the status is in the 2xx range.
function tecs.io.http.Response.ok(self): booleanArguments
| Name | Type | Description |
|---|---|---|
self |
Response |
Returns
| Type | Description |
|---|---|
boolean |
Functions
tecs.io.http.getOpenClientCount Static
Returns the open-client count.
function tecs.io.http.getOpenClientCount(): integerArguments
None.
Returns
| Type | Description |
|---|---|
integer |
Returns the process-wide count of clients not yet closed. |