// app/bridge_server.prg — 1a.3-3 end-to-end glue. // // Wires httpserver (Go) → bridge_capi (Go) → bridge_*.prg so a PRG // handler written in the mod_harbour AP_* style runs inside the // fivenode_go single binary exactly as it would have run inside the // koffi/N-API fivenode bridge. // // Demo routing is hard-coded to two paths so we can curl the binary // and see the AP_* surface working end-to-end. File-name dispatch // (POST /api/foo.prg → app/api/foo.prg) is sub-phase 1a.4 work. FUNCTION Main() LOCAL cErr := HTTP_SERVER_START( ":8090", "BRIDGEDISPATCH" ) IF cErr != NIL ? "httpserver:", cErr ENDIF RETURN NIL // Entry point invoked by httpserver. hReq comes from // buildRequestHash in hbrtl_ext/httpserver/server.go. FUNCTION BridgeDispatch( hReq ) LOCAL hCtx // Translate the Go-side request hash into the ctx fields the // mod_harbour AP_* PRG layer expects to read. hCtx := { ; "method" => hReq[ "method" ], ; "filename" => hReq[ "path" ], ; "query_string" => hReq[ "query" ], ; "body" => hReq[ "body" ], ; "remote_ip" => RemoteIPOnly( hReq[ "remote_addr" ] ), ; "headers_in" => hReq[ "headers" ], ; "headers_out" => { => }, ; "status" => 200 ; } _CTX_SET_JSON( hb_jsonEncode( hCtx ) ) _OUT_CLEAR() // Hard-coded route dispatch. Replace with file-name dispatch in // 1a.4 once we want to land the labdb API surface unchanged. DO CASE CASE hReq[ "path" ] == "/api/hello" ApiHello() CASE hReq[ "path" ] == "/api/echo" ApiEcho() OTHERWISE ctx_set( "status", 404 ) AP_JSONRESPONSE( { "error" => "not found", "path" => hReq[ "path" ] } ) ENDCASE // Assemble hResp from the buffered AP_* output. RETURN { ; "status" => ctx_get( "status", 200 ), ; "headers" => ctx_get( "headers_out", { => } ), ; "body" => _OUT_GET() ; } FUNCTION ApiHello() AP_JSONRESPONSE( { ; "ok" => .t., ; "msg" => "hello from fivenode_go bridge layer", ; "method" => AP_METHOD(), ; "ip" => AP_USERIP() ; } ) RETURN NIL FUNCTION ApiEcho() LOCAL cBody := AP_BODY() LOCAL hPayload := IIF( Empty( cBody ), { => }, hb_jsonDecode( cBody ) ) AP_JSONRESPONSE( { ; "ok" => .t., ; "method" => AP_METHOD(), ; "query" => AP_ARGS(), ; "body_len" => Len( cBody ), ; "body_parsed" => hPayload, ; "user_agent" => hb_HGetDef( ctx_get( "headers_in", { => } ), "user-agent", "?" ) ; } ) RETURN NIL // Strip the ":port" portion of a Go RemoteAddr so AP_USERIP returns // the bare IP the way mod_harbour does. FUNCTION RemoteIPOnly( cAddr ) LOCAL n IF Empty( cAddr ) RETURN "" ENDIF n := RAt( ":", cAddr ) IF n > 0 RETURN Left( cAddr, n - 1 ) ENDIF RETURN cAddr