Files
fivenode_go/app/echo_server.prg
Charles KWON OhJun 384f957f4e feat(httpserver): HTTP_SERVER_START / _STOP with PRG handler dispatch
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>
2026-05-27 10:30:50 +09:00

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 );
}