websh

Timeline
Login

Timeline

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

40 check-ins

2026-07-12
20:47
pkgIndex: version-switched loading so Tcl 8.6 and Tcl 9 flavours coexist in one directory

pkgIndex.tcl.in now branches on [package vsatisfies [package provide Tcl] 9.0-] and loads libtcl9websh<version> under Tcl 9 and libwebsh<version> under Tcl 8.6 (same pattern as tzint/tserialport), passing the init prefix [string totitle websh] explicitly instead of relying on filename derivation. Verified by loading the package from a single directory containing both libraries with tclsh8.6 and tclsh9.0.

Makefile.in install rules were hardcoded to the libwebsh prefix and would have missed a Tcl 9 build's library; install-binaries and install-libraries now use $(web_LIB_FILE) throughout (pkgdir copy, libdir symlinks, moddir symlink). A Tcl 9 "make install" lands under its own TCL_PREFIX (/opt/tcl/9.0), separate from the 8.6 deployment. leaf check-in: 47e9a57ea8 user: alex tags: towardsTcl9

20:39
tests: convert 13 test files from ISO-8859-1 to UTF-8

Tcl 9 sources scripts strictly as UTF-8 and aborts on invalid byte sequences ("invalid or incomplete multibyte or wide character"). Content is unchanged; on Tcl 8.6 with a UTF-8 locale the files read identically. Affected: context, dehtmlify, dispatch, encrypt, filecontext, htmlify, messages, ncad, nocrypt, script, time, uricode, webout. check-in: e49f5c8924 user: alex tags: towardsTcl9

20:36
cfg.test: version-neutral file-permission comparison (Tcl 8.6 vs 9)

Tcl 9 reports [file attributes -permissions] as 0o644 while Tcl 8.6 reports 00644, and Tcl 9 no longer parses leading-zero integers as octal. New helper cfgTestPerms normalizes both forms via scan %o and compares as 0644; golden values in cfg-4.2 - cfg-4.6 updated accordingly (00644 -> 0644). No change in what is being tested. check-in: 61e3e0699f user: alex tags: towardsTcl9

20:03
first steps to Tcl9

New src/generic/webtclcompat.h: Tcl_Size/TCL_SIZE_MAX/TCL_SIZE_MODIFIER shims for Tcl <= 8.6, per the official migration guide (core.tcl-lang.org tclconfig wiki).

Mechanical API migration: 34 length out-parameters converted from int to Tcl_Size (Tcl_GetStringFromObj, Tcl_GetByteArrayFromObj, Tcl_ListObjGetElements/Length, Tcl_SplitList); CONST -> const (130 sites, removed in Tcl 9); Tcl_InitStubs "8.2" -> "8.6-" (5 sites, web.c now checks the return value); tclAppInit.c rewritten as a modern Tcl_Main template (_ANSI_ARGS_/Tcl_StaticPackage gone); log.c includes stdarg.h (no longer transitive via tcl.h) and uses Tcl_SetObjResult instead of Tcl_SetResult+Tcl_Free (freeProc signature mismatch in Tcl 9).

Channel types (apchannel.c, varchannel.c): lifted to TCL_CHANNEL_VERSION_5 with close2Proc wrappers — Tcl 9 accepts only v5; identical behaviour on 8.6.

