Function HTTP_MCP(Request, ProcErr) /*********************************************************************************************************************** This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written permission from SRP Computer Solutions, Inc. Name : HTTP_MCP (HTTP Master Controller Program) Description : HTTP Controller program for the application. Notes : In an MVC framework this is the 'Controller' routine that accepts HTTP requests nad routes them to the core Master Controller Program (MCP). HTTP_MCP is written as a replacement to the RUN_OECGI_REQUEST listner. It is intended to be a cleaner listener designed to allow REST style API calls and better control over request and response handling. Since it is a listner, it should be only modified when core functionality needs to be added or modified. Application specific changes should be kept within one or more of the called services. Parameters : Request [in] -- The request array which includes the HTTP request and OECGI provided information. ProcErr [in] -- Contains error messages in the event of a prior crash (such as a runtime error). The specified listener is automatically called in these situations with the ProcErr argument populated. Therefore, a check for data in this argument needs to occur immediately so that the error can be properly managed and returned to the caller in a suitable format. For RUN_OECGI_REQUEST applications, INET_ABORTED would normally be called to handle this. Response [out] -- HTTP response to send back to the OECGI. History : (Date, Initials, Notes) 02/06/15 dmb [SRPFW-90] Original programmer. Copied from INET_MCP but refactored for REST API requests. 04/14/15 dmb [SRPFW-90] Add missing '\' in the path for debug files to be written to. 02/23/16 dmb [SRPFW-103] Move the request/response capture path defintion into the SYSENV\SRP_HTTP_FRAMEWORK_SETUP record. 02/25/16 dmb [SRPFW-108] Replace Xlate with GetCapturePath service. 03/09/16 dmb [SRPFW-111] Call GetEntryPointService before calling RunHTTPService. 03/09/16 dmb [SRPFW-112] Remove HTTP_SERVICE_SETUP insert. 03/09/16 dmb [SRPFW-112] Call GetHTTPPathInfo before calling RunHTTPService. 05/17/16 dmb [SRPFW-125] Add support for ProcErr. Create a generic error response. 10/01/16 dmb [SRPFW-128] Add code to track API execution time using SRP_Stopwatch. Display the time in the Response log that is written into the debug folder. 02/18/17 dmb [SRPFW-151] Report the decoded Authorization data in the Response log. 02/27/17 dmb [SRPFW-125] Add support for the GetProcErrService service. If missing, the default ProcErr process logic will continue to work. 03/03/17 dmb [SRPFW-154] Replace direct logging with the CreateLogFile service. 03/08/17 dmb [SRPFW-155] Add support for setting the debugger mode and intercept. 07/01/17 dmb [SRPFW-184] Refactor using Enhanced BASIC+ syntax. 11/01/18 dmb [SRPFW-256] Add support for the GetServerEnabled service. Set status to 503 is server is not enabled. 11/18/18 dmb [SRPFW-257] Add support for the GetAPICallProcedure service. Use the RunWebAPI or RunHTTPService service as appropriate. 12/12/18 dmb [SRPFW-257] If Get_Status returns an error, produce a GetStatus log and then use the SetResponseError service so the client gets a detailed response. 12/16/19 dmb [SRPFW-296] Update code that calls the CreateLogFile service for Get_Status conditions so that the status detail is better formatted. Also, clear the error condition to prevent the OECGI from making a ProcErr call. ***********************************************************************************************************************/ #pragma precomp SRP_PreCompiler $insert LOGICAL $insert HTTP_INSERTS $insert INET_EQUATES $insert INET_HEADERS $insert Msg_Equates Equ CRLF$ to \0D0A\ Declare subroutine SRP_Stopwatch, Set_Status, RTI_Set_Debugger Declare function SRP_Stopwatch, RTI_OS_Directory If Assigned(Request) else Request = '' If Assigned(ProcErr) else ProcErr = '' If ProcErr NE '' then // Runtime errors produce two copies of the error description in the ProcErr argument. Just divide in half to get // one copy. If ProcErr[1, Len(ProcErr) / 2] EQ ProcErr[Len(ProcErr) / 2 + 1, Len(ProcErr) / 2] then ProcErr = ProcErr[1, Len(ProcErr) / 2] end end // Start timing the overall API. SRP_Stopwatch('Reset') SRP_Stopwatch('Start', 'WebAPI') // Set the mode for the debugger and identify the debugger intercept service if applicable. DebuggerSetting = HTTP_Services('GetDebuggerSetting') If DebuggerSetting EQ 2 then DebuggerService = HTTP_Services('GetDebuggerService') end else DebuggerService = '' end RTI_Set_Debugger(DebuggerSetting, DebuggerService) // Use HTTP_Services to store the HTTP request as provided by the OECGI and also to retreive the relevant request // information that will be used below. HTTP_Services('SetSessionID') HTTP_Services('SetOECGIRequest', Request) HTTP_Services('SetOECGIProcErr', ProcErr) HTTP_Services('SetRequestHeaderFields') HTTP_Services('SetQueryFields') // Create the HTTP Request log file. HTTP_Services('CreateLogFile', 'Request') If ProcErr NE '' then // An unexpected error occurred with the most recent request. The nature of the error (usually a runtime error) will // be contained in the ProcErr argument. Generate a response so the caller will receive a well formatted reply. AbortedService = HTTP_Services('GetAbortedService') If AbortedService NE '' then // There is a procedural error service designated to handle this condition. Allow it to process the error and // generate the response. Call @AbortedService(ProcErr) end else // There is no procedural error service so process this using default logic. Swap \00\ with \0D0A\ in ProcErr Swap @FM with \0D0A\ in ProcErr Swap @VM with \0D0A\ in ProcErr Swap @SVM with \0D0A\ in ProcErr // The ProcErr always contains two copies of the error description so just divide in half to get one copy. If ProcErr[1, Len(ProcErr) / 2] EQ ProcErr[Len(ProcErr) / 2 + 1, Len(ProcErr) / 2] then ProcErr = ProcErr[1, Len(ProcErr) / 2] end HTTP_Services('SetResponseError', '', '', 500, ProcErr, FullEndpointURL) end end else ServerEnabled = HTTP_Services('GetServerEnabled') // Check to see if the server is still enabled. If ServerEnabled then // Authenticate the request using HTTP Authentication Services. If the user is not validated then the appropriate // response status and headers will be set. If no authentication is required then the AuthenticateRequest service // should set the UserAuthenticated response to True as a default. // // This service is also where global response headers are set, regardless of whether the user is authenticated. // // Note: Even if authentication is disabled via the SRP_HTTP_FRAMEWORK_SETUP configuration record, the // AuthenticateRequest should still be called. It will inspect the flag and set the response accordingly. UserAuthenticated = HTTP_Authentication_Services('AuthenticateRequest') If UserAuthenticated then // Call the API based on the type of calling procedure specified in the setup. APICallProcedure = HTTP_Services('GetAPICallProcedure') If APICallProcedure EQ 'Web API' then HTTP_Services('RunWebAPI') end else EntryPointService = HTTP_Services('GetEntryPointService') RemainingURL = HTTP_Services('GetHTTPPathInfo') HTTP_Services('RunHTTPService', EntryPointService, RemainingURL) end end end else HTTP_Services('SetResponseError', '', '', 503, 'Server is temporarily disabled.') end end // Get the full response to send back to the requesting client. Response = HTTP_Services('GetResponse') // Stop timing the overall API. SRP_Stopwatch('Stop', 'WebAPI') TimeToExecute = SRP_Stopwatch('GetBenchmark', 'WebAPI') // Check the status before logging and returning the HTTP Response. If there is a status error then the Response // variable should be cleared and no log generated. The OEngineServer will resubmit a request with the error detail // the the ProcErr service will handle and log this. If Get_Status() EQ 0 then HTTP_Services('CreateLogFile', 'Response', Response) end else StatusCode = '' Status = Get_Status(StatusCode) HTTP_Services('SetResponseError', '', '', 500, 'Get_Status Error', FullEndpointURL, 'Status' : @FM : 'StatusCode', Status : @FM : StatusCode<1, 1> : ' - ' : StatusCode<1, 2>) Response = HTTP_Services('GetResponse') HTTP_Services('CreateLogFile', 'GetStatus', Response) Set_Status(0) end // Engage the debugger if requested. If HTTP_Services('GetRequestHeaderField', 'Debug') then Debug // Clear all saved values that were set in this session to avoid subsequent requests to a running engine from getting // invalid data. HTTP_Services('ClearSettings') // Clean up processes, as needed, that were set in this session to avoid subsequent requests to a running engine from // getting invalid data. HTTP_Authentication_Services('CleanUp') // Clear any possible internal OpenInsight errors so everything will process normally. Note, traditional INET does not // clear this flag automatically. This is how INET_ABORTED gets called if there is an SSP error. The SRP HTTP Framework // clears this flag by default because the developer can still trap Get_Status() in the relevant web service code and // create a custom HTTP response. Thus, the ProcErr service will only be called if there is a runtime error condition. * Set_Status(0) Return Response