/* * bridge_request.prg - FiveNode * * Copyright (c) 2026 Charles KWON ( Charles KWON Ohjun, charleskwonohjun@gmail.com ) * All rights reserved. */ // bridge_request.prg // AP_* function reimplementation - 100% identical function names/signatures // ── Request functions ── FUNCTION AP_METHOD() RETURN ctx_get( "method", "GET" ) FUNCTION AP_FILENAME() RETURN ctx_get( "filename", "" ) FUNCTION AP_ARGS() RETURN ctx_get( "query_string", "" ) FUNCTION AP_USERIP() RETURN ctx_get( "remote_ip", "127.0.0.1" ) FUNCTION AP_BODY() RETURN ctx_get( "body", "" ) FUNCTION AP_GETBODY() RETURN ctx_get( "body", "" ) FUNCTION AP_GETENV( cVarName ) LOCAL hEnv := ctx_get( "env", { => } ) IF hb_HHasKey( hEnv, Upper( cVarName ) ) RETURN hEnv[ Upper( cVarName ) ] ENDIF RETURN hb_GetEnv( cVarName, "" ) // ── Headers In ── FUNCTION AP_HEADERSIN() RETURN ctx_get( "headers_in", { => } ) FUNCTION AP_HEADERSINCOUNT() RETURN Len( ctx_get( "headers_in", { => } ) ) FUNCTION AP_HEADERSINKEY( n ) LOCAL hHeaders := ctx_get( "headers_in", { => } ) IF n >= 1 .AND. n <= Len( hHeaders ) RETURN hb_HKeyAt( hHeaders, n ) ENDIF RETURN "" FUNCTION AP_HEADERSINVAL( n ) LOCAL hHeaders := ctx_get( "headers_in", { => } ) IF n >= 1 .AND. n <= Len( hHeaders ) RETURN hb_HValueAt( hHeaders, n ) ENDIF RETURN "" // ── Headers Out ── FUNCTION AP_HEADERSOUT() RETURN ctx_get( "headers_out", { => } ) FUNCTION AP_HEADERSOUTCOUNT() RETURN Len( ctx_get( "headers_out", { => } ) ) FUNCTION AP_HEADERSOUTKEY( n ) LOCAL hHeaders := ctx_get( "headers_out", { => } ) IF n >= 1 .AND. n <= Len( hHeaders ) RETURN hb_HKeyAt( hHeaders, n ) ENDIF RETURN "" FUNCTION AP_HEADERSOUTVAL( n ) LOCAL hHeaders := ctx_get( "headers_out", { => } ) IF n >= 1 .AND. n <= Len( hHeaders ) RETURN hb_HValueAt( hHeaders, n ) ENDIF RETURN "" FUNCTION AP_HEADERSOUTSET( cKey, cValue ) LOCAL hHeaders := ctx_get( "headers_out", { => } ) hHeaders[ cKey ] := cValue ctx_set( "headers_out", hHeaders ) RETURN NIL FUNCTION AP_SETCONTENTTYPE( cType ) RETURN AP_HEADERSOUTSET( "Content-Type", cType ) // ── Output ── FUNCTION AP_RPUTS( ... ) LOCAL n, u FOR n := 1 TO PCount() u := PValue( n ) IF ValType( u ) == "C" bridge_output_append( u ) ELSE bridge_output_append( fn_ValToChar( u ) ) ENDIF NEXT RETURN NIL FUNCTION AP_ECHO( ... ) LOCAL n, u FOR n := 1 TO PCount() u := PValue( n ) IF ValType( u ) == "C" bridge_output_append( u ) ELSEIF ValType( u ) != "U" bridge_output_append( fn_ValToChar( u ) ) ENDIF IF n < PCount() bridge_output_append( " " ) ENDIF NEXT RETURN NIL // ── Parameter Parsing ── FUNCTION AP_GetPairs( lUrlDecode ) LOCAL cArgs, aParams, aPair, hResult, cParam hb_default( @lUrlDecode, .T. ) hResult := { => } cArgs := AP_ARGS() IF ! Empty( cArgs ) aParams := hb_ATokens( cArgs, "&" ) FOR EACH cParam IN aParams aPair := hb_ATokens( cParam, "=" ) IF Len( aPair ) == 2 hResult[ aPair[ 1 ] ] := iif( lUrlDecode, hb_UrlDecode( aPair[ 2 ] ), aPair[ 2 ] ) ELSEIF Len( aPair ) == 1 hResult[ aPair[ 1 ] ] := "" ENDIF NEXT ENDIF RETURN hResult FUNCTION AP_PostPairs( lUrlDecode ) LOCAL cBody, aParams, aPair, hResult, cParam hb_default( @lUrlDecode, .T. ) hResult := { => } cBody := AP_BODY() IF ! Empty( cBody ) aParams := hb_ATokens( cBody, "&" ) FOR EACH cParam IN aParams aPair := hb_ATokens( cParam, "=" ) IF Len( aPair ) == 2 hResult[ aPair[ 1 ] ] := iif( lUrlDecode, hb_UrlDecode( aPair[ 2 ] ), aPair[ 2 ] ) ELSEIF Len( aPair ) == 1 hResult[ aPair[ 1 ] ] := "" ENDIF NEXT ENDIF RETURN hResult // ── URL decode ── FUNCTION hb_UrlDecode( cString ) LOCAL cResult := "", i, c cString := StrTran( cString, "+", " " ) i := 1 DO WHILE i <= Len( cString ) c := SubStr( cString, i, 1 ) IF c == "%" .AND. i + 2 <= Len( cString ) cResult += Chr( hb_HexToNum( SubStr( cString, i + 1, 2 ) ) ) i += 3 ELSE cResult += c i++ ENDIF ENDDO RETURN cResult // ── Path/URL ── FUNCTION fn_PathUrl() RETURN ctx_get( "uri", "/" ) FUNCTION fn_PathBase( cDirFile ) LOCAL cRoot := ctx_get( "document_root", "." ) IF cDirFile != NIL RETURN cRoot + "/" + cDirFile ENDIF RETURN cRoot FUNCTION fn_Include( cFile ) RETURN hb_MemoRead( fn_PathBase( cFile ) ) FUNCTION fn_GetUri() LOCAL cScheme := iif( ctx_get( "https", .F. ), "https", "http" ) LOCAL cHost := "" LOCAL hHeaders := AP_HEADERSIN() IF hb_HHasKey( hHeaders, "Host" ) cHost := hHeaders[ "Host" ] ENDIF RETURN cScheme + "://" + cHost + ctx_get( "uri", "/" ) FUNCTION fn_Redirect( cUrl ) AP_HEADERSOUTSET( "Location", cUrl ) ctx_set( "status", 302 ) RETURN NIL // ── Data Conversion ── FUNCTION fn_ValToChar( u ) LOCAL cType := ValType( u ) SWITCH cType CASE "C" ; RETURN u CASE "N" ; RETURN hb_ntos( u ) CASE "D" ; RETURN DToC( u ) CASE "L" ; RETURN iif( u, ".T.", ".F." ) CASE "U" ; RETURN "NIL" CASE "A" ; RETURN hb_jsonEncode( u ) CASE "H" ; RETURN hb_jsonEncode( u ) CASE "T" ; RETURN hb_TToC( u ) ENDSWITCH RETURN "(" + cType + ")" // ctx_get / ctx_set are defined as PUBLIC in bridge_context.prg // -- mod_harbour compatibility wrappers -- FUNCTION MH_PathUrl() RETURN fn_PathUrl() FUNCTION MH_PathBase( cDirFile ) RETURN fn_PathBase( cDirFile ) FUNCTION MH_Include( cFile ) RETURN fn_Include( cFile ) FUNCTION mh_GetUri() RETURN fn_GetUri() FUNCTION mh_Redirect( cUrl ) RETURN fn_Redirect( cUrl ) FUNCTION MH_ValToChar( u ) RETURN fn_ValToChar( u ) // ── JSON Response helpers ── FUNCTION AP_JSONRESPONSE( xData, nStatus ) IF nStatus != NIL ctx_set( "status", nStatus ) ENDIF AP_SETCONTENTTYPE( "application/json" ) AP_RPUTS( hb_jsonEncode( xData ) ) RETURN NIL FUNCTION AP_SETSTATUS( nStatus ) ctx_set( "status", nStatus ) RETURN NIL // ── Auth (read-only from .prg, set by Node.js only) ── FUNCTION fn_Auth() LOCAL cAuth := _AUTH_GET() LOCAL hAuth IF Empty( cAuth ) RETURN { "authenticated" => .F. } ENDIF hb_jsonDecode( cAuth, @hAuth ) IF ValType( hAuth ) != "H" RETURN { "authenticated" => .F. } ENDIF RETURN hAuth FUNCTION fn_IsAuth() RETURN fn_HGetDef( fn_Auth(), "authenticated", .F. ) FUNCTION fn_AuthUser() RETURN fn_HGetDef( fn_Auth(), "user", "" ) FUNCTION fn_AuthRole() RETURN fn_HGetDef( fn_Auth(), "role", "" ) // ── Hash helper ── FUNCTION fn_HGetDef( hHash, cKey, xDefault ) RETURN hb_HGetDef( hHash, cKey, xDefault ) // ── JSON Body helper ── FUNCTION AP_JSONBODY() LOCAL cBody := AP_BODY() LOCAL xResult IF ! Empty( cBody ) hb_jsonDecode( cBody, @xResult ) ENDIF RETURN xResult