2024-03-25 15:15:48 -07:00

196 lines
11 KiB
Plaintext

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