LispPad
  • Home
  • Applications
    • 🖥️LispPad
      • Sessions
      • Editor
      • Preferences
    • 📱LispPad Go
    • 📜Language
    • 📖Libraries
  • Libraries
    • ⚙️LispKit
      • (lispkit archive tar)
      • (lispkit archive zip)
      • (lispkit base)
      • (lispkit bitset)
      • (lispkit box)
      • (lispkit bytevector)
      • (lispkit char)
      • (lispkit char-set)
      • (lispkit combinator)
      • (lispkit comparator)
      • (lispkit control)
      • (lispkit core)
      • (lispkit crypto)
      • (lispkit csv)
      • (lispkit datatype)
      • (lispkit date-time)
      • (lispkit debug)
      • (lispkit disjoint-set)
      • (lispkit draw)
      • (lispkit draw turtle)
      • (lispkit draw barcode)
      • (lispkit draw chart bar)
      • (lispkit dynamic)
      • (lispkit enum)
      • (lispkit format)
      • (lispkit graph)
      • (lispkit gvector)
      • (lispkit hashtable)
      • (lispkit heap)
      • (lispkit http)
      • (lispkit http oauth)
      • (lispkit http server)
      • (lispkit iterate)
      • (lispkit json)
      • (lispkit json schema)
      • (lispkit list)
      • (lispkit list set)
      • (lispkit log)
      • (lispkit markdown)
      • (lispkit match)
      • (lispkit math)
      • (lispkit math matrix)
      • (lispkit math stats)
      • (lispkit math util)
      • (lispkit object)
      • (lispkit port)
      • (lispkit prolog)
      • (lispkit queue)
      • (lispkit record)
      • (lispkit regexp)
      • (lispkit serialize)
      • (lispkit set)
      • (lispkit sqlite)
      • (lispkit stack)
      • (lispkit stream)
      • (lispkit string)
      • (lispkit styled-text)
      • (lispkit system)
      • (lispkit system call)
      • (lispkit system keychain)
      • (lispkit system pasteboard)
      • (lispkit test)
      • (lispkit text-table)
      • (lispkit thread)
      • (lispkit thread channel)
      • (lispkit-thread-future)
      • (lispkit thread shared-queue)
      • (lispkit type)
      • (lispkit url)
      • (lispkit vector)
    • ⚙️LispPad
      • (lisppad applescript)
      • (lisppad draw map)
      • (lisppad location)
      • (lisppad speech)
      • (lisppad system)
      • (lisppad turtle)
    • ⚙️SRFI
  • Examples
    • 📝LispKit
    • 📝LispPad
    • 📝LispPad Go
  • Releases
    • 🖥️LispPad
    • 📱LispPad Go
  • Downloads
  • Privacy Policy
  • Contact
Powered by GitBook
On this page
  • HTTP servers
  • Server requests
  • HTTP requests
  • HTTP multi-part requests
  • Server responses
  • Generic API
  • Common responses
  • Utilities
  1. Libraries
  2. LispKit

(lispkit http server)

Library (lispkit http server) implements a simple multi-threaded HTTP server which can be freely configured for different use cases. The HTTP server allows two different types of request processors to be registered: middleware processors which are applied to all incoming requests sequentially and regular request processors which define how a request for a specified route is turned into a response.

For processing an HTTP request, the HTTP server first sends the request through the middleware processors in the order they were registered. As soon as one middleware processor returns a response, this response becomes the response of the request. Only when all middleware processors did not return a response, the request handler matching the route is being invoked and the result of this handler defines the response for the request.

The following script configures and starts a simple web server. Please note that procedure http-server-start! does not terminate until the web server is shut down (e.g. by visiting "/quit" for the server below).

(import (lispkit thread)
        (lispkit http server))
;; Make a new HTTP server
(define server (make-http-server))
;; Register a default handler which returns a "not found" response
(http-server-register-default! server
  (lambda (request) (srv-response-not-found)))
;; Register a simple handler for the "/hello" route
(http-server-register! server "GET" "/hello/:name"
  (lambda (request)
    (make-srv-response 200 #f
      (string-append
        "Hello "
        (srv-request-path-param request "name") "!"))))
