2 Tutorials
2.1 Using built-in HTTP/2 client
[in package HTTP2/CLIENT]
There is a simple client in the package http2/client.
[function] RETRIEVE-URL URL &REST PARS &KEY METHOD CONTENT CONTENT-FN ADDITIONAL-HEADERS CONTENT-TYPE CHARSET GZIP-CONTENT &ALLOW-OTHER-KEYS
Retrieve
URL(a string) through HTTP/2 over TLS.See
FETCH-RESOURCEfor documentation of the keyword parameters.Example:
(http2/client:retrieve-url "https://example.com") ==> "<!doctype html> ... <html> ... <head> ... <title>Example Domain</title> ... ... <meta charset="utf-8" /> ... <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> ... <meta name="viewport" conten...[sly-elided string of length 1256]" ==> 200 (8 bits, #xC8, #o310, #b11001000) ==> (("content-length" . "1256") ("x-cache" . "HIT") ("vary" . "Accept-Encoding") ... ("server" . "ECS (bsb/27E0)") ... ("last-modified" . "Thu, 17 Oct 2019 07:18:26 GMT") ... ("expires" . "Thu, 28 Sep 2023 19:38:44 GMT") ... ("etag" . "\"3147526947+ident\"") ("date" . "Thu, 21 Sep 2023 19:38:44 GMT") ... ("content-type" . "text/html; charset=UTF-8") ... ("cache-control" . "max-age=604800") ("age" . "151654")) ==> "/" ==> #<VANILLA-CLIENT-CONNECTION > ==> NIL ==> "HTTP2 does not provide reason phrases"See
DRAKMA-STYLE-STREAM-VALUESfor meaning of the individual values.
[function] REQUEST-HEADERS METHOD PATH AUTHORITY &KEY (SCHEME "https") CONTENT-TYPE GZIP-CONTENT ADDITIONAL-HEADERS
Encode standard request headers. The obligatory headers are passed as the positional arguments.
ADDITIONAL-HEADERSare a list of conses, each containing header name and value.
[function] DRAKMA-STYLE-STREAM-VALUES RAW-STREAM &KEY CLOSE-STREAM
Return values as from
DRAKMA:HTTP-REQUEST. Some of the values are meaningless, but kept for compatibility purposes.body of the reply
status code as integer
alist of headers
the
URLthe reply came from (bogus value)the connection the reply comes from (not network stream as in Drakma, but same purpose - can be reused for ruther queries.)
whether connection is closed (passed as parameter)
reason phrase (bogus value)
2.2 Starting HTTP/2 server
[in package HTTP2/SERVER with nicknames HTTP2/SERVER/SHARED, HTTP2/SERVER/POLL, HTTP2/SERVER/THREADED]
Start server on foreground with RUN, or on background with START.
This creates (as of this version) a multithreaded server that serves 404 Not found responses on any request.
[function] RUN PORT &REST PARS &KEY CERTIFICATE-FILE PRIVATE-KEY-FILE
Run a default HTTP/2 server on
PORTon foreground.
[function] START PORT &REST ARGS &KEY (HOST
*VANILLA-HOST*) (DISPATCHER*VANILLA-SERVER-DISPATCHER*) &ALLOW-OTHER-KEYSStart a default HTTP/2 https server on
PORTon background.Returns two values with a detached (see below) dispatcher, which is default:
Server instance (that is appropriate parameter for stop)
base url of the server (most useful when
PORTwas 0 - any free port)
With a non-detached dispatcher the value is not specified.
DISPATCHERparameter sets the dispatcher to use for the server. Dispatchers determine how are new requests handled. Presently there are several sets of dispatchers defined, see Server classesDetached variants run the server in a separate thread and returns immediately after opening the socket.
Value of
*VANILLA-SERVER-DISPATCHER*is not specified (set it if you care) but should be presently best detached dispatcher.FIND-PRIVATE-KEY-FILE and
FIND-CERTIFICATE-FILEas default values for the respective parameters try to locate the files.
[function] STOP &OPTIONAL (SERVER (
CAR*SERVERS*))Stop a server and remove it from list of servers.
2.3 Define content for HTTP/2 server
[in package HTTP2/SERVER with nicknames HTTP2/SERVER/SHARED, HTTP2/SERVER/POLL, HTTP2/SERVER/THREADED]
To server something else than 404 Not found, you need to define handlers for specific paths. Simple handler definition can look like
(define-exact-handler "/hello-world"
(handler (foo :utf-8 nil)
(with-open-stream (foo foo)
(send-headers
'((:status "200")
("content-type" "text/html; charset=utf-8")))
(format foo "Hello World, this is random: ~a" (random 10)))))
This defines a handler on "/hello-world" path that sends reasonable headers, writes some text to the stream and closes the stream (via WITH-OPEN-STREAM). The text written is passed to the client as data (body).
In general, the handlers are set using DEFINE-PREFIX-HANDLER or
DEFINE-EXACT-HANDLER, and are functions typically created by HANDLER macro,
or (in simple cases) by REDIRECT-HANDLER or SEND-TEXT-HANDLER functions.
[macro] DEFINE-PREFIX-HANDLER PREFIX FN &OPTIONAL CONNECTION
Define function to run when peer closes http stream on
CONNECTION(or any server defined in future) if the path of the stream starts withPREFIX.
[macro] DEFINE-EXACT-HANDLER PATH FN &OPTIONAL CONNECTION
Define function to run when peer closes http stream on
CONNECTION(or any server defined in future) if the path of the stream isPATH.
-
Function that can be called with
CONNECTIONandHTTP2-STREAMto write a response to the http request described bySTREAMobject.
[macro] HANDLER (FLEXI-STREAM-NAME CHARSET GZIP) &BODY BODY
Return a
HANDLERtype function.This handler, when called, runs
BODYin a context whereFLEXI-STREAM-NAMEis bound to an open flexi stream that can be written to (to write response). On background, written text is converted fromCHARSETto octets, possibly compressed byGZIPand split into frames,and two lexical functions are defined,
SEND-HEADERSandSEND-GOAWAY.
The
SEND-HEADERSsends the provided headers to theSTREAM.The
SEND-GOAWAYsends go away frame to the client to close connection.The handler body needs to close the underlying stream if the response is actually to be sent, or possibly schedule sending more data for later.
[macro] CONSTANT-HANDLER (FLEXI-STREAM-NAME CHARSET GZIP HEADERS) &BODY BODY
Run
BODYto print the output toFLEXI-STREAM-NAMEin compile time. This constant (static) page is served every time as-is.
[function] REDIRECT-HANDLER TARGET &KEY (CODE "301") (CONTENT-TYPE "text/html; charset=UTF-8") CONTENT
A handler that emits redirect response with http status being
CODE, and optionally providesCONTENTwithCONTENT-TYPE.
[function] SEND-TEXT-HANDLER TEXT &KEY (CONTENT-TYPE "text/html; charset=UTF-8") (GZIP
T) ADDITIONAL-HEADERSA handler that returns
TEXTas content ofCONTENT-TYPE.TEXTis evaluated when handler is defined, not when handler is invoked. For content that can change on individual invocations write to the stream.ADDITIONAL-HEADERSare sent along with :status and content-type headers.
[function] SEND-HEADERS STREAM HEADERS &KEY END-STREAM (END-HEADERS
T) &ALLOW-OTHER-KEYSSend
HEADERSto a HTTP2 stream. The stream is returned.The END-HEADERS and END-STREAM allow to set the appropriate flags.
Inside
HANDLERmacro, this names a function that has theSTREAMargument implicit and onlyHEADERSand key parameters are to be provided.
[function] SEND-GOAWAY CODE DEBUG-DATA
Start closing connection, sending
CODEandDEBUG-DATAin the go-away frame to peer. Must be called from inside ofHANDLERmacro.
2.4 Getting request details
[in package HTTP2/SERVER with nicknames HTTP2/SERVER/SHARED, HTTP2/SERVER/POLL, HTTP2/SERVER/THREADED]
Sometimes you need to get some data from the request.
These data can be carried by querying the HTTP/2 stream object involved. If you
define handlers by HANDLER macro, it is available in a lexically bound STREAM
variable.
(define-exact-handler "/body"
(handler (foo :utf-8 nil)
(with-open-stream (foo foo)
(send-headers
'((:status "200")
("content-type" "text; charset=utf-8")))
(format foo "Hello World, this is a ~s request.~3%content~%~s~3%headers~%~s~%~3%body~%~s~%"
(http2/core::get-method stream)
(http-stream-to-string stream)
(http2/core::get-headers stream)
(http2/core::get-body stream)))))
- [generic-function] GET-PATH OBJECT
[accessor] GET-HEADERS HEADER-COLLECTING-MIXIN (:HEADERS)
List of collected (header . value) pairs. Does not include
:method,:path, etc.
[accessor] GET-METHOD SERVER-STREAM (:METHOD)
The HTTP method ([RFC7231], Section 4)
[accessor] GET-SCHEME SERVER-STREAM (:SCHEME)
Scheme portion of the target URI ([RFC3986], Section 3.1).
Not restricted to "http" and "https" schemed URIs. A proxy or gateway can translate requests for non-HTTP schemes, enabling the use of HTTP to interact with non-HTTP services
[accessor] GET-AUTHORITY SERVER-STREAM (:AUTHORITY)
The authority portion of the target URI ([RFC3986], Section 3.2)
2.4.1 Body of the request
Sometimes there is a body in the client request.
When sending a such a request, you can use CONTENT parameter of the
HTTP2/CLIENT:RETRIEVE-URL, together with CONTENT-TYPE.
(http2/client:retrieve-url "https://localhost:8080/body" :content "Hello")
(http2/client:retrieve-url "https://localhost:8080/body"
:content #(1 2 3) :content-type "application/octet-stream")
When you write a handler for such a request, you should know if you want binary
or text data. The vanilla class for the server streams looks at the headers, and
if they look like UTF-8 (as per IS-UTF8-P), it processes the data as text, if
not, they are collected as binary vector.
When your client systematically send headers that do not make it TEXT and you
want to read text, as last resort change class of your streams to include
FALLBACK-ALL-IS-ASCII (or improve IS-UTF8-P, or add some other decoding function).
If you do not want to see text at all, change class to NOT include
UTF8-PARSER-MIXIN or any other conversion mixin.
[accessor] GET-BODY BODY-COLLECTING-MIXIN (:BODY)
Body of the request as an octet vector.
May be empty if some higher priority mixin (e.g.,
UTF8-PARSER-MIXIN) processed the data.
[function] HTTP-STREAM-TO-STRING HTTP-STREAM
HTTP-STREAMshould be aTEXT-COLLECTING-STREAM.HTTP-STREAM-TO-VECTOR then assembles the text from individual chunks.
2.5 Customize client
[in package HTTP2/CLIENT]
RETRIEVE-URL is in fact a thin wrapper over FETCH-RESOURCE generic function.
[generic-function] FETCH-RESOURCE MEDIUM URL PARS
Retrieve
URLover some medium - HTTP/2 connection, network socket, .....The
ARGSis a property list used by some methods and ignored/passed down by others.
You can customize its behaviour by creating subclasses for the GENERIC-REQUEST
class and specialized methods for your new classes, as well as by changing
documented variables.
2.5.1 Client example: multiple requests
For example, this is how you can make a client that download several pages from same source.
First, you need a custom request class, both to have something to specialize
FETCH-RESOURCE on and to keep the list of resources to fetch.
(defclass multi-url-request (simple-request)
((urls :accessor get-urls :initarg :urls)))
(defmethod fetch-resource ((connection client-http2-connection)
(request multi-url-request) args)
(dolist (r (get-urls request))
(fetch-resource connection r nil)))
Then you need a specialized stream class to specialize what to do when the responses arrive. Here it prints out the response body for simplicity, and ends if it got the last response.
(defclass my-client-stream (vanilla-client-stream)
())
(defmethod peer-ends-http-stream ((stream my-client-stream))
(print (or (get-body stream) (http-stream-to-string stream)))
(when (null (get-streams (get-connection stream)))
(signal 'client-done))
(terpri))
See GET-BODY, HTTP-STREAM-TO-STRING and CLIENT-DONE.
And finally, you need to pass these as the call parameter:
(http2/client:retrieve-url "https://localhost:8088/body"
:request-class 'multi-url-request
:urls '("https://localhost:8088/body" "https://localhost:8088/")
:stream-class 'my-client-stream)
- [accessor] GET-CONNECTION HTTP2-STREAM-MINIMAL (:CONNECTION)
[accessor] GET-STREAMS STREAM-COLLECTION (:STREAMS)
Sequence of streams the connection knows about. This includes all open and half-closed streams, but not the already closed and idle streams.
2.5.2 Client reference
For a simple request without body, following documented methods are called in sequence:
[method] FETCH-RESOURCE MEDIUM (URL
STRING(01)) ARGSParse
URLintoPURI:URIobject and fetch the resource using that.
[method] FETCH-RESOURCE MEDIUM (URL
PURI:URI) ARGSConvert
URLinto an instance ofSIMPLE-REQUEST,REQUEST-WITH-UTF8-BODYorREQUEST-WITH-BINARY-BODYclass.Pass
ARGSto theMAKE-INSTANCEcall.If
ARGS(a property list) hasCONTENTproperty, check its type - if it is a string,REQUEST-WITH-UTF8-BODYis created, if a simple or octet vector,REQUEST-WITH-BINARY-BODY, if not present or nilSIMPLE-REQUEST. If it is something else, behaviour is undocumented.Note that some of the methods actually wait to get the responses.
[method] FETCH-RESOURCE (NETWORK-STREAM
STREAM) (REQUESTGENERIC-REQUEST) ARGSOpen HTTP/2 connection over the
STREAMand fetch the resource using this connection.Wait for the pending frames to actually receive the response.
Class of the new connection is taken from
:CONNECTION-CLASSproperty ofARGS, falling back to*DEFAULT-CLIENT-CONNECTION-CLASS*.ARGSare passed to theMAKE-INSTANCE.
[method] FETCH-RESOURCE (CONNECTION
CLIENT-HTTP2-CONNECTION) (REQUESTGENERIC-REQUEST) ARGSOpen the new stream by sending headers frame to the server.
Details of the frame are taken from the
REQUESTinstance.Return the new stream.
[method] FETCH-RESOURCE (CONNECTION
CLIENT-HTTP2-CONNECTION) (REQUESTREQUEST-WITH-UTF8-BODY) ARGSOpen the HTTP/2 stream and send out the content as UTF-8.
[method] FETCH-RESOURCE (CONNECTION
CLIENT-HTTP2-CONNECTION) (REQUESTREQUEST-WITH-BINARY-BODY) ARGSOpen the HTTP/2 stream and send out the content as octets sequence.
-
Base class for requests. It has slots for
URI, HTTPMETHOD,HEADERS(some headers are implicit) andNO-BODYflag to determine whether there would be data frames sent.
[class] SIMPLE-REQUEST GENERIC-REQUEST
Class for requests with no body. Implies
GETmethod by default.
[class] REQUEST-WITH-BODY GENERIC-REQUEST
Base class for requests with a body. Implies POST method by default. Some method for
FETCH-RESOURCEmust be defined to actually send the content in data frames.
[variable] *DEFAULT-CLIENT-CONNECTION-CLASS* VANILLA-CLIENT-CONNECTION
Default class to be used for new connections in
FETCH-RESOURCE.
-
Handled by
RETRIEVE-URL. The client should signal it when the processing is done.