Changelog
Format loosely follows Keep a Changelog;
this project doesn't cut versioned releases yet, so entries accumulate
under Unreleased until that changes.
Unreleased
Added
Documentation site under
tools/site/, built by `qjsm tools/site/build.jsand published from the orphangh-pages` branch: a landing page plus everydoc/**page rendered to HTML, with no toolchain beyond qjsm (the markdown renderer and syntax highlighter live intools/site/). See tools/site/README.md.lib/lws/url.js: a conforming subset of the WHATWG URL Standard —URLandURLSearchParams, implemented from the spec's basic URL parser state machine (special schemes, relative resolution, IPv4/IPv6 hosts,file:/opaque-path URLs, percent-encoding per component).URLSearchParamswrites back through to its parentURL'ssearch/href. Known deviation: no IDNA/Punycode (non-ASCII domain labels stay as lowercased UTF-8 rather thanxn--form). See doc/js/helpers.md.Responsegained aredirectedproperty (defaultfalse, preserved byclone());lib/fetch.jssets it totruewhen lws follows a redirect.New
USE_EPOLLCMake option (defaultOFF, Linux-only): routes pollfd management through a singleepoll(7)instance (lws-epoll.c/lws-epoll.h) instead of oneos.setReadHandler/setWriteHandlerregistration per fd. Previously these sources existed but were unconditionally excluded from the build. See doc/native/event-loop.md.Traffic logging under
LLL_USER: every payload actually handed tolws_write()viawsi.write()/wsi.respond()now logs a `TX <n> bytes (proto=<p>): <preview>line (lws-socket.c`), and every `LWS_CALLBACK_{CLIENT_RECEIVE,CLIENT_RECEIVE_PONG,MQTT_CLIENT_RX, RAW_PROXY_CLI_RX,RAW_PROXY_SRV_RX,RAW_RX,RAW_RX_FILE,RECEIVE, RECEIVE_CLIENT_HTTP,RECEIVE_CLIENT_HTTP_READ,RECEIVE_PONG}` fires a matchingRX <reason>: <n> bytes: <preview>line (lws-context.c,callback_protocol()). The preview is the first 40 bytes with non-printable bytes replaced by.(log_preview(),lws.h) — no new logging plumbing, this rides the existinglogLevel()mechanism, so it's silent unlessLLL_USERis enabled (aslib/lws/context.js's default logger already does whenDEBUGis set) and colorized/filtered the same way any otherLLL_USERmessage is.
Fixed
lib/lws/body.js:Body.prototype.text()calledTextEncoder.encode()instead ofTextDecoder.decode(), soresponse.text()/.json()returned garbage instead of the actual body for every realfetch()response.lib/lws/stream-utils.js:concatArrayBuffer()passed a rawArrayBufferchunk straight toUint8Array.prototype.set(), which silently copies nothing (a bareArrayBufferhas no indexed properties) — bodies constructed directly from anArrayBuffer(e.g.new Response(arrayBuffer)) decoded to all-zero bytes.lib/lws/body.js: theBodyconstructor only treatedundefinedas "no body", notnull—Response.error()and `new Response(null)` (the standard no-body idiom, e.g. for 204/304 responses) always threwTypeError: bad body: object.lib/lws/response.js:Response.redirect(url)had no defaultstatus, so omitting it threw instead of defaulting to 302 per spec.lib/lws/headers.js:Headersvalues were never validated —normalizeValue()now trims leading/trailing HTTP whitespace and throwsTypeErroron embedded NUL/CR/LF, matching the Fetch spec and closing a header-injection gap (an untrusted value containing"\r\n"could otherwise smuggle extra headers onto the wire viawsi.addHeader).LWSSocketgainedpipelineLeader,isPipelineLeader, andpipelineQueueDepthaccessors for introspecting libwebsockets'LCCSCF_PIPELINEclient connection queueing/muxing (h1 pipelining, h2 mux streams) — previously this state was private tostruct lwsand unobservable from JS. See doc/native/LWSSocket.md and doc/native/http-client.md.Requires a small patch to the vendored libwebsockets adding the underlying
lws_get_txn_queue_leader(),lws_wsi_is_txn_queue_leader(), andlws_get_txn_queue_depth()C accessors (not present upstream). Applied on thelibwebsocketssubmodule'stxn-queue-introspectionbranch; seepatches/0001-libwebsockets-txn-queue-introspection.patch.lib/fetch.jsnow reuses oneLWSContext(and its single'http'protocol registration) across calls by default, and setsLCCSCF_PIPELINE, so repeatedfetch()calls to the same origin queue (h1) or mux (h2) onto an existing connection instead of always opening a new one — the point being a crawler that does many requests without a new TCP connection per request. Per-request state (req/resp/resolve/reject/controller) moved from closure variables onto thewsiobjectclientConnect()returns, since many requests now share one protocol object and every callback for a connection receives that same wrapper. PasskeepAlive: false, or a customtlsoption, to get the previous one-off-context-per-call behaviour. The exportedfetch(url, options)signature andResponse/Request/Headersshapes are unchanged (still WHATWG/W3C-shaped).Added
tests/test-fetch.js: a same-host crawler demo. Fetches an HTML fixture, RegExp-scans it for<a href>,<img src>,<script src>,<link href>, CSSurl(...), and barehttp(s)://URLs, queues and fetches same-origin links, reports (without fetching) foreign-origin ones, and reports how many distinct TCP connections (wsi.network.fd) the whole crawl actually used — ideally one. Serves its own tiny fixed fixture site via anLWSMPRO_FILEmount so the crawl is deterministic and needs no network access.
Not yet verified live: the qjs-lws C build was mid-edit (unrelated,
in-progress work) for the whole time this was written, so fetch.js's
new connection-reuse path and tests/test-fetch.js have not actually
been run yet. Re-run tests/test-fetch.js once the build is stable and
confirm the summary line reports exactly one distinct TCP connection.