Guru: Web Concepts For The RPG Developer, Part 2
June 10, 2024 Chris Ringer
Hello again! Part 1 of Web Concepts for the RPG Developer was an introduction on how to build components in an HTTP request and I hope you enjoyed it. Part 2 is just a continuation of that vast topic because there is so much to learn. I encourage you to click on the links in this article and explore on your own too.
JSON
A web API endpoint may require the data in the body of an HTTP POST request to be constructed as JSON (“jay sahn,” short for JavaScript Object Notation). JSON is formatted text (a string) containing key:value pairs of data. JSON data may be in a single level (example 1) or nested (JSON in JSON), and contain arrays noted by the [square] brackets (example 2). JSON has largely replaced XML because JSON is more compact in size and is more human readable. Also, JSON maps nicely to RPG data structures.
01 { 02 "userID":"Tom0121", 03 "hireDate":19900531, 04 "canSeePayroll":true 05 }
Example 1
Line 01: A JSON string begins with a left-hand curly brace {. This code is in the pretty format. This could all be coded as a single line too without the line feeds and spacing. JSON parsers don’t care.
{"userID":"Tom0121","hireDate":19900531,"canSeePayroll":true}
Line 02: Key names are double quoted. String values are also double quoted. Each key and value pair is separated by a colon. The trailing comma means more key:value pairs follow.
Line 03: This value is numeric so is unquoted.
Line 04: This value is boolean so is unquoted.
Line 05: A JSON string ends with a right-hand curly brace }.
You could easily build this JSON string using just RPG concatenation.
Here is the equivalent RPG data structure:
dcl-ds *n; userID varchar(50) inz('Tom0121'); hireDate int(10) inz(19900531); canSeePayroll ind inz(*On); end-ds;
And this JSON example includes an array of values.
01 { 02 "company":"Kramer Inc", 03 "users":[ 04 {"userID":"Tom0121", "hireDate":19900531, "canSeePayroll":true}, 05 {"userID":"Bob5678", "hireDate":19920815, "canSeePayroll":false} 06 ] 07 }
Example 2
Line 01: The beginning of the JSON string.
Line 02: The company name is Kramer Inc.
Line 03: This key contains an array of values, indicated by the left-hand square bracket [.
Lines 04 and 05: Here we see 2 array elements separated by a comma.
Line 06: The right-hand square bracket ] ends the array.
Line 07: The end of the JSON string.
Epoch
What time is it? It sounds like a simple question but the answer is of course “it depends”. If you were to manually set the time zone offset on your computer, you’ll be presented with 38 different time zone choices (figure 1) ranging from -12:00 hours in the western hemisphere to +14:00 hours in the eastern hemisphere. So maybe a better question is “Whose time is it?”
To avoid ambiguity in timestamps in an HTTP request on the world wide web, a common time reference known as GMT (Greenwich Mean Time) may be used. GMT is time zone offset zero AKA the prime meridian AKA 0 degrees longitude which runs through Greenwich England (figure 2). And a common data format to represent GMT is the epoch.
An epoch (“eee pahk”) is the number of seconds since Jan 1, 1970 in GMT. An epoch may be expressed as 10 digits or 13 digits (with 3 implied decimal positions). Since all computers are configured with a time zone offset, It’s easy to convert the local time to an epoch, just subtract the time zone offset seconds.
01 dcl-s wkEpochSecs uns(10); 02 exec sql values( 86400 * 03 (DAYS(CURRENT TIMESTAMP - CURRENT TIMEZONE) - DAYS('1970-01-01')) + 04 MIDNIGHT_SECONDS(CURRENT TIMESTAMP - CURRENT TIMEZONE) ) 05 into :wkEpochSecs ;
Line 01: Our unsigned integer will hold the epoch value. Signed epoch integers will max out in the year 2038.
Line 02: The number of seconds in one day (24 hours x 60 minutes x 60 seconds).
Line 03: The number of DAYS since Jan 1, 1970, in GMT, multiplied by the number of seconds in a day.
Here your IBM i time zone offset is subtracted from your local time. This means in the western hemisphere (IE: USA) where your time zone offset is negative, you are actually adding time.
Line 04: MIDNIGHT_SECONDS is a scalar function that returns the current number of seconds since midnight today, in this case in GMT. This value is added to Line 03.
Line 05: The result is a 10-digit epoch integer. If today is 27 May 2024 16:45:59 GMT then the equivalent epoch value is 1716828359.
OK, so that’s great but when is an epoch needed? Epochs are often present in JWTs (JSON Web Tokens), when a client (a user or a machine) is requesting temporary access to a resource on a web server. The authorization server will want to know when the requested access would begin (now) and end (in a few hours or even minutes). I’ll cover JWTs in detail in a future article. For this article, here is some simple JSON that could be sent in the body of an HTTP request.
01 { 02 "userID":"Tom0121", 03 "iat":1716828359, 04 "exp":1716828659, 05 }
Line 01: The beginning of the JSON string.
Line 02: The client requesting the access.
Line 03: “iat” means “issued at” and this epoch value means now, the current GMT date and time.
Line 04: “exp” means the expiration time. The difference between lines 04 and 03 is 300 seconds, so in 5 minutes from now.
Line 05: The end of the JSON string.
Message Digests
A message digest is a one-way conversion of any string or file into a shorter representation to verify data integrity. The known message digest may be compared to the current computed message digest. If even a single bit in the string or file changes, the message digest also changes.
We see this a very similar mechanism in Db2 for the various file types. When an RPG program containing an F-Spec (DCL-F) is compiled, that current record format ID is saved in the program object (and is visible with DSPPGMREF). When the program attempts to open that file defined as LVLCHK(*YES), if the current record format ID does not equal the saved value, we get a CPF4131 level check error, alerting us that something has changed.
A popular message digest is SHA256 which converts a string or file into a 256 bit representation (32 binary bytes). For display purposes, the 32 non-text bytes are usually converted to 64 readable nibble values. Here we convert some JSON to SHA256 in QShell. In a 5250 session, run STRQSH from a command line and copy/paste this echo command into QShell (without the line number “01”).
01 echo '{"userID":"Bob9876","canSeePayroll":false}' | openssl dgst -sha256 02 SHA2-256(stdin)= 2d409491eef33c4ae2ef154c0a361fb94d3cbfc160e692ed76770f3a4240d28e
Line 01: The echo command just outputs the string inside the single quotes to the screen.
The pipe | character redirects the screen output to instead be the input of the next command, in this case openssl dgst (message digest). Openssl is a cool cryptographic tool available in Qshell. The dgst option -sha256 is the specific message digest type to create.
Line 02: This is the output message digest value, 64 characters long. A message digest is not a unique value. If you created an exact copy of a stream file, both stream files would generate the same SHA256 message digest.
Now let’s see what happens when we change the payroll flag from false to true. Copy/paste this echo command into Qshell. The message digest has now changed drastically and we therefore know the data has been tampered with.
echo '{"userID":"Bob9876","canSeePayroll":true}' | openssl dgst -sha256 SHA2-256(stdin)= e3f6e8290ec35b9b57aba409d9540d237572fb3a01628ea622e275aded294425
Outro
I mentioned this in part 1 but it’s worth repeating. The schema (blueprint) of the web API endpoint you need to consume will determine how the HTTP request must be formatted. If you need to send a PDF and the schema requires the PDF to be converted into base64 and embedded as a value in JSON, then that’s what you must do.
Stay tuned for Part 3, when we revisit base64 and then tackle asymmetric encryption using a private and public key pair. Happy coding!
Chris Ringer began coding RPG programs in 1989, and after a recent unexpected but valuable detour to C# is happy to be back in the IBM world. In his spare time he enjoys cycling and running – and taking the family dog Eddie for walks.
RELATED STORIES
Guru: Web Concepts For The RPG Developer, Part 1
Guru: The PHP Path To Victory, Part 1
BTW, there are also HASH function in SQL (usable in RPG).
Or, even API use for other tasks (like HMAC).
BTW, to get straight EPOCH seconds you can even call time64 function from RPG to get them directly.