.response~content_type = "application/json; charset=utf-8"
-- This rexxlet fetches two sample posts from the public JSONPlaceholder
-- API, parses each one, and emits the two of them merged into a single
-- JSON array. It is the JSON-returning sibling of the aggregate-html
-- example: same fetch, same merge, but the result is served as JSON text
-- rather than rendered as a page. A client (a browser fetch, another
-- rexxlet, a command-line tool) can consume it directly.
Base = "https://jsonplaceholder.typicode.com/posts/"
posts = .Array~new
Do id = 1 To 2
post = FetchJSON(Base || id)
If post \== .nil Then posts~append(post)
End
Say .json~toJson(posts)
Exit
-- Fetch a URL and parse the JSON body into a Rexx object.
-- Returns .nil if the download fails or the body is not valid JSON.
-- Note: this blocks the CGI process until curl returns, so we keep it
-- on a short leash -- --connect-timeout caps the connect phase at 5s,
-- and --max-time caps the whole transfer at 15s, so a dead remote host
-- cannot hang the request. -f makes curl return a non-zero RC on HTTP
-- >= 400 instead of handing back the error page as if it were the body.
-- curl ships with Windows 10/11, macOS and most Linux; wget does not.
FetchJSON: Procedure
Use Strict Arg url
-- Keep the probe silent in Apache's error.log when curl is absent. Two
-- INDEPENDENT channels would otherwise write there, so both are muted:
-- * the interpreter's trace: a missing binary is a FAILURE, which the
-- default Trace Normal echoes. Trace("Off") silences it; Trace(setting)
-- pushes the new setting and returns the previous one, restored below.
-- * the child's stderr (the shell's "curl: not found"): Error Stem
-- captures it instead of letting it flow through to the server log.
-- Neither subsumes the other. The .nil return below already handles curl
-- being absent or the fetch failing.
savedTrace = Trace("Off")
Address Command "curl -sf --connect-timeout 5 --max-time 15" url With Output Stem lines. Error Stem discard.
Call Trace savedTrace
If RC \= 0 Then Return .nil
text = ""
Do i = 1 To lines.0
If i > 1 Then text = text || "0a"x
text = text || lines.i
End
Signal On Syntax Name BadJSON
Return .json~fromJson(text)
BadJSON:
Return .nil
::Requires "json.cls"