HTTPREXX: REXX Server Pages on rexx370 — Phase 1#2
Merged
Conversation
Replace the brexx370 module build (HTTPSAY + HTTPREXX via [[link.module]], crent370 dependency) with the mbt v2 schema: type=application, a single HTTPREXX [[module]] (startup=crt1), and httpd + ufsd dependencies. The v1 [mvs.build.datasets] and [[link.module]] blocks and the BREXX linkage are dropped; the mbt submodule is bumped to the v2 line. The rexx370 IRX services (IRXINIT/IRXEXEC/IRXTERM) are resolved at runtime against the modules already installed on the system, so rexx370 is not a build dependency -- only its headers are vendored later under include/. src/httprexx.c is a minimal CGI skeleton: the httpd/httpc handshake, the per-connection UFS session, and the extension-router shape. It builds and links cleanly under cc370. The brexx370 sources (cgistart.c, httprexx.c, httpsay.c) are removed in favor of the rexx370 rewrite.
rxptrans.c transforms a .rxp template into a single REXX program: each source line that produces output becomes one `say` built with `||` concatenation, while `<?rexx ... ?>` statement tags pass through verbatim and `<?rexx= e ?>` expressions are emitted as `(e)`. Literals are escaped by REXX quote-doubling. The newline immediately after a statement tag's `?>` is swallowed so a statement-only line emits no stray `say ''`; blank literal lines are preserved; a mid-line statement tag splits the line into two SAYs. This is the pure-C path that feeds the REXX source to IRXEXEC (spec section 5). test/tstrxp.c is a table-driven host unit test (make test-host) covering the spec's hello.rxp byte-exact, the mid-line statement edge case, quote doubling, blank-line preservation, and the error path. 12/12 assertions pass.
httprexx.c now drives the installed rexx370 IRX services per request to run a
.rexx/.rxp page and return its SAY output:
- reads the source from UFS (libufs) by SCRIPT_FILENAME, transpiling .rxp;
- IRXINIT a fresh LPE via __linkds (7-slot CALL,VL list);
- overwrites IRXEXTE.io_routine with httprexx_io (the MODNAMET override is a
no-op in rexx370; the SAY opcode reads the pointer by indirection, so a
post-init patch redirects every SAY) and binds the request context through
ENVBLOCK userfield;
- IRXEXEC the in-storage INSTBLK (10-slot CALL,VL list; the ENVBLOCK is passed
in P9, the query string as an ARGTABLE for PARSE ARG, SUBROUTINE call type);
- IRXTERM via a small C-callable asm shim (asm/htrxterm.asm) because IRXTERM
takes the ENVBLOCK in R0, which __linkds cannot set;
- flushes the buffered page to httpc (default text/html), or a clean 500.
httprexx_io is heap-free: it memcpy's SAY output into a fixed buffer allocated
in the request context (it runs nested in IRXEXEC's call context). irxbind.h is
a minimal local view of the IRX control blocks (no wholesale header vendoring),
with a compile-time INSTBLK size check. doc/rexx370-bindings.md records the
confirmed call sequences and the flagged Phase-1 deviations.
Builds clean: cc370 (-Wall -Werror) + as370 + ld370.
doc/phase1-smoketest.md walks the manual end-to-end check (deploy, MOD= routing, upload, curl) with the exact expected render for examples/hello.rexx and examples/hello.rxp, plus the wtof diagnostics and the flagged Phase-1 deferrals. The samples include the spec's hello.rxp.
…_send http_send is httpd's raw vector path; the rendered page is text and must go through the EBCDIC->ASCII translation that http_printf performs (the same path httplua uses for its body). Sending raw EBCDIC would garble the response on the wire. The output buffer now reserves a trailing NUL for the "%s" emit.
The STM line's inline comment ran to column 77. Column 72 is the as370/HLASM continuation column, so a non-blank there made the assembler read the next line (BALR R12,0) as a continuation of the STM operand -- silently dropping the BALR that establishes the base register. HRXTERM then ran with a garbage base, read garbage literals, and issued GETMAIN/LOAD with garbage operands, corrupting the caller frame and abending S0C1 on return into run_rexx. Rewrite htrxterm.asm with disciplined columns (short inline comments, nothing past column 71). Verified against the as370 listing: BALR now assembles (05C0) immediately after the STM.
The as370 LOAD macro emits the "return-code requested" SVC 8 form (R1 = X'80000000'), which MVS 3.8j does not honor: LOAD EP=IRXTERM came back with entry point 0, so HRXTERM branched to R15=0 (low core) and abended S0C1. Obtain the IRXTERM entry point in C via __load() -- the crent370/libc370 loader rexx370 itself uses -- and balance it with __delete(). The asm shim shrinks to HRXCALL, which only sets R0=env and BALRs the supplied entry over a GETMAIN'd save area (no LOAD/DELETE macros). Defensive: a NULL entry logs and skips (the LPE is not freed, but the request still renders) instead of branching to 0.
- send_page now translates the rendered body EBCDIC(IBM-1047)->ASCII with
the 1047 table and sends it raw, matching httpd's static-file path. The
server default codepage (CP037) mangled the variant characters [ ] { } # @ |.
- dispatch strips CR bytes so CRLF-terminated .rxp/.rexx sources (uploaded via
FTP or MVSMF) tokenize as clean newline-only text.
- IRXTERM is temporarily compiled out: terminating an LPE after an IRXEXEC run
faults inside IRXTERM (pending a rexx370 fix); the LPE is leaked per request
for now. The __load/HRXCALL path is kept for a one-line re-enable.
- Removed the temporary debug WTO/hex-dump instrumentation; kept the real
HTTPREXX: error logs.
The "S0C1 after IRXTERM" crash that forced IRXTERM to be disabled was
never in rexx370 - it was HRXCALL's own epilog:
LM R0,R12,20(,R13)
LM is RS-format (D(B) operand syntax); as370 silently assembles the
RX-style D(,B) spelling with BASE=0, so the epilog reloaded R0-R12
from PSA low core 0x14-0x48 (old PSWs, CVT pointer) instead of the
caller's save area. The adjacent RX-format L R14,12(,R13) was correct,
which is why R13/R14 always looked sane while the base registers came
back as garbage - wild branches with rotating abend codes that
imitated storage corruption downstream.
Write the operand as 20(R13) and re-enable the per-request IRXTERM
teardown (no more leaked LPEs). Root-cause analysis: rexx370
docs/irxterm-c-host-crash.md; assembler issue: mvslovers/cc370#12;
rexx370 side: mvslovers/rexx370#205.
Contributor
Author
|
80e742d re-enables the per-request IRXTERM teardown: the "S0C1 after IRXTERM" that forced the disable was root-caused to HRXCALL's own epilog — |
…load failure Three focused fixes to the CGI handler, none of which changes the render path: - send_page now emits Content-Length. The whole page is buffered, so the exact byte count is known; sending it (as httpd's static path does) is deterministic and preserves keep-alive without relying on httpd's fallback that injects Transfer-Encoding: chunked when no length is present. The EBCDIC->ASCII xlate is 1:1, so the count is stable. - read_ufs sizes its buffer to the file via ufs_stat (capped at HRX_SRC_MAX) instead of always mallocing a fixed 64 KB per request, which matters on the memory-constrained target. Falls back to the full cap if stat is unavailable. - run_rexx logs when __load(IRXTERM) fails. Without IRXTERM the IRXINIT LPE cannot be torn down, so a persistent worker would leak that storage per request; the failure is now visible instead of silent. Measurement context: demo.rxp latency (TTFB ~0.42s) is dominated by REXX generation, not delivery (transfer ~8 ms), so these are correctness/memory fixes, not the performance lever.
Wire httprexx into the shared mvslovers/mbt reusable workflows, matching the other ecosystem repos (httpd et al.): build on every pull_request and on push to main, release on v* tags. httprexx had no .github/ at all, so PRs were merged ungated; this closes that gap.
mgrossmann
added a commit
that referenced
this pull request
Jul 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1.
Rebuilds HTTPREXX on rexx370, implementing Phase 1 of the REXX Server Pages
spec. Green-field rewrite (the brexx370 sources are removed).
Stage 1 — mbt v2 migration
project.tomlto the v2 schema:type=application, oneHTTPREXX[[module]],httpd+ufsddependencies; the v1[mvs.build.datasets]/[[link.module]]blocks, HTTPSAY, and the crent370/BREXX linkage are dropped.Stage 2 — Phase 1
.rxptranspiler (src/rxptrans.c): per-source-linesaycoalescing with||, quote-doubling escaper, one newline swallowed after a statement?>,blank lines preserved, mid-line statement split. Pure C; table-driven host
unit tests (
make test-host) — 12/12, incl. the spec'shello.rxpbyte-exact.src/httprexx.c): reads the page from UFS (libufs),transpiles
.rxp, then per request: IRXINIT a fresh LPE via__linkds,overwrite
IRXEXTE.io_routinewith a heap-freehttprexx_io(the MODNAMEToverride is a no-op in rexx370; the SAY opcode reads the pointer by
indirection, so the post-init patch is sufficient), IRXEXEC the in-storage
INSTBLK (ENVBLOCK in P9, query string as an ARGTABLE for
PARSE ARG), IRXTERMvia a small C-callable asm shim (
asm/htrxterm.asm, R0 linkage). The body isemitted on the text path (
http_printf) so httpd translates EBCDIC→ASCII.rexx370 is not a build dependency; only its struct layouts are reproduced
locally in
include/irxbind.h(no rexx370 change; honors spec §2).Docs
doc/rexx370-bindings.md— the confirmed IRX call sequences and the flaggedPhase-1 deferrals (POST/PULL → EOF, pool variables → IRXEXCOM later).
doc/phase1-smoketest.md+examples/hello.{rexx,rxp}— the manualend-to-end check (MOD= config, requests, expected render).
Verification
-Wall -Werror) + as370 + ld370.doc/phase1-smoketest.md(not run here). First-run watch item: the IRXmodules must be installed/loadable by the httpd address space.