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 RETRIEVE-URL-USING-CONNECTION for 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-VALUES
for meaning of the individual values.
[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
URL
the 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]
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
PORT
on foreground.
[function] START PORT &KEY (HOST
*VANILLA-HOST*
) (DISPATCHER*VANILLA-SERVER-DISPATCHER*
) (CERTIFICATE-FILE 'FIND-CERTIFICATE-FILE
) (PRIVATE-KEY-FILE 'FIND-PRIVATE-KEY-FILE
)Start a default HTTP/2 https server on
PORT
on background.Returns two values:
thread with the server (to be able to close the server). The specific object returned is subject to change, what is guaranteed is that it is suitable parameter for
STOP
.base url of the server (most useful when
PORT
was 0 - any free port)
2.3 Define content for HTTP/2 server
[in package HTTP2-SERVER with nicknames HTTP2/SERVER]
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
CONNECTION
andHTTP2-STREAM
to write a response to the http request described bySTREAM
object.
[macro] HANDLER (FLEXI-STREAM-NAME CHARSET GZIP) &BODY BODY
Return a
HANDLER
type function.This handler, when called, runs
BODY
in a context whereFLEXI-STREAM-NAME
is bound to an open flexi stream that can be written to (to write response). On background, written text is converted fromCHARSET
to octets, possibly compressed byGZIP
and split into frames,and two lexical functions are defined,
SEND-HEADERS
andSEND-GOAWAY
.
The
SEND-HEADERS
sends the provided headers to theSTREAM
.The
SEND-GOAWAY
sends 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
BODY
to print the output toFLEXI-STREAM-NAME
in 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 providesCONTENT
withCONTENT-TYPE
.
[function] SEND-TEXT-HANDLER TEXT &KEY (CONTENT-TYPE "text/html; charset=UTF-8") (GZIP
T
) ADDITIONAL-HEADERSA handler that returns
TEXT
as content ofCONTENT-TYPE
.TEXT
is evaluated when handler is defined, not when handler is invoked. For content that can change on individual invocations write to the stream.ADDITIONAL-HEADERS
are sent along with :status and content-type headers.
[function] SEND-HEADERS STREAM HEADERS &KEY END-STREAM (END-HEADERS
T
) &ALLOW-OTHER-KEYSSend
HEADERS
to a HTTP2 stream. The stream is returned.The END-HEADERS and END-STREAM allow to set the appropriate flags.
Inside
HANDLER
macro, this names a function that has theSTREAM
argument implicit and onlyHEADERS
and key parameters are to be provided.
[function] SEND-GOAWAY CODE DEBUG-DATA
Start closing connection, sending
CODE
andDEBUG-DATA
in the go-away frame to peer. Must be called from inside ofHANDLER
macro.
2.4 Getting request details
[in package HTTP2-SERVER with nicknames HTTP2/SERVER]
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-STREAM
should 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
URL
over some medium - HTTP/2 connection, network socket, .....The
ARGS
is 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
(0
1
)) ARGSParse
URL
intoPURI:URI
object and fetch the resource using that.
[method] FETCH-RESOURCE MEDIUM (URL
PURI:URI
) ARGSConvert
URL
into an instance ofSIMPLE-REQUEST
,REQUEST-WITH-UTF8-BODY
orREQUEST-WITH-BINARY-BODY
class.Pass
ARGS
to theMAKE-INSTANCE
call.If
ARGS
(a property list) hasCONTENT
property, check its type - if it is a string,REQUEST-WITH-UTF8-BODY
is 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
STREAM
and 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-CLASS
property ofARGS
, falling back to*DEFAULT-CLIENT-CONNECTION-CLASS*
.ARGS
are 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
REQUEST
instance.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-BODY
flag to determine whether there would be data frames sent.
[class] SIMPLE-REQUEST GENERIC-REQUEST
Class for requests with no body. Implies
GET
method by default.
[class] REQUEST-WITH-BODY GENERIC-REQUEST
Base class for requests with a body. Implies POST method by default. Some method for
FETCH-RESOURCE
must 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.