Chapter 6. The Server Module Interface
A server module is a load module that HTTPD runs for a route. The route names the module with the keyword MOD=, as described in Chapter 2, “Routes and Authentication Policy”. The module receives the request, writes the response and returns. HTTPD itself needs no more from it.
This chapter is the contract between a module and the server. It describes how HTTPD starts a module and what the module finds when it runs, the control blocks that it may read, every entry of the function vector (HTTPX), the variables of a request, the storage rules, what happens when a module abends, and what the started task needs for a module to work. The header httpcgi.h declares everything in this chapter. A module includes it and nothing else of HTTPD.
The HTTPD User’s Guide shows how to build and install a module, in “Writing a Server Module”. The four modules that come with HTTPD are in Chapter 7, “The Display Modules”.
How a Module Is Run
HTTPD runs a module with the LINK SVC, in the task of the worker that serves the request. It runs the module once per request, whatever the method is: GET, HEAD, PUT, POST and DELETE all arrive at the module in the same way. The module has to complete the response before it returns. When it returns, HTTPD ends the request, whatever the return code is.
The LINK is made through the C library routine __linkdsx. The routine sets an ESTAE first, so that an abend of the module comes back to the server as a result and does not end the worker (see “Abends”). The module name is the name in the route. If the server holds the module in storage, the LINK finds that copy (see “Resident Modules”).
Before the module runs, HTTPD has read the request, parsed it into variables (see “The Request Variables”), looked for a credential and found the route. It then applies the authentication rule of the route. A request that fails the rule is answered with 401 or 403 by the server and never reaches the module. HTTPD itself answers the paths /login and /logout. Only after these steps does the server run the module of a route that has one (MOD=). A route without a module (LOC=) serves static files.
The Parameter List
HTTPD builds a parameter list of three words for the module.
| Word | Content | Meaning |
| 0 | address of a parameter | A halfword length and the text of the parameter: the request path in double quotes, for example "/zosmf/info". The text has at most 254 characters, the quotes included; a longer path is cut. |
| 1 | address of HTTPD | The server control block, with the eye catcher *HTTPD*. |
| 2 | address of HTTPC | The control block of the client, with the eye catcher *HTTPC*. The high bit is set to mark the end of the list. |
Table 52. The parameter list of a module
A module does not read this list. The startup code that every module is linked with, cgistart, reads it.
What the Startup Code Does
cgistart supplies the entry point __start of the module. The module is linked with it; it comes from the CGI library of HTTPD. In this order it does the following.
- It scans the list for the two eye catchers and stores the address of HTTPD and the address of HTTPC in the global runtime table (GRT) of the task, in the fields
grtapp1andgrtapp2. - If the module is run outside HTTPD, it stops here. It writes “is an HTTPD server module and cannot run on its own” and ends with the exit status
EXIT_FAILURE. The display modules of Chapter 7, “The Display Modules” write the messageHTTPD920Eand return 12 instead. - It opens
stdouton the DDHTTPDOUT,stderron the DDHTTPDERRandstdinon the DDHTTPDIN. IfHTTPDINis missing, it opens'NULLFILE'forstdin. The started task procedure has the three DDs (see “The Started Task Procedure”). - It loads environment variables from the DD
SYSENV. If there is no such DD, it tries the DDENVIRON. - It calls
tzset(). - It builds
argvfrom the parameter of word 0 and callsmain(argc, argv). Around the call it switches the heap of the module to the request subpool (see “Storage Rules”).
The module finds in main:
argc- 2.
argv[0]- The name of the module, up to 8 characters.
argv[1]- The decoded
REQUEST_PATH, without the quotes. - HTTPD, HTTPC
- Read
grt->grtapp1andgrt->grtapp2. The server has the functionscgihttpd()andcgihttpc()for this lookup, but they are not part of the client library, and a module cannot link them. - HTTPX
- Call
http_get_httpx(httpd).
printf needs a note. The startup code contains its own printf, and it takes the place of the library function in a module. When the module runs under HTTPD, printf sends its output as part of the response, like http_printv. It does not write to stdout. A call of fprintf(stdout, ...) writes to HTTPDOUT.
The module must have a C stack that is smaller than the default. The default of the runtime asks for 256 KB of contiguous storage in subpool 0 for every request. The modules of HTTPD set a smaller one in their own source file:
unsigned __stklen = 64*1024;
Put this variable in the source file of the module and not in a library.
The Return Code
When main returns, the startup code clamps a negative value to 0 and exits with the value. HTTPD takes the return code in this way:
- A value of 0 or more is not an error. The server does not look at it.
- An abend, a module that cannot be found or loaded, and a few other failures are errors. The server answers them as described in “Abends”.
After the module has returned, the server sets the state of the client to CSTATE_DONE. It closes the files, writes the statistics (see Chapter 8, “SMF Records”) and then resets the client or closes the connection. The module does not call any of these steps.
A Minimal Module
This module answers every request with a line of text.
#include <stdio.h> #include <mvs/crt.h> #include "httpcgi.h" unsigned __stklen = 64*1024; int main(int argc, char **argv) { CLIBGRT *grt = __grtget(); HTTPD *httpd = grt->grtapp1; HTTPC *httpc = grt->grtapp2; HTTPX *httpx = http_get_httpx(httpd); http_resp(httpc, 200); http_printf(httpc, "Content-Type: text/plain\r\n"); http_printf(httpc, "\r\n"); http_printf(httpc, "Hello from %s\n", argv[1]); return 0; }
The macros of httpcgi.h expand into a call through the local variable httpx. The variable must be in scope where a macro is used. The modules in the HTTPD source that include httpd.h instead define httpx as (httpd->httpx).
The Structures
The header httpcgi.h defines the structures that a module may look into: HTTPC, HTTPV and HTTPM, and the enumerations CSTATE and RDW. It leaves the others opaque: HTTPD, CRED, UFS, UFSFILE, CIB and HTTPROUTE. A module holds pointers to them and passes them to the vector functions. It does not look inside them. The two accessors that read HTTPD are in “The Direct Accessors”.
HTTPC, the Client
HTTPC is 4096 bytes long. HTTPD allocates it when it accepts a connection and frees it when the connection ends. The structure belongs to the server. A module reads the fields and lets the vector functions change them.
| Offset | Name | Type | Meaning |
| 00 | eye | char[8] | *HTTPC*. |
| 08 | httpd | HTTPD * | The server. |
| 0C | env | HTTPV ** | The array of request variables. Use the variable functions of “Request Variables” and do not walk the array. |
| 10 | state | short | The state of the request (see below). |
| 12 | subtype | UCHAR | A state of the server’s own file transfer. Do not use it. |
| 13 | substate | UCHAR | As subtype. |
| 14 | socket | int | The socket of the client. |
| 18 | addr | unsigned | The IP address of the client. http_ntoa converts it. |
| 1C | port | int | The port of the client. |
| 20 | fp | FILE * | The file of the response, if the server opened one. http_done closes it. |
| 24 | len | unsigned | The length of data in buf. |
| 28 | pos | unsigned | The position of the next unsent byte in buf. |
| 2C | sent | unsigned | The bytes sent to the client for this request. It counts every raw byte, the status line, the headers and the chunk framing included. |
| 30 | start | double | The start time of the request in seconds. |
| 38 | end | double | The end time of the request in seconds. |
| 40 | rdw | UCHAR | A value of RDW. http_printf also uses this byte as the flag “the headers have ended” (see “Output”). |
| 41 | chunked | UCHAR | 1 if the response is sent in chunks. |
| 42 | resp | short | The status code. It is 0 until http_resp has been called. |
| 44 | cred | CRED * | The credential of the client, or NULL. A module does not use it directly. Use the functions of “Authentication”. |
| 48 | ufs | UFS * | The UFS session of the client, or NULL. http_get_ufs creates it. |
| 4C | ufp | UFSFILE * | The UFS file that http_open opened, or NULL. |
| 50 | ssi | UCHAR | 1 if the file is processed for server side includes. |
| 51 | ssilevel | UCHAR | The nesting level of the includes. The maximum is 10. |
| 52 | content_length_set | UCHAR | 1 if a Content-Length header was sent. |
| 53 | keepalive | UCHAR | 1 if the connection stays open after this response. |
| 54 | connect_time | unsigned | The time of the connect in 1/100 s (SMF time). |
| 58 | total_bytes_sent | unsigned | The bytes sent on this connection, over all requests. |
| 5C | request_count | unsigned | The requests answered on this connection. |
| 60 | buf | UCHAR[4000] | The work buffer of the server for input and file transfer. A module does not keep data in it. |
Table 53. The fields of HTTPC
The header comments for subtype and substate give the offsets 11 and 12. The structure puts them at 12 and 13, because state is two bytes long.
The field state holds a value of CSTATE:
| Name | Value | Meaning |
CSTATE_IN | 0 | HTTPD reads the request. |
CSTATE_PARSE | 1 | HTTPD parses the request. |
CSTATE_GET | 2 | The method is GET. |
CSTATE_HEAD | 3 | The method is HEAD. |
CSTATE_PUT | 4 | The method is PUT. |
CSTATE_POST | 5 | The method is POST. |
CSTATE_DELETE | 6 | The method is DELETE. |
CSTATE_DONE | 10 | The response is complete. No more output is accepted. |
CSTATE_REPORT | 11 | HTTPD writes the statistics. |
CSTATE_RESET | 12 | HTTPD resets the client. |
CSTATE_CLOSE | 13 | HTTPD closes the connection. |
Table 54. The values of CSTATE
A module runs in one of the states 2 to 6. It reads the state to see the method. The state becomes CSTATE_DONE when the module returns, and also when a send fails because the client has gone. After that http_printf, http_printv and http_send return −1. A module that loops over a long response tests the state or the return codes and stops.
The values of RDW are RDW_NONE (0), RDW_B2, RDW_L2, RDW_B4 and RDW_L4. The server sets rdw to 1 itself when the headers end (see “Output”).
HTTPV, a Variable
A request variable is a block of this form.
| Offset | Name | Type | Meaning |
| 00 | eye | char[8] | *HTTPV*. |
| 08 | value | char * | The value. It points into the same block. |
| 0C | name | char[2] | The name, a zero byte, the value and a zero byte, one after the other. |
Table 55. The fields of HTTPV
HTTPM, a MIME Type
http_mime returns a pointer to this structure of 12 bytes.
| Offset | Name | Type | Meaning |
| 00 | ext | UCHAR * | The file extension. |
| 04 | type | UCHAR * | The MIME type, for example text/html. |
| 08 | binary | int | 1 if the file is sent as binary, 0 if it is text. |
Table 56. The fields of HTTPM
The Request Variables
Before the module runs, HTTPD has read the request line and the headers and has stored them as variables of the client. A module reads them with http_get_env. The names are not case sensitive. All values are in EBCDIC.
| Name | Value |
HTTP_REQUEST | The request line as it was sent. |
REQUEST_METHOD | The method. |
REQUEST_URI | The URI as it was sent, with the query. |
REQUEST_VERSION | The version of the request: HTTP/1.0 or HTTP/1.1. HTTPD rejects any other with the status 505. |
REQUEST_PATH | The path of the URI, decoded, without the query. |
QUERY_STRING | The text after the question mark of the URI, not decoded. The variable exists only if the URI has a query. |
QUERY_name | One variable for each parameter of the query. The value is decoded, the name is not. A parameter without an equals sign has the value “”. |
HTTP_name | One variable for each header of the request, for example HTTP_Host or HTTP_Content-Type. The name is as the client sent it. The value follows the colon, without leading blanks. The header Cookie makes no HTTP_Cookie see the next row. |
HTTP_Cookie-name | One variable for each cookie. A cookie without an equals sign has the value “”. |
POST_STRING | The body of a POST request (see below). |
POST_name | One variable for each field of a form body (see below). |
SERVER_ADDR | The address of the server end of the connection. |
SERVER_PORT | The port of the server end of the connection. |
SERVER_PROTOCOL | Always HTTP/1.1, also for an HTTP/1.0 request. |
SERVER_SOFTWARE | HTTPD/ and the version of the server. |
SCRIPT_FILENAME | The document root and the request path. HTTPD sets it only for a route whose path begins with *., for example *.lua. If it cannot set the variable, the request fails with the status 500 and the module does not run. |
Table 57. The request variables
A request with more than 256 parameters in the query or in a form body gets the status 400 and the connection is closed. The request line must fit in the buffer, else the status is 414. A header with a name that has a character other than a token character, or a value with a control character, gets 400. So does a request with two Host headers. A request with a malformed Content-Length gets 400. HTTP/1.1 requests without a Host header get 400 too. A method other than GET, HEAD, PUT, POST and DELETE gets
- A request with
Expect: 100-continueis answered by the server
(HTTP/1.1 only). None of these requests reaches the module.
The body of a request is treated by the method.
- POST
- HTTPD reads the body with one
recvif the headerContent-Lengthis present and the length is between 1 and 3999. It converts the body to EBCDIC withhttp_atoeand stores it inPOST_STRING. If the headerContent-Typeis exactlyapplication/x-www-form-urlencoded, in any case, it also splits the body into the fieldsPOST_nameand decodes the values. A body of 4000 bytes or more is not read. On a route without a program the status is 413 and the connection is closed. On a route with a program, the module runs after the authentication check, the body is still on the socket, and the connection closes after the response. A request without a body, or with a chunked body, has noPOST_STRING. A request with a body and noContent-Typeis not read either. - PUT
- HTTPD does not read the body. The module reads it from the socket in
httpc->socket. - DELETE, GET, HEAD
- HTTPD does not read a body.
Every request that has a Content-Length or a Transfer-Encoding header ends the connection after the response. HTTPD turns keep-alive off because it cannot be sure that it has read the whole body.
The Function Vector
A module calls the server through the function vector HTTPX. The vector is a structure of function pointers. HTTPD fills it once, when it starts. The address of the structure is in HTTPD at offset 8, and http_get_httpx(httpd) returns it. The vector is 328 bytes (X’148′) long.
The Rules
The vector is append-only. An entry never moves and never changes its meaning. A new function is added at the end. Two consequences follow for a module.
- A module that was built against an older header runs on a newer server.
- A module that was built against a newer header can run on an older server, but an entry that the older server does not have holds 0 there, or lies beyond the end of the vector. There is no function that tells a module which entries exist. The only entry that a module can test before it calls is
http_cgi_subpool: it is 0 on a server that is too old to have the request subpool. Name the lowest HTTPD level of the module in its own documentation.
The tables below give the level in which each entry first appears. The level “4.0” means that the entry is in HTTPD 4.0.0 and every later release. The layouts of earlier releases, HTTPD 3.3 included, are not covered here.
The header declares a macro for every entry. The macro has the name of the entry and expands into a call through the variable httpx. The names http_find_cgi, http_add_cgi and http_process_cgi are the earlier names of http_find_route, http_add_route and http_process_route. The header keeps them. The entries did not move.
The entries 0x10C and http_read (0xD4) hold 0 in every release. Do not call them.
The Entries
Table 58. The entries of the function vector
The following sections describe the entries by group. A group begins with the declarations of its functions as the macros take them. The sections list return values and constraints for each function.
Output
int http_resp(HTTPC *httpc, int code); int http_printf(HTTPC *httpc, const char *fmt, ...); int http_printv(HTTPC *httpc, const char *fmt, va_list args); int http_send(HTTPC *httpc, const UCHAR *buf, int len); UCHAR *http_server_name(HTTPD *httpd);
A response has four parts, in this order:
http_resp(httpc, code)for the status line and the standard headers.- Zero or more header lines, each with
http_printf. - One call with the text
"\r\n"and nothing else. It ends the headers. - The body, with
http_printforhttp_send.
http_resp- Sets
httpc->respto the code and sends the status line. The line has the version of the request, HTTP/1.1 or HTTP/1.0. The function also sends the headersDate,Server: HTTPD Server,X-Powered-By: MVSLOVERSandConnection, which iskeep-aliveorclose. It does not end the headers. It returns 0, or a value other than 0 if the send fails. The codes that HTTPD knows are 200, 201, 202, 204, 206, 301, 302, 303, 304, 400, 401, 403, 404, 405, 409, 410, 412, 413, 414, 429, 500, 501, 502, 503, 505 and 507. For any other code it writes the console messageHTTPD909Eand sends 500. http_printf,http_printv- Format the text in a buffer of 5120 bytes, convert it to ASCII and send it. Text that is longer than 5119 bytes is cut. They return 0 on success and −1 on failure. They do not return a count of bytes. They return −1 at once if the state of the client is
CSTATE_DONEor later. The two functions are the same:http_printvtakes ava_list. http_send, raw bytes- Sends the bytes of the buffer as they are. The caller provides ASCII or binary data. The function does not look at the data. If the response is not chunked, it returns the number of bytes sent, which can be less than
len. It returns 0 if the socket is not ready and −1 if the connection is dead. In the second case it sets the state toCSTATE_DONE. If the response is chunked, it sends the buffer as one chunk and returnslen, or −1 after a failure. A failure turns chunking and keep-alive off. A call withlen0 or less returns 0. http_server_name- Returns the string
HTTPD Server. The argument is not used.
The end of the headers. http_printf watches for the call that ends the headers. This call has the text "\r\n" alone, exactly two characters. When it sees the call, it sets httpc->rdw to 1. If the request is HTTP/1.1, the status is not 1xx, 204 or 304, and no Content-Length header has been sent, it sends Transfer-Encoding: chunked first and turns chunking on. From then on every send is a chunk, and the server sends the final empty chunk when the request is done.
A HEAD request. While a module runs for a HEAD, httpc->state is CSTATE_HEAD. A response to a HEAD ends at the empty line after the headers. Once the call that ends the headers has been made, http_printf and http_printv send nothing and return 0, so a body that the module writes with them does not reach the client. The server does not send Transfer-Encoding: chunked for a HEAD, whatever the module sent. The status line and the header lines before the empty line go out as for any method, a Content-Length among them. http_send is not changed: bytes that a module sends with it reach the client. A module that answers a HEAD therefore sends the status and the headers, and no body. Releases before HTTPD 4.5.0 chunk the response of a module to a HEAD and send its body.
Two consequences follow.
- Send the end of the headers as a call of its own. Do not write
"...\r\n\r\n". HTTPD does not recognize the end of the headers in such a call. The response is then not chunked, and the client does not know where the body ends. A later call with exactly"\r\n"in the body would be taken for the end of the headers and would add a header line to the body. - A header line that goes out with
http_sendis not examined. HTTPD does not see aContent-Lengthin it.
A line that begins with Content-Length: in a call of http_printf sets content_length_set. Chunking is for HTTP/1.1 only. For an HTTP/1.0 request send a Content-Length.
If a send makes no progress, the server retries it, with a short pause between the tries. It gives up when the server is quiescing or shutting down, or when the retries have used their budget. It then writes a message to the console (throttled) and sets the state to CSTATE_DONE.
Standard Error Responses
int http_resp_not_found(HTTPC *httpc, const UCHAR *path); int http_resp_internal_error(HTTPC *httpc); int http_resp_not_implemented(HTTPC *httpc);
Each of the three functions sends a complete response, with the end of the headers, and returns 0, or a value other than 0 if the send fails. They do not set the state to CSTATE_DONE. Return from the module afterwards.
| Function | Status | Body |
http_resp_not_found | 404 | 404 document "path" not found. The headers are Cache-Control: no-store and Content-Type: text/plain. If path is NULL, the body has no text. |
http_resp_internal_error | 500 | 500 Internal Server Error, as text/plain. |
http_resp_not_implemented | 501 | 501 Not Implemented, as text/plain, with Cache-Control: no-store. |
Table 59. The standard error responses
Request Variables
UCHAR *http_get_env(HTTPC *httpc, const UCHAR *name); int http_set_env(HTTPC *httpc, const UCHAR *name, const UCHAR *value); unsigned http_find_env(HTTPC *httpc, const UCHAR *name); int http_del_env(HTTPC *httpc, const UCHAR *name); HTTPV *http_new_env(const UCHAR *name, const UCHAR *value); int http_set_http_env(HTTPC *httpc, const UCHAR *name, const UCHAR *value); int http_set_query_env(HTTPC *httpc, const UCHAR *name, const UCHAR *value); UCHAR *http_decode(UCHAR *str);
The names are compared without regard to case.
http_get_env- Returns the value of the variable, or NULL if there is none. The pointer is valid until the variable is changed or deleted, or until the request ends.
http_set_env- Creates the variable, or replaces the value of an existing one. Returns 0, or −1 on failure. A failure also writes the message
HTTPD904EorHTTPD905E. http_find_env- Returns the index of the variable plus one, or 0 if there is none.
http_del_env- Deletes the variable and always returns 0.
http_new_env- Builds a variable block and returns it, or NULL. The sum of name and value must not exceed 8192 bytes, else the function fails with
errnoE2BIG. It also returns NULL when it has no storage. The block comes from subpool 0. The function does not add it to the client. The other functions do that. http_set_http_env- Stores the value as
HTTP_name. The server uses it for the headers. A header calledCookieis split into the variablesHTTP_Cookie-name. The name must fit in 256 bytes. http_set_query_env- Stores the value as
QUERY_name. The name must fit in 256 bytes. http_decode- Decodes a URL-encoded string in place and returns it. A sequence
%xxbecomes the character of the server code page (see “Translation”). A+becomes a blank. A%that is not followed by two hexadecimal digits stays as it is, and so does the escape%00. The server decodes the path of a request with a function of its own, in which a+stays a plus sign (“URL Decoding and Escaping”).
A module that creates its own variables chooses names that do not collide with the names of “The Request Variables”. The variables live until the request is reset.
Translation
UCHAR *http_etoa(UCHAR *buf, int len); UCHAR *http_atoe(UCHAR *buf, int len); unsigned char *http_xlate(unsigned char *buf, int len, const unsigned char *tbl); typedef struct { const unsigned char *atoe; const unsigned char *etoa; } HTTPCP; /* in the vector: HTTPCP *xlate_cp037, *xlate_1047, *xlate_legacy */
HTTPD works in EBCDIC and talks ASCII. The conversion tables depend on the code page of the server (see Chapter 1, “The HTTPPRM Keywords” for the keyword).
http_etoa,http_atoe- Translate the buffer in place with the table of the server code page and return the buffer. A
lenof 0 means the length of the string. If the server has no context, the code page is CP037. Never pass a string literal: the literal may sit in storage that the module cannot write, and the call abends with S0C4. Copy the text to an array first. http_xlate- Translates in place with a table of 256 bytes that the caller gives. A table of NULL means the table EBCDIC to ASCII of the server. Returns the buffer.
xlate_cp037,xlate_1047,xlate_legacy- Entries of the vector that point to the pairs of tables for the code pages CP037, IBM-1047 and LEGACY. Use the member
etoaoratoeas the table ofhttp_xlate.
The names of the code pages that the server accepts are CP037, IBM1047 and LEGACY. A UFS file is stored in IBM-1047. http_send_file uses that table for it.
Time, MIME Types and Addresses
UCHAR *http_date_rfc1123(time64_t t, UCHAR *buf, size_t size); double *http_secs(double *psecs); const HTTPM *http_mime(const UCHAR *path); char *http_ntoa(struct in_addr addr);
http_date_rfc1123- Writes the time as a date of the form
Thu, 01 Jan 2026 12:00:00 GMT(the format of RFC 1123) into the buffer and returns the buffer. http_secs- Writes the time since the epoch, with microseconds, into the double that the argument points to. Read the double that you gave and ignore the return value. The declaration says that the function returns a pointer, but the function returns a double by value. Only the lower 32 bits of the seconds are used.
http_mime- Returns the MIME type of the path. The type comes from the extension of the last part of the path. The search does not depend on case. For a name of the form
dd:name(member)the part in the parentheses is dropped. An unknown extension gives the typetext/plainas text. The function never returns NULL. The extensionshtmlandhtmgivetext/html. The extensionsshtmandshtmlgivetext/x-server-parsed-html. http_ntoa- Writes the address as
a.b.c.dinto a buffer of the C runtime of the task and returns the buffer. The next call in the same task overwrites it.
Files
FILE *http_open(HTTPC *httpc, const UCHAR *path, const HTTPM *mime); int http_send_text(HTTPC *httpc); int http_send_binary(HTTPC *httpc); int http_send_file(HTTPC *httpc, int binary); UFS *http_get_ufs(HTTPC *httpc);
http_open- Opens a file of the document root. It always returns NULL. Look at
httpc->ufpto see whether the open worked: it is not NULL if it did. The function refuses a path that contains..and cuts a path to 255 bytes. Ifmimeis NULL, it callshttp_mimeitself. It opens the file as binary if the type is binary and as text if not. It setshttpc->ssito 1 for the typestext/htmlandtext/x-server-parsed-htmland to 0 for the others. The file name is the document root with the path appended. http_send_text,http_send_binary,http_send_file- Send the open file in steps. One call reads a piece of the file into
httpc->buf, sends what it can and returns 0. Call it again and again untilhttpc->stateisCSTATE_DONE. The state becomesCSTATE_DONEat the end of the file, and also when the send fails or the UFS read fails. The text form translates the data from IBM-1047 to ASCII, the binary form sends it as it is.http_send_textishttp_send_file(httpc, 0)andhttp_send_binaryishttp_send_file(httpc, 1). The function processes server side includes ifhttpc->ssiis 1:<!--#echo var="X" -->,<!--#include file="x" -->, the form withvirtualand<!--#printenv -->. Send the headers before the first call. Do not close the file.http_donecloses it. If a UFS read fails, the response ends: chunking and keep-alive go off and the server writes a console message. http_get_ufs- Returns the UFS session of the client. It creates the session the first time. It returns NULL if the server has no UFS.
The server uses these functions for its own static GET. Its steps are a model for a module that serves files: get the type with http_mime, open the file with http_open, send the status, Content-Type and the end of the headers, then send the file.
Dynamic Arrays
void *array_new(unsigned count); int array_add(void *varray, void *item); int array_addf(void *varray, const char *fmt, ...); unsigned array_count(void *varray); unsigned array_size(void *varray); void *array_get(void *varray, unsigned index); void *array_del(void *varray, unsigned index); int array_free(void *varray);
The array entries give a module the dynamic arrays of the C library, so that the module does not need to be linked with them. An array is a pointer to the items. The first argument of the functions is the address of that pointer, not the pointer itself. The indexes run from 1 to the count. array_free frees the array and does not free the items. HTTPD itself tests the result of array_add against 0 for success. The C library documents the rest.
Comparison
int http_cmp(const UCHAR *s1, const UCHAR *s2); int http_cmpn(const UCHAR *s1, const UCHAR *s2, int n);
http_cmp- Compares two strings without regard to case. It returns the difference of the first characters that differ, and 0 if the strings are the same.
http_cmpn- The same, for at most
ncharacters.
Debug Output
int http_dbgw(const char *buf, int len); int http_dbgs(const char *str); int http_dbgf(const char *fmt, ...); int http_dump(void *addr, int len, const char *title, ...); int http_enter(const char *fmt, ...); int http_exit(const char *fmt, ...);
The six functions write to the debug file of the server, the DD HTTPDBG. The server opens the file only if the keyword DEBUG is on (see Chapter 1, “The HTTPPRM Keywords”). If it is off, the functions write nothing. If the file cannot be opened, the server writes HTTPD020E and the output goes to stderr. If a write fails, the server writes HTTPD039W once and switches the debug output off until the next start. From then on the functions write nothing.
http_dbgw- Writes
lenbytes. http_dbgs- Writes a string.
http_dbgf- Writes formatted text.
http_dump- Writes a dump of
lenbytes in lines of 16 bytes with a title. The title is formatted into a buffer of 80 bytes. Keep it short. http_enter,http_exit- Write the text with the prefix “Enter “ or “Exit “ to mark the entry to a function and the exit from it.
Authentication
UCHAR *http_get_userid(HTTPC *httpc, UCHAR *out, unsigned outlen); struct acee *http_get_acee(HTTPC *httpc); int http_get_token(HTTPC *httpc, UCHAR *out, unsigned outlen); int http_check_auth(HTTPC *httpc, const char *class, const char *resource, int attr); int http_logout(HTTPC *httpc); UCHAR *http_get_password(HTTPC *httpc, UCHAR *out, unsigned outlen); const char *http_realm(HTTPD *httpd);
The server has already identified the client when the module runs. It reads the credential from these sources in this order: the cookie Sec-Token, the cookie LtpaToken2, a Bearer token and then Basic authentication. The mode of the route (AUTH=, see Chapter 2, “Routes and Authentication Policy”) changes only the response to a client that has no credential. A route with AUTH=NONE passes the request without a credential. All functions in this group accept a client without a credential.
http_get_userid- Copies the userid of the client into
outand returnsout. A userid has at most 8 characters, so a buffer of 9 bytes holds it; in a smaller buffer the userid is cut. The function returns NULL, with an empty string inout, if the client has no credential. It returns NULL ifoutis NULL oroutlenis 0. http_get_acee- Returns the ACEE of the client, or NULL. A module that calls the RACF routines itself with this ACEE sees the raw SAF code. An unprotected resource then answers 4, not 0. Use
http_check_authunless you need the difference. http_get_token- Copies the token of the session of the client, 32 bytes, into
outand returns 32. It returns 0 if there is no credential oroutlenis less than 32. The token is binary. The caller encodes it with base64 to send it. http_check_auth- Asks RACF whether the client may use the resource under the class, with the attribute
attr:RACF_ATTR_READ,RACF_ATTR_UPDATE,RACF_ATTR_CONTROLorRACF_ATTR_ALTER. 0 means READ. It returns 0 if the access is permitted, 8 or more (the return code of SAF) if it is refused, and −1 if the client is not authenticated. The case “no profile” is also returned as 0. Test for 0. Never test for a value of 4 or less: that would let an unauthenticated client (-1) through. http_logout- Removes the session of the client and clears the credential of the request. It returns 0, or −1 if there was no session.
http_get_password- Decrypts the password of the client and writes it into
out. The password is in upper case. It returnsout, or NULL if there is no credential or if the credential has no password (a session that is known only by token). In a buffer that is too small the password is cut. Overwrite the buffer when you are done with it. http_realm- Returns the realm of the server: the value of the keyword
REALM, or the SMF identifier of the system if there is none (see Chapter 1, “The HTTPPRM Keywords”). It is never NULL. The string stays valid for the life of the address space. A module that sends its ownWWW-Authenticatechallenge uses it as the realm, so that the challenge of the module and that of the server agree.
Sessions
int http_new_session(HTTPC *httpc);
http_new_session creates a new login session for the client of the request. The session has the same userid, password and address as the credential of the request. It has a token of its own and an ACEE of its own. The function is for a login endpoint of a module. Each call costs a RACF logon and a place in the credential store. The entry exists from HTTPD 4.2.
The function returns 0 on success. Then http_get_token returns the token of the new session. If the module cannot send the token to the client, it calls http_logout so that the session does not stay.
From HTTPD 4.5 the function reports why it failed:
| Constant | Value | Meaning |
HTTP_SESSION_NONE | −1 | The request is not authenticated, or it carries no password to log on again with. A credential that came from a token alone has no password. |
HTTP_SESSION_REFUSED | −2 | RACF refused the logon (return code 4, 8, 12 or 28 of RACINIT). The server marks the credential as stale. It does not answer by password or token from it any more and frees it 600 seconds later. The current request still runs on it. The module answers 401. |
HTTP_SESSION_BUSY | −3 | The client has 16 sessions, all in use, or the credential store is full. A session is replaced only if it has been idle for 60 seconds or more. |
HTTP_SESSION_NOMEM | −4 | The server is out of storage, or RACF could not be asked: RACF is not active, an installation exit or a terminal or time restriction stopped the logon. The server then writes the message HTTPD009W a refusal of the password is not written. |
Table 60. The results of http_new_session
Before HTTPD 4.5 every failure returns −1. Test the result for != 0 or < 0, not for == -1. Treat any other negative value as an unknown failure.
The Context Block
void *http_cgictx_get(HTTPD *httpd, const char *eye, unsigned size);
The function returns a block of storage that lives as long as the server and is the same for every request. See “Context Pointers” for the details.
Routes
HTTPROUTE *http_find_route(HTTPD *httpd, const char *path); HTTPROUTE *http_add_route(HTTPD *httpd, const char *pgm, const char *path, int login);
http_find_route- Searches the route table in the order of the Parmlib and returns the first route that matches the path, or NULL. A route with a wildcard (
*or?) matches by pattern, any other route by equality without case. http_add_route- Adds a route. It copies
pgmandpath. Apgmof NULL makes a route for static files (as the keywordLOC=). The argumentloginis not used. The new route is public, as withAUTH=NONE. It returns NULL on failure.
Entries for the Server Only
The following entries are the steps of the request cycle and the control of the worker pool. The server calls them. A module does not call them. The table says what each one does.
| Entry | What it does |
http_in, http_parse | Read the request and parse it into variables. |
http_get | Serves a static file for GET. See Chapter 5, “HTTP Behaviour and Limits”. |
http_head | Serves a static file for HEAD. It calls http_get, which sends the status and the headers of a GET and stops at the empty line when the state is CSTATE_HEAD. The state then becomes CSTATE_DONE. See “Methods”. Before HTTPD 4.5.0 it was a stub that answered 501. |
http_put, http_post | Stubs. Each answers 501 and sets the state to CSTATE_DONE. POST is routed to http_put. |
http_done | Closes fp and ufp, sends the final chunk of a chunked response, sets the end time and the state CSTATE_REPORT. |
http_report | Adds to the counters and writes the SMF record (see Chapter 8, “SMF Records”). The state becomes CSTATE_RESET. |
http_reset | Frees the variables and clears the request fields. With keep-alive the state becomes CSTATE_IN, else CSTATE_CLOSE. |
http_close | Removes the client from the table, closes the files, the UFS session and the socket, and frees HTTPC. |
http_is_busy, http_set_busy, http_reset_busy | Manage the table of the clients that a worker is serving. |
http_process_clients, http_process_client | The loop of the worker. |
http_console | Handles an operator command. |
http_link, http_process_route | Run a module (see “How a Module Is Run”). http_link returns the result codes of “Result Codes of a Link”. |
Table 61. The entries that belong to the server
The Direct Accessors
HTTPD is opaque. Two macros read a field of it. They are not entries of the vector. They read the storage at a fixed offset, and the server checks the offsets when it is built.
#define http_get_httpx(h) (*(HTTPX **)((char *)(h) + 8)) #define http_get_flag(h) (*(volatile unsigned char *)((char *)(h) + 0x2C)) #define HTTPD_FLAG_QUIESCE 0x40 #define HTTPD_FLAG_SHUTDOWN 0x80
http_get_httpx- Returns the function vector.
http_get_flag- Returns the flag byte of the server. The bit
HTTPD_FLAG_QUIESCEmeans that the server accepts no new requests. The bitHTTPD_FLAG_SHUTDOWNmeans that the server is shutting down.
Both macros exist from HTTPD 4.0. See “Long-Running Handlers and Shutdown” for the use of the flags.
Result Codes of a Link
http_link returns the outcome of the LINK as one integer. The server uses it to build the answer that is described in “Abends”. The values:
| Value | Number | Meaning |
| 0 or more | The module ran and returned this code. | |
HTTP_LINK_ENOLOAD | −0x01000001 | The LINK failed and the cause is not S806. |
HTTP_LINK_ENOPGM | −0x01000002 | The route has no program name. |
HTTP_LINK_EESTAE | −0x01000003 | The ESTAE could not be set. The module did not run. |
HTTP_LINK_ENOMOD | −0x01000004 | The module is not in the STEPLIB or the link list (S806). |
HTTP_LINK_EPGMRC(rc) | −0x02000000 + rc | The module ran and returned a negative code rc. The macros HTTP_LINK_IS_PGMRC(v) and HTTP_LINK_PGMRC(v) test the value and take the code out. The startup code clamps a negative code to 0, so only a module with its own start code returns one. |
| any other negative value | The module abended. The value is the negated completion code in the form X’00sssuuu’ (system code sss, user code uuu). |
Table 62. The result of http_link
Every negative value means that the module did not finish its work. For the values that say that the module never ran, there is no dump to look for.
Storage Rules
A module runs in the task of a worker of HTTPD. Its storage follows from that.
- Heap
- The startup code switches the heap of the module to the request subpool, subpool 5, for the time of
main. Everything that the module gets withmallocbelongs to the request. A module frees what it allocates, on every path. If the module abends, the server frees the whole subpool with one FREEMAIN. When the worker task ends, MVS frees what is left. http_cgi_subpoolUCHAR http_cgi_subpool(HTTPC *httpc)returns the number of the request subpool, 5, also available as the constantHTTP_CGI_SUBPOOL. It returns 0 for a client that the server does not own, and the entry holds 0 in a server that is too old to have the subpool. In that case the startup code sets no subpool and the heap is as it was. A module does not need to call it.- Storage that must live longer
- Get it with
__getmsp(size, 0)(declared inmvs/storage.h), or ask for a block withhttp_cgictx_get(see “Context Pointers”). Storage in any other subpool does not survive the request. - No writable static data
- A module must not write to a file-scope variable or to a static variable of a function. A module that is fetched from an APF-authorized library or from the link list is in storage of key 0, while its code runs in key 8. The first write to the module storage then abends with S0C4. The library decides this, not the attribute AC(1) and not RENT. A switch to key 0 with
__super()does not help, because a module can be in key 0 without the right to switch. - Where to keep state
- Keep the state of a request in local variables, on the heap, or in the memory that
__wsaget()returns. Keep state of the server in the block ofhttp_cgictx_get. Variables that the module only reads are fine in the static data: tables, and constant strings. - Strings that are translated
- Declare
UCHAR text[] = "..."and notUCHAR *text = "...", if you translate the text in place. The array is a copy on the stack. The pointer points to the constant itself. - Server allocations
- The functions
http_new_env,http_open,http_get_ufsandhttp_add_routeget their storage from subpool 0 inside the server. A reclaim of subpool 5 does not touch it. - Stack
- See “What the Startup Code Does” for
__stklen.
Long-Running Handlers and Shutdown
A request that takes long holds a worker. When the server stops, it sets the flags HTTPD_FLAG_QUIESCE and HTTPD_FLAG_SHUTDOWN in the flag byte, and it waits up to 10 seconds, in steps of 1 second, for the socket thread to end (it writes the messages HTTPD040I and HTTPD041I while it waits). The sends of the server honor the flags: they stop and set the state to CSTATE_DONE.
A module that loops, waits or sends in pieces looks at the flags in each round and returns when a flag is set:
while (more_to_send) { if (http_get_flag(httpd) & (HTTPD_FLAG_QUIESCE | HTTPD_FLAG_SHUTDOWN)) break; if (httpc->state == CSTATE_DONE) break; /* ... send the next piece ... */ }
Abends
Two layers of recovery protect a worker.
The module. The LINK of the module is made through __linkdsx, which sets an ESTAE. If the module abends, the ESTAE retries the call of the server. The worker lives and serves its next request. The server then does this in order:
- It frees the subpool 5. If the FREEMAIN fails, it writes
HTTPD906W STORAGE RECLAIM FOR pgm FAILED, RC=n. - It builds the text of the answer from the result of “Result Codes of a Link”.
- If no response was started (
httpc->respis 0), it sends the status 503 withContent-Type: text/plainand the end of the headers. If the module had begun the response, the text goes after what the module sent. - It writes the body
External program pgm text, and the console messageHTTPD908E EXTERNAL PROGRAM pgm text. - It sets the state to
CSTATE_DONE.
The text is one of these:
| Text | Cause |
| was not found | The module is in no library of the STEPLIB or link list (S806). |
| could not be loaded | The LINK failed for another reason, for example a fetch error or too little storage. |
| was dispatched without a program name | The route has no program. |
| could not be started, ESTAE create failed | The ESTAE could not be set. |
| returned rc n | The module returned the negative code n. |
| failed with Snnn ABEND | A system abend with the code Snnn. |
| failed with Unnnn ABEND | A user abend with the code Unnnn, in decimal. |
Table 63. The texts of a failed module
An abend of a module does not end the worker. A reclaim of subpool 5 releases what the module allocated with malloc. It does not release what the module holds outside subpool 5: files that the module opened itself, a UFS session that it created, or storage from other subpools. A module that needs to clean these up after an abend must do so with its own recovery.
The worker. Each worker runs under an ESTAE of its own, with a retry. If the abend happens in the code of the server and not in a module, the server writes HTTPD062E ABEND code IN WORKER(id) CLIENT(addr) SOCKET(n), clears the busy mark of the client, closes the client and the worker serves on. If the worker cannot recover, it writes the message HTTPD063E, which says that there is no recovery in the worker and that the client is dropped. The messages are in Appendix A, “Messages”.
The Started Task Procedure
A module runs in the address space of the server. It has no allocations of its own. When it opens a ddname, the system looks for it in the procedure of the server. A module is therefore a part of the contract of the procedure: if the procedure lacks a DD that a module needs, the module fails.
The procedure that comes with HTTPD has these DDs. It also has the DDs SYSUDUMP, SNAP and HTTPSTAT, all with SYSOUT=*. The DDs HASPCKPT and HASPACE1 are in it as comment lines.
| DD | Use |
STEPLIB | The load library of the server and of the modules. |
HTTPDERR | stderr of a module. |
HTTPDOUT | stdout of a module. |
HTTPDIN | stdin of a module. It is DUMMY. |
HTTPDBG | The debug file (see “Debug Output”). |
HTTPPRM | The Parmlib member (FREE=CLOSE). |
HASPCKPT, HASPACE1 | Optional, comment lines in the sample. The JES2 checkpoint data set and the spool volume. HTTPD never opens them. See below. |
Table 64. The DDs of the HTTPD procedure
The two JES2 DDs are for the modules that read JES2. A module that calls the C library routine jesopen() opens the checkpoint and the spool. The jobs interface of mvsMF is such a module. A DD in the procedure wins. When the procedure has no DD, the routine of LIBC/370 2.24.0 and later allocates SYS1.HASPCKPT and SYS1.HASPACE itself. The DDs are therefore optional for a module built with such a level, which includes mvsMF 1.4.0 and later. They stay mandatory in two cases:
- The module is built with an older level of the C library. The jobs interface of mvsMF 1.3.0 and earlier is such a module. Without the DDs, every job list answers 500 and every lookup of a job by name and id answers 404, a spool read included, and two lines go to the console.
- The JES2 of the system builds its data set names with a prefix other than
SYS1. The allocation looks only for theSYS1names.
A job that is submitted does not need the DDs, because the submit goes through the internal reader, which the module allocates itself. The messages and the answers are in “The JES2 data sets”.
To use the DDs, uncomment them in the sample member:
//HASPCKPT DD DSN=SYS1.HASPCKPT,DISP=SHR,UNIT=3350,VOL=SER=MVS000 //HASPACE1 DD DSN=SYS1.HASPACE,DISP=SHR,UNIT=3350,VOL=SER=SPOOL1
The unit and the volume serial must be those of the JES2 procedure of the site. The values above are those of the sample procedure. Before you remove a DD that is active in a procedure, find out which modules of other products use it.
Resident Modules
With the keyword RESIDENT=YES, which is the default, the server loads each module of its routes once, when it starts. It does a BLDL for every different name in a MOD= keyword and then a LOAD in the main task. A LINK of the worker then finds the module in storage and does not fetch it from disk again. The keyword is described in Chapter 1, “The HTTPPRM Keywords”.
The server holds a module only if it is reentrant or reusable (RENT or REUS). For any other case it writes a message and leaves the module to be fetched at each request:
| Message | Meaning |
HTTPD440W | The module was not found when the server started. It is not held. |
HTTPD441W | The module is not reusable. It is fetched for each request. |
HTTPD442W | The load failed with the abend code shown. The module is fetched for each request. |
HTTPD443W | The value of RESIDENT is not YES or NO. |
Table 65. The messages of the resident load
The server holds every module that it could load. Its count of the held modules, which D C and the server block show, stops at 255. The server deletes the modules when it ends. Two consequences follow for the module author:
- A new level of a resident module does not take effect until the server is restarted. With
RESIDENT=NOthe next request fetches the new level. - A module that is not RENT or REUS works, but it is fetched anew for every request.
The display module HTTPDSRV (see Chapter 7, “The Display Modules”) shows the setting and the number of held modules.
Context Pointers
Storage that a module allocates in a request does not outlive it. A module that needs data across requests, such as a cache or a table of open resources, asks for a block of the server:
void *http_cgictx_get(HTTPD *httpd, const char *eye, unsigned size);
The server keeps a table of blocks. The block is identified by an eye catcher of 8 bytes. The first call with an eye catcher creates the block. It gets the requested size from storage that lives as long as the address space, clears it and writes the eye catcher into its first 8 bytes. Every later call with the same eye catcher returns the same block. The size counts only when the block is created, so a module always gives the same size. The server never frees the block.
The function returns NULL if the table is full or if there is no storage. The table is locked for the duration of the call, but the block itself is not. A module that shares a block between requests, which can run at the same time in different workers, serializes its own access.
The size of the table is set by the Parmlib keyword CGI_CONTEXT_POINTERS. The default is 255 and the maximum is 255. The server cuts a larger value to 255 and ignores a negative value. The table has one more place than the value. The keyword is in Chapter 1, “The HTTPPRM Keywords”. HTTPDSRV shows the value (see Chapter 7, “The Display Modules”).
The function exists from HTTPD 4.0.