Examples cookbook
End-to-end snippets you can paste into a .js file and run with
qjs -I path/to/build script.js. They use only the C-side API
documented in module.md; the higher-level wrappers
under lib/ are covered in ../js/helpers.md.
1. WebSocket echo server
import { createServer, LWSMPRO_NO_MOUNT } from 'lws';
createServer({
port: 8080,
vhostName: 'localhost',
mounts: [{ mountpoint: '/echo', protocol: 'echo', originProtocol: LWSMPRO_NO_MOUNT }],
protocols: [{
name: 'echo',
onReceive(wsi, data) { wsi.write(data); },
}],
});
console.log('listening on ws://localhost:8080/echo');2. WebSocket broadcast
import { createServer, LWSMPRO_NO_MOUNT, LWS_WRITE_TEXT, toString } from 'lws';
const clients = new Set();
createServer({
port: 8080,
vhostName: 'localhost',
mounts: [{ mountpoint: '/chat', protocol: 'chat', originProtocol: LWSMPRO_NO_MOUNT }],
protocols: [{
name: 'chat',
onEstablished(wsi) { clients.add(wsi); },
onClosed(wsi) { clients.delete(wsi); },
onReceive(wsi, msg) {
const text = typeof msg === 'string' ? msg : toString(msg);
for(const c of clients) c.write(text, LWS_WRITE_TEXT);
},
}],
});3. WebSocket client
import { LWSContext, toString } from 'lws';
const ctx = new LWSContext({
protocols: [{
name: 'ws',
onClientEstablished(wsi) { wsi.write('ping'); },
onClientReceive(wsi, data) { console.log('got', toString(data)); wsi.close(); },
onClientClosed(wsi) { ctx.cancelService(); },
onClientConnectionError(wsi, m) { console.error(m); ctx.cancelService(); },
}],
});
ctx.clientConnect('wss://echo.websocket.events/');4. Static-file server
import { createServer, LWSMPRO_FILE } from 'lws';
createServer({
port: 8080,
vhostName: 'localhost',
mounts: [{
mountpoint: '/',
origin: './public',
def: 'index.html',
originProtocol: LWSMPRO_FILE,
}],
protocols: [{ name: 'http' }],
});5. HTTP endpoint that dispatches to JS
import { createServer, LWSMPRO_CALLBACK, LWS_WRITE_HTTP_FINAL } from 'lws';
createServer({
port: 8080,
vhostName: 'localhost',
mounts: [{ mountpoint: '/api', protocol: 'api', originProtocol: LWSMPRO_CALLBACK }],
protocols: [{
name: 'api',
onHttp(wsi, uri) {
const body = JSON.stringify({ uri, method: wsi.method });
wsi.respond(200, { 'content-type': 'application/json' }, body.length);
wsi.write(body, LWS_WRITE_HTTP_FINAL);
},
}],
});6. HTTP server with HTTPS
import {
createServer, LWSMPRO_CALLBACK, LWS_WRITE_HTTP_FINAL,
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT,
LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX,
LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS,
} from 'lws';
createServer({
port: 443,
vhostName: 'localhost',
options:
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT
| LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX
| LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS,
serverSslCert: 'localhost.crt',
serverSslPrivateKey: 'localhost.key',
serverSslCa: 'ca.crt',
mounts: [{ mountpoint: '/', protocol: 'http', originProtocol: LWSMPRO_CALLBACK }],
protocols: [{
name: 'http',
onHttp(wsi) {
wsi.respond(200, { 'content-type': 'text/plain' });
wsi.write('secure hello\n', LWS_WRITE_HTTP_FINAL);
},
}],
});7. HTTP client (GET) that prints the body
import { LWSContext, toString } from 'lws';
const ctx = new LWSContext({
protocols: [{
name: 'http',
onEstablishedClientHttp(wsi, status) { console.error('status', status); },
onReceiveClientHttp(wsi) {
const buf = new ArrayBuffer(64 * 1024);
if(wsi.httpClientRead(buf)) this.onReceiveClientHttpRead(wsi, buf);
},
onReceiveClientHttpRead(wsi, buf, len) {
console.log(toString(buf, 0, len));
},
onClosedClientHttp(wsi) { ctx.cancelService(); },
onClientConnectionError(wsi, m) { console.error(m); ctx.cancelService(); },
}],
});
ctx.clientConnect('https://example.com/');8. POSTing JSON
import { LWSContext, LWS_WRITE_HTTP_FINAL, LCCSCF_USE_SSL } from 'lws';
const payload = JSON.stringify({ hello: 'world' });
const ctx = new LWSContext({
protocols: [{
name: 'http',
onClientAppendHandshakeHeader(wsi, buf, len) {
wsi.addHeader('content-type', 'application/json', buf, len);
wsi.addHeader('content-length', String(payload.length), buf, len);
if(wsi.method === 'POST') wsi.bodyPending = 1;
},
onClientHttpWriteable(wsi) {
wsi.write(payload, LWS_WRITE_HTTP_FINAL);
wsi.bodyPending = 0;
},
onEstablishedClientHttp(wsi, status) { console.error('->', status); },
onClosedClientHttp(wsi) { ctx.cancelService(); },
}],
});
ctx.clientConnect('https://httpbin.org/post', {
method: 'POST',
sslConnection: LCCSCF_USE_SSL,
});9. Multipart file upload (server)
import { createServer, LWSSPA, LWSMPRO_CALLBACK, LWS_WRITE_HTTP_FINAL } from 'lws';
const spaMap = new WeakMap();
createServer({
port: 8080,
vhostName: 'localhost',
mounts: [{ mountpoint: '/upload', protocol: 'up', originProtocol: LWSMPRO_CALLBACK }],
protocols: [{
name: 'up',
onFilterHttpConnection(wsi, url) {
spaMap.set(wsi, new LWSSPA(wsi, {
maxStorage: 1 << 17,
onContent(name, file, buf) { console.log(name, file, buf.byteLength); },
}));
},
onHttpBody(wsi, buf) { spaMap.get(wsi).process(buf, 0, buf.byteLength); },
onHttpBodyCompletion(wsi) {
spaMap.get(wsi).finalize();
wsi.wantWrite(() => {
wsi.respond(200, { 'content-type': 'text/plain' });
wsi.write('uploaded\n', LWS_WRITE_HTTP_FINAL);
return -1;
});
},
}],
});10. Raw TCP echo on port 1234
import {
createServer,
LWS_SERVER_OPTION_ONLY_RAW,
LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG,
} from 'lws';
createServer({
port: 1234,
options:
LWS_SERVER_OPTION_ONLY_RAW
| LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG,
listenAcceptRole: 'raw-skt',
listenAcceptProtocol: 'echo',
protocols: [{
name: 'echo',
onRawAdopt(wsi) { console.log('accept', wsi.peer?.host, wsi.peer?.port); },
onRawRx(wsi, data) { wsi.write(data); },
onRawClose(wsi) { console.log('close'); },
}],
});11. Logging into your own sink
import { logLevel, getLogLevelName, LLL_USER, LLL_WARN, LLL_ERR } from 'lws';
logLevel(LLL_USER | LLL_WARN | LLL_ERR, (level, line) => {
console.error(`[${getLogLevelName(level)}] ${line}`);
});12. Inspecting live sockets
import { LWSSocket } from 'lws';
setInterval(() => {
for(const s of LWSSocket.list())
console.error(s.id, s.protocol, s.peer?.host);
}, 5000);