INTERFACEHTTPApp ;
This interface provides a general structure for HTTP applications. The client writes the handlers for arguments, logging, request, and replies while the library handles the basic HTTP processing.
IMPORT App, HTTP, IP, Rd, Wr; TYPE RequestPriority = {High, Normal, Low}; RequestHandler <: RequestHandlerPublic; RequestHandlerPublic = OBJECT priority: RequestPriority := RequestPriority.Normal; METHODS accept(request: HTTP.Request; serverData: REFANY; VAR (* OUT *) acceptState: REFANY; log: App.Log): BOOLEAN RAISES {App.Error}; (* returns TRUE if the handler is prepared to accept the request. "serverData" is data given to the "Serve" procedure by the application. "acceptState" is passed on to "request" if if the request is accepted. *) request(request: HTTP.Request; serverData, acceptState: REFANY; rd: Rd.T; wr: Wr.T; log: App.Log) RAISES {App.Error}; (* "request" is invoked in its own thread for a request received by the server. "serverData" is data passed to the "Serve" procedure "acceptState" is data created in the "accept" method "request" is a parsed HTTP header, "rd" is a reader on the the remainder of the request. "wr" is a writer for the reply. "log" is for reporting conditions to the server's log. "status" and "error" messages to "log" are also written to "wr". *) END; TYPE ReplyHandler <: ReplyHandlerPublic; ReplyHandlerPublic = OBJECT METHODS reply(reply: HTTP.Reply; rd: Rd.T; wr: Wr.T; log: App.Log) RAISES {App.Error}; (* reply is normally invoked to handle the reply of a client request or proxied request. "reply" is the parsed headers of the reply. "rd" is a reader on the remainder of the reply. "wr" is the writer given to the "Client" call. "log" is for reporting conditions to the server's log. The reply handler filters the contents of "rd" and writes them to "wr". *) END; Server <: ServerPublic; ServerPublic = OBJECT server: TEXT; port: INTEGER; endpoint: IP.Endpoint; METHODS init(server: TEXT; port: INTEGER; log: App.Log): Server RAISES {App.Error}; (* initializes server, port and sets the endpoint. If "server" = "DIRECT" then the server will not proxy but will go directly to the true destination *) initParse(serverAndPort: TEXT; log: App.Log): Server RAISES {App.Error}; (* "serverAndPort" is in <server>[:<port>] or https://<server>[:port]/" format (the latter is for compatibility with Mosaic environment variables). If the port is not given, then "defaultPort" is used. If serverAndPort = "DIRECT" then the server will not proxy but will go directly to the true destination *) END; TYPE ServerList = REF RECORD head: Server; tail: ServerList := NIL; END; ProxyRules = REF RECORD hostPattern: TEXT; proxy : ServerList; tail : ProxyRules; END; Proxy <: ProxyPublic; (* a "proxy" is a list of proxy rules: host patterns and proxies. When proxying, a host is matched against each pattern in order and the first matching pattern wins. Each proxy in the proxy server list is tried in turn. If the proxy server is "DIRECT" then a direct connection to the URL's server is tried. If any connection fails, the next proxy server is tried. There is a "*:DIRECT" proxy rule as the last rule (set by "init"). *) ProxyPublic = OBJECT rules, tail: ProxyRules; METHODS init (): Proxy; add (rule: TEXT; log: App.Log) RAISES {App.Error}; (* "add" adds a new proxy rule to the end of the proxy rules "rule" is a text formatted: <hostPattern> <server>[,<server>]* "hostPattern" is a simple pattern including '?' to match any one character and '*' to match any sequence of 0 or more characters if any "server" is "DIRECT" then it corresponds to a non-proxy direct connection *) END; PROCEDURE Serve (port, serviceValue: INTEGER; log : App.Log := NIL; serverData : REFANY := NIL ) RAISES {App.Error};
Listen
sits in a loop waiting for HTTP requests onport
. Messages written tolog
should be presented to the user of the application (as desired).
serverData
is passed to theaccept
andrequest
methods of the RequestHandlers when requests are made. Any request handler that matches the port or the serviceValue may be called.If
log
= NIL then the log handler will write to stdout and to the wr of the request
PROCEDURE ServerPort(port, service: INTEGER): BOOLEAN;
Return TRUE if there has been a call onServe
forport
andservice
PROCEDURE Client(request: HTTP.Request; proxy: Proxy; style: HTTP.Style; rd: Rd.T; wr: Wr.T; handler: ReplyHandler; service: INTEGER; log: App.Log := NIL) RAISES {App.Error};
Make a client request or proxy a client request. The request is made directly if there is no proxy given or the proxy rule matching the URL specifiesDIRECT
.If
style
is NIL, then DefaultStyle() is used.Program information (user-agent, or via) header is added automatically to the request.
Host: header is added automatically to the request
If
rd
is not NIL, read fromrd
and send it with the request.After
request
is sent to the server, the header of the reply is parsed andhandler.reply
is called withwr
for its output.
service
is the number of the service associated with aServe
call. It is used to determine if the request is to a port associated with the current service or another. If you didn't make aServe
call then useAnyService
.If log = NIL then log := App.defaultLog
CONST AnyPort = 0; AnyService = 0; PROCEDURE RegisterRequestHandler(port: INTEGER; handler: RequestHandler);
Addshandler
to the handlers known to the server. When the server gets a request, it invokes the handlers until one services the request. The handler will only get called ifport
matches the server's port orport = AnyPort
or ifport
< 0 thenport
represents a service type, and the request handler is invoked ifport
matches the server's service type. (Make sure the services have different values, :-)NOTE: a given request handler can only listen on a single port
CONST ServerPushBoundaryString = "ServerPushBoundary\n"; ServerPushBoundaryStringStart = "--ServerPushBoundary\n"; ServerPushBoundaryStringEnd = "--ServerPushBoundary--\n"; PROCEDURE ServerPushSupported(request: HTTP.Request): BOOLEAN; PROCEDURE ServerPushFrame(wr: Wr.T; contentType: TEXT := "text/html"; msg: TEXT; log: App.Log) RAISES {App.Error}; PROCEDURE DefaultProxy(log: App.Log := NIL): Proxy RAISES {App.Error};
returns the proxy defined by the user arguments given (must be called AFTER App.InitializeArguments):switch env config default -pac HTTP_PAC pac:
*: DIRECT
If log = NIL then DefaultLog will be used.
END HTTPApp.