Runtime fixes surfaced by the Tcl 9 test runs, all version-guarded and behaviour-preserving on 8.6: - Octal permissions: Tcl 9 parses a leading zero as decimal. web::config filepermissions (cfg.c) and web::filecounter -perms (filecounter.c) now parse base-8 explicitly (0o prefix accepted); sessctx.ws3 converts via scan %o before open. - nca_d.c decryptNcaD: assemble decrypted bytes in a Tcl_DString and create the string object in one go — Tcl 9 validates UTF-8 on append, byte-wise Tcl_AppendToObj corrupted multi-byte sequences. - checksum.c crcCalc: emulate the historic char&0xFF semantics explicitly (Tcl 9's Tcl_GetByteArrayFromObj returns NULL for chars > U+00FF); checksums stay bit-identical to existing ciphertexts for BMP content. Known limitation: chars > U+FFFF have different string reps on 8.6 vs 9, so astral-char ciphertexts do not decrypt cross-version (round-trip within one version always works). - formdata.c: multipart channel gets -profile tcl8 on Tcl 9 (strict would abort on 8-bit bytes); readAndDumpBody now reads upload bodies byte-faithfully via iso8859-1 — this also fixes a latent 8.6 bug where valid UTF-8 sequences inside binary uploads were collapsed by the utf-8 channel encoding (silently truncated files). - varchannel.c: NULL guard after Tcl_GetByteArrayFromObj (Tcl 9 returns NULL for chars > U+00FF -> EILSEQ instead of crash); Tcl_ExternalToUtf uses TCL_ENCODING_PROFILE_TCL8 on Tcl 9 so binary bytes keep flowing. - webutils.tcl web::getContent: normalize IANA charset names to Tcl encoding names (iso-8859-1 -> iso8859-1 etc.) — the previous behaviour only worked by accident through Tcl 8.6's lenient utf-8 decoding; uses -profile replace on Tcl 9. - htmlify.c: silence set-but-unused err counter (0 warnings on both builds). - tclconfig/tcl.m4 to TEA 3.14 check-in: 6b5597a451 user: alex tags: towardsTcl9

2026-07-02
09:17
Version-3.7.7 leaf check-in: f2d12700f0 user: alex tags: trunk, version-3.7.7
09:07
docs+tests: quickref.xml revived at 3.7.6, webutils test coverage, web::genPasswd rework, web::getContent JSON fix

doc/quickref.xml: brought the DocBook reference from the 2014 3.6.0b5 state to 3.7.6. Recovered the 15 misc commands (ts, configSetup, sessionSetup, loggingSetup, sessionGet/New/Init/Close/Refresh, uuidV4, getContent, mimeType, returnJson/Text/Binary) from the previously published 3.7.3 HTML (fossil uv; the XML source of those edits was never committed). Added new sections: JSON Web Tokens (::jwt::sign/::jwt::verify incl. -json/-claims/-leeway), web::sseStart/sseSend, web::response -flush, web::randombytes, web::filerandom, web::genPasswd, web::list2uri/uri2list, web::logfilter alias. Removed stale $Id keywords, fixed two DTD validity errors present since 2014.

doc build: network URLs for DocBook XSL/DTD are dead; quickref.xsl now imports the catalog-resolvable current/ URI and doc/Makefile resolves via the local XML catalog (brew docbook + docbook-xsl on macOS, distro packages on Linux), builds with --nonet, and gains a "make check" DTD validation target. Chunker output switched to UTF-8 (was ISO-8859-1) to fix U+FFFD mojibake in the served pages. Generated doc/html/ is fossil-ignored; publishing works via fossil uv add --as basename + uv sync.

webutils.tcl: web::getContent now decodes JSON content types (application/json*, *+json) without a charset parameter as UTF-8 per RFC 8259; previously the C layer's iso8859-1 CONTENT_ENCODING default won and mangled UTF-8 JSON bodies.

genpasswd.tcl: class renamed genPasswd -> web::genPasswd (consistency with web::filerandom), randomness switched from Tcl rand() to web::randombytes with rejection sampling (no modulo bias), shuffle replaced by Fisher-Yates (the old n-random-swaps shuffle produced a biased permutation), new constructor length argument and configure API for all rules; package genpasswd 1.1. Now suitable for real secrets.

tests: new src/tests/webutils.test (32 cases) covering web::response -flush, SSE framing, getContent encoding resolution, charset extraction in the raw POST path, multipart UTF-8 roundtrip, returnJson/Text/Binary headers, randombytes, uuidV4, ts, mimeType, full session lifecycle, JSON POST dispatch incl. invalid-JSON error path, and web::genPasswd. mintest.test now cleans up its temp files on the error path too (removed leftover mintest1_1a/b.tcl). Suite: 526 total, 521 passed, 5 skipped, 0 failed. check-in: 8688b218c4 user: alex tags: trunk

2026-06-16
14:41
Version-3.7.6 check-in: 526be95da8 user: alex tags: trunk, release, version-3.7.6
2026-06-15
18:17
sessionInit: uuid argument now takes precedence over AUTH_BEARER

- web::sessionInit now treats the uuid parameter as an override - previously it was only a fallback used when AUTH_BEARER was empty (set id [web::request AUTH_BEARER $uuid]); this made it impossible to load a known session when the request was already authenticated with a different bearer - Use case: server-side sliding-idle keyed off the access-JWT — the refresh session id lives in the JWT claims (rjti), but the HTTP bearer is the access-JWT itself; without this patch the caller had to overwrite AUTH_BEARER via web::request -set before the call and restore it afterwards - API change is formally backwards-incompatible - in practice the fallback default has no real users since there would be no session-id source either; the previous doc wording „uuid arg for testing" was misleading anyway and is now explicit check-in: f62d197a53 user: alex tags: trunk

2026-05-18
23:53
feat(jwt): predictable -json result + opt-in exp/nbf validation

::jwt::verify -json embedded payload/header as nested rl_json objects, so `json get $res payload` returned non-re-parsable Tcl-dict form — an undocumented footgun. Not yet in production; deliberate breaking redesign.

- -json: header/payload now raw JSON *string* values (re-parsable). Read claims in two steps: `set c [json get $res payload]; json get $c exp`. verify without -json unchanged (boolean, signature only). - -claims: opt-in RFC 7519 exp/nbf check (default stays signature only). Adds `reason` field to -json (ok|signature|notbefore| expired|payload), only when -claims given. - -leeway sec: opt-in clock-skew tolerance for -claims (default 0, no-op without -claims).

jwt.tcl is in SCRIPT_SOURCES (compiled into script.h via make). New src/tests/jwt.test (24 cases). Baseline unchanged (470: 465 passed, 5 skipped, 0 failed); full suite 494/489/5/0. SKILL.md: new "JWT (jwt.tcl)" section. check-in: d9c28bbfee user: alex tags: trunk

2026-05-17
18:27
build(macOS): embed absolute install_name in libwebsh.dylib

mod_websh.so loaded fine when started via `httpd -X` from /opt/homebrew/etc/httpd, but failed under the LaunchAgent (CWD=/) with no error_log entry — dyld could not resolve the bare-name LC_LOAD_DYLIB for libwebsh.

Root cause: libwebsh.dylib was linked without -install_name, so its LC_ID_DYLIB was just the bare filename. mod_websh.so inherited that bare reference in its LC_LOAD_DYLIB. All other dependencies (APR, APR-util, Tcl framework, libSystem) had absolute paths and worked.

Fix: on Darwin, link libwebsh with -Wl,-install_name,$(pkgdir)/$(web_LIB_FILE) so the embedded install_name is the configured install location. mod_websh picks this up automatically at link time. No post-build install_name_tool or -headerpad_max_install_names dance required. The path is prefix-relative and resolved at build time, so it follows --prefix / --with-tcl correctly.

Implemented as SHLIB_INSTALL_NAME, appended to MAKE_LIB / MAKE_SHARED_LIB. Empty on non-Darwin platforms — Linux uses SONAME + ldconfig and is unaffected. check-in: 95cdb054ae user: alex tags: trunk

2026-05-04
13:01
build: macOS support and C23 compatibility

Make the build work on macOS (Apple Silicon, Homebrew) without manual configure flags. ./configure --enable-apache, make, make test, and sudo make install all work out of the box.

C23 compatibility (clang on macOS now defaults to -std=gnu23):

* url.c: rename `int bool` to `int boolVal` — `bool` is a reserved type keyword in C23. * tclAppInit.c: convert K&R-style `int main(argc, argv)` to ANSI `int main(int argc, char **argv)` — K&R declarations were removed in C23.

Both changes also compile cleanly under C99/C11/C17, so Linux builds are unaffected.

Build system (configure.ac, Makefile.in):

* Auto-detect APR/APU link flags via `apr-1-config --link-ld --libs` and `apu-1-config --link-ld --libs`, with Homebrew fallbacks. Previously only the headers were auto-detected; the libraries required explicit --with-apr= / --with-apu=. On macOS, mod_websh.so is a bundle linked with -bundle_loader against the httpd binary, which does not export APR symbols (Homebrew httpd loads APR as shared libs at runtime), so explicit -lapr-1 / -laprutil-1 are required.
* Introduce SHLIB_EXT (.dylib on Darwin, .so elsewhere) so the unversioned install symlink (libwebsh.dylib / libwebsh.so) and the SHARED_LIB_SUFFIX match the platform's actual library extension. pkgIndex.tcl already uses [info sharedlibextension] and is unchanged.
* Rename SYSTEMCTL_APACHE to RESTART_APACHE and default it to ":" (no-op) so the install rule is syntactically valid on platforms without an Apache restart hook. LDCONFIG gets the same default. The recipe lines are now `@$(LDCONFIG)` / `@$(RESTART_APACHE)`.
* Add a Darwin case that sets RESTART_APACHE to `brew services restart httpd` when Homebrew httpd is installed. When invoked via sudo, the command runs as the original user via `sudo -u "$SUDO_USER"` so brew does not chown httpd paths to root:admin (which breaks future `brew upgrade httpd`).
* `make clean` now also removes libwebsh*.dylib.

Tests pass on macOS 26.4 (Apple Silicon, Tcl 8.6 framework, Homebrew httpd/apr/apr-util): 470 total, 465 passed, 5 skipped, 0 failed. check-in: 166114797f user: alex tags: trunk

2026-05-03
21:12
Version-3.7.5 check-in: 9c9da42e34 user: alex tags: trunk, release, version-3.7.5
19:40
Fix double UTF-8 encoding of multipart text form fields

Tcl's -translation binary implicitly sets -encoding binary as a side effect. In parseMultipartFormData this caused text field values to be stored as raw byte sequences rather than decoded Unicode strings: UTF-8 bytes C3 A4 (ä) were kept as two separate chars U+00C3 and U+00A4 instead of the single char U+00E4. On output through a UTF-8 response channel those chars were re-encoded, producing the double-encoded sequence C3 83 C2 A4 (ä) visible in the browser.

Fix: after setting -translation binary, explicitly restore the channel encoding to its saved value so that multi-byte sequences in text fields are decoded correctly during parsing.

websh-test.ws3: switch page charset to utf-8 to match the server's channel encoding and modern browser form submission behaviour; set Content-Type response header explicitly via web::response -set. check-in: 56cc68ed13 user: alex tags: trunk

19:04
Run ldconfig and systemctl as part of make install on Linux; fix make clean after version bumps

configure.ac detects /sbin/ldconfig on Linux and sets LDCONFIG accordingly. When Apache support is enabled it also probes for the apache2 or httpd systemd service via "systemctl cat" and sets SYSTEMCTL_APACHE to "systemctl restart <service>". Both variables are empty on non-Linux platforms so the install targets remain portable.

install-binaries runs $(LDCONFIG) after placing the shared library. install-libraries runs $(SYSTEMCTL_APACHE) after installing the Apache module. Both calls are guarded with "test -z" so an empty variable is a silent no-op; the leading "-" prevents a failed restart from aborting the install.

The clean target now additionally removes versioned build artifacts via shell globs (websh[0-9]*, libwebsh[0-9]*.so, mod_websh[0-9]*.so) so that old binaries are cleaned up even after a version bump and reconfigure. check-in: 730c03e21c user: alex tags: trunk

18:53
Fix make install layout and make clean after version bumps

install-binaries now installs the Tcl package into lib/websh<VERSION>/ (pkgdir) with pkgIndex.tcl and creates relative symlinks lib/libwebsh<VERSION>.so and lib/libwebsh.so pointing into pkgdir — matching the layout expected by the Tcl package loader and previously handled manually by the ./b script.

install-libraries (Apache only, guarded by INSTALL_APACHE) installs mod_websh<VERSION>.so to lib/httpd/modules/ with mod_websh.so and libwebsh<VERSION>.so symlinks. install-dirs no longer creates the Apache module directory; install-libraries does that itself so the directory is only created when Apache support is enabled.

All ln -s calls changed to ln -sf so repeated installs are idempotent.

The clean target now also removes versioned artifacts via shell globs (websh[0-9]*, libwebsh[0-9]*.so, mod_websh[0-9]*.so) so that old build products are cleaned up even after a version bump and reconfigure. check-in: a7fea1fbef user: alex tags: trunk

18:16
README modified check-in: 8efac65967 user: alex tags: trunk
18:04
Fix compiler warnings, runtime crashes, and encoding handling

Compiler warnings (-Wunused-but-set-variable, dangerous function): - nca_d.c: remove unused keyBytes in Web_DecryptD - formdata.c: remove unused bytesWritten; use (void)readAndDumpBody() - log.c: replace silent ires assignments with proper TCL_OK checks for all four registerLogPlugIn() calls; fixes a logtocmd/logtosyslog WebAssertData typo as a side effect - request.c: replace tempnam() (TOCTOU-unsafe linker warning) with mkstemp() + immediate unlink() + P_tmpdir fallback for missing dirs; add <unistd.h> - interpool.c: check return values of Tcl_Init() and Websh_Init(), log failures via ap_log_error() and return NULL on error

Runtime crash (SIGSEGV in rawReadPostData): - formdata.c: guard CONTENT_TYPE hash lookup against NULL before passing to Tcl_GetString / Tcl_StringCaseMatch; fix cJSON_Delete() call to execute on all code paths; guard cmdTag NULL dereference

Encoding handling (web::getContent, CONTENT_ENCODING): - formdata.c rawReadPostData: derive CONTENT_ENCODING from charset= in the Content-Type header; fall back to iso8859-1 (HTTP/1.1 default) instead of the channel's system locale encoding - webutils.tcl web::getContent: resolve encoding in priority order — charset= from Content-Type, then CONTENT_ENCODING, then utf-8 — so JSON/XML bodies without an explicit charset= are decoded correctly

Missing header: - web.h: add #include "randombytes.h" to fix implicit declaration of randombytes_Init() in web.c check-in: d11b978e8c user: alex tags: trunk

18:02
Migrate build system to TEA 3.13 (Tcl Extension Architecture)

Replace the Scriptics-era SC_* macro build system with the standard TEA (Tcl Extension Architecture) 3.13 framework.

- Add configure.ac (replaces configure.in) using TEA_* macros - Add tclconfig/ with tcl.m4 (TEA 3.13), install-sh, and support files - Add pkgIndex.tcl.in template for package registration - Update aclocal.m4 to include tclconfig/tcl.m4 - Remove obsolete configure.in and standalone tcl.m4 - Update Makefile.in for TEA compatibility: - Apache module build made optional via APACHE_TARGET / INSTALL_APACHE substitution variables; default: auto-detect, --disable-apache skips mod_websh, --enable-apache requires it - Override TEA's MAKE_LIB to preserve websh's $($@_OBJECTS) link pattern - Keep web_LIB_FILE as compatibility alias for PKG_LIB_FILE - Fix LD_LIBRARY_PATH order in test target so the freshly built library takes precedence over any installed version - Auto-detect APR/APU include paths via apr-1-config / apu-1-config; add Homebrew prefix search on macOS (Intel and Apple Silicon) check-in: 26dd4f11e4 user: alex tags: trunk

2026-05-01
15:34
Missing randombytes.h 3.7.4 check-in: 6531bf2c67 user: alex tags: trunk
14:53
Version-3.7.4 check-in: df70fdeaa4 user: alex tags: trunk, release, version-3.7.4
12:48
sse-client.html with sse-dispatch.ws3, which uses the web::command / web::dispatch routing pattern; includes a simple progress stream and a long-running job with client-disconnect detection via catch. check-in: 937ffe2f69 user: alex tags: trunk
12:31
Add SSE streaming support and update examples

Introduce real-time Server-Sent Events streaming for mod_websh:

- webout.h: add ResponseFlushHandler typedef and flushHandler pointer to ResponseObj (analogous to existing headerHandler) - weboutint.c: initialise flushHandler to NULL in createResponseObj - webout.c: implement web::response -flush — calls Tcl_Flush on the response channel, then invokes flushHandler if set - response_ap.c: add apFlushHandler (calls ap_rflush) and wire it into createDefaultResponseObj_AP; without this ap_rwrite fills the APR bucket chain but never pushes data to the network until request end - webutils.tcl: web::sseStart sets -buffering none -buffersize 0 instead of -buffering line

Without ap_rflush, SSE events were buffered by Apache and only delivered after the command completed. Tcl_Flush alone is insufficient because it drains Tcl's internal buffer via outputProc but does not call flushProc from Tcl_ChannelType (that is only invoked on channel close). check-in: 40063bce69 user: alex tags: trunk

2025-02-17
20:41
Version-3.7.3 check-in: 996e75b4ff user: alex tags: trunk, release, version-3.7.3
20:32
Webutils completely revised: ::web::configSetup, ::web::loggingSetup, ::web::sessionClose, ::web::sessionGet, ::web::sessionInit, ::web::sessionNew, ::web::sessionRefresh, and ::web::sessionSetup. check-in: 7b873279ef user: alex tags: trunk
20:25
Check for Authorization Bearer and save token AUTH_BEARER. check-in: 481ce9478b user: alex tags: trunk
2024-06-26
15:43
add unix Makefile check-in: 3dbacf24c8 user: alex tags: trunk
15:41
some changes to webutils: web::ts, web::getContent and web::returnBinary; new: web::mimeType and web::returnText check-in: 6afcbe2d22 user: alex tags: trunk
2024-03-29
08:21
Version-3.7.2; embedded some new tcl files: filerandom.tcl genpassword.tcl jwt.tcl webutils.tcl; added c function web::randombytes check-in: e36242c28b user: alex tags: trunk, release, version-3.7.2
2022-01-05
16:08
Version-3.7.1 check-in: 82f8a9adad user: alex tags: trunk, release, version-3.7.1
15:51
dispatch via formvar; dispatch order is now: 1. http get, 2. http header, 3. http post json, 4. form var check-in: b1b36b4a7b user: alex tags: trunk
12:41
logdest -format new format specifications: $U microseconds and $M milliseconds check-in: b41a3d3fc6 user: alex tags: trunk
2020-06-26
13:40
dispatch via cmd tag in http header; example: 'cmd' = 'X-CMD' -> request 'HTTP_X_CMD'; dispatch order is now: 1. http get, 2. http header, 3. http post json check-in: e4f003341f user: alex tags: trunk
2020-06-23
13:26
prepare v3.7 check-in: 5e388a2532 user: alex tags: trunk
13:15
cJSON merged into trunk check-in: e3cabd351a user: alex tags: trunk
12:48
if post data is 'application/json', parse JSON for cmdTag and set request-POST_CMDTAG_JSON. if there is no cmdTag in get data then dispatch command from POST_CMDTAG_JSON. closed check-in: 13e07f6bc8 user: alex tags: cJSON
2020-06-22
14:43
remove unnecessary files check-in: 901bf599f8 user: alex tags: cJSON
14:41
add cJSON into rawReadPostData for implementing cmd dispatch and track via application/json check-in: 7946711abf user: alex tags: cJSON
2018-03-22
12:21
websh rev1827371 patch; raw post data check-in: c1623783ca user: alex tags: trunk
2014-10-30
20:53
inital commit websh-3.6.0b5 check-in: dfd8dc58d2 user: alex tags: trunk
20:52
initial empty check-in check-in: 55648d839d user: alex tags: trunk