API Reference
Client Requests
HTTP.request
— FunctionHTTP.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")
r = HTTP.get("http://httpbin.org/ip") # equivalent shortcut
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. By convention, if a header value is an empty string, it will not be written when sending a request (following the curl convention). By default, a copy of provided headers is made (since required headers are typically set during the request); to avoid this copy and have HTTP.jl mutate the provided headers array, pass copyheaders=false
as an additional keyword argument to the request.
The body
argument can be a variety of objects:
- an
AbstractDict
orNamedTuple
to be serialized as the "application/x-www-form-urlencoded" content type - any
AbstractString
orAbstractVector{UInt8}
which will be sent "as is" for the request body - a readable
IO
stream or anyIO
-like typeT
for whicheof(T)
andreadavailable(T)
are defined. This stream will be read and sent untileof
istrue
. This object should support themark
/reset
methods if request retries are desired (if not, no retries will be attempted). - Any collection or iterable of the above (
AbstractDict
,AbstractString
,AbstractVector{UInt8}
, orIO
) which will result in a "chunked" request body, where each iterated element will be sent as a separate chunk - a
HTTP.Form
, which will be serialized as the "multipart/form-data" content-type
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}
or::IO
, the Response Body bytes or theio
argument provided via theresponse_stream
keyword argument
Functions HTTP.get
, HTTP.put
, HTTP.post
and HTTP.head
are defined as shorthand for HTTP.request("GET", ...)
, etc.
Supported optional keyword arguments:
query = nothing
, aPair
orDict
of key => values to be included in the urlresponse_stream = nothing
, a writeableIO
stream or anyIO
-like typeT
for whichwrite(T, AbstractVector{UInt8})
is defined. The response body will be written to this stream instead of returned as aVector{UInt8}
.verbose = 0
, set to1
or2
for increasingly verbose logging of the request and response processconnect_timeout = 30
, close the connection after this many seconds if it is still attempting to connect. Useconnect_timeout = 0
to disable.pool = nothing
, anHTTP.Pool
object to use for managing the reuse of connections between requests. By default, a global pool is used, which is shared across all requests. To create a pool for a specific set of requests, usepool = HTTP.Pool(max::Int)
, wheremax
controls the maximum number of concurrent connections allowed to be used for requests at a given time.readtimeout = 0
, abort a request after this many seconds. Will trigger retries if applicable. Usereadtimeout = 0
to disable.status_exception = true
, throwHTTP.StatusError
for response status >= 300.- Basic authentication is detected automatically from the provided url's
userinfo
(in the formscheme://user:password@host
) and adds theAuthorization: Basic
header; this can be disabled by passingbasicauth=false
canonicalize_headers = false
, rewrite request and response headers in Canonical-Camel-Dash-Format.proxy = proxyurl
, pass request through a proxy given as a url; alternatively, thehttp_proxy
,HTTP_PROXY
,https_proxy
,HTTPS_PROXY
, andno_proxy
environment variables are also detected/used; if set, they will be used automatically when making requests.detect_content_type = false
: iftrue
and the request body is not a form orIO
, it will be inspected and the "Content-Type" header will be set to the detected content type.decompress = nothing
, by default, decompress the response body if the response has a "Content-Encoding" header set to "gzip". Ifdecompress=true
, decompress the response body regardless ofContent-Encoding
header. Ifdecompress=false
, do not decompress the response body.logerrors = false
, iftrue
,HTTP.StatusError
,HTTP.TimeoutError
,HTTP.IOError
, andHTTP.ConnectError
will be logged via@error
as they happen, regardless of whether the request is then retried or not. Useful for debugging or monitoring requests where there's worry of certain errors happening but ignored because of retries.logtag = nothing
, if provided, will be used as the tag for error logging. Useful for debugging or monitoring requests.observelayers = false
, iftrue
, enables theHTTP.observelayer
to wrap each client-side "layer" to track the amount of time spent in each layer as a request is processed. This can be useful for debugging performance issues. Note that when retries or redirects happen, the time spent in each layer is cumulative, as noted by the[layer]_count
. The metrics are stored in theRequest.context
dictionary, and can be accessed likeHTTP.get(...).request.context
Retry arguments:
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.retry_delays = ExponentialBackOff(n = retries)
, provide a customExponentialBackOff
object to control the delay between retries.retry_check = (s, ex, req, resp, resp_body) -> Bool
, provide a custom function to control whether a retry should be attempted. The function should accept 5 arguments: the delay state, exception, request, response (anHTTP.Response
object if a request was successfully made, otherwisenothing
), andresp_body
response body (which may benothing
if there is no response yet, otherwise aVector{UInt8}
), and returntrue
if a retry should be attempted.
Redirect arguments:
redirect = true
, follow 3xx redirect responses; i.e. additional requests will be made to the redirected locationredirect_limit = 3
, maximum number of times a redirect will be followedredirect_method = nothing
, the method to use for the redirected request; by default, GET will be used, only responses with 307/308 will use the same original request method. Passredirect_method=:same
to pass the same method as the orginal request though note that some servers may not respond/accept the same method. It's also valid to pass the exact method to use as a string, likeredirect_method="PUT"
.forwardheaders = true
, forward original headers on redirect.
SSL arguments:
require_ssl_verification = NetworkOptions.verify_host(host)
, 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)
socket_type_tls = MbedTLS.SSLContext
, the type of socket to use for TLS connections. Defaults toMbedTLS.SSLContext
. Also supported is passingsocket_type_tls = OpenSSL.SSLStream
. To change the global default, setHTTP.SOCKET_TYPE_TLS[] = OpenSSL.SSLStream
.
Cookie arguments:
cookies::Union{Bool, Dict{<:AbstractString, <:AbstractString}} = true
, enable cookies, or alternatively, pass aDict{AbstractString, AbstractString}
of name-value pairs to manually pass cookies in the request "Cookie" headercookiejar::HTTP.CookieJar=HTTP.COOKIEJAR
: threadsafe cookie jar struct for keeping track of cookies per host; a global cookie jar is used by default.
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:
r = HTTP.get("http://httpbin.org/get", response_stream=IOBuffer())
println(String(take!(r.body)))
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
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
Interfacing with RESTful JSON APIs:
using JSON
params = Dict("user"=>"RAO...tjN", "token"=>"NzU...Wnp", "message"=>"Hello!")
url = "http://api.domain.com/1/messages.json"
r = HTTP.post(url, body=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
:
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.get
— FunctionHTTP.get(url [, headers]; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("GET", ...)
. See HTTP.request
.
HTTP.put
— FunctionHTTP.put(url, headers, body; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("PUT", ...)
. See HTTP.request
.
HTTP.post
— FunctionHTTP.post(url, headers, body; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("POST", ...)
. See HTTP.request
.
HTTP.head
— FunctionHTTP.head(url; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("HEAD", ...)
. See HTTP.request
.
HTTP.patch
— FunctionHTTP.patch(url, headers, body; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("PATCH", ...)
. See HTTP.request
.
HTTP.delete
— FunctionHTTP.delete(url [, headers]; <keyword arguments>) -> HTTP.Response
Shorthand for HTTP.request("DELETE", ...)
. See HTTP.request
.
HTTP.open
— FunctionWebSockets.open(handler, url; verbose=false, kw...)
Initiate a websocket connection to url
(which should have schema like ws://
or wss://
), and call handler(ws)
with the websocket connection. Passing verbose=true
or verbose=2
will enable debug logging for the life of the websocket connection. handler
should be a function of the form f(ws) -> nothing
, where ws
is a WebSocket
. Supported keyword arguments are the same as supported by HTTP.request
. Typical websocket usage is:
WebSockets.open(url) do ws
# iterate incoming websocket messages
for msg in ws
# send message back to server or do other logic here
send(ws, msg)
end
# iteration ends when the websocket connection is closed by server or error
end
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.download
— Functiondownload(url, [local_path], [headers]; update_period=1, kw...)
Similar to Base.download
this downloads a file, returning the filename. If the local_path
:
- is not provided, then it is saved in a temporary directory
- if part to a directory is provided then it is saved into that directory
- otherwise the local path is uses as the filename to save to.
When saving into a directory, the filename is determined (where possible), from the rules of the HTTP.
update_period
controls how often (in seconds) to report the progress.- set to
Inf
to disable reporting
- set to
headers
specifies headers to be used for the HTTP GET request- any additional keyword args (
kw...
) are passed on to the HTTP request.
Request/Response Objects
HTTP.Messages.Request
— TypeHTTP.Request(
method, target, headers=[], body=nobody;
version=v"1.1", url::URI=URI(), responsebody=nothing, parent=nothing, context=HTTP.Context()
)
Represents a HTTP Request Message with fields:
method::String
RFC7230 3.1.1target::String
RFC7230 5.3version::HTTPVersion
RFC7230 2.6headers::HTTP.Headers
RFC7230 3.2body::Union{Vector{UInt8}, IO}
RFC7230 3.3response
, theResponse
to thisRequest
url::URI
, the full URI of the requestparent
, theResponse
(if any) that led to this request (e.g. in the case of a redirect). RFC7230 6.4context
, aDict{Symbol, Any}
store used by middleware to share state
HTTP.Messages.Response
— TypeHTTP.Response(status, headers::HTTP.Headers, body; request=nothing)
HTTP.Response(status, body)
HTTP.Response(body)
Represents an HTTP response message with fields:
version::HTTPVersion
RFC7230 2.6status::Int16
RFC7230 3.1.2 RFC7231 6headers::Vector{Pair{String,String}}
RFC7230 3.2body::Vector{UInt8}
orbody::IO
RFC7230 3.3request
, theRequest
that yielded thisResponse
.
HTTP.Streams.Stream
— TypeStream(::Request, ::IO)
Creates a HTTP.Stream
that wraps an existing IO
stream.
startwrite(::Stream)
sends theRequest
headers to theIO
stream.write(::Stream, body)
sends thebody
(or a chunk of the body).closewrite(::Stream)
sends the final0
chunk (if needed) and callsclosewrite
on theIO
stream.startread(::Stream)
callsstartread
on theIO
stream then reads and parses theResponse
headers.eof(::Stream)
andreadavailable(::Stream)
parse the body from theIO
stream.closeread(::Stream)
reads the trailers and callscloseread
on theIO
stream. When theIO
stream is aHTTP.Connections.Connection
, callingcloseread
releases the connection back to the connection pool for reuse. If a complete response has not been received,closeread
throwsEOFError
.
HTTP.WebSockets.WebSocket
— TypeWebSocket(io::HTTP.Connection, req, resp; client=true)
Representation of a websocket connection. Use WebSockets.open
to open a websocket connection, passing a handler function f(ws)
to send and receive messages. Use WebSockets.listen
to listen for incoming websocket connections, passing a handler function f(ws)
to send and receive messages.
Call send(ws, msg)
to send a message; if msg
is an AbstractString
, a TEXT websocket message will be sent; if msg
is an AbstractVector{UInt8}
, a BINARY websocket message will be sent. Otherwise, msg
should be an iterable of either AbstractString
or AbstractVector{UInt8}
, and a fragmented message will be sent, one frame for each iterated element.
Control frames can be sent by calling ping(ws[, data])
, pong(ws[, data])
, or close(ws[, body::WebSockets.CloseFrameBody])
. Calling close
will initiate the close sequence and close the underlying connection.
To receive messages, call receive(ws)
, which will block until a non-control, full message is received. PING messages will automatically be responded to when received. CLOSE messages will also be acknowledged and then a WebSocketError
will be thrown with the WebSockets.CloseFrameBody
payload, which may include a non-error CLOSE frame status code. WebSockets.isok(err)
can be called to check if the CLOSE was normal or unexpected. Fragmented messages will be received until the final frame is received and the full concatenated payload can be returned. receive(ws)
returns a Vector{UInt8}
for BINARY messages, and a String
for TEXT messages.
For convenience, WebSocket
s support the iteration protocol, where each iteration will receive
a non-control message, with iteration terminating when the connection is closed. E.g.:
WebSockets.open(url) do ws
for msg in ws
# do cool stuff with msg
end
end
HTTP.Messages.header
— FunctionHTTP.header(::Message, key [, default=""]) -> String
Get header value for key
(case-insensitive).
HTTP.Messages.headers
— FunctionHTTP.headers(m::Message, key) -> Vector{String}
Get all headers with key k
or empty if none
HTTP.Messages.hasheader
— FunctionHTTP.hasheader(::Message, key) -> Bool
Does header value for key
exist (case-insensitive)?
HTTP.hasheader(::Message, key, value) -> Bool
Does header for key
match value
(both case-insensitive)?
HTTP.Messages.headercontains
— FunctionHTTP.headercontains(::Message, key, value) -> Bool
Does the header for key
(interpreted as comma-separated list) contain value
(both case-insensitive)?
HTTP.Messages.setheader
— FunctionHTTP.setheader(::Message, key => value)
Set header value
for key
(case-insensitive).
HTTP.Messages.appendheader
— FunctionHTTP.appendheader(::Message, key => value)
Append a header value to message.headers
.
If key
is the same as the previous header, the value
is appended to the value of the previous header with a comma delimiter
Set-Cookie
headers are not comma-combined because cookies often contain internal commas.
HTTP.Messages.removeheader
— FunctionHTTP.removeheader(::Message, key)
Remove header for key
(case-insensitive).
HTTP.Messages.decode
— FunctionHTTP.decode(r::Union{Request, Response}) -> Vector{UInt8}
For a gzip encoded request/response body, decompress it and return the decompressed body.
Request body types
HTTP.Forms.Form
— TypeHTTP.Form(data; boundary=string(rand(UInt128), base=16))
Construct a request body for multipart/form-data encoding from data
.
data
must iterate key-value pairs (e.g. AbstractDict
or Vector{Pair}
) where the key/value of the iterator is the key/value of each mutipart boundary chunk. Files and other large data arguments can be provided as values as IO arguments: either an IOStream
such as returned via open(file)
, or an IOBuffer
for in-memory data.
For complete control over a multipart chunk's details, an HTTP.Multipart
type is provided to support setting the filename
, Content-Type
, and Content-Transfer-Encoding
.
Examples
data = Dict(
"text" => "text data",
# filename (cat.png) and content-type (image/png) inferred from the IOStream
"file1" => open("cat.png"),
# manully controlled chunk
"file2" => HTTP.Multipart("dog.jpeg", open("mydog.jpg"), "image/jpeg"),
)
body = HTTP.Form(data)
headers = []
HTTP.post(url, headers, body)
HTTP.Forms.Multipart
— TypeHTTP.Multipart(filename::String, data::IO, content_type=HTTP.sniff(data), content_transfer_encoding="")
A type to represent a single multipart upload chunk for a file. This type would be used as the value in a key-value pair when constructing a HTTP.Form
for a request body (see example below). The data
argument must be an IO
type such as IOStream
, or IOBuffer
. The content_type
and content_transfer_encoding
arguments allow manual setting of these multipart headers. Content-Type
will default to the result of the HTTP.sniff(data)
mimetype detection algorithm, whereas Content-Transfer-Encoding
will be left out if not specified.
Examples
body = HTTP.Form(Dict(
"key" => HTTP.Multipart("File.txt", open("MyFile.txt"), "text/plain"),
))
headers = []
HTTP.post(url, headers, body)
Extended help
Filename SHOULD be included when the Multipart represents the contents of a file RFC7578 4.2
Content-Disposition set to "form-data" MUST be included with each Multipart. An additional "name" parameter MUST be included An optional "filename" parameter SHOULD be included if the contents of a file are sent This will be formatted such as: Content-Disposition: form-data; name="user"; filename="myfile.txt" RFC7578 4.2
Content-Type for each Multipart is optional, but SHOULD be included if the contents of a file are sent. RFC7578 4.4
Content-Transfer-Encoding for each Multipart is deprecated RFC7578 4.7
Other Content- header fields MUST be ignored RFC7578 4.8
Request exceptions
Request functions may throw the following exceptions:
HTTP.Exceptions.ConnectError
— TypeHTTP.ConnectError
Raised when an error occurs while trying to establish a request connection to the remote server. To see the underlying error, see the error
field.
HTTP.Exceptions.TimeoutError
— TypeHTTP.TimeoutError
Raised when a request times out according to readtimeout
keyword argument provided.
HTTP.Exceptions.StatusError
— TypeHTTP.StatusError
Raised when an HTTP.Response
has a 4xx
, 5xx
or unrecognised status code.
Fields:
status::Int16
, the response status code.method::String
, the request method.target::String
, the request target.response
, theHTTP.Response
HTTP.Exceptions.RequestError
— TypeHTTP.RequestError
Raised when an error occurs while physically sending a request to the remote server or reading the response back. To see the underlying error, see the error
field.
URIs
HTTP.jl uses the URIs.jl package for handling URIs. Some functionality from URIs.jl, relevant to HTTP.jl, are listed below:
URIs.URI
— TypeURI(; scheme="", host="", port="", etc...)
URI(str) = parse(URI, str::String)
A type representing a URI (e.g. a URL). Can be constructed from distinct parts using the various supported keyword arguments, or from a string. The URI
constructors will automatically escape any provided query
arguments, typically provided as "key"=>"value"::Pair
or Dict("key"=>"value")
. For all other components, you need to manually percent encode them before passing them to the URI
constructor. Note that multiple values for a single query key can provided like Dict("key"=>["value1", "value2"])
, in which case the constructor will percent encode only the values you pass in as the query
part.
When constructing a URI
from a String
, you need to ensure that the string is correctly percent encoded already.
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 queryparams(::URI)
function returns a Dict
containing the query
.
Note that you manually need to percent decode the content of the individual component fields before you further use their content, as they are returned in percent-encoded form.
URIs.escapeuri
— Functionescapeuri(x)
Apply URI percent-encoding to escape special characters in x
.
URIs.unescapeuri
— Functionunescapeuri(str)
Percent-decode a string according to the URI escaping rules.
URIs.splitpath
— FunctionURIs.splitpath(path|uri; rstrip_empty_segment=true)
Splits the path into component segments based on /
, according to http://tools.ietf.org/html/rfc3986#section-3.3. Any fragment and query parts of the string are ignored if present.
A final empty path segment (trailing '/') is removed, if present. This is technically incompatible with the segment grammar of RFC3986, but it seems to be a common recommendation to make paths with and without a trailing slash equivalent. To preserve any final empty path segment, set rstrip_empty_segment=false
.
Examples
julia> URIs.splitpath(URI("http://example.com/foo/bar?a=b&c=d"))
2-element Array{String,1}:
"foo"
"bar"
julia> URIs.splitpath("/foo/bar/")
2-element Array{String,1}:
"foo"
"bar"
Base.isvalid
— Methodchecks if a URI
is valid
Cookies
HTTP.Cookies.Cookie
— TypeCookie()
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::String
: name of the cookievalue::String
: value of the cookiepath::String
: applicable path for the cookiedomain::String
: applicable domain for the cookieexpires::Dates.DateTime
: when the cookie should expiremaxage::Int
:maxage == 0
means no max age,maxage < 0
means delete cookie now,maxage > 0
means the # of seconds until expirationsecure::Bool
: secure cookie attributehttponly::Bool
: httponly cookie attributehostonly::Bool
: hostonly cookie attributesamesite::Bool
: SameSite cookie attribute
See IETF RFC 6265 for details.
The string representation of a cookie is generated by calling stringify(cookie; isrequest=true)
, where isrequest=true
will only include the name=value
pair for requests, and if false
, will generate the "Set-Cookie"
representation for a response header.
A Vector{Cookie}
can be retrieved from a Request
/Response
object by calling Cookies.cookies(r)
.
A Cookie
can be added to a Request
/Response
object by calling Cookies.addcookie!(r, cookie)
.
HTTP.Cookies.stringify
— Functionstringify(cookie::Cookie, isrequest=true)
Generate the string representation of a cookie. By default, isrequest=true
, and only the name=value
pair will be included in the cookie string. For isrequest=false
, the other cookie attributes will be included, ;
-separated, for use in a "Set-Cookie" header.
HTTP.Cookies.addcookie!
— FunctionCookies.addcookie!(r::Union{HTTP.Request, HTTP.Response}, c::Cookie)
Convenience function for adding a single cookie to a request or response object. For requests, the cookie will be stringified and concatenated to any existing "Cookie" header. For responses, an additional "Set-Cookie" header will be appended.
HTTP.Cookies.cookies
— FunctionHTTP.cookies(r::Union{Request, Response}) -> Vector{Cookie}
Return a list of cookies, if any, parsed from the request "Cookie" or response "Set-Cookie" headers.
WebSockets
Sockets.send
— Methodsend(ws::WebSocket, msg)
Send a message on a websocket connection. If msg
is an AbstractString
, a TEXT websocket message will be sent; if msg
is an AbstractVector{UInt8}
, a BINARY websocket message will be sent. Otherwise, msg
should be an iterable of either AbstractString
or AbstractVector{UInt8}
, and a fragmented message will be sent, one frame for each iterated element.
Control frames can be sent by calling ping(ws[, data])
, pong(ws[, data])
, or close(ws[, body::WebSockets.CloseFrameBody])
. Calling close
will initiate the close sequence and close the underlying connection.
HTTP.WebSockets.receive
— Functionreceive(ws::WebSocket) -> Union{String, Vector{UInt8}}
Receive a message from a websocket connection. Returns a String
if the message was TEXT, or a Vector{UInt8}
if the message was BINARY. If control frames (ping or pong) are received, they are handled automatically and a non-control message is waited for. If a CLOSE message is received, it is responded to and a WebSocketError
is thrown with the WebSockets.CloseFrameBody
as the error value. This error can be checked with WebSockets.isok(err)
to see if the closing was "normal" or if an actual error occurred. For fragmented messages, the incoming frames will continue to be read until the final fragment is received. The bodies of each fragment are concatenated into the final message returned by receive
. Note that WebSocket
objects can be iterated, where each iteration yields a message until the connection is closed.
Missing docstring for HTTP.WebSockets.close(::HTTP.WebSockets.WebSocket, body)
. Check Documenter's build log for details.
HTTP.WebSockets.ping
— Functionping(ws, data=[])
Send a PING control frame on a websocket connection. data
is an optional body to send with the message. PONG messages are automatically responded to when a PING message is received by a websocket connection.
HTTP.WebSockets.pong
— Functionpong(ws, data=[])
Send a PONG control frame on a websocket connection. data
is an optional body to send with the message. Note that PING messages are automatically responded to internally by the websocket connection with a corresponding PONG message, but in certain cases, a unidirectional PONG message can be used as a one-way heartbeat.
Base.iterate
— Methoditerate(ws)
Continuously call receive(ws)
on a WebSocket
connection, with each iteration yielding a message until the connection is closed. E.g.
for msg in ws
# do something with msg
end
HTTP.WebSockets.isclosed
— FunctionWebSockets.isclosed(ws) -> Bool
Check whether a WebSocket
has sent and received CLOSE frames.
HTTP.WebSockets.isok
— FunctionWebSockets.isok(x::WebSocketError) -> Bool
Returns true if the WebSocketError
has a non-error status code. When calling receive(websocket)
, if a CLOSE frame is received, the CLOSE frame body is parsed and thrown inside the WebSocketError
, but if the CLOSE frame has a non-error status code, it's safe to ignore the error and return from the WebSockets.open
or WebSockets.listen
calls without throwing.
Utilities
HTTP.Sniff.sniff
— FunctionHTTP.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.
Examples
julia> HTTP.sniff("Hello world!!")
"text/plain; charset=utf-8"
julia> HTTP.sniff("<html><body>Hello world!!</body></html>")
"text/html; charset=utf-8"
julia> HTTP.sniff("{"a": -1.0}")
"application/json; charset=utf-8"
HTTP.Strings.escapehtml
— Functionescapehtml(i::String)
Returns a string with special HTML characters escaped: &, <, >, ", '
HTTP.Strings.tocameldash
— Functiontocameldash(s::String)
Ensure the first character and characters that follow a '-' are uppercase.
HTTP.Strings.iso8859_1_to_utf8
— Functioniso8859_1_to_utf8(bytes::AbstractVector{UInt8})
Convert from ISO8859_1 to UTF8.
HTTP.Strings.ascii_lc_isequal
— FunctionCase insensitive ASCII character comparison.
HTTP.ascii_lc_isequal(a::String, b::String)
Case insensitive ASCII string comparison.
HTTP.StatusCodes.statustext
— Functionstatustext(::Int) -> String
String
representation of a HTTP status code.
Examples
julia> statustext(200)
"OK"
julia> statustext(404)
"Not Found"
Server / Handlers
Core Server
HTTP.Servers.listen
— FunctionHTTP.listen(handler, host=Sockets.localhost, port=8081; kw...)
HTTP.listen(handler, port::Integer=8081; kw...)
HTTP.listen(handler, server::Base.IOServer; kw...)
HTTP.listen!(args...; kw...) -> HTTP.Server
Listen for HTTP connections and execute the handler
function for each request. Listening details can be passed as host
/port
pair, a single port
(host
will default to localhost
), or an already listening server
object, as returned from Sockets.listen
. To open up a server to external requests, the host
argument is typically "0.0.0.0"
.
The HTTP.listen!
form is non-blocking and returns an HTTP.Server
object which can be wait(server)
ed on manually, or close(server)
ed to gracefully shut down the server. Calling HTTP.forceclose(server)
will immediately force close the server and all active connections. HTTP.listen
will block on the server listening loop until interrupted or and an irrecoverable error occurs.
The handler
function should be of the form f(::HTTP.Stream)::Nothing
, and should at the minimum set a status via setstatus()
and call startwrite()
either explicitly or implicitly by writing out a response via write()
. Failure to do this will result in an HTTP 500 error being transmitted to the client.
Optional keyword arguments:
sslconfig=nothing
, Provide anMbedTLS.SSLConfig
object to handle ssl connections. Passsslconfig=MbedTLS.SSLConfig(false)
to disable ssl verification (useful for testing). Construct a customSSLConfig
object withMbedTLS.SSLConfig(certfile, keyfile)
.tcpisvalid = tcp->true
, functionf(::TCPSocket)::Bool
to check if accepted connections are valid before processing requests. e.g. to do source IP filtering.readtimeout::Int=0
, 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. Not supported on some OS platforms. Can checkHTTP.Servers.supportsreuseaddr()
.server::Base.IOServer=nothing
, provide anIOServer
object to listen on; allows manually closing or configuring the server socket.verbose::Bool=false
, log connection information tostdout
.access_log::Function
, function for formatting access log messages. The function should accept two arguments,io::IO
to which the messages should be written, andhttp::HTTP.Stream
which can be used to query information from. See also@logfmt_str
.on_shutdown::Union{Function, Vector{<:Function}, Nothing}=nothing
, one or more functions to be run if the server is closed (for example by anInterruptException
). Note, shutdown function(s) will not run if anIOServer
object is supplied to theserver
keyword argument and closed byclose(server)
.
e.g.
# start a blocking server
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
# non-blocking server
server = 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
# can gracefully close server manually
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)
Threads.@spawn 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
— FunctionHTTP.serve(handler, host=Sockets.localhost, port=8081; kw...)
HTTP.serve(handler, port::Integer=8081; kw...)
HTTP.serve(handler, server::Base.IOServer; kw...)
HTTP.serve!(args...; kw...) -> HTTP.Server
Listen for HTTP connections and execute the handler
function for each request. Listening details can be passed as host
/port
pair, a single port
(host
will default to localhost
), or an already listening server
object, as returned from Sockets.listen
. To open up a server to external requests, the host
argument is typically "0.0.0.0"
.
The HTTP.serve!
form is non-blocking and returns an HTTP.Server
object which can be wait(server)
ed on manually, or close(server)
ed to gracefully shut down the server. Calling HTTP.forceclose(server)
will immediately force close the server and all active connections. HTTP.serve
will block on the server listening loop until interrupted or and an irrecoverable error occurs.
The handler
function should be of the form f(req::HTTP.Request)::HTTP.Response
. Alternatively, passing stream=true
requires the handler
to be of the form f(stream::HTTP.Stream) -> Nothing
. See HTTP.Router
for details on using it as a request 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). Construct a customSSLConfig
object withMbedTLS.SSLConfig(certfile, keyfile)
.tcpisvalid = tcp->true
, functionf(::TCPSocket)::Bool
to check if accepted connections are valid before processing requests. e.g. to do source IP filtering.readtimeout::Int=0
, 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. Not supported on some OS platforms. Can checkHTTP.Servers.supportsreuseaddr()
.server::Base.IOServer=nothing
, provide anIOServer
object to listen on; allows manually closing or configuring the server socket.verbose::Bool=false
, log connection information tostdout
.access_log::Function
, function for formatting access log messages. The function should accept two arguments,io::IO
to which the messages should be written, andhttp::HTTP.Stream
which can be used to query information from. See also@logfmt_str
.on_shutdown::Union{Function, Vector{<:Function}, Nothing}=nothing
, one or more functions to be run if the server is closed (for example by anInterruptException
). Note, shutdown function(s) will not run if anIOServer
object is supplied to theserver
keyword argument and closed byclose(server)
.
# start a blocking echo server
HTTP.serve("127.0.0.1", 8081) do req
return HTTP.Response(200, req.body)
end
# non-blocking server
server = HTTP.serve!(8081) do req
return HTTP.Response(200, "response body")
end
# can gracefully close server manually
close(server)
HTTP.WebSockets.listen
— FunctionWebSockets.listen(handler, host, port; verbose=false, kw...)
WebSockets.listen!(handler, host, port; verbose=false, kw...) -> HTTP.Server
Listen for websocket connections on host
and port
, and call handler(ws)
, which should be a function taking a single WebSocket
argument. Keyword arguments kw...
are the same as supported by HTTP.listen
. Typical usage is like:
WebSockets.listen(host, port) do ws
# iterate incoming websocket messages
for msg in ws
# send message back to client or do other logic here
send(ws, msg)
end
# iteration ends when the websocket connection is closed by client or error
end
Middleware / Handlers
HTTP.Handlers.Handler
— TypeHandler
Abstract type for the handler interface that exists for documentation purposes. A Handler
is any function of the form f(req::HTTP.Request) -> HTTP.Response
. There is no requirement to subtype Handler
and users should not rely on or dispatch on Handler
. A Handler
function f
can be passed to HTTP.serve
wherein a server will pass each incoming request to f
to be handled and a response to be returned. Handler functions are also the inputs to Middleware
functions which are functions of the form f(::Handler) -> Handler
, i.e. they take a Handler
function as input, and return a "modified" or enhanced Handler
function.
For advanced cases, a Handler
function can also be of the form f(stream::HTTP.Stream) -> Nothing
. In this case, the server would be run like HTTP.serve(f, ...; stream=true)
. For this use-case, the handler function reads the request and writes the response to the stream directly. Note that any middleware used with a stream handler also needs to be of the form f(stream_handler) -> stream_handler
, i.e. it needs to accept a stream Handler
function and return a stream Handler
function.
HTTP.Handlers.Middleware
— TypeMiddleware
Abstract type for the middleware interface that exists for documentation purposes. A Middleware
is any function of the form f(::Handler) -> Handler
(ref: Handler
). There is no requirement to subtype Middleware
and users should not rely on or dispatch on the Middleware
type. While HTTP.serve(f, ...)
requires a handler function f
to be passed, middleware can be "stacked" to create a chain of functions that are called in sequence, like HTTP.serve(base_handler |> cookie_middleware |> auth_middlware, ...)
, where the base_handler
Handler
function is passed to cookie_middleware
, which takes the handler and returns a "modified" handler (that parses and stores cookies). This "modified" handler is then an input to the auth_middlware
, which further enhances/modifies the handler.
HTTP.Handlers.streamhandler
— Functionstreamhandler(request_handler) -> stream handler
Middleware that takes a request handler and returns a stream handler. Used by default in HTTP.serve
to take the user-provided request handler and process the Stream
from HTTP.listen
and pass the parsed Request
to the handler.
Is included by default in HTTP.serve
as the base "middleware" when stream=false
is passed.
HTTP.Handlers.Router
— TypeHTTP.Router(_404, _405, middleware=nothing)
Define a router object that maps incoming requests by path to registered routes and associated handlers. Paths can be registered using HTTP.register!
. The router object itself is a "request handler" that can be called like:
r = HTTP.Router()
resp = r(request)
Which will inspect the request
, find the matching, registered handler from the url, and pass the request on to be handled further.
See HTTP.register!
for additional information on registering handlers based on routes.
If a request doesn't have a matching, registered handler, the _404
handler is called which, by default, returns a HTTP.Response(404)
. If a route matches the path, but not the method/verb (e.g. there's a registerd route for "GET /api", but the request is "POST /api"), then the _405
handler is called, which by default returns HTTP.Response(405)
(method not allowed).
A middleware
(Middleware
) can optionally be provided as well, which will be called after the router has matched the request to a route, but before the route's handler is called. This provides a "hook" for matched routes that can be helpful for metric tracking, logging, etc. Note that the middleware is only called if the route is matched; for the 404 and 405 cases, users should wrap those handlers in the middleware
manually.
HTTP.Handlers.register!
— FunctionHTTP.register!(r::Router, method, path, handler)
HTTP.register!(r::Router, path, handler)
Register a handler function that should be called when an incoming request matches path
and the optionally provided method
(if not provided, any method is allowed). Can be used to dynamically register routes. When a registered route is matched, the original route string is stored in the request.context[:route]
variable. The following path types are allowed for matching:
/api/widgets
: exact match of static strings/api/*/owner
: single*
to wildcard match anything for a single segment/api/widget/{id}
: Define a path variableid
that matches any valued provided for this segment; path variables are available in the request context likeHTTP.getparams(req)["id"]
/api/widget/{id:[0-9]+}
: Define a path variableid
that does a regex match for integers for this segment/api/**
: double wildcard matches any number of trailing segments in the request path; the double wildcard must be the last segment in the path
HTTP.Handlers.getparam
— FunctionHTTP.getparam(req, name, default=nothing) -> String
Retrieve a matched path parameter with name name
from request context. If a path was registered with a router via HTTP.register!
like "/api/widget/{id}", then the path parameter can be retrieved like `id = HTTP.getparam(req, "id").
HTTP.Handlers.getparams
— FunctionHTTP.getparams(req) -> Dict{String, String}
Retrieve any matched path parameters from the request context. If a path was registered with a router via HTTP.register!
like "/api/widget/{id}", then the path parameters are available in the request context and can be retrieved like id = HTTP.getparams(req)["id"]
.
HTTP.Handlers.cookie_middleware
— FunctionHTTP.Handlers.cookie_middleware(handler) -> handler
Middleware that parses and stores any cookies in the incoming request in the request context. Cookies can then be retrieved by calling HTTP.getcookies(req)
in subsequent middlewares/handlers.
HTTP.Handlers.getcookies
— FunctionHTTP.getcookies(req) -> Vector{Cookie}
Retrieve any parsed cookies from a request context. Cookies are expected to be stored in the req.context[:cookies]
of the request context as implemented in the HTTP.Handlers.cookie_middleware
middleware.
HTTP.@logfmt_str
— Macrologfmt"..."
Parse an NGINX-style log format string and return a function mapping (io::IO, http::HTTP.Stream) -> body
suitable for passing to HTTP.listen
using the access_log
keyword argument.
The following variables are currently supported:
$http_name
: arbitrary request header (with-
replaced with_
, e.g.http_user_agent
)$sent_http_name
: arbitrary response header (with-
replaced with_
)$request
: the request line, e.g.GET /index.html HTTP/1.1
$request_method
: the request method$request_uri
: the request URI$remote_addr
: client address$remote_port
: client port$remote_user
: user name supplied with the Basic authentication$server_protocol
: server protocol$time_iso8601
: local time in ISO8601 format$time_local
: local time in Common Log Format$status
: response status code$body_bytes_sent
: number of bytes in response body
Examples
logfmt"[$time_iso8601] \"$request\" $status" # [2021-05-01T12:34:40+0100] "GET /index.html HTTP/1.1" 200
logfmt"$remote_addr \"$http_user_agent\"" # 127.0.0.1 "curl/7.47.0"
Advanced Topics
Messages Interface
HTTP.Messages.iserror
— Functioniserror(::Response)
Does this Response
have an error status?
HTTP.Messages.isredirect
— Functionisredirect(::Response)
Does this Response
have a redirect status?
HTTP.Messages.ischunked
— Functionischunked(::Message)
Does the Message
have a "Transfer-Encoding: chunked" header?
HTTP.Messages.issafe
— Functionissafe(::Request)
https://tools.ietf.org/html/rfc7231#section-4.2.1
HTTP.Messages.isidempotent
— Functionisidempotent(::Request)
https://tools.ietf.org/html/rfc7231#section-4.2.2
HTTP.Messages.retryable
— Functionretryable(::Request)
Whether a Request
is eligible to be retried.
HTTP.Messages.defaultheader!
— Functiondefaultheader!(::Message, key => value)
Set header value
in message for key
if it is not already set.
HTTP.Messages.readheaders
— Functionreadheaders(::IO, ::Message)
Read headers (and startline) from an IO
stream into a Message
struct. Throw EOFError
if input is incomplete.
HTTP.HeadersRequest.setuseragent!
— Functionsetuseragent!(x::Union{String, Nothing})
Set the default User-Agent string to be used in each HTTP request. Can be manually overridden by passing an explicit User-Agent
header. Setting nothing
will prevent the default User-Agent
header from being passed.
HTTP.Messages.readchunksize
— FunctionRead chunk-size from an IO
stream. After the final zero size chunk, read trailers into a Message
struct.
HTTP.Messages.headerscomplete
— Methodheaderscomplete(::Message)
Have the headers been read into this Message
?
HTTP.Messages.writestartline
— Functionwritestartline(::IO, ::Message)
e.g. "GET /path HTTP/1.1\r\n"
or "HTTP/1.1 200 OK\r\n"
HTTP.Messages.writeheaders
— Functionwriteheaders(::IO, ::Message)
Write Message
start line and a line for each "name: value" pair and a trailing blank line.
Base.write
— Methodwrite(::IO, ::Message)
Write start line, headers and body of HTTP Message.
HTTP.Streams.closebody
— Functionclosebody(::Stream)
Write the final 0
chunk if needed.
HTTP.Streams.isaborted
— Functionisaborted(::Stream{<:Response})
Has the server signaled that it does not wish to receive the message body?
"If [the response] indicates the server does not wish to receive the message body and is closing the connection, the client SHOULD immediately cease transmitting the body and close the connection." RFC7230, 6.5
Cookie Persistence
HTTP.Cookies.CookieJar
— TypeCookieJar()
A thread-safe object for storing cookies returned in "Set-Cookie" response headers. Keyed by appropriate host from the original request made. Can be created manually and passed like HTTP.get(url; cookiejar=mycookiejar)
to avoid using the default global CookieJar
. The 2 main functions for interacting with a CookieJar
are Cookies.getcookies!
, which returns a Vector{Cookie}
for a given url (and will remove expired cookies from the jar), and Cookies.setcookies!
, which will store "Set-Cookie" response headers in the cookie jar.
HTTP.Cookies.getcookies!
— FunctionCookies.getcookies!(jar::CookieJar, url::URI)
Retrieve valid Cookie
s from the CookieJar
according to the provided url
. Cookies will be returned as a Vector{Cookie}
. Only cookies for http
or https
scheme in the url will be returned. Cookies will be checked according to the canonical host of the url and any cookie max age or expiration will be accounted for. Expired cookies will not be returned and will be removed from the cookie jar.
HTTP.Cookies.setcookies!
— FunctionCookies.setcookies!(jar::CookieJar, url::URI, headers::Headers)
Identify, "Set-Cookie" response headers from headers
, parse the Cookie
s, and store valid entries in the cookie jar
according to the canonical host in url
. Cookies can be retrieved from the jar
via Cookies.getcookies!
.
Client-side Middleware (Layers)
HTTP.Layer
— TypeLayer
Abstract type to represent a client-side middleware that exists for documentation purposes. A layer is any function of the form f(::Handler) -> Handler
, where Handler
is a function of the form f(::Request) -> Response
. Note that the Handler
definition is from the server-side documentation, and is "hard-coded" on the client side. It may also be apparent that a Layer
is the same as the Middleware
interface from server-side, which is true, but we define Layer
to clarify the client-side distinction and its unique usage. Custom layers can be deployed in one of two ways:
HTTP.@client
: Create a custom "client" with shorthand verb definitions, but which include custom layers; only these new verb methods will use the custom layers.HTTP.pushlayer!
/HTTP.poplayer!
: Allows globally adding and removing layers from the default HTTP.jl layer stack; all http requests will then use the custom layers
Quick Examples
module Auth
using HTTP
function auth_layer(handler)
# returns a `Handler` function; check for a custom keyword arg `authcreds` that
# a user would pass like `HTTP.get(...; authcreds=creds)`.
# We also accept trailing keyword args `kw...` and pass them along later.
return function(req; authcreds=nothing, kw...)
# only apply the auth layer if the user passed `authcreds`
if authcreds !== nothing
# we add a custom header with stringified auth creds
HTTP.setheader(req, "X-Auth-Creds" => string(authcreds))
end
# pass the request along to the next layer by calling `auth_layer` arg `handler`
# also pass along the trailing keyword args `kw...`
return handler(req; kw...)
end
end
# Create a new client with the auth layer added
HTTP.@client [auth_layer]
end # module
# Can now use custom client like:
Auth.get(url; authcreds=creds) # performs GET request with auth_layer layer included
# Or can include layer globally in all HTTP.jl requests
HTTP.pushlayer!(Auth.auth_layer)
# Now can use normal HTTP.jl methods and auth_layer will be included
HTTP.get(url; authcreds=creds)
HTTP.@client
— MacroHTTP.@client requestlayers
HTTP.@client requestlayers streamlayers
HTTP.@client (first=requestlayers, last=requestlayers) (first=streamlayers, last=streamlayers)
Convenience macro for creating a custom HTTP.jl client that will include custom layers when performing requests. It's common to want to define a custom Layer
to enhance a specific category of requests, such as custom authentcation for a web API. Instead of affecting the global HTTP.jl request stack via HTTP.pushlayer!
, a custom wrapper client can be defined with convenient shorthand methods. See Layer
for an example of defining a custom layer and creating a new client that includes the layer.
Custom layer arguments can be provided as a collection of request or stream-based layers; alternatively, a NamedTuple with keys first
and last
can be provided with values being a collection of layers. The NamedTuple form provides finer control over the order in which the layers will be included in the default http layer stack: first
request layers are executed before all other layers, last
request layers are executed right before all stream layers, and similarly for stream layers.
An empty collection can always be passed for request or stream layers when not needed.
One use case for custom clients is to control the value of standard HTTP.request
keyword arguments. This can be achieved by passing a (first=[defaultkeywordlayer],)
where defaultkeywordlayer
is defined like:
defaultkeywordlayer(handler) = (req; kw...) -> handler(req; retry=false, redirect=false, kw...)
This client-side layer is basically a no-op as it doesn't modify the request at all, except that it hard-codes the value of the retry
and redirect
keyword arguments. When we pass this layer as (first=[defaultkeywordlayer],)
this ensures this layer will be executed before all other layers, effectively over-writing the default and any user-provided keyword arguments for retry
or redirect
.
HTTP.pushlayer!
— FunctionHTTP.pushlayer!(layer; request=true)
Push a layer onto the stack of layers that will be applied to all requests. The "layer" is expected to be a function that takes and returns a Handler
function. See Layer
for more details. If request=false
, the layer is expected to take and return a "stream" handler function. The custom layer
will be put on the top of the stack, so it will be the first layer executed. To add a layer at the bottom of the stack, see HTTP.pushfirstlayer!
.
HTTP.pushfirstlayer!
— FunctionHTTP.pushfirstlayer!(layer; request=true)
Push a layer to the start of the stack of layers that will be applied to all requests. The "layer" is expected to be a function that takes and returns a Handler
function. See Layer
for more details. If request=false
, the layer is expected to take and return a "stream" handler function. The custom layer
will be put on the bottom of the stack, so it will be the last layer executed. To add a layer at the top of the stack, see HTTP.pushlayer!
.
HTTP.poplayer!
— FunctionHTTP.poplayer!(; request=true)
Inverse of HTTP.pushlayer!
, removes the top layer of the global HTTP.jl layer stack. Can be used to "cleanup" after a custom layer has been added. If request=false
, will remove the top "stream" layer as opposed to top "request" layer.
HTTP.popfirstlayer!
— FunctionHTTP.popfirstlayer!(; request=true)
Inverse of HTTP.pushfirstlayer!
, removes the bottom layer of the global HTTP.jl layer stack. Can be used to "cleanup" after a custom layer has been added. If request=false
, will remove the bottom "stream" layer as opposed to bottom "request" layer.
HTTP.MessageRequest.messagelayer
— Functionmessagelayer(handler) -> handler
Construct a Request
object from method, url, headers, and body. Hard-coded as the first layer in the request pipeline.
HTTP.RedirectRequest.redirectlayer
— Functionredirectlayer(handler) -> handler
Redirects the request in the case of 3xx response status.
HTTP.HeadersRequest.headerslayer
— Functionheaderslayer(handler) -> handler
Sets default expected headers.
HTTP.CookieRequest.cookielayer
— Functioncookielayer(handler) -> handler
Check for host-appropriate cookies to include in the outgoing request from the cookiejar
keyword argument (by default, a global cookiejar is used). Store "Set-Cookie" cookies from the response headers.
HTTP.TimeoutRequest.timeoutlayer
— Functiontimeoutlayer(handler) -> handler
Close the HTTP.Stream
if no data has been received for readtimeout
seconds.
HTTP.ExceptionRequest.exceptionlayer
— Functionexceptionlayer(handler) -> handler
Throw a StatusError
if the request returns an error response status.
HTTP.RetryRequest.retrylayer
— Functionretrylayer(handler) -> handler
Retry the request if it throws a recoverable exception.
Base.retry
and Base.ExponentialBackOff
implement a randomised exponentially increasing delay is introduced between attempts to avoid exacerbating network congestion.
By default, requests that have a retryable body, where the request wasn't written or is idempotent will be retried. If the request is made and a response is received with a status code of 403, 408, 409, 429, or 5xx, the request will be retried.
retries
controls the # of total retries that will be attempted.
retry_check
allows passing a custom retry check in the case where the default retry check wouldn't retry, if retry_check
returns true, then the request will be retried anyway.
HTTP.ConnectionRequest.connectionlayer
— Functionconnectionlayer(handler) -> handler
Retrieve an IO
connection from the Connections.
Close the connection if the request throws an exception. Otherwise leave it open so that it can be reused.
HTTP.StreamRequest.streamlayer
— Functionstreamlayer(stream) -> HTTP.Response
Create a Stream
to send a Request
and body
to an IO
stream and read the response.
Send the Request
body in a background task and begins reading the response immediately so that the transmission can be aborted if the Response
status indicates that the server does not wish to receive the message body. RFC7230 6.5.
Raw Request Connection
HTTP.openraw
— FunctionHTTP.openraw(method, url, [, headers])::Tuple{Connection, Response}
Open a raw socket that is unmanaged by HTTP.jl. Useful for doing HTTP upgrades to other protocols. Any bytes of the body read from the socket when reading headers, is returned as excess bytes in the last tuple argument.
Example of a WebSocket upgrade:
headers = Dict(
"Upgrade" => "websocket",
"Connection" => "Upgrade",
"Sec-WebSocket-Key" => "dGhlIHNhbXBsZSBub25jZQ==",
"Sec-WebSocket-Version" => "13")
socket, response, excess = HTTP.openraw("GET", "ws://echo.websocket.org", headers)
# Write a WebSocket frame
frame = UInt8[0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58]
write(socket, frame)
HTTP.Connections.Connection
— TypeConnection
A Sockets.TCPSocket
, MbedTLS.SSLContext
or OpenSSL.SSLStream
connection to a HTTP host
and port
.
Fields:
host::String
port::String
, exactly as specified in the URI (i.e. may be empty).idle_timeout
, No. of seconds to maintain connection after last request/response.require_ssl_verification
, whether ssl verification is required for an ssl connectionkeepalive
, whether the tcp socket should have keepalive enabledpeerip
, remote IP adress (used for debug/log messages).peerport
, remote TCP port number (used for debug/log messages).localport
, local TCP port number (used for debug messages).io::T
, theSockets.TCPSocket
,MbedTLS.SSLContext
orOpenSSL.SSLStream
.clientconnection::Bool
, whether the Connection was created from client code (as opposed to server code)buffer::IOBuffer
, left over bytes read from the connection after the end of a response header (or chunksize). These bytes are usually part of the response body.timestamp
, time data was last received.readable
, whether the Connection object is readablewritable
, whether the Connection object is writable
Parser Interface
HTTP.Parsers.find_end_of_header
— Functionfind_end_of_header(bytes) -> length or 0
Find length of header delimited by \r\n\r\n
or \n\n
.
HTTP.Parsers.find_end_of_chunk_size
— FunctionFind \n
after chunk size in bytes
.
HTTP.Parsers.find_end_of_trailer
— Functionfind_end_of_trailer(bytes) -> length or 0
Find length of trailer delimited by \r\n\r\n
(or starting with \r\n
). RFC7230 4.1
HTTP.Parsers.parse_status_line!
— FunctionParse HTTP response-line bytes
and set the status
and version
fields of response
. Return a SubString
containing the header-field lines.
HTTP.Parsers.parse_request_line!
— FunctionParse HTTP request-line bytes
and set the method
, target
and version
fields of request
. Return a SubString
containing the header-field lines.
HTTP.Parsers.parse_header_field
— FunctionParse HTTP header-field. Return Pair(field-name => field-value)
and a SubString
containing the remaining header-field lines.
HTTP.Parsers.parse_chunk_size
— Function