Public Interface
Requests
HTTP.request
— Function.HTTP.request(method, url [, headers [, body]]; <keyword arguments>]) -> HTTP.Response
Send a HTTP Request Message and receive a HTTP Response Message.
e.g.
r = HTTP.request("GET", "http://httpbin.org/ip")
println(r.status)
println(String(r.body))
headers
can be any collection where [string(k) => string(v) for (k,v) in headers]
yields Vector{Pair}
. e.g. a Dict()
, a Vector{Tuple}
, a Vector{Pair}
or an iterator.
body
can take a number of forms:
- a
String
, aVector{UInt8}
or anyT
accepted bywrite(::IO, ::T)
- a collection of
String
orAbstractVector{UInt8}
orIO
streams or items of any typeT
accepted bywrite(::IO, ::T...)
- a readable
IO
stream or anyIO
-like typeT
for whicheof(T)
andreadavailable(T)
are defined.
The HTTP.Response
struct contains:
status::Int16
e.g.200
headers::Vector{Pair{String,String}}
e.g. ["Server" => "Apache", "Content-Type" => "text/html"]body::Vector{UInt8}
, the Response Body bytes (empty if aresponse_stream
was specified in therequest
).
Functions HTTP.get
, HTTP.put
, HTTP.post
and HTTP.head
are defined as shorthand for HTTP.request("GET", ...)
, etc.
HTTP.request
and HTTP.open
also accept optional keyword parameters.
e.g.
HTTP.request("GET", "http://httpbin.org/ip"; retries=4, cookies=true)
HTTP.get("http://s3.us-east-1.amazonaws.com/"; aws_authorization=true)
conf = (readtimeout = 10,
pipeline_limit = 4,
retry = false,
redirect = false)
HTTP.get("http://httpbin.org/ip"; conf..)
HTTP.put("http://httpbin.org/put", [], "Hello"; conf..)
URL options
query = nothing
, replaces the query part ofurl
.
Streaming options
response_stream = nothing
, a writeableIO
stream or anyIO
-like typeT
for whichwrite(T, AbstractVector{UInt8})
is defined.verbose = 0
, set to1
or2
for extra message logging.
Connection Pool options
connection_limit = 8
, number of concurrent connections to each host:port.pipeline_limit = 16
, number of concurrent requests per connection.reuse_limit = nolimit
, number of times a connection is reused after the first request.socket_type = TCPSocket
Timeout options
readtimeout = 60
, close the connection if no data is received for this many seconds. Usereadtimeout = 0
to disable.
Retry options
retry = true
, retry idempotent requests in case of error.retries = 4
, number of times to retry.retry_non_idempotent = false
, retry non-idempotent requests too. e.g. POST.
Redirect options
redirect = true
, follow 3xx redirect responses.redirect_limit = 3
, number of times to redirect.forwardheaders = true
, forward original headers on redirect.
Status Exception options
status_exception = true
, throwHTTP.StatusError
for response status >= 300.
SSLContext options
require_ssl_verification = false
, passMBEDTLS_SSL_VERIFY_REQUIRED
to the mbed TLS library. "... peer must present a valid certificate, handshake is aborted if verification failed."sslconfig = SSLConfig(require_ssl_verification)
Basic Authentication options
- Basic authentication is detected automatically from the provided url's
userinfo
(in the formscheme://user:password@host
) and adds theAuthorization: Basic
header
AWS Authentication options
aws_authorization = false
, enable AWS4 Authentication.aws_service = split(url.host, ".")[1]
aws_region = split(url.host, ".")[2]
aws_access_key_id = ENV["AWS_ACCESS_KEY_ID"]
aws_secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
aws_session_token = get(ENV, "AWS_SESSION_TOKEN", "")
body_sha256 = digest(MD_SHA256, body)
,body_md5 = digest(MD_MD5, body)
,
Cookie options
cookies::Union{Bool, Dict{String, String}} = false
, enable cookies, or alternatively, pass aDict{String, String}
of name-value pairs to manually pass cookiescookiejar::Dict{String, Set{Cookie}}=default_cookiejar
,
Canonicalization options
canonicalize_headers = false
, rewrite request and response headers in Canonical-Camel-Dash-Format.
Proxy options
proxy = proxyurl
, pass request through a proxy given as a url
Alternatively, HTTP.jl also respects the http_proxy
, https_proxy
, and no_proxy
environment variables; if set, they will be used automatically when making requests.
Request Body Examples
String body:
HTTP.request("POST", "http://httpbin.org/post", [], "post body data")
Stream body from file:
io = open("post_data.txt", "r")
HTTP.request("POST", "http://httpbin.org/post", [], io)
Generator body:
chunks = ("chunk$i" for i in 1:1000)
HTTP.request("POST", "http://httpbin.org/post", [], chunks)
Collection body:
chunks = [preamble_chunk, data_chunk, checksum(data_chunk)]
HTTP.request("POST", "http://httpbin.org/post", [], chunks)
open() do io
body:
HTTP.open("POST", "http://httpbin.org/post") do io
write(io, preamble_chunk)
write(io, data_chunk)
write(io, checksum(data_chunk))
end
Response Body Examples
String body:
r = HTTP.request("GET", "http://httpbin.org/get")
println(String(r.body))
Stream body to file:
io = open("get_data.txt", "w")
r = HTTP.request("GET", "http://httpbin.org/get", response_stream=io)
close(io)
println(read("get_data.txt"))
Stream body through buffer:
io = Base.BufferStream()
@async while !eof(io)
bytes = readavailable(io))
println("GET data: $bytes")
end
r = HTTP.request("GET", "http://httpbin.org/get", response_stream=io)
close(io)
Stream body through open() do io
:
r = HTTP.open("GET", "http://httpbin.org/stream/10") do io
while !eof(io)
println(String(readavailable(io)))
end
end
using HTTP.IOExtras
HTTP.open("GET", "https://tinyurl.com/bach-cello-suite-1-ogg") do http
n = 0
r = startread(http)
l = parse(Int, HTTP.header(r, "Content-Length"))
open(`vlc -q --play-and-exit --intf dummy -`, "w") do vlc
while !eof(http)
bytes = readavailable(http)
write(vlc, bytes)
n += length(bytes)
println("streamed $n-bytes $((100*n)÷l)%\u1b[1A")
end
end
end
Request and Response Body Examples
String bodies:
r = HTTP.request("POST", "http://httpbin.org/post", [], "post body data")
println(String(r.body))
Interfacing with RESTful JSON APIs:
using JSON
params = Dict("user"=>"RAO...tjN", "token"=>"NzU...Wnp", "message"=>"Hello!")
base_url = "http://api.domain.com"
endpoint = "/1/messages.json"
url = base_url * endpoint
r = HTTP.request("POST", url,
["Content-Type" => "application/json"],
JSON.json(params))
println(JSON.parse(String(r.body)))
Stream bodies from and to files:
in = open("foo.png", "r")
out = open("foo.jpg", "w")
HTTP.request("POST", "http://convert.com/png2jpg", [], in, response_stream=out)
Stream bodies through: open() do io
:
using HTTP.IOExtras
HTTP.open("POST", "http://music.com/play") do io
write(io, JSON.json([
"auth" => "12345XXXX",
"song_id" => 7,
]))
r = startread(io)
@show r.status
while !eof(io)
bytes = readavailable(io))
play_audio(bytes)
end
end
HTTP.open
— Function.HTTP.open(method, url, [,headers]) do io
write(io, body)
[startread(io) -> HTTP.Response]
while !eof(io)
readavailable(io) -> AbstractVector{UInt8}
end
end -> HTTP.Response
The HTTP.open
API allows the Request Body to be written to (and/or the Response Body to be read from) an IO
stream.
e.g. Streaming an audio file to the vlc
player:
HTTP.open(:GET, "https://tinyurl.com/bach-cello-suite-1-ogg") do http
open(`vlc -q --play-and-exit --intf dummy -`, "w") do vlc
write(vlc, http)
end
end
HTTP.get
— Function.HTTP.get(url [, headers]; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("GET", ...)
. See HTTP.request
.
HTTP.put
— Function.HTTP.put(url, headers, body; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("PUT", ...)
. See HTTP.request
.
HTTP.post
— Function.HTTP.post(url, headers, body; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("POST", ...)
. See HTTP.request
.
HTTP.head
— Function.HTTP.head(url; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("HEAD", ...)
. See HTTP.request
.
Request functions may throw the following exceptions:
StatusError <: Exception
The Response
has a 4xx
, 5xx
or unrecognised status code.
Fields:
status::Int16
, the response status code.response
theHTTP.Response
HTTP.Parsers.ParseError
— Type.ParseError <: Exception
Parser input was invalid.
Fields:
code
, error codebytes
, the offending input.
HTTP.IOExtras.IOError
— Type.IOError <: Exception
The request terminated with due to an IO-related error.
Fields:
e
, the error.
Sockets.DNSError
URIs
HTTP.URIs.URI
— Type.HTTP.URI(; scheme="", host="", port="", etc...)
HTTP.URI(str) = parse(HTTP.URI, str::String)
A type representing a valid uri. Can be constructed from distinct parts using the various supported keyword arguments. With a raw, already-encoded uri string, use parse(HTTP.URI, str)
to parse the HTTP.URI
directly. The HTTP.URI
constructors will automatically escape any provided query
arguments, typically provided as "key"=>"value"::Pair
or Dict("key"=>"value")
. Note that multiple values for a single query key can provided like Dict("key"=>["value1", "value2"])
.
The URI
struct stores the complete URI in the uri::String
field and the component parts in the following SubString
fields:
scheme
, e.g."http"
or"https"
userinfo
, e.g."username:password"
host
e.g."julialang.org"
port
e.g."80"
or""
path
e.g"/"
query
e.g."Foo=1&Bar=2"
fragment
The HTTP.resource(::URI)
function returns a target-resource string for the URI RFC7230 5.3. e.g. "$path?$query#$fragment"
.
The HTTP.queryparams(::URI)
function returns a Dict
containing the query
.
HTTP.URIs.escapeuri
— Function.percent-encode a string, dict, or pair for a uri
HTTP.URIs.unescapeuri
— Function.unescape a percent-encoded uri/url
HTTP.URIs.splitpath
— Function.Splits the path into components See: http://tools.ietf.org/html/rfc3986#section-3.3
Base.isvalid
— Method.checks if a HTTP.URI
is valid
Cookies
HTTP.Cookies.Cookie
— Type.Cookie()
Cookie(; kwargs...)
Cookie(name, value; kwargs...)
A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an HTTP response or the Cookie header of an HTTP request. Supported fields (which can be set using keyword arguments) include:
name
: name of the cookievalue
: value of the cookiepath
: applicable path for the cookiedomain
: applicable domain for the cookieexpires
: aDates.DateTime
representing when the cookie should expiremaxage
:maxage == 0
means no max age,maxage < 0
means delete cookie now,max age > 0
means the # of seconds until expirationsecure::Bool
: secure cookie attributehttponly::Bool
: httponly cookie attributehostonly::Bool
: hostonly cookie attribute
See http:#tools.ietf.org/html/rfc6265 for details.
Utilities
HTTP.sniff
— Function.HTTP.sniff(content::Union{Vector{UInt8}, String, IO})
=> String
(mimetype)
HTTP.sniff
will look at the first 512 bytes of content
to try and determine a valid mimetype. If a mimetype can't be determined appropriately, "application/octet-stream"
is returned.
Supports JSON detection through the HTTP.isjson(content)
function.
HTTP.Strings.escapehtml
— Function.escapehtml(i::String)
Returns a string with special HTML characters escaped: &, <, >, ", '
Server / Handlers
HTTP.Servers.listen
— Function.HTTP.listen([host=Sockets.localhost[, port=8081]]; kw...) do http::HTTP.Stream
...
end
Listen for HTTP connections and execute the do
function for each request.
The do
function should be of the form f(::HTTP.Stream)::Nothing
.
Optional keyword arguments:
sslconfig=nothing
, Provide anMbedTLS.SSLConfig
object to handle ssl connections. Passsslconfig=MbedTLS.SSLConfig(false)
to disable ssl verification (useful for testing).reuse_limit = nolimit
, number of times a connection is allowed to be reused after the first request.tcpisvalid = tcp->true
, functionf(::TCPSocket)::Bool
to, check accepted connection before processing requests. e.g. to do source IP filtering.readtimeout::Int=60
, close the connection if no data is received for this many seconds. Use readtimeout = 0 to disable.reuseaddr::Bool=false
, allow multiple servers to listen on the same port.server::Base.IOServer=nothing
, provide anIOServer
object to listen on; allows closing the server.connection_count::Ref{Int}
, reference to track the number of currently open connections.rate_limit::Rational{Int}=nothing"
, number ofconnections//second
allowed per client IP address; excess connections are immediately closed. e.g. 5//1.verbose::Bool=false
, log connection information tostdout
.
e.g.
HTTP.listen("127.0.0.1", 8081) do http
HTTP.setheader(http, "Content-Type" => "text/html")
write(http, "target uri: $(http.message.target)<BR>")
write(http, "request body:<BR><PRE>")
write(http, read(http))
write(http, "</PRE>")
return
end
HTTP.listen("127.0.0.1", 8081) do http
@show http.message
@show HTTP.header(http, "Content-Type")
while !eof(http)
println("body data: ", String(readavailable(http)))
end
HTTP.setstatus(http, 404)
HTTP.setheader(http, "Foo-Header" => "bar")
startwrite(http)
write(http, "response body")
write(http, "more response body")
end
The server=
option can be used to pass an already listening socket to HTTP.listen
. This allows manual control of server shutdown.
e.g.
using Sockets
server = Sockets.listen(Sockets.InetAddr(parse(IPAddr, host), port))
@async HTTP.listen(f, host, port; server=server)
# Closing server will stop HTTP.listen.
close(server)
To run the following HTTP chat example, open two Julia REPL windows and paste the example code into both of them. Then in one window run chat_server()
and in the other run chat_client()
, then type hello
and press return. Whatever you type on the client will be displayed on the server and vis-versa.
using HTTP
function chat(io::HTTP.Stream)
@async while !eof(io)
write(stdout, readavailable(io), "\n")
end
while isopen(io)
write(io, readline(stdin))
end
end
chat_server() = HTTP.listen("127.0.0.1", 8087) do io
write(io, "HTTP.jl Chat Server. Welcome!")
chat(io)
end
chat_client() = HTTP.open("POST", "http://127.0.0.1:8087") do io
chat(io)
end
HTTP.Handlers.serve
— Function.HTTP.serve([host=Sockets.localhost[, port=8081]]; kw...) do req::HTTP.Request
...
end
HTTP.serve([host=Sockets.localhost[, port=8081]]; stream=true, kw...) do stream::HTTP.Stream
...
end
HTTP.serve(handler, [host=Sockets.localhost[, port=8081]]; kw...)
Listen for HTTP connections and handle each request received. The "handler" can be a function that operates directly on HTTP.Stream
, HTTP.Request
, or any kind of HTTP.Handler
instance. For functions like f(::HTTP.Stream)
, also pass stream=true
to signal a streaming handler.
Optional keyword arguments:
sslconfig=nothing
, Provide anMbedTLS.SSLConfig
object to handle ssl connections. Passsslconfig=MbedTLS.SSLConfig(false)
to disable ssl verification (useful for testing)reuse_limit = nolimit
, number of times a connection is allowed to be reused after the first request.tcpisvalid::Function (::TCPSocket) -> Bool
, check accepted connection before processing requests. e.g. to implement source IP filtering, rate-limiting, etc.readtimeout::Int=60
, close the connection if no data is received for this many seconds. Use readtimeout = 0 to disable.reuseaddr::Bool=false
, allow multiple server processes to listen on the same port. Only fully supported on linux; OSX will allow multiple server processes to listen, but only one will accept connectionsserver::Base.IOServer=nothing
, provide anIOServer
object to listen on; allows manual control over closing the server.connection_count::Ref{Int}
, reference to track the # of currently open connections.rate_limit::Rational{Int}=nothing"
, number ofconnections//second
allowed per client IP address; excess connections are immediately closed. e.g. 5//1.stream::Bool=false
, the handler will operate on anHTTP.Stream
instead ofHTTP.Request
verbose::Bool=false
, log connection information tostdout
.
Examples
HTTP.serve(; stream=true) do http::HTTP.Stream
@show http.message
@show HTTP.header(http, "Content-Type")
while !eof(http)
println("body data: ", String(readavailable(http)))
end
HTTP.setstatus(http, 404)
HTTP.setheader(http, "Foo-Header" => "bar")
startwrite(http)
write(http, "response body")
write(http, "more response body")
return
end
# pass in own server socket to control shutdown
using Sockets
server = Sockets.serve(Sockets.InetAddr(parse(IPAddr, host), port))
@async HTTP.serve(f, host, port; server=server)
# close server which will stop HTTP.serve
close(server)
HTTP.Handlers
— Module.Examples
Let's put together an example http REST server for our hypothetical "ZooApplication" that utilizes various parts of the Servers & Handler frameworks.
Our application allows users to interact with custom "animal" JSON objects.
First we have our "model" or data structures:
mutable struct Animal
id::Int
type::String
name::String
end
Now we want to define our REST api, or how do we allow users to create, update, retrieve and delete animals:
# use a plain `Dict` as a "data store"
const ANIMALS = Dict{Int, Animal}()
const NEXT_ID = Ref(0)
function getNextId()
id = NEXT_ID[]
NEXT_ID[] += 1
return id
end
# "service" functions to actually do the work
function createAnimal(req::HTTP.Request)
animal = JSON2.read(IOBuffer(HTTP.payload(req)), Animal)
animal.id = getNextId()
ANIMALS[animal.id] = animal
return HTTP.Response(200, JSON2.write(animal))
end
function getAnimal(req::HTTP.Request)
animalId = HTTP.URIs.splitpath(req.target)[5] # /api/zoo/v1/animals/10, get 10
animal = ANIMALS[parse(Int, animalId)]
return HTTP.Response(200, JSON2.write(animal))
end
function updateAnimal(req::HTTP.Request)
animal = JSON2.read(IOBuffer(HTTP.payload(req)), Animal)
ANIMALS[animal.id] = animal
return HTTP.Response(200, JSON2.write(animal))
end
function deleteAnimal(req::HTTP.Request)
animalId = HTTP.URIs.splitpath(req.target)[5] # /api/zoo/v1/animals/10, get 10
delete!(ANIMALS, parse(Int, animal.id))
return HTTP.Response(200)
end
# define REST endpoints to dispatch to "service" functions
const ANIMAL_ROUTER = HTTP.Router()
HTTP.@register(ANIMAL_ROUTER, "POST", "/api/zoo/v1/animals", createAnimal)
# note the use of `*` to capture the path segment "variable" animal id
HTTP.@register(ANIMAL_ROUTER, "GET", "/api/zoo/v1/animals/*", getAnimal)
HTTP.@register(ANIMAL_ROUTER, "PUT", "/api/zoo/v1/animals", updateAnimal)
HTTP.@register(ANIMAL_ROUTER, "DELETE", "/api/zoo/v1/animals/*", deleteAnimal)
Great! At this point, we could spin up our server and let users start managing their animals:
HTTP.serve(ANIMAL_ROUTER, Sockets.localhost, 8081)
Now, you may have noticed that there was a bit of repetition in our "service" functions, particularly with regards to the JSON serialization/deserialization. Perhaps we can simplify things by writing a custom "JSONHandler" to do some of the repetitive work for us.
function JSONHandler(req::HTTP.Request)
# first check if there's any request body
body = IOBuffer(HTTP.payload(req))
if eof(body)
# no request body
response_body = handle(ANIMAL_ROUTER, req)
else
# there's a body, so pass it on to the handler we dispatch to
response_body = handle(ANIMAL_ROUTER, req, JSON2.read(body, Animal))
end
return HTTP.Response(200, JSON2.write(response_body))
end
# **simplified** "service" functions
function createAnimal(req::HTTP.Request, animal)
animal.id = getNextId()
ANIMALS[animal.id] = animal
return animal
end
function getAnimal(req::HTTP.Request)
animalId = HTTP.URIs.splitpath(req.target)[5] # /api/zoo/v1/animals/10, get 10
return ANIMALS[animalId]
end
function updateAnimal(req::HTTP.Request, animal)
ANIMALS[animal.id] = animal
return animal
end
function deleteAnimal(req::HTTP.Request)
animalId = HTTP.URIs.splitpath(req.target)[5] # /api/zoo/v1/animals/10, get 10
delete!(ANIMALS, animal.id)
return ""
end
And we modify slightly how we run our server, letting our new JSONHandler
be the entry point instead of our router:
HTTP.serve(JSONHandler, Sockets.localhost, 8081)
Our JSONHandler
is nice because it saves us a bunch of repetition: if a request body comes in, we automatically deserialize it and pass it on to the service function. And each service function doesn't need to worry about returning HTTP.Response
s anymore, but can just focus on returning plain Julia objects/strings. The other huge advantage is it provides a clean separation of concerns between the "service" layer, which should really concern itself with application logic, and the "REST API" layer, which should take care of translating between our model and a web data format (JSON).
Let's take this one step further and allow multiple users to manage users, and add in one more custom handler to provide an authentication layer to our application. We can't just let anybody be modifying another user's animals!
# modified Animal struct to associate with specific user
mutable struct Animal
id::Int
userId::Base.UUID
type::String
name::String
end
# modify our data store to allow for multiple users
const ANIMALS = Dict{Base.UUID, Dict{Int, Animal}}()
# creating a user returns a new UUID key unique to the user
createUser(req) = Base.UUID(rand(UInt128))
# add an additional endpoint for user creation
HTTP.@register(ANIMAL_ROUTER, "POST", "/api/zoo/v1/users", createUser)
# modify service endpoints to have user pass UUID in
HTTP.@register(ANIMAL_ROUTER, "GET", "/api/zoo/v1/users/*/animals/*", getAnimal)
HTTP.@register(ANIMAL_ROUTER, "DELETE", "/api/zoo/v1/users/*/animals/*", deleteAnimal)
# modified service functions to account for multiple users
function createAnimal(req::HTTP.Request, animal)
animal.id = getNextId()
ANIMALS[animal.userId][animal.id] = animal
return animal
end
function getAnimal(req::HTTP.Request)
paths = HTTP.URIs.splitpath(req.target)
userId = path[5] # /api/zoo/v1/users/x92jf-.../animals/10, get user UUID
animalId = path[7] # /api/zoo/v1/users/x92jf-.../animals/10, get 10
return ANIMALS[userId][parse(Int, animalId)]
end
function updateAnimal(req::HTTP.Request, animal)
ANIMALS[animal.userId][animal.id] = animal
return animal
end
function deleteAnimal(req::HTTP.Request)
paths = HTTP.URIs.splitpath(req.target)
userId = path[5] # /api/zoo/v1/users/x92jf-.../animals/10, get user UUID
animalId = path[7] # /api/zoo/v1/users/x92jf-.../animals/10, get 10
delete!(ANIMALS[userId], parse(Int, animal.id))
return ""
end
# AuthHandler to reject any unknown users
function AuthHandler(req)
if HTTP.hasheader(req, "Animal-UUID")
uuid = HTTP.header(req, "Animal-UUID")
if haskey(ANIMALS, uuid)
return JSONHandler(req)
end
end
return HTTP.Response(401, "unauthorized")
end
And our modified server invocation:
HTTP.serve(AuthHandler, Sockets.localhost, 8081)
Let's review what's going on here:
- Each
Animal
object now includes aUUID
object unique to a user - We added a
/api/zoo/v1/users
endpoint for creating a new user - Each of our service functions now account for individual users
- We made a new
AuthHandler
as the very first entry point in our middleware stack, this means that every single request must first pass through this authentication layer before reaching the service layer. OurAuthHandler
checks that the user provided our security request headerAnimal-UUID
and if so, ensures the provided UUID corresponds to a valid user. If not, theAuthHandler
returns a 401 HTTP response, signalling that the request is unauthorized
Voila, hopefully that helps provide a slightly-more-than-trivial example of utilizing the HTTP.Handler framework in conjunction with running an HTTP server.
HTTP.Handlers.handle
— Function.HTTP.handle(handler::HTTP.RequestHandler, ::HTTP.Request) => HTTP.Response
HTTP.handle(handler::HTTP.StreamHandler, ::HTTP.Stream)
Dispatch function used to handle incoming requests to a server. Can be overloaded by custom HTTP.Handler
subtypes to implement custom "handling" behavior.
RequestHandlerFunction(f)
A function-wrapper type that is a subtype of RequestHandler
. Takes a single function as an argument that should be of the form f(::HTTP.Request) => HTTP.Response
StreamHandlerFunction(f)
A function-wrapper type that is a subtype of StreamHandler
. Takes a single function as an argument that should be of the form f(::HTTP.Stream) => Nothing
, i.e. it accepts a raw HTTP.Stream
, handles the incoming request, writes a response back out to the stream directly, then returns.
HTTP.Handlers.Router
— Type.HTTP.Router(h::Handler)
HTTP.Router(f::Function)
HTTP.Router()
An HTTP.Handler
type that supports pattern matching request url paths to registered HTTP.Handler
s. Can accept a default Handler
or Function
that will be used in case no other handlers match; by default, a 404 response handler is used. Paths can be mapped to a handler via HTTP.@register(r::Router, path, handler)
, see ?HTTP.@register
for more details.
HTTP.Handlers.@register
— Macro.HTTP.@register(r::Router, path, handler)
HTTP.@register(r::Router, method::String, path, handler)
HTTP.@register(r::Router, method::String, scheme::String, host::String, path, handler)
Function to map request urls matching path
and optional method, scheme, host to another handler::HTTP.Handler
. URL paths are registered one at a time, and multiple urls can map to the same handler. The URL can be passed as a String. Requests can be routed based on: method, scheme, hostname, or path. The following examples show how various urls will direct how a request is routed by a server:
"http://*"
: match all HTTP requests, regardless of path"https://*"
: match all HTTPS requests, regardless of path"/gmail"
: regardless of scheme or host, match any request with a path starting with "gmail""/gmail/userId/*/inbox
: match any request matching the path pattern, "*" is used as a wildcard that matches any value between the two "/"
Note that due to being a macro (and the internal routing functionality), routes can only be registered statically, i.e. at the top level of a module, and not dynamically, i.e. inside a function.