;; Define a counter for the requests served
(define requests-served (make-atomic-box 0))
;; Increment the counter in a middleware processor
(http-server-register-middleware! server
  (lambda (request)
    (atomic-box-inc+mul! requests-served 1) #f))
;; Register a handler for "/quit" which terminates the server
(http-server-register! server "GET" "/quit"
  (lambda (request)
    ; Terminate the server with a 1 second delay
    (spawn (thunk
             (thread-sleep! 1.0)
             (http-server-stop! (srv-request-server request))))
    (make-srv-response 200 #f
      (string-append
        "terminating the server; "
        (number->string (atomic-box-ref requests-served))
        " requests served"))))
;; Enable debug logging
(http-server-log-severity-set! server 0)
;; Start the server; this call blocks until the server is terminated
(http-server-start! server 3000)

After starting the server, three requests are made for /hello/Matthias, /hello/World and /quit. The last request also terminates the server. This is the server log for this interaction:

[http/worker] started worker 0/0
[http/worker] started worker 1/1
[http/worker] started worker 2/2
[http/server] server started for port 3000; try connecting at http://192.168.10.175:3000
[http/req] GET /hello/Matthias (::8044:1200:60:0)
[http/worker] worker 0 received request
[http/worker] worker 0: GET /hello/Matthias
[http/req] GET /hello/World (::8044:1200:60:0)
[http/worker] worker 0: GET /hello/World
[http/req] GET /quit (::8044:1200:60:0)
[http/worker] worker 0: GET /quit
[http/worker] worker 0 idle
[http/server] server stopped for port 3000
[http/worker] closed worker 1/2
[http/worker] closed worker 0/1
[http/worker] closed worker 2/0

HTTP servers

Symbol representing the http-server type. The type-for procedure of library (lispkit type) returns this symbol for all HTTP server objects.

Returns #t if obj is a HTTP server object; #f otherwise.

Creates a new HTTP server. queue-size is the maximum number of requests the server is able to queue up before starting to reject requests. log-level is the minimum log level for the new HTTP server. log-level is an integer between 0 (= debug) and 4 (= fatal). Only log messages above and including the level will be output. For details on log levels see the documentation of (lispkit log).

Returns #t if the HTTP server server is currently running; #f otherwise.

Returns #t if the HTTP server server is currently running; #f otherwise.

Returns #t if the HTTP server server is forcing the usage of IPv4; returns #f otherwise.

Returns the number of open HTTP connections for HTTP server server.

Returns a list of routes, i.e. URL paths, supported by the HTTP server server. An HTTP server supports a route if it explicitly defines a request handler for it.

Returns an association list mapping routes, i.e. URL paths, to request handler procedures for HTTP server server.

Returns the minimum log level for HTTP server server. log-level is an integer between 0 (= debug) and 4 (= fatal). Only log messages above and including the level will be output.

Sets the minimum log level for HTTP server server to log-level. log-level is an integer between 0 and 4. Only log messages above and including the level will be output. It is possible to use the constants defined in library (lispkit log): debug (0), info (1), warn (2), err (3), and fatal (4).

Returns the timeout used by HTTP server server in seconds. Once a request is received by server, it is put into a queue from which request processing worker threads pick up their work. The timeout determines how long requests stay in that queue at most before they are either processed or the server returns an error indicating that the request was not processed.

Sets the timeout used by HTTP server server to timeout seconds.

Returns the number of worker threads used by HTTP server server to process incoming requests concurrently.

Concatenates string representations of the message values and outputs them via the logger of HTTP server server if log-level is at least as high as the minimum log level supported by server. tag is a string defining a logging tag. See (lispkit log) for more details.

Registers a request handler in HTTP server server which processes incoming requests for the given HTTP method and a URL path that matches the route pattern. handler is a procedure which accepts an HTTP request of type srv-request and returns a corresponding HTTP response of type srv-response. method is a string specifying the HTTP method the handler is handling. Supported are: "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", and "PATCH". Specifying #f or leaving our the method argument implies that handler is handling all possible methods. route is a pattern for URL paths supported by the provided handler. route is a string consists of segments separated by /. Each segment can either be:

  • *: Segment wildcard (any path segment matches)

  • **: Path wildcard (any sequence of path segments matches)

  • :svar: Path parameter (any path segment matches; the path segment is assigned to path parameter svar)

  • ::pvar: Path variable (any sequence of path segments matches; the sequence of path segments is assigned to path variable pvar)

  • Any other string not containing a /: Concrete segment (a path segment of the same string matches)

Examples for valid routes are:

  • "/person/address": This route matches only the exact URL path "/person/address"

  • "/person/:id/:role": This route matches URL paths such as "/person/matthias/admin" and during the matching process, the path parameters id and role are assigned to matthias and admin respectively

  • "/person/:id/*": This route is equivalent to the former route but there is no variable assignment to role

  • "/person/:id/::roles": This route includes all paths matching the previous two examples and, in addition, also handles paths that have segments beyond id and role. For instance, "/person/matthias/admin/misc" also matches and path parameter id gets assigned to matthias and path variable roles gets assigned to admin/misc

  • "/person/:id/**": This route is equivalent to the previous route without the roles variable being assigned

Registers a default handler for HTTP server server. Without a default handler, server, by default, returns an error whenever a URL path was used that did not have a matching route. By using http-server-register-default!, this behavior can be customized and all requests without a matching route are processed by handler. handler is a procedure which accepts an HTTP request of type srv-request and returns a corresponding HTTP response of type srv-response.

Registers a middleware processor for the given HTTP server server. A middleware processor is a function that receives an HTTP request of type srv-request and either returns #f (request not handled) or an HTTP response of type srv-response (request handled). For processing an HTTP request, server first sends the request through the middleware processors in the order they were registered. As soon as one middleware processor returns a response, this response becomes the response of the request. Only after all middleware processors returned #f, the request handler matching the route is being invoked and the result of this handler defines the response for the request.

Starts the HTTP server server listening on port for requests. If boolean argument forceIPv4 is set to #t, the usage of IPv4 is enforced. num is the number of worker threads used for processing requests (default is 3). name is the prefix used for naming worker threads. name followed by the worker thread number determines each worker threads name. The default for name is "worker ". Procedure http-server-start! terminates only when the server is stopped, i.e. it is typically invoked on a thread to not block the execution of a program.

Stops a running HTTP server server. All worker threads are terminated and procedure http-server-start!, which was used to start the server, returns.

Server requests

HTTP requests

Symbol representing the srv-request type. The type-for procedure of library (lispkit type) returns this symbol for all HTTP server request objects.

Returns #t if obj is a HTTP server request object; #f otherwise.

Returns the HTTP server which issued this HTTP server request object. srv-request-server returns #f if req was persisted and the corresponding HTTP server object was garbage collected.

Returns the HTTP method for the given HTTP server request req as a string. The supported HTTP methods are: "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", and "PATCH".

Returns the URL path of the given HTTP server request req as a string.

Returns the URL query of the given HTTP server request req as a string. If incl-path? is provided and set to true, then the query is prefixed with the path of req.

Returns the values of the query parameter name (a string) for the given HTTP server request req as a list of strings. If the query parameter name was not used in req, the empty list is returned.

Returns all query parameters for the given HTTP server request req as an association list consisting of string pairs, mapping query parameter names to query parameter values.

Returns the value of the path parameter name (a string) for the given HTTP server request req as a string. If the path parameter name is undefined in req, default is returned instead if provided. Otherwise, #f is returned.

Path parameters are determined when a request path gets matched with the available routes of an HTTP server. For details, see documentation for procedure http-server-register!.

Returns all path parameters for the given HTTP server request req as an association list consisting of string pairs, mapping path parameter names to path parameter values. Path parameters are determined when a request path gets matched with the available routes of an HTTP server. For details, see documentation for procedure http-server-register!.

Sets the value of the path parameter name (a string) for the given HTTP server request req to string value.

Removes the path parameter name (a string) from the given HTTP server request req.

Returns the header name (a string) for the given HTTP server request req as a string. If the header name is undefined in req, default is returned instead if provided. Otherwise, #f is returned.

Returns all headers for the given HTTP server request req as an association list consisting of string pairs, mapping header names to header values.

Sets the header name (a string) for the given HTTP server request req to string value.

Removes the header name (a string) from the given HTTP server request req.

Returns the body of the HTTP server request req as a bytevector.

Returns the body of the HTTP server request req as a UTF8-encoded string. If an interpretation of the body as a UTF8-encoded string fails, #f is returned.

Parses the body if its content type is application/x-www-form-urlencoded and returns the query parameters in the body as an association list mapping query names to query values.

Parses the body if its content type is multipart/form-data and returns a list of multi-part request objects of type srv-multipart.

Returns the IP address of the client issueing the HTTP server request req as a string. If the IP address cannot be determind, #f is returned.

HTTP multi-part requests

Symbol representing the srv-multipart type. The type-for procedure of library (lispkit type) returns this symbol for all HTTP server multipart request objects.

Returns #t if obj is a HTTP server multipart request object; #f otherwise.

Returns the header name (a string) for the given HTTP server multipart request mp as a string. If the header name is undefined in mp, default is returned instead if provided. Otherwise, #f is returned.

Returns all headers for the given HTTP server multipart request mp as an association list consisting of string pairs, mapping header names to header values.

Returns the body of the HTTP server multipart request mp as a bytevector.

Returns the body of the HTTP server multipart request mp as a UTF8-encoded string. If an interpretation of the body as a UTF8-encoded string fails, #f is returned.

Server responses

Generic API

Symbol representing the srv-response type. The type-for procedure of library (lispkit type) returns this symbol for all HTTP server response objects.

Returns #t if obj is a HTTP server response object; #f otherwise.

Returns a new HTTP server response based on the provided arguments. status is a fixnum defining the status code (default: 200). headers is an association list consisting of string pairs, mapping header names to header values. body is a value (default: #f) which gets mapped to suitable response content with ct being a string describing the MIME type for the content.

The following mapping rules are deriving the content of the response from value body:

  • #f: Sets an empty body.

  • String: The body is assigned the textual content of the string. If last is provided, it is interpreted as a string defining the MIME type of the content. Otherwise, the MIME type is assumed to be text/plain.

  • Bytevector: The body is assigned the binary data of the bytevector. If last is provided, it is interpreted as a string defining the MIME type of the content. Otherwise, the MIME type is assumed to be application/octet-stream.

  • Pair: body is assumed to be in an SXML representation of HTML. The body of res is assigned a textual HTML representation of body. If last is provided and set to #t or if it is detected that the outermost markup of body is not html, then the body is wrapped automatically in missing html and body markup. The MIME type is set to text/html.

  • markdown, markdown-block, markdown-inline object: The body is assigned an HTML representation of the given markdown object provided by library (lispkit markdown). The MIME type is set to text/html.

  • json, mutable-json object: The body is assigned a textual JSON representation of the given JSON object provided by library (lispkit json). The MIME type is set to application/json.

  • styled-text object: If last is provided and set to true, the body is assigned an RTF representation (MIME type application/rtf) of the given styled text object provided by library (lispkit styled-text). If last is not provided or set to #f, the body is assigned an HTML representation (MIME type text/html) of the given styled text object.

  • image object: The body is assigned a binary representation of the bitmap image provided by library (lispkit draw). If last is provided, it is interpreted as a string defining the MIME type of the content. Supported are image/tiff, image/png, image/jpeg, image/gif, and image/bmp. If last is not provided, image/png is assumed to be the default.

  • All other data types are interpreted as Scheme objects representing JSON values. They are converted via procedure json into a JSON object and a textual representation of this JSON object is assigned to the body of res with MIME type application/json. If the conversion fails, an error is returned.

Returns the HTTP status code of the HTTP server response res.

Sets the HTTP status code of the HTTP server response res to status.

Returns the header name (a string) for the given HTTP server response res as a string. If the header name is undefined in res, default is returned instead if provided. Otherwise, #f is returned.

Returns all headers for the given HTTP server response res as an association list consisting of string pairs, mapping header names to header values.

Sets the header name (a string) for the given HTTP server response res to string value.

Removes the header name (a string) from the given HTTP server response res.

Sets the body of the HTTP server response res to content derived from value body. The following mapping rules are used for the given value body:

  • #f: Sets an empty body.

  • String: The body is assigned the textual content of the string. If last is provided, it is interpreted as a string defining the MIME type of the content. Otherwise, the MIME type is assumed to be text/plain.

  • Bytevector: The body is assigned the binary data of the bytevector. If last is provided, it is interpreted as a string defining the MIME type of the content. Otherwise, the MIME type is assumed to be application/octet-stream.

  • Pair: body is assumed to be in an SXML representation of HTML. The body of res is assigned a textual HTML representation of body. If last is provided and set to #t or if it is detected that the outermost markup of body is not html, then the body is wrapped automatically in missing html and body markup. The MIME type is set to text/html.

  • markdown, markdown-block, markdown-inline object: The body is assigned an HTML representation of the given markdown object provided by library (lispkit markdown). The MIME type is set to text/html.

  • json, mutable-json object: The body is assigned a textual JSON representation of the given JSON object provided by library (lispkit json). The MIME type is set to application/json.

  • styled-text object: If last is provided and set to true, the body is assigned an RTF representation (MIME type application/rtf) of the given styled text object provided by library (lispkit styled-text). If last is not provided or set to #f, the body is assigned an HTML representation (MIME type text/html) of the given styled text object.

  • image object: The body is assigned a binary representation of the bitmap image provided by library (lispkit draw). If last is provided, it is interpreted as a string defining the MIME type of the content. Supported are image/tiff, image/png, image/jpeg, image/gif, and image/bmp. If last is not provided, image/png is assumed to be the default.

  • All other data types are interpreted as Scheme objects representing JSON values. They are converted via procedure json into a JSON object and a textual representation of this JSON object is assigned to the body of res with MIME type application/json. If the conversion fails, an error is returned.

Sets the body of the HTTP server response res to string str representing HTML content. If just-body? is provided and set to #t, it is assumed that str only represents the body of a HTML document and srv-response-body-html-set! will automatically decorate str such that it represents a full HTML document.

Common responses

Returns a HTTP server response for status code 200 (= OK). This is quivalent to (make-srv-response 200 headers body).

Returns a HTTP server response for status code 400 (= Bad Request). This is quivalent to (make-srv-response 400 headers body).

Returns a HTTP server response for status code 401 (= Unauthorized). This is quivalent to (make-srv-response 401 headers body).

Returns a HTTP server response for status code 403 (= Forbidden). This is quivalent to (make-srv-response 403 headers body).

Returns a HTTP server response for status code 404 (= Not Found). This is quivalent to (make-srv-response 404 headers body).

Returns a HTTP server response for status code 405 (= Not Allowed). This is quivalent to (make-srv-response 405 headers body).

Returns a HTTP server response for status code 406 (= Not Acceptable). This is quivalent to (make-srv-response 406 headers body).

Returns a HTTP server response for status code 500 (= Internal Server Error). This is quivalent to (make-srv-response 500 headers body).

Returns a HTTP server response for status code 501 (= Not Implemented). This is quivalent to (make-srv-response 501 headers body).

Returns a HTTP server response for status code 201 (= Created). This is quivalent to (make-srv-response 201 #f #f).

Returns a HTTP server response for status code 202 (= Accepted). This is quivalent to (make-srv-response 202 #f #f).

Returns a HTTP server response for status code 301 (= Moved Permanently). This is quivalent to (make-srv-response 301 (cons (cons "Location" redirect) headers) #f).

Returns a HTTP server response for status code 302 (= Moved Temporarily). This is quivalent to (make-srv-response 302 (cons (cons "Location" redirect) headers) #f).

Utilities

Parses the header value string str into a list of strings and string pairs using a universal header parsing algorithm.

(parse-http-header-value "a, b, c")
  ⇒  (("a") ("b") ("c"))
(parse-http-header-value "a; b; c")
  ⇒  (("a" "b" "c"))
(parse-http-header-value "a, b1 ; b2, c")
  ⇒  (("a") ("b1" "b2") ("c"))
(parse-http-header-value "a; b=one; c=two")
  ⇒   (("a" ("b" . "one") ("c" . "two")))
(parse-http-header-value
  "foo/bar;p=\"A,B,C\", bob/dole;x=\"apples,oranges\"")
  ⇒  (("foo/bar" ("p" . "A,B,C")) ("bob/dole" ("x" . "apples,oranges")))

Extracts the header value for header name from the association list headers, or returns #f if name is not contained in headers. headers is an association list of string pairs, mapping header names to header values.

(http-header-param '(("one" . "1")("Two" . "2")) "TWO")
  ⇒  "2"
(http-header-param '(("one" . "1")("Two" . "2")) "Three")
  ⇒  #f

Returns a HTTP request handler for downloading a file at the given filepath, an absolute file path.

Returns a HTTP request handler for downloading a file at the file path consisting of the first path variable of the request which is considered to be relative to absolute directory path root.

Returns a HTTP request handler for browsing the files in the directory consisting of the first path variable of the request which is considered to be relative to absolute directory path root.

Last updated 6 months ago

http-server-type-tag

(http-server? obj)

(make-http-server) (make-http-server queue-size) (make-http-server queue-size log-level)

(http-server-running? server)

(http-server-port server)

(http-server-ipv4? server)

(http-server-open-connections server)

(http-server-routes server)

(http-server-handlers server)

(http-server-log-severity server)

(http-server-log-severity-set! server log-level)

(http-server-timeout server)

(http-server-timeout-set! server timeout)

(http-server-num-workers server)

(http-server-log server log-level tag message ...)

(http-server-register! server route handler) (http-server-register! server method route handler)

(http-server-register-default! server handler)

(http-server-register-middleware! server processor)

(http-server-start! server port) (http-server-start! server port forceIPv4) (http-server-start! server port forceIPv4 num) (http-server-start! server port forceIPv4 num name)

(http-server-stop! server)

srv-request-type-tag

(srv-request? obj)

(srv-request-server req)

(srv-request-method req)

(srv-request-path req)

(srv-request-query req) (srv-request-query req incl-path?)

(srv-request-query-param req name)

(srv-request-query-params req)

(srv-request-path-param req name) (srv-request-path-param req name default)

(srv-request-path-params req)

(srv-request-path-param-set! req name value)

(srv-request-path-param-remove! req name)

(srv-request-header req name) (srv-request-header req name default)

(srv-request-headers req)

(srv-request-header-set! req name value)

(srv-request-header-remove! req name)

(srv-request-body req)

(srv-request-body->string req)

(srv-request-form-attributes req)

(srv-request-form-multiparts req)

(srv-request-address req)

HTTP multipart, specifically multipart/form-data, is a media type that allows the encoding of information as a series of parts in a single message as defined by . This format is commonly used for forms that are expressed in HTML and where the form values are sent via HTTP.

srv-multipart-type-tag

(srv-multipart? obj)

(srv-multipart-header mp name) (srv-multipart-header mp name default)

(srv-multipart-headers mp)

(srv-multipart-body mp)

(srv-multipart-body->string mp)

srv-response-type-tag

(srv-response? obj)

(make-srv-response) (make-srv-response status) (make-srv-response status headers) (make-srv-response status headers body) (make-srv-response status headers body ct)

(srv-response-status-code res)

(srv-response-status-code-set! res status)

(srv-response-header res name) (srv-response-header res name default)

(srv-response-headers res)

(srv-response-header-set! res name value)

(srv-response-header-remove! res name)

(srv-response-body-set! res body) (srv-response-body-set! res body last)

(srv-response-body-html-set! res str) (srv-response-body-html-set! res str just-body?)

(srv-response-ok body) (srv-response-ok headers body)

(srv-response-bad-request) (srv-response-bad-request body) (srv-response-bad-request headers body)

(srv-response-unauthorized) (srv-response-unauthorized body) (srv-response-unauthorized headers body)

(srv-response-forbidden) (srv-response-forbidden body) (srv-response-forbidden headers body)

(srv-response-not-found) (srv-response-not-found body) (srv-response-not-found headers body)

(srv-response-method-not-allowed) (srv-response-method-not-allowed body) (srv-response-method-not-allowed headers body)

(srv-response-not-acceptable) (srv-response-not-acceptable body) (srv-response-not-acceptable headers body)

(srv-response-internal-server-error) (srv-response-internal-server-error body) (srv-response-internal-server-error headers body)

(srv-response-not-implemented) (srv-response-not-implemented body) (srv-response-not-implemented headers body)

(srv-response-created)

(srv-response-accepted)

(srv-response-moved-permanently redirect) (srv-response-moved-permanently redirect headers)

(srv-response-moved-temporarily redirect) (srv-response-moved-temporarily redirect headers)

(parse-http-header-value str)

(http-header-param headers name)

(share-file-handler filepath)

(share-directory-handler root)

(browse-directory-handler root)

⚙️
RFC 2388