restincl — Enduro/X HTTP REST incoming call handler (HTTP Server, XATMI Client)
This is XATMI client which accepts HTTP/HTTPS REST requests, calls the XATMI server specified in configuration. The binary works with go routines (very cheap threads). But the number of concurrent invocations to XATMI sub-system is limited by workers initialization configuration flag. In case if all workers are busy, the incoming thread will wait for the XATMI worker to become free.
The configuration is defined in mixed for with INI and JSON formats. The configuration file contains keys which value can be set as multi-line JSON. Configuration file contains defaults key which defines the default parameters for the route. Then when new route is added, firstly it is initialized from defaults and then additionally it is overridden by route it self.
Incoming HTTP REST server (XATMI client), supports multiple buffer formats and different error handling schemes. Buffer formats can be:
The error handling can be done in following ways:
' ext - Special services and UBF buffers are used for handling the error cases.
The operation modes of the REST interface can by:
The typical restincl configuration looks like:
[@restin] port=8080 ip=0.0.0.0 # # Defaults: conv=json2ubf # async - call service in async way, if submitted ok, just reply back with ok defaults={"errors":"http" ,"reqlogsvc": "GETFILE" ,"noreqfilersp": true } # Service 1 /svc1={"svc":"DATASV1", "notime":false, "conv":"json2ubf", "errors":"json", "async":false ,"errfmt_json_code":"\"error_code1\":%d" ,"errfmt_json_msg":"\"error_message1\":\"%s\""} # Echo service: /echo={"notime":false, "conv":"json2ubf", "errors":"json", "async":false, "echo":true}
HTTPS mode is also supported. In this case configuration file contains ssl certificate file and key file. The format of these files are in x509 format.
It is possible to configure per request logging files. Then the parameter reqlogsvc must be set which indicates the service name which to invocate to acquire the request logging file name.
In case if multiple REST-IN handlers are needed with different binding IP/PORTS/HTTPS settings. it is possible to use NDRX_CCTAG setting (environment or in client process monitor configuration).
Next sections will describe supported buffer formats and it’s handling in the Enduro/X.
Following sub-chapters describes the modes, how buffers are converted from network message to Enduro/X format and vice-versa.
External handling mode, incoming message is loaded into generic UBF fields. Field names are following:
If fields are prepared OK, list of comma separated services found in finman are executed with UBF buffer. This can be used to build up the target request buffer. In case if any service fails from mandatory list, it is treated as general error and then incoming error service chain finerr is executed (as optional). When generating response EX_NETRCODE field with expected http code is read. If field is missing in buffers, then restincl will respond with error 500, without any body. In case of error and if buffer contains EX_NETRCODE, then system will attempt to return the EX_IF_RSPDATA.
If there was no error, after all incoming filter chains done, target service is executed. Once that is done, the response chain is activated. If there was no error during target service call, the foutman mandatory chain is executed. If it succeed, then optional chain foutopt chain is executed.
In case if destination service was called in synchronous mode, the user return value tpurcode(3) from tpcall(3) is added to UBF buffer field EX_IF_TPURCODE.
If there was error during the target service execution or mandatory out filter chain execution (foutman), the fouterr service chain is executed. If EX_NETRCODE is found in the body, then UBF content is used for response generation and http status is set to EX_NETRCODE. If in case of error EX_NETRCODE is not found, the response is set to 500 with out any content.
The response is generated in following way:
NOTE: The conv mode works only with ext error handling mode. And error handling mode ext works only with buffer conversion mode ext. See more details bellow for ext error handler mode.
For response EX_IF_RSPHN and EX_IF_RSPHV header field pairs generally supports multi-occurrence headers, exception is for Content-Type: for which if multi-occurrence are used, only last value will be present in response (or field will be set automatically set depending to the scenario).
restincl supports file upload to temporary folder TEMP_DIR set by tempdir flag. File upload works only in ext buffer mode. Upload mode is enabled by setting fileupload parameter to true.
The upload process performs following steps:
Following list of fields are added of the uploaded files:
Following HTML form may be used for data upload:
<html> <body> <form enctype="multipart/form-data" action="http://localhost:8080/ext_fileupload" method="post"> <input type="file" name="files" multiple /> <input type="submit" value="upload" /> </form> </body> </html>
Upload call of 3 files to service would look like:
EX_NREQLOGFILE /home/user1/endurox-connect/tests/01_restin/runtime/log/TRACE_1606490385644802141 EX_IF_URL /ext_fileupload EX_IF_METHOD POST EX_IF_REQFILEDISK /home/user1/endurox-connect/tests/01_restin/runtime/tmp/@restin-162189307 EX_IF_REQFILEDISK /home/user1/endurox-connect/tests/01_restin/runtime/tmp/@restin-410813726 EX_IF_REQFILEDISK /home/user1/endurox-connect/tests/01_restin/runtime/tmp/@restin-398043621 EX_IF_REQFILENAME 312329.image0.jpg EX_IF_REQFILENAME 312332.image0.jpg EX_IF_REQFILENAME consoleText (1).txt EX_IF_REQFILEMIME image/jpeg EX_IF_REQFILEMIME image/jpeg EX_IF_REQFILEMIME text/plain EX_IF_REQFILEFORM files EX_IF_REQFILEFORM files EX_IF_REQFILEFORM files
If wanting to keep third file in temp directory, following reply must be given:
EX_IF_RSPFILEACTION D EX_IF_RSPFILEACTION D EX_IF_RSPFILEACTION K
Additional caveats:
With JSON2UBF mode, it is expected that configured web service will receive JSON document formatted in one level, where basically data is encode in key:value format. Array’s types is supported. The array elements are loaded into UBF buffer field occurrences. The BLOB elements are encoded as Base64 data and are loaded into UBF buffer’s BFLD_CARRAY typed fields in decoded (binary form). If target field is not BFLD_CARRAY, then it is treated as string data and loaded into field via conversion functions.
The JSON2UBF POST REST data of service invocation of would look like:
{ "T_CHAR_FLD":"A", "T_SHORT_FLD":123, "T_LONG_FLD":444444444, "T_FLOAT_FLD":1.33, "T_DOUBLE_FLD":4444.3333, "T_STRING_FLD":["HELLO", "WORLD"], "T_CARRAY_FLD":"SGVsbG8=" }
That would be converted into following UBF buffer:
T_CHAR_FLD A T_SHORT_FLD 123 T_LONG_FLD 444444444 T_FLOAT_FLD 1.33 T_DOUBLE_FLD 4444.3333 T_STRING_FLD HELLO T_STRING_FLD WORLD T_CARRAY_FLD Hello
When response is generated for caller, the UBF buffer coming back from Enduro/X IPC would be in the same JSON format as in request - single level JSON document with arrays if necessary i.e. have multiple occurrences for field.
The restincl for incoming data does not check the MIME type, but in response MIME type will be set to: text/plain.
With JSON2VIEW mode, it is expected that configured web service will receive JSON document formatted in two levels, outer level is object with view name (which is configured in Enduro/X environment VIEWDIR and VIEWFILES. The second level of the JSON document basically is fields encoded in key:value format. Array’s types is supported. The array elements are loaded into UBF buffer field occurrences. The BLOB elements are encoded as Base64 data and are loaded into VIEW buffer’s carray fields typed fields in decoded (binary form). The standard UBF data conversation functions (CBchg(3)) are used for data converting.
The JSON2VIEW POST REST data of service invocation of would look like:
{ "MYVIEW":{ "char_fld":"a", "short_fld":123, "long_fld":444444444, "float_fld":1.33, "double_fld":4444.3333, "string_fld":["hello", "world"], "carray_fld":"SGVsbG8=" } }
That would be converted into following VIEW buffer:
VIEW MYVIEW #type cname fbname count flag size null char char_fld - 1 - - - short short_fld - 1 - - - long long_fld - 1 - - - float float_fld - 1 - - - double double_fld - 1 - - - string string_fld - 2 - 20 - carray carray_fld - 1 - 25 - # optional response fields, used if configured so: string rspmessage - 1 - 255 - short rspcode - 1 - - - END
When response is generated for caller, the VIEW buffer coming back from Enduro/X IPC would be in the same JSON format as in request - two level JSON document with arrays if necessary i.e. have multiple occurrences for field.
The restincl for incoming data does not check the MIME type, but in response MIME type will be set to: text/plain.
For error handling if configured so (using json2view errors), the restincl can install the ATMI error code and message in the VIEW before converting to JSON, Thus rspcode and rspmessage can be produced back in the JSON with corresponding content. In case of wrong configuration (errfmt_view_rsp does not contain response fields) or errfmt_view_rsp is not set at all, but error mechanism is json2view and response view does not have response fields, the restincl will generate {} - empty JSON object error. The caller shall assume this as format error or timeout (because there are no knowledge to caller of what have happened at the service).
In this case JSON text is received in POST message and buffer is loaded into XATMI buffer type JSON. Buffer is sent to target service. It is expected that target service will respond with valid JSON text back which is returned in HTTP response. In this case too, the response type is set to text/plain.
In this case arbitrary string is received from POST message. The string is loaded into Enduro/X buffer type STRING. And with this buffer the message is delivered to destination service (svc field from route configuration). The response also is generated as pure string, with MIME set to text/plain.
In this case arbitrary binary (BLOB) data is received from POST message. The BLOB is loaded inti CARRAY typed buffer and destination service is invoked with this buffer. If service invocation is success, then the received BLOB message from XATMI sub-system is returned to caller. In this case response will be generated as application/octet-stream.
restincl supports different error handling strategies for different URL setting/targets. Following sub-sections describes each handling mode.
As described in buffer ext buffer conversion mode, this error handling mechanism consists of following key features:
Ext mode of error handling includes additional diagnostics information for the service errors. When incoming processing fails (including target service), following fields are add to the buffer and are available for filters:
With this error handling method, the error codes are returned within HTTP protocol. The error code can be mapped from XATMI subsystem to HTTP codes by using errors_fmt_http_map parameter in service or default parameter block. The default mapping which is set if errors_fmt_http_map is not present, is following:
This is suitable for json and json2ubf buffer types. On response the JSON block is appended at then end with two fields. The fields are set with format string %s for error message in errfmt_json_msg parameter, for example "error_message":"%s". The error code format is set with %d in errfmt_json_code parameter, for example: "error_code":%d. The error codes are XATMI standard defined in xatmi.h. For example if calling JSON service and call times out, then response will be look like:
{"error_code":13,"error_message":"13:TPETIME (last error 13: ndrx_mq_receive failed: Connection timed out)"}
With this error handling mechanisms, which is suitable for JSON2UBF buffer conversion mode, the error message is loaded into top level JSON field EX_IF_ECODE and error message is loaded into EX_IF_EMSG field. This is suitable in case if using restout on the other Enduor/X server to bridge the servers using HTTP/Rest method.
The error code and message is generated in free form text which is provided by errfmt_text service parameter block field. The first parameter in format string must be %d - for XATMI error, and next parameter in format string must be %s- for error message. For example errfmt_text could be set to %d: %s.
No matter of which error handling mechanism is selected http/json/json2ubf/text, the list of Enduro/X error codes is following:
"errors_fmt_http_map":"<ATMI_ERROR_CODE_1>:<HTTP_STATUS_CODE_1>,..., <ATMI_ERROR_CODE_N>:<HTTP_STATUS_CODE_N>,*:<HTTP_STATUS_CODE_FOR_ANY_OTHER>"
for example:
"errors_fmt_http_map":"13:404,*:200"
means that XATMI error code 13 (time-out) will be mapped to HTTP status code 404. In case of any other XATMI error (*), the HTTP status code will be set to 200. The default value is as described above.
For example:
/static.*={"svc":"@STATIC", "format":"regexp", "conv":"static", "staticdir":"${NDRX_APPHOME}/static"} /={"svc":"@STATIC", "conv":"static", "staticdir":"${NDRX_APPHOME}/static"} /index.html={"svc":"@STATIC", "conv":"static", "staticdir":"${NDRX_APPHOME}/static"}
will perform following logic:
This section describes special built-in API which purpose is to allow to invoke XATMI services with-in global transaction. Where the global transaction is managed by web services consumer.
To enable transactions API, special route shall be configured. For example:
/tran_api={"transaction_handler":true}
Such configured route basically works as ext mode destination, thus filter calls such finman, finopt, etc.. are available and can be configured for the route.
API works as performing http POST of JSON message to the route. And in reply API provides result JSON with error code, error message and transaction id (if applicable).
In any case of success or error JSON body is returned (if it was possible to generate response). In case if request was success, HTTP code 200 is returned. In case if request is invalid (invalid args, etc), response code is set to 400, in case if some Enduro/X internal issues has happened, status code 500 is returned.
Table 1. List of basic data types
Type name | Description |
<Type>..X | Field length (range) form 0 to X. |
<Type>X..Y | Field length (range) from X to Y. |
<Type>X | Fixed field length (range) X. |
LONG | Signed long field. 32bit or 64bit value. Depending on platform. In case if range is specified and upper value is greater than 10, then for 32bit platforms this is defaulted to 10. |
STRING | String value. May contains all ASCII characters according to JSON standard. |
INT | Signed integer type, 32bit type. |
ULONG | Unsigned long number. 32 or 64 bit value. . Depending on platform. In case if range is specified and upper value is greater than 10, then for 32bit platforms this is defaulted to 10. |
Request shall be HTTP POST JSON to the specified URL.
Table 2. API Request message
JSON field | Format | Cond | Description |
---|---|---|---|
operation | STRING1..16 | Mand | Values: tpbegin - begin transaction. tpcommit - commit transaction. tpabort - abort transaction. |
timeout | ULONG1..20 | Optional | Transaction timeout in seconds. If not specified, default value 0 is used, which means maximum transaction time. Field is used only when operation is tpbegin, otherwise ignored. |
flags | LONG1..20 | Opt | Reserved for future use, and if specified, shall be set to 0. |
tptranid | STRING1..256 | Cond | Transaction token value. Used for tpcommit, tpabort. |
Request example message - begin transaction
{ "operation":"tpbegin" ,"timeout":60 ,"flags":0 }
Request example message - commit transaction
{ "operation":"tpcommit" "flags":0 ,"tptranid":"AABZWlQzb1VCQWtKUXdQNVF2UyttVTlpMlh5cDdyc0FFQUFnREkAAAAAAAAAAAAAAAAAAAEAAgDIAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA" }
Table 3. API response message
JSON field | Format | Cond | Description |
---|---|---|---|
operation | STRING1..16 | Mand | Echo from original call. |
error_code | INT1..2 | Mand | XATMI Error code. See section Error codes and it’s meaning for codes. In case of success 0 is returned. |
error_message | STRING1..1024 | Mand | Error description. In case of success success, value Succeed is returned. |
tptranid | STRING1..256 | Cond | Transaction token value returned by tpbegin or echo from tpcommit or tabort calls. Field is returned whenever transaction identifier is available. |
NOTE: The system which processes responses shall ignore any unknown (for which system is not aware of) which might appear in future releases on restincl.
Response example message - begin transaction
{ "operation":"tpbegin" ,"error_code":0 ,"error_message":"Succeed" ,"tptranid":"AABZWlQzb1VCQTdZMElsem9ZVGpPREl4ZkZVMkxHUHdFQUFnREkAAAAAAAAAAAAAAAAAAAEAAgDIAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA" }
Response example message - commit transaction
{ "operation":"tpcommit" ,"error_code":0 ,"error_message":"Succeed" ,"tptranid":"AABZWlQzb1VCQTdZMElsem9ZVGpPREl4ZkZVMkxHUHdFQUFnREkAAAAAAAAAAAAAAAAAAAEAAgDIAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA" }
Response example message - commit transaction (error)
{ "operation":"tpcommit" ,"error_code":1 ,"error_message":"1:TPEABORT (last error 1: tpcommit: Transaction was marked for abort and aborted now)" ,"tptranid":"AQBZWlQzb1VCQXBnVDBiaVQ1VHRtaFplY0JkamhhOXdFQUFnREkAAAAAAAAAAAAAAAAAAAEAAgDIAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA" }
Value is platform architecture dependent (CPU, OS version, data model). Value may be re-used in the same Enduro/X cluster strictly if other server has the same architecture and the other server has accessible originating tmsrv(8) instance.
XATMI service may be put in the transactions mode, in case if in request special header is set with the transaction id previously returned by transactions API. This header is used for what ever conv mode used. When XATMI server process finishes it returns updated transaction id header. For next service call or API operations last known transaction id must be used (if available).
Transaction mode is supported only synchronous service invocations, i.e. flags async and asyncecho must be false (which is default values). Transaction headers are ignored in these modes.
To call XATMI service in transaction mode, following steps must be accomplished (assuming that service’s transaction group is configured and that restincl is configured with NULL switch, routes for transaction handler and resource are defined):
For sample code of transaction usage see endurox-connect/tests/01_restin/src/trancl/trancl.go source code.
This section lists additional aspects that needs to be considered while calling services in transaction modes.
If route flag txnooptim (default true) is set to true and transaction is expired when performing service calls with endurox-tptranid-req header set, the error TPESYSTEM (code 12) is returned, as internally tpresume(3) call would fail with this error code. If flag txnooptim is set to false and call is made to previously already called resource manager (RM), then it is possible that TPETRAN (code 14) would be returned. Recommended way is to to leave txnooptim as true, as this ensures that transaction is terminated as soon as possible, and there is less chance for orphaned transaction, in case if for known RM on TMJOIN (if supported) on rolled back transaction does not return error code.
Retry commits currently are not supported. Thus if particular tptranid did succeed with tpcommit (error was 0), if performing commit again, error TPEABORT (code 1) would be returned. This is due to fact that tmsrv(8) does not hold the history of transactions, and by trying commit unknown transaction tmsrv reports it is TPEABORT this would be the typical case when unknown transaction id is being committed but tmsrv rolled it back due to time-out.
restincl must be configured with standard NULL switch (libndrxxanulls.so), other switches are not supported. NULL switch ensures that process may participate in global transaction by not touching any real resources by it self.
To put restincl in NULL switch mode, requires to certain configuration to be used and also instance of tmsrv(8) is required.
Fragment from ndrxconfig.xml(5):
<?xml version="1.0" ?> <endurox> ... <servers> ... <server name="tmsrv"> <min>1</min> <max>1</max> <cctag>TRAN</cctag> <srvid>200</srvid> <sysopt>-e ${NDRX_APPHOME}/log/tmsrv-rm1.log -r -- -t1 -l${NDRX_APPHOME}/tmlogs/rm2</sysopt> </server> </servers> ... <clients> ... <client cmdline="restincl"> <exec tag="TRAN" autostart="Y" cctag="TRAN" subsect="" log="${NDRX_APPHOME}/log/restin-tran.log"/> </client> ... </clients> ... </endurox>
Fragment from app.ini(5) definition of NULL transaction group:
... [@global/TRAN] NDRX_XA_RES_ID=1 NDRX_XA_OPEN_STR=- NDRX_XA_CLOSE_STR=- NDRX_XA_DRIVERLIB=libndrxxanulls.so NDRX_XA_RMLIB=- NDRX_XA_LAZY_INIT=1 ...
Report bugs to support@mavimax.com
Golang compiler problems:
go1.14 introduced use of SIGURG for internal purposes of the language runtime. This causes Enduro/X Unix system calls to interrupt with EINTR, which can lead to incorrect work of the binary using Enduro/X Go bindings.
The bug is reported here: https://github.com/golang/go/issues/50521
While this bug is not fixed, the restincl shall be started with following environment variable set:
export GODEBUG="asyncpreemptoff=1"
The setting may be applied to app.ini in [@global] section as:
[@global] ... GODEBUG=asyncpreemptoff=1