Adds hbrtl_ext/httpserver — a Five RTL extension that exposes a
single-process HTTP server controlled entirely from PRG.
Wire contract:
HTTP_SERVER_START(cAddr, cHandlerFunc) → blocking; returns NIL or cErr
HTTP_SERVER_STOP() → graceful shutdown
PRG handler signature:
FUNCTION OnRequest( hReq ) -> hResp
hReq: method, path, query, headers (hash), body, remote_addr
hResp: status (default 200), headers (hash), body
Each request runs on its own hbrt.Thread via vm.NewThread(), the same
pattern pgserver uses for connection isolation. Handler panics are
caught and turned into a 500.
The package is wired into fnode's defaultRTL list so any build that
doesn't override --rtl picks it up automatically.
Verified end-to-end with app/echo_server.prg: GET/POST against :8089
return JSON envelopes with the correct method, path, query, body
length, remote_addr, and roundtripped user-agent header.
The mod_harbour-compatible AP_* surface (AP_METHOD, AP_RPUTS,
AP_JSONRESPONSE, etc.) will sit on top of this dispatcher in
sub-phase 1a.3 as PRG, not Go.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
34 lines
1.2 KiB
Plaintext
34 lines
1.2 KiB
Plaintext
// app/echo_server.prg — 1a.2b end-to-end test.
|
|
//
|
|
// Starts the built-in HTTP server on :8089 with OnRequest as the
|
|
// dispatcher. The handler echoes the request back as JSON so we can
|
|
// curl it and verify request parsing + response writing both work.
|
|
|
|
FUNCTION Main()
|
|
LOCAL cErr := HTTP_SERVER_START(":8089", "OnRequest")
|
|
IF cErr != NIL
|
|
? "httpserver:", cErr
|
|
ENDIF
|
|
RETURN NIL
|
|
|
|
// hReq fields (set by httpserver/server.go buildRequestHash):
|
|
// method, path, query, headers (hash), body, remote_addr
|
|
//
|
|
// Reply with a JSON envelope listing every field plus a roundtripped
|
|
// header so we know the hash walk is intact.
|
|
FUNCTION OnRequest( hReq )
|
|
LOCAL hPayload := {;
|
|
"ok" => .t.,;
|
|
"method" => hReq[ "method" ],;
|
|
"path" => hReq[ "path" ],;
|
|
"query" => hReq[ "query" ],;
|
|
"body_len" => Len( hReq[ "body" ] ),;
|
|
"remote_addr" => hReq[ "remote_addr" ],;
|
|
"user_agent" => hb_HGetDef( hReq[ "headers" ], "user-agent", "?" );
|
|
}
|
|
RETURN {;
|
|
"status" => 200,;
|
|
"headers" => { "Content-Type" => "application/json; charset=utf-8" },;
|
|
"body" => hb_jsonEncode( hPayload );
|
|
}
|