HTTP.jl Documentation
HTTP.jl is a Julia library for HTTP Messages.
HTTP.request sends a HTTP Request Message and returns a Response Message.
r = HTTP.request("GET", "http://httpbin.org/ip")
println(r.status)
println(String(r.body))HTTP.open sends a HTTP Request Message and opens an IO stream from which the Response can be read.
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
endRequests
HTTP.request — Method.HTTP.request(method, url [, headers [, body]]; <keyword arguments>]) -> HTTP.ResponseSend a HTTP Request Message and recieve 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 anyTaccepted bywrite(::IO, ::T)a collection of
StringorAbstractVector{UInt8}orIOstreams or items of any typeTaccepted bywrite(::IO, ::T...)a readable
IOstream or anyIO-like typeTfor whicheof(T)andreadavailable(T)are defined.
The HTTP.Response struct contains:
status::Int16e.g.200headers::Vector{Pair{String,String}}e.g. ["Server" => "Apache", "Content-Type" => "text/html"]body::Vector{UInt8}, the Response Body bytes (empty if aresponse_streamwas 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..)Streaming options
response_stream = nothing, a writeableIOstream or anyIO-like typeTfor whichwrite(T, AbstractVector{UInt8})is defined.verbose = 0, set to1or2for 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 recieved for this many seconds. Usereadtimeout = 0to 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 = false, forward original headers on redirect.
Status Exception options
statusexception = true, throwHTTP.StatusErrorfor response status >= 300.
SSLContext options
require_ssl_verification = false, passMBEDTLS_SSL_VERIFY_REQUIREDto the mbed TLS library. "... peer must present a valid certificate, handshake is aborted if verification failed."sslconfig = SSLConfig(require_ssl_verification)`
Basic Authenticaiton options
basicauthorization=false, add
Authorization: Basicheader using credentials from url userinfo.
AWS Authenticaiton options
awsauthorization = false, enable AWS4 Authentication.aws_service = split(uri.host, ".")[1]aws_region = split(uri.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 = false, enable cookies.cookiejar::Dict{String, Set{Cookie}}=default_cookiejar
Cananoincalization options
canonicalizeheaders = false, rewrite request and response headers in Canonical-Camel-Dash-Format.
Request Body Examples
String body:
request("POST", "http://httpbin.org/post", [], "post body data")Stream body from file:
io = open("post_data.txt", "r")
request("POST", "http://httpbin.org/post", [], io)Generator body:
chunks = ("chunk$i" for i in 1:1000)
request("POST", "http://httpbin.org/post", [], chunks)Collection body:
chunks = [preamble_chunk, data_chunk, checksum(data_chunk)]
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))
endResponse Body Examples
String body:
r = request("GET", "http://httpbin.org/get")
println(String(r.body))Stream body to file:
io = open("get_data.txt", "w")
r = request("GET", "http://httpbin.org/get", response_stream=io)
close(io)
println(read("get_data.txt"))Stream body through buffer:
io = BufferStream()
@async while !eof(io)
bytes = readavailable(io))
println("GET data: $bytes")
end
r = 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
HTTP.open("GET", "https://tinyurl.com/bach-cello-suite-1-ogg") do http
n = 0
r = startread(http)
l = parse(Int, 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
endRequest and Response Body Examples
String bodies:
r = request("POST", "http://httpbin.org/post", [], "post body data")
println(String(r.body))Stream bodies from and to files:
in = open("foo.png", "r")
out = open("foo.jpg", "w")
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 = readresponse(io)
@show r.status
while !eof(io)
bytes = readavailable(io))
play_audio(bytes)
end
endHTTP.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.ResponseThe 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
endHTTP.get — Function.HTTP.get(url [, headers]; <keyword arguments>) -> HTTP.ResponseShorthand for HTTP.request("GET", ...). See HTTP.request.
HTTP.put — Function.HTTP.put(url, headers, body; <keyword arguments>) -> HTTP.ResponseShorthand for HTTP.request("PUT", ...). See HTTP.request.
HTTP.post — Function.HTTP.post(url, headers, body; <keyword arguments>) -> HTTP.ResponseShorthand for HTTP.request("POST", ...). See HTTP.request.
HTTP.head — Function.HTTP.head(url; <keyword arguments>) -> HTTP.ResponseShorthand for HTTP.request("HEAD", ...). See HTTP.request.
Request functions may throw the following exceptions:
The Response has a 4xx, 5xx or unrecognised status code.
Fields:
status::Int16, the response status code.responsetheHTTP.Response
HTTP.Parsers.ParsingError — Type.The [Parser] input was invalid.
Fields:
code, internal@enum ParsingErrorCode.state, internal parsing state.status::Int32, HTTP response status.msg::String, error message.
HTTP.IOExtras.IOError — Type.The request terminated with due to an IO-related error.
Fields:
e, the error.
Base.DNSErrorServer / Handlers
HTTP.Nitrogen.serve — Function.HTTP.serve([server,] host::IPAddr, port::Int; verbose::Bool=true, kwargs...)Start a server listening on the provided host and port. verbose indicates whether server activity should be logged. Optional keyword arguments allow construction of Server on the fly if the server argument isn't provided directly. See ?HTTP.Server for more details on server construction and supported keyword arguments. By default, HTTP.serve aims to "never die", catching and recovering from all internal errors. Two methods for stopping HTTP.serve include interrupting (ctrl/cmd+c) if blocking on the main task, or sending the kill signal via the server's in channel (put!(server.in, HTTP.KILL)).
HTTP.Nitrogen.Server — Type.Server(handler, logger::IO=STDOUT; kwargs...)An http/https server. Supports listening on a host and port via the HTTP.serve(server, host, port) function. handler is a function of the form f(::Request, ::Response) -> HTTP.Response, i.e. it takes both a Request and pre-built Response objects as inputs and returns the, potentially modified, Response. logger indicates where logging output should be directed. When HTTP.serve is called, it aims to "never die", catching and recovering from all internal errors. To forcefully stop, one can obviously kill the julia process, interrupt (ctrl/cmd+c) if main task, or send the kill signal over a server in channel like: put!(server.in, HTTP.KILL).
Supported keyword arguments include:
cert: if https, the cert file to use, as passed toHTTP.MbedTLS.SSLConfig(cert, key)key: if https, the key file to use, as passed toHTTP.MbedTLS.SSLConfig(cert, key)tlsconfig: pass in an already-constructedHTTP.MbedTLS.SSLConfiginstancereadtimeout: how long a client connection will be left open without receiving any bytesratelimit: aRational{Int}of the form5//1indicating how manymessages//secondshould be allowed per client IP address; requests exceeding the rate limit will be droppedsupport100continue: aBoolindicating whetherExpect: 100-continueheaders should be supported for delayed request body sending; default =truelogbody: whether the Response body should be logged whenverbose=truelogging is enabled; default =true
HTTP.Handlers.Handler — Type.Abstract type representing an object that knows how to "handle" a server request.
Types of handlers include HandlerFunction (a julia function of the form f(request, response) and Router (which pattern matches request url paths to other specific Handler types).
HTTP.Handlers.HandlerFunction — Type.HandlerFunction(f::Function)
A Function-wrapper type that is a subtype of Handler. Takes a single Function as an argument. The provided argument should be of the form f(request, response) => Response, i.e. it accepts both a Request and Response and returns a Response.
HTTP.Handlers.Router — Type.Router(h::Handler) Router(f::Function) Router()
An HTTP.Handler type that supports mapping request url paths to other HTTP.Handler types. 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! — Function.HTTP.register!(r::Router, url, handler) HTTP.register!(r::Router, m::Union{HTTP.Method, String}, url, handler)
Function to map request urls matching url and an optional method m to another handler::HTTP.Handler. URLs are registered one at a time, and multiple urls can map to the same handler. Methods can be passed as a string "GET" or enum object directly HTTP.GET. The URL can be passed as a String or HTTP.URI object directly. 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"google": regardless of scheme, match requests to the hostname "google""google/gmail": match requests to hostname "google", and path starting with "gmail""/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 "/"
URIs
HTTP.URIs.URI — Type.HTTP.URL(host; userinfo="", path="", query="", fragment="", isconnect=false)
HTTP.URI(; scheme="", host="", port="", ...)
HTTP.URI(str; isconnect=false)
parse(HTTP.URI, str::String; isconnect=false)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"]).
For efficiency, the internal representation is stored as a set of offsets and lengths to the various uri components. To access and return these components as strings, use the various accessor methods:
HTTP.scheme: returns the scheme (if any) associated with the uriHTTP.userinfo: returns the userinfo (if any) associated with the uriHTTP.host: returns the host only of the uriHTTP.port: returns the port of the uri; will return "80" or "443" by default if the scheme is "http" or "https", respectivelyHTTP.hostport: returns the "host:port" combination; if the port is not provided or is the default port for the uri scheme, it will be omittedHTTP.path: returns the path for a uriHTTP.query: returns the query for a uriHTTP.fragment: returns the fragment for a uriHTTP.resource: returns the path-query-fragment combination
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.DateTimerepresenting when the cookie should expiremaxage:maxage == 0means no max age,maxage < 0means delete cookie now,max age > 0means 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: &, <, >, ", '
HTTP.jl Internal Architecture
HTTP.Layer — Type.Request Execution Stack
The Request Execution Stack is separated into composable layers.
Each layer is defined by a nested type Layer{Next} where the Next parameter defines the next layer in the stack. The request method for each layer takes a Layer{Next} type as its first argument and dispatches the request to the next layer using request(Next, ...).
The example below defines three layers and three stacks each with a different combination of layers.
abstract type Layer end
abstract type Layer1{Next <: Layer} <: Layer end
abstract type Layer2{Next <: Layer} <: Layer end
abstract type Layer3 <: Layer end
request(::Type{Layer1{Next}}, data) where Next = "L1", request(Next, data)
request(::Type{Layer2{Next}}, data) where Next = "L2", request(Next, data)
request(::Type{Layer3}, data) = "L3", data
const stack1 = Layer1{Layer2{Layer3}}
const stack2 = Layer2{Layer1{Layer3}}
const stack3 = Layer1{Layer3}julia> request(stack1, "foo")
("L1", ("L2", ("L3", "foo")))
julia> request(stack2, "bar")
("L2", ("L1", ("L3", "bar")))
julia> request(stack3, "boo")
("L1", ("L3", "boo"))This stack definition pattern gives the user flexibility in how layers are combined but still allows Julia to do whole-stack comiple time optimistations.
e.g. the request(stack1, "foo") call above is optimised down to a single function:
julia> code_typed(request, (Type{stack1}, String))[1].first
CodeInfo(:(begin
return (Core.tuple)("L1", (Core.tuple)("L2", (Core.tuple)("L3", data)))
end))HTTP.stack — Function.The stack() function returns the default HTTP Layer-stack type. This type is passed as the first parameter to the HTTP.request function.
stack() accepts optional keyword arguments to enable/disable specific layers in the stack: request(method, args...; kw...) request(stack(;kw...), args...; kw...)
The minimal request execution stack is:
stack = MessageLayer{ConnectionPoolLayer{StreamLayer}}The figure below illustrates the full request exection stack and its relationship with HTTP.Response, HTTP.Parser, HTTP.Stream and the HTTP.ConnectionPool.
┌────────────────────────────────────────────────────────────────────────────┐
│ ┌───────────────────┐ │
│ HTTP.jl Request Execution Stack │ HTTP.ParsingError ├ ─ ─ ─ ─ ┐ │
│ └───────────────────┘ │
│ ┌───────────────────┐ │ │
│ │ HTTP.IOError ├ ─ ─ ─ │
│ └───────────────────┘ │ │ │
│ ┌───────────────────┐ │
│ │ HTTP.StatusError │─ ─ │ │ │
│ └───────────────────┘ │ │
│ ┌───────────────────┐ │ │ │
│ request(method, uri, headers, body) -> │ HTTP.Response │ │ │
│ ────────────────────────── └─────────▲─────────┘ │ │ │
│ ║ ║ │ │
│ ┌────────────────────────────────────────────────────────────┐ │ │ │
│ │ request(RedirectLayer, method, ::URI, ::Headers, body) │ │ │
│ ├────────────────────────────────────────────────────────────┤ │ │ │
│ │ request(BasicAuthLayer, method, ::URI, ::Headers, body) │ │ │
│ ├────────────────────────────────────────────────────────────┤ │ │ │
│ │ request(CookieLayer, method, ::URI, ::Headers, body) │ │ │
│ ├────────────────────────────────────────────────────────────┤ │ │ │
│ │ request(CanonicalizeLayer, method, ::URI, ::Headers, body) │ │ │
│ ├────────────────────────────────────────────────────────────┤ │ │ │
│ │ request(MessageLayer, method, ::URI, ::Headers, body) │ │ │
│ ├────────────────────────────────────────────────────────────┤ │ │ │
│ │ request(AWS4AuthLayer, ::URI, ::Request, body) │ │ │
│ ├────────────────────────────────────────────────────────────┤ │ │ │
│ │ request(RetryLayer, ::URI, ::Request, body) │ │ │
│ ├────────────────────────────────────────────────────────────┤ │ │ │
│ │ request(ExceptionLayer, ::URI, ::Request, body) ├ ─ ┘ │
│ ├────────────────────────────────────────────────────────────┤ │ │ │
┌┼───┤ request(ConnectionPoolLayer, ::URI, ::Request, body) ├ ─ ─ ─ │
││ ├────────────────────────────────────────────────────────────┤ │ │
││ │ request(TimeoutLayer, ::IO, ::Request, body) │ │
││ ├────────────────────────────────────────────────────────────┤ │ │
││ │ request(StreamLayer, ::IO, ::Request, body) │ │
││ └──────────────┬───────────────────┬─────────────────────────┘ │ │
│└──────────────────┼────────║──────────┼───────────────║─────────────────────┘
│ │ ║ │ ║ │
│┌──────────────────▼───────────────┐ │ ┌──────────────────────────────────┐
││ HTTP.Request │ │ │ HTTP.Response │ │
││ │ │ │ │
││ method::String ◀───┼──▶ status::Int │ │
││ uri::String │ │ │ headers::Vector{Pair} │
││ headers::Vector{Pair} │ │ │ body::Vector{UInt8} │ │
││ body::Vector{UInt8} │ │ │ │
│└──────────────────▲───────────────┘ │ └───────────────▲────────────────┼─┘
│┌──────────────────┴────────║──────────▼───────────────║──┴──────────────────┐
││ HTTP.Stream <:IO ║ ╔══════╗ ║ │ │
││ ┌───────────────────────────┐ ║ ┌──▼─────────────────────────┐ │
││ │ startwrite(::Stream) │ ║ │ startread(::Stream) │ │ │
││ │ write(::Stream, body) │ ║ │ read(::Stream) -> body │ │
││ │ ... │ ║ │ ... │ │ │
││ │ closewrite(::Stream) │ ║ │ closeread(::Stream) │ │
││ └───────────────────────────┘ ║ └────────────────────────────┘ │ │
│└───────────────────────────║────────┬──║──────║───────║──┬──────────────────┘
│┌──────────────────────────────────┐ │ ║ ┌────▼───────║──▼────────────────┴─┐
││ HTTP.Messages │ │ ║ │ HTTP.Parser │
││ │ │ ║ │ │
││ writestartline(::IO, ::Request) │ │ ║ │ parseheaders(bytes) do h::Pair │
││ writeheaders(::IO, ::Request) │ │ ║ │ parsebody(bytes) -> bytes │
│└──────────────────────────────────┘ │ ║ └──────────────────────────────────┘
│ ║ │ ║
│┌───────────────────────────║────────┼──║────────────────────────────────────┐
└▶ HTTP.ConnectionPool ║ │ ║ │
│ ┌──────────────▼────────┐ ┌───────────────────────┐ │
│ getconnection() -> │ HTTP.Transaction <:IO │ │ HTTP.Transaction <:IO │ │
│ └───────────────────────┘ └───────────────────────┘ │
│ ║ ╲│╱ ║ ╲│╱ │
│ ║ │ ║ │ │
│ ┌───────────▼───────────┐ ┌───────────▼───────────┐ │
│ pool: [│ HTTP.Connection │,│ HTTP.Connection │...]│
│ └───────────┬───────────┘ └───────────┬───────────┘ │
│ ║ │ ║ │ │
│ ┌───────────▼───────────┐ ┌───────────▼───────────┐ │
│ │ Base.TCPSocket <:IO │ │MbedTLS.SSLContext <:IO│ │
│ └───────────────────────┘ └───────────┬───────────┘ │
│ ║ ║ │ │
│ ║ ║ ┌───────────▼───────────┐ │
│ ║ ║ │ Base.TCPSocket <:IO │ │
│ ║ ║ └───────────────────────┘ │
└───────────────────────────║───────────║────────────────────────────────────┘
║ ║
┌───────────────────────────║───────────║──────────────┐ ┏━━━━━━━━━━━━━━━━━━┓
│ HTTP Server ▼ │ ┃ data flow: ════▶ ┃
│ Request Response │ ┃ reference: ────▶ ┃
└──────────────────────────────────────────────────────┘ ┗━━━━━━━━━━━━━━━━━━┛See docs/src/layers.monopic.
Request Execution Layers
request(RedirectLayer, method, ::URI, headers, body) -> HTTP.ResponseRedirects the request in the case of 3xx response status.
request(BasicAuthLayer, method, ::URI, headers, body) -> HTTP.ResponseAdd Authorization: Basic header using credentials from url userinfo.
HTTP.CookieRequest.CookieLayer — Type.request(CookieLayer, method, ::URI, headers, body) -> HTTP.ResponseAdd locally stored Cookies to the request headers. Store new Cookies found in the response headers.
request(CanonicalizeLayer, method, ::URI, headers, body) -> HTTP.ResponseRewrite request and response headers in Canonical-Camel-Dash-Format.
HTTP.MessageRequest.MessageLayer — Type.request(MessageLayer, method, ::URI, headers, body) -> HTTP.ResponseConstruct a Request object and set mandatory headers.
request(AWS4AuthLayer, ::URI, ::Request, body) -> HTTP.ResponseAdd a AWS Signature Version 4 Authorization header to a Request.
Credentials are read from environment variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN.
HTTP.RetryRequest.RetryLayer — Type.request(RetryLayer, ::URI, ::Request, body) -> HTTP.ResponseRetry 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.
Methods of isrecoverable(e) define which exception types lead to a retry. e.g. HTTP.IOError, Base.DNSError, Base.EOFError and HTTP.StatusError (if status is `5xx).
request(ExceptionLayer, ::URI, ::Request, body) -> HTTP.ResponseThrow a StatusError if the request returns an error response status.
request(ConnectionPoolLayer, ::URI, ::Request, body) -> HTTP.ResponseRetrieve an IO connection from the ConnectionPool.
Close the connection if the request throws an exception. Otherwise leave it open so that it can be reused.
IO related exceptions from Base are wrapped in HTTP.IOError. See isioerror.
HTTP.TimeoutRequest.TimeoutLayer — Type.request(TimeoutLayer, ::IO, ::Request, body) -> HTTP.ResponseClose IO if no data has been received for timeout seconds.
HTTP.StreamRequest.StreamLayer — Type.request(StreamLayer, ::IO, ::Request, body) -> HTTP.ResponseCreate a Stream to send a Request and body to an IO stream and read the response.
Sens 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.
Parser
Source: Parsers.jl
HTTP.Parsers.Parser — Type.The parser separates a raw HTTP Message into its component parts.
If the input data is invalid the Parser throws a ParsingError.
The parser processes a single HTTP Message. If the input stream contains multiple Messages the Parser stops at the end of the first Message. The parseheaders and parsebody functions return a SubArray containing the unuses portion of the input.
The Parser does not interpret the Message Headers except as needed to parse the Message Body. It is beyond the scope of the Parser to deal with repeated header fields, multi-line values, cookies or case normalization.
The Parser has no knowledge of the high-level Request and Response structs defined in Messages.jl. The Parser has it's own low level Message struct that represents both Request and Response Messages.
Messages
Source: Messages.jl
HTTP.Messages — Module.The Messages module defines structs that represent HTTP.Request and HTTP.Response Messages.
The Response struct has a request field that points to the corresponding Request; and the Request struct has a response field. The Request struct also has a parent field that points to a Response in the case of HTTP Redirect.
The Messages module defines IO read and write methods for Messages but it does not deal with URIs, creating connections, or executing requests. The
The read methods throw EOFError exceptions if input data is incomplete. and call parser functions that may throw HTTP.ParsingError exceptions. The read and write methods may also result in low level IO exceptions.
Sending Messages
Messages are formatted and written to an IO stream by Base.write(::IO,::HTTP.Messages.Message) and or HTTP.Messages.writeheaders.
Receiving Messages
Messages are parsed from IO stream data by HTTP.Messages.readheaders. This function calls HTTP.Messages.appendheader and HTTP.Messages.readstartline!.
The read methods rely on HTTP.IOExtras.unread! to push excess data back to the input stream.
Headers
Headers are represented by Vector{Pair{String,String}}. As compared to Dict{String,String} this allows repeated header fields and preservation of order.
Header values can be accessed by name using HTTP.Messages.header and HTTP.Messages.setheader (case-insensitive).
The HTTP.Messages.appendheader function handles combining multi-line values, repeated header fields and special handling of multiple Set-Cookie headers.
Bodies
The HTTP.Message structs represent the Message Body as Vector{UInt8}.
Streaming of request and response bodies is handled by the HTTP.StreamLayer and the HTTP.Stream <: IO stream.
Streams
Source: Streams.jl
HTTP.Streams.Stream — Type.Stream(::IO, ::Request, ::Parser)Creates a HTTP.Stream that wraps an existing IO stream.
startwrite(::Stream)sends theRequestheaders to theIOstream.write(::Stream, body)sends thebody(or a chunk of the bocdy).closewrite(::Stream)sends the final0chunk (if needed) and callsclosewriteon theIOstream. When theIOstream is aHTTP.ConnectionPool.Transaction, callingclosewritereleases theHTTP.ConnectionPool.Connectionback into the pool for use by the next pipelined request.startread(::Stream)callsstartreadon theIOstream then reads and parses theResponseheaders. When theIOstream is aHTTP.ConnectionPool.Transaction, callingstartreadwaits for other pipelined responses to be read from theHTTP.ConnectionPool.Connection.eof(::Stream)andreadavailable(::Stream)parse the body from theIOstream.closeread(::Stream)reads the trailers and callsclosereadon theIOstream. When theIOstream is aHTTP.ConnectionPool.Transaction, callingclosereadreleases the readlock and allows the next pipelined response to be read by anotherStreamthat is waiting instartread. If theParserhas not recieved a complete response,closereadthrows anEOFError.
Connections
Source: ConnectionPool.jl
HTTP.ConnectionPool — Module.This module provides the getconnection function with support for:
Opening TCP and SSL connections.
Reusing connections for multiple Request/Response Messages,
Pipelining Request/Response Messages. i.e. allowing a new Request to be sent before previous Responses have been read.
This module defines a Connection struct to manage pipelining and connection reuse and a Transaction<: IO struct to manage a single pipelined request. Methods are provided for eof, readavailable, unsafe_write and close. This allows the Transaction object to act as a proxy for the TCPSocket or SSLContext that it wraps.
The pool is a collection of open Connections. The request function calls getconnection to retrieve a connection from the pool. When the request function has written a Request Message it calls closewrite to signal that the Connection can be reused for writing (to send the next Request). When the request function has read the Response Message it calls closeread to signal that the Connection can be reused for reading.
Internal Interfaces
Parser Interface
HTTP.Parsers.Message — Type.method::Method: internal parser@enumfor HTTP method.majorandminor: HTTP versionurl::String: request URLstatus::Int: response statusupgrade::Bool: Connection should be upgraded to a different protocol. e.g.CONNECTorConnection: upgrade.
HTTP.Parsers.Parser — Method.Parser()Create an unconfigured Parser.
HTTP.Parsers.parseheaders — Function.parseheaders(::Parser, bytes) do h::Pair{String,String} ... -> excessRead headers from bytes, passing each field/value pair to f. Returns a SubArray containing bytes not parsed.
e.g.
excess = parseheaders(p, bytes) do (k,v)
println("$k: $v")
endHTTP.Parsers.parsebody — Function.parsebody(::Parser, bytes) -> data, excessParse body data from bytes. Returns decoded data and excess bytes not parsed.
HTTP.Parsers.reset! — Function.reset!(::Parser)Revert Parser to unconfigured state.
HTTP.Parsers.messagestarted — Function.messagestarted(::Parser)Has the Parser begun processng a Message?
HTTP.Parsers.headerscomplete — Function.headerscomplete(::Parser)Has the Parser processed the entire Message Header?
headerscomplete(::Message)Have the headers been read into this Message?
HTTP.Parsers.bodycomplete — Function.bodycomplete(::Parser)Has the Parser processed the Message Body?
HTTP.Parsers.messagecomplete — Function.messagecomplete(::Parser)Has the Parser processed the entire Message?
HTTP.Parsers.messagehastrailing — Function.messagehastrailing(::Parser)Is the Parser ready to process trailing headers?
HTTP.Parsers.waitingforeof — Function.waitingforeof(::Parser)Is the Parser waiting for the peer to close the connection to signal the end of the Message Body?
HTTP.Parsers.seteof — Function.seteof(::Parser)Signal that the peer has closed the connection.
HTTP.Parsers.connectionclosed — Function.connectionclosed(::Parser)Was "Connection: close" parsed?
HTTP.Parsers.setnobody — Function.setnobody(::Parser)Tell the Parser not to look for a Message Body. e.g. for the Response to a HEAD Request.
Messages Interface
HTTP.Messages.Request — Type.Request <: MessageRepresents a HTTP Request Message.
method::Stringuri::Stringversion::VersionNumberheaders::Vector{Pair{String,String}}body::Vector{UInt8}response, theResponseto thisRequestparent, theResponse(if any) that led to this request (e.g. in the case of a redirect).
HTTP.Messages.Response — Type.Response <: MessageRepresents a HTTP Response Message.
version::VersionNumberstatus::Int16headers::Vector{Pair{String,String}}body::Vector{UInt8}request, theRequestthat yielded thisResponse.
HTTP.Messages.iserror — Function.iserror(::Response)Does this Response have an error status?
HTTP.Messages.isredirect — Function.isredirect(::Response)Does this Response have a redirect status?
HTTP.Messages.ischunked — Function.ischunked(::Message)Does the Message have a "Transfer-Encoding: chunked" header?
HTTP.Messages.issafe — Function.issafe(::Request)https://tools.ietf.org/html/rfc7231#section-4.2.1
HTTP.Messages.isidempotent — Function.isidempotent(::Request)https://tools.ietf.org/html/rfc7231#section-4.2.2
HTTP.Messages.header — Function.header(::Message, key [, default=""]) -> StringGet header value for key (case-insensitive).
HTTP.Messages.hasheader — Function.hasheader(::Message, key) -> BoolDoes header value for key exist (case-insensitive)?
HTTP.Messages.setheader — Function.setheader(::Message, key => value)Set header value for key (case-insensitive).
HTTP.Messages.defaultheader — Function.defaultheader(::Message, key => value)Set header value for key if it is not already set.
HTTP.Messages.appendheader — Function.appendheader(::Message, key => value)Append a header value to message.headers.
If key is "" the value is appended to the value of the previous header.
If key is the same as the previous header, the vale 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.readheaders — Function.readheaders(::IO, ::Parser, ::Message)Read headers (and startline) from an IO stream into a Message struct. Throw EOFError if input is incomplete.
HTTP.Messages.readstartline! — Function.readstartline!(::Parsers.Message, ::Message)Read the start-line metadata from Parser into a ::Message struct.
HTTP.Parsers.headerscomplete — Method.headerscomplete(::Message)Have the headers been read into this Message?
HTTP.Messages.readtrailers — Function.readtrailers(::IO, ::Parser, ::Message)Read trailers from an IO stream into a Message struct.
HTTP.Messages.writestartline — Function.writestartline(::IO, ::Message)e.g. "GET /path HTTP/1.1\r\n" or "HTTP/1.1 200 OK\r\n"
HTTP.Messages.writeheaders — Function.writeheaders(::IO, ::Message)Write Message start line and a line for each "name: value" pair and a trailing blank line.
Base.write — Method.write(::IO, ::Message)Write start line, headers and body of HTTP Message.
IOExtras Interface
HTTP.IOExtras — Module.This module defines extensions to the Base.IO interface to support:
an
unread!function for pushing excess bytes back into a stream,startwrite,closewrite,startreadandclosereadfor streams with transactional semantics.
HTTP.IOExtras.unread! — Function.unread!(::IO, bytes)Push bytes back into a connection (to be returned by the next read).
unread!(::Transaction, bytes)Push bytes back into a connection's excess buffer (to be returned by the next read).
HTTP.IOExtras.startwrite — Method.startwrite(::IO)
closewrite(::IO)
startread(::IO)
closeread(::IO)Signal start/end of write or read operations.
HTTP.IOExtras.isioerror — Function.isioerror(exception)Is exception caused by a possibly recoverable IO error.
Streams Interface
HTTP.Streams.closebody — Function.closebody(::Stream)Write the final 0 chunk if needed.
HTTP.Streams.isaborted — Function.isaborted(::Stream{Response})Has the server signalled 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
Connection Pooling Interface
HTTP.ConnectionPool.Connection — Type.Connection{T <: IO}A TCPSocket or SSLContext connection to a HTTP host and port.
Fields:
host::Stringport::String, exactly as specified in the URI (i.e. may be empty).pipeline_linit, number of requests to send before waiting for responses.peerport, remote TCP port number (used for debug messages).localport, local TCP port number (used for debug messages).io::T, theTCPSocketor `SSLContext.excess::ByteView, left over bytes read from the connection after the end of a response message. These bytes are probably the start of the next response message.writebusy, is aTransactionbusy writing to thisConnection?writecount, number of Request Messages that have been written.readcount, number of Response Messages that have been read.writelock, busy writing a Request toio.readlock, busy reading a Response fromio.`timestamp, time data was last recieved.
parser::Parser, reuse aParserwhen thisConnectionis reused.
HTTP.ConnectionPool.Transaction — Type.A single pipelined HTTP Request/Response transaction`.
Fields:
c, the sharedConnectionused for thisTransaction.sequence::Int, identifies thisTransactionamong the others that sharec.
HTTP.ConnectionPool.pool — Constant.The pool is a collection of open Connections. The request function calls getconnection to retrieve a connection from the pool. When the request function has written a Request Message it calls closewrite to signal that the Connection can be reused for writing (to send the next Request). When the request function has read the Response Message it calls closeread to signal that the Connection can be reused for reading.
HTTP.ConnectionPool.getconnection — Function.getconnection(type, host, port) -> ConnectionFind a reusable Connection in the pool, or create a new Connection if required.
HTTP.IOExtras.unread! — Method.unread!(::Transaction, bytes)Push bytes back into a connection's excess buffer (to be returned by the next read).
HTTP.IOExtras.startwrite — Method.startwrite(::Transaction)Set writebusy. Should only be called by the Transaction constructor because getconnection only creates new Transactions when a Connection is available for writing.
HTTP.IOExtras.closewrite — Method.closewrite(::Transaction)Signal that an entire Request Message has been written to the Transaction.
HTTP.IOExtras.startread — Method.startread(::Transaction)Wait for prior pending reads to complete, then lock the readlock.
HTTP.IOExtras.closeread — Method.closeread(::Transaction)Signal that an entire Response Message has been read from the Transaction.
Increment readcount and wake up tasks waiting in startread by unlocking readlock.