Skip to content

Commit

Permalink
Separate HTTP.request from Layers.request, fixes #463.
Browse files Browse the repository at this point in the history
  • Loading branch information
fredrikekre committed Nov 6, 2019
1 parent 7bcb6f5 commit 069ea7f
Show file tree
Hide file tree
Showing 18 changed files with 144 additions and 97 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,23 +111,23 @@ Hello
```

## Custom HTTP Layer Examples
#####Notes:
##### Notes:
- There is no enforcement of a "well-defined" stack, you can insert a layer anywhere in the stack even if it logically
does not make sense
- When creating a custom layer, you need to create a `request()`, see below for an example
- Custom layers is only implemented with the "low-level" `request()` calls, not the "convenience" functions such as
- Custom layers is only implemented with the "low-level" `HTTP.request()` calls, not the "convenience" functions such as
`HTTP.get()`, `HTTP.put()`, etc.

```julia
julia> module TestRequest
import HTTP: Layer, request, Response
import HTTP: Layer, Layers, Response

abstract type TestLayer{Next <: Layer} <: Layer{Next} end
export TestLayer, request
export TestLayer

function request(::Type{TestLayer{Next}}, io::IO, req, body; kw...)::Response where Next
function Layers.request(::Type{TestLayer{Next}}, io::IO, req, body; kw...)::Response where Next
println("Insert your custom layer logic here!")
return request(Next, io, req, body; kw...)
return Layers.request(Next, io, req, body; kw...)
end
end

Expand Down Expand Up @@ -159,7 +159,7 @@ Connection: keep-alive
}
"""

julia>
julia>
```

[docs-dev-img]: https://img.shields.io/badge/docs-dev-blue.svg
Expand Down
8 changes: 4 additions & 4 deletions src/AWS4AuthRequest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ module AWS4AuthRequest
using ..Base64
using ..Dates
using MbedTLS: digest, MD_SHA256, MD_MD5
import ..Layer, ..request, ..Headers
import ..Layers, ..Layer, ..Headers
using ..URIs
using ..Pairs: getkv, setkv, rmkv
import ..@debug, ..DEBUG_LEVEL

"""
request(AWS4AuthLayer, ::URI, ::Request, body) -> HTTP.Response
Layers.request(AWS4AuthLayer, ::URI, ::Request, body) -> HTTP.Response
Add a [AWS Signature Version 4](http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html)
`Authorization` header to a `Request`.
Expand All @@ -21,7 +21,7 @@ Credentials are read from environment variables `AWS_ACCESS_KEY_ID`,
abstract type AWS4AuthLayer{Next <: Layer} <: Layer{Next} end
export AWS4AuthLayer

function request(::Type{AWS4AuthLayer{Next}},
function Layers.request(::Type{AWS4AuthLayer{Next}},
url::URI, req, body; kw...) where Next

if !haskey(kw, :aws_access_key_id) &&
Expand All @@ -31,7 +31,7 @@ function request(::Type{AWS4AuthLayer{Next}},

sign_aws4!(req.method, url, req.headers, req.body; kw...)

return request(Next, url, req, body; kw...)
return Layers.request(Next, url, req, body; kw...)
end

# Normalize whitespace to the form required in the canonical headers.
Expand Down
14 changes: 7 additions & 7 deletions src/BasicAuthRequest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@ module BasicAuthRequest

using ..Base64

import ..Layer, ..request
import ..Layer, ..Layers
using ..URIs
using ..Pairs: getkv, setkv
import ..@debug, ..DEBUG_LEVEL

"""
request(BasicAuthLayer, method, ::URI, headers, body) -> HTTP.Response
Layers.request(BasicAuthLayer, method, ::URI, headers, body) -> HTTP.Response
Add `Authorization: Basic` header using credentials from url userinfo.
"""
abstract type BasicAuthLayer{Next <: Layer} <: Layer{Next} end
export BasicAuthLayer

function request(::Type{BasicAuthLayer{Next}},
method::String, url::URI, headers, body; kw...) where Next
function Layers.request(::Type{BasicAuthLayer{Next}},
method::String, url::URI, headers, body; kw...) where Next

userinfo = unescapeuri(url.userinfo)

if !isempty(userinfo) && getkv(headers, "Authorization", "") == ""
@debug 1 "Adding Authorization: Basic header."
setkv(headers, "Authorization", "Basic $(base64encode(userinfo))")
end
return request(Next, method, url, headers, body; kw...)

return Layers.request(Next, method, url, headers, body; kw...)
end


Expand Down
10 changes: 5 additions & 5 deletions src/CanonicalizeRequest.jl
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
module CanonicalizeRequest

import ..Layer, ..request
import ..Layer, ..Layers
using ..Messages
using ..Strings: tocameldash

"""
request(CanonicalizeLayer, method, ::URI, headers, body) -> HTTP.Response
Layers.request(CanonicalizeLayer, method, ::URI, headers, body) -> HTTP.Response
Rewrite request and response headers in Canonical-Camel-Dash-Format.
"""
abstract type CanonicalizeLayer{Next <: Layer} <: Layer{Next} end
export CanonicalizeLayer

function request(::Type{CanonicalizeLayer{Next}},
function Layers.request(::Type{CanonicalizeLayer{Next}},
method::String, url, headers, body; kw...) where Next

headers = canonicalizeheaders(headers)
res = request(Next, method, url, headers, body; kw...)

res = Layers.request(Next, method, url, headers, body; kw...)

res.headers = canonicalizeheaders(res.headers)

Expand Down
10 changes: 5 additions & 5 deletions src/ConnectionRequest.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ConnectionRequest

import ..Layer, ..request
import ..Layer, ..Layers
using ..URIs, ..Sockets
using ..Messages
using ..IOExtras
Expand All @@ -9,7 +9,7 @@ using MbedTLS: SSLContext
import ..@debug, ..DEBUG_LEVEL

"""
request(ConnectionPoolLayer, ::URI, ::Request, body) -> HTTP.Response
Layers.request(ConnectionPoolLayer, ::URI, ::Request, body) -> HTTP.Response
Retrieve an `IO` connection from the [`ConnectionPool`](@ref).
Expand All @@ -22,7 +22,7 @@ See [`isioerror`](@ref).
abstract type ConnectionPoolLayer{Next <: Layer} <: Layer{Next} end
export ConnectionPoolLayer

function request(::Type{ConnectionPoolLayer{Next}}, url::URI, req, body;
function Layers.request(::Type{ConnectionPoolLayer{Next}}, url::URI, req, body;
proxy=nothing,
socket_type::Type=TCPSocket,
reuse_limit::Int=ConnectionPool.nolimit, kw...) where Next
Expand Down Expand Up @@ -53,7 +53,7 @@ function request(::Type{ConnectionPoolLayer{Next}}, url::URI, req, body;
return tunnel_request(Next, io, target_url, req, body; kw...)
end

return request(Next, io, req, body; kw...)
return Layers.request(Next, io, req, body; kw...)

catch e
@debug 1 "❗️ ConnectionLayer $e. Closing: $io"
Expand All @@ -78,7 +78,7 @@ function tunnel_request(Next, io, target_url, req, body; kw...)
end
io = ConnectionPool.sslupgrade(io, target_url.host; kw...)
req.headers = filter(x->x.first != "Proxy-Authorization", req.headers)
return request(Next, io, req, body; kw...)
return Layers.request(Next, io, req, body; kw...)
end

function connect_tunnel(io, target_url, req)
Expand Down
6 changes: 3 additions & 3 deletions src/ContentTypeRequest.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ContentTypeDetection

import ..Layer, ..request
import ..Layer, ..Layers
using ..URIs
using ..Pairs: getkv, setkv
import ..sniff
Expand All @@ -12,7 +12,7 @@ import ..@debug, ..DEBUG_LEVEL
abstract type ContentTypeDetectionLayer{Next <: Layer} <: Layer{Next} end
export ContentTypeDetectionLayer

function request(::Type{ContentTypeDetectionLayer{Next}},
function Layers.request(::Type{ContentTypeDetectionLayer{Next}},
method::String, url::URI, headers, body; kw...) where Next

if (getkv(headers, "Content-Type", "") == ""
Expand All @@ -24,7 +24,7 @@ function request(::Type{ContentTypeDetectionLayer{Next}},
setkv(headers, "Content-Type", sn)
@debug 1 "setting Content-Type header to: $sn"
end
return request(Next, method, url, headers, body; kw...)
return Layers.request(Next, method, url, headers, body; kw...)
end

end # module
8 changes: 4 additions & 4 deletions src/CookieRequest.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module CookieRequest

import ..Dates
import ..Layer, ..request
import ..Layer, ..Layers
using ..URIs
using ..Cookies
using ..Pairs: getkv, setkv
Expand All @@ -10,15 +10,15 @@ import ..@debug, ..DEBUG_LEVEL
const default_cookiejar = Dict{String, Set{Cookie}}()

"""
request(CookieLayer, method, ::URI, headers, body) -> HTTP.Response
Layers.request(CookieLayer, method, ::URI, headers, body) -> HTTP.Response
Add locally stored Cookies to the request headers.
Store new Cookies found in the response headers.
"""
abstract type CookieLayer{Next <: Layer} <: Layer{Next} end
export CookieLayer

function request(::Type{CookieLayer{Next}},
function Layers.request(::Type{CookieLayer{Next}},
method::String, url::URI, headers, body;
cookies::Union{Bool, Dict{String, String}}=Dict{String, String}(),
cookiejar::Dict{String, Set{Cookie}}=default_cookiejar,
Expand All @@ -36,7 +36,7 @@ function request(::Type{CookieLayer{Next}},
setkv(headers, "Cookie", string(getkv(headers, "Cookie", ""), cookiestosend))
end

res = request(Next, method, url, headers, body; kw...)
res = Layers.request(Next, method, url, headers, body; kw...)

setcookies(hostcookies, url.host, res.headers)

Expand Down
10 changes: 5 additions & 5 deletions src/DebugRequest.jl
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
module DebugRequest

import ..Layer, ..request
import ..Layer, ..Layers
using ..IOExtras

const live_mode = true

include("IODebug.jl")

"""
request(DebugLayer, ::IO, ::Request, body) -> HTTP.Response
Layers.request(DebugLayer, ::IO, ::Request, body) -> HTTP.Response
Wrap the `IO` stream in an `IODebug` stream and print Message data.
"""
abstract type DebugLayer{Next <:Layer} <: Layer{Next} end
export DebugLayer

function request(::Type{DebugLayer{Next}}, io::IO, req, body; kw...) where Next
function Layers.request(::Type{DebugLayer{Next}}, io::IO, req, body; kw...) where Next

@static if live_mode
return request(Next, IODebug(io), req, body; kw...)
return Layers.request(Next, IODebug(io), req, body; kw...)
else
iod = IODebug(io)
try
return request(Next, iod, req, body; kw...)
return Layers.request(Next, iod, req, body; kw...)
finally
show_log(stdout, iod)
end
Expand Down
8 changes: 4 additions & 4 deletions src/ExceptionRequest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@ module ExceptionRequest

export StatusError

import ..Layer, ..request
import ..Layer, ..Layers
import ..HTTP
using ..Messages: iserror

"""
request(ExceptionLayer, ::URI, ::Request, body) -> HTTP.Response
Layers.request(ExceptionLayer, ::URI, ::Request, body) -> HTTP.Response
Throw a `StatusError` if the request returns an error response status.
"""
abstract type ExceptionLayer{Next <: Layer} <: Layer{Next} end
export ExceptionLayer

function request(::Type{ExceptionLayer{Next}}, a...; kw...) where Next
function Layers.request(::Type{ExceptionLayer{Next}}, a...; kw...) where Next

res = request(Next, a...; kw...)
res = Layers.request(Next, a...; kw...)

if iserror(res)
throw(StatusError(res.status, res.request.method, res.request.target, res))
Expand Down
19 changes: 10 additions & 9 deletions src/HTTP.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module HTTP

export startwrite, startread, closewrite, closeread, stack, insert, AWS4AuthLayer,
export startwrite, startread, closewrite, closeread, stack, insert, AWS4AuthLayer,
BasicAuthLayer, CanonicalizeLayer, ConnectionPoolLayer, ContentTypeDetectionLayer,
DebugLayer, ExceptionLayer, MessageLayer, RedirectLayer, RetryLayer, StreamLayer,
DebugLayer, ExceptionLayer, MessageLayer, RedirectLayer, RetryLayer, StreamLayer,
TimeoutLayer

const DEBUG_LEVEL = Ref(0)
Expand Down Expand Up @@ -160,7 +160,7 @@ Cookie options
- `cookies::Union{Bool, Dict{String, String}} = false`, enable cookies, or alternatively,
pass a `Dict{String, String}` of name-value pairs to manually pass cookies
- `cookiejar::Dict{String, Set{Cookie}}=default_cookiejar`,
- `cookiejar::Dict{String, Set{Cookie}}=default_cookiejar`,
Canonicalization options
Expand Down Expand Up @@ -303,18 +303,19 @@ HTTP.open("POST", "http://music.com/play") do io
end
```
"""
function request(method, url, h=Header[], b=nobody;
headers=h, body=b, kw...)::Response
request(HTTP.stack(;kw...), method, url, headers, body; kw...)
end
function request(stack::Type{<:Layer}, method, url, h=Header[], b=nobody;
headers=h, body=b, query=nothing, kw...)::Response
headers=h, body=b, query=nothing, kw...)::Response
uri = URI(url)
if query !== nothing
uri = merge(uri, query=query)
uri = merge(uri; query=query)
end

return request(stack, string(method), uri, mkheaders(headers), body; kw...)
Layers.request(stack, string(method), uri, mkheaders(headers), body; kw...)
end

request(a...; kw...)::Response = return request(HTTP.stack(;kw...), a...; kw...)

"""
HTTP.open(method, url, [,headers]) do io
write(io, body)
Expand Down
8 changes: 4 additions & 4 deletions src/MessageRequest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module MessageRequest
export body_is_a_stream, body_was_streamed, setuseragent!


import ..Layer, ..request
import ..Layer, ..Layers
using ..IOExtras
using ..URIs
using ..Messages
Expand All @@ -12,14 +12,14 @@ import ..Headers
import ..Form

"""
request(MessageLayer, method, ::URI, headers, body) -> HTTP.Response
Layers.request(MessageLayer, method, ::URI, headers, body) -> HTTP.Response
Construct a [`Request`](@ref) object and set mandatory headers.
"""
struct MessageLayer{Next <: Layer} <: Layer{Next} end
export MessageLayer

function request(::Type{MessageLayer{Next}},
function Layers.request(::Type{MessageLayer{Next}},
method::String, url::URI, headers::Headers, body;
http_version=v"1.1",
target=resource(url),
Expand All @@ -44,7 +44,7 @@ function request(::Type{MessageLayer{Next}},
req = Request(method, target, headers, bodybytes(body);
parent=parent, version=http_version)

return request(Next, url, req, body; iofunction=iofunction, kw...)
return Layers.request(Next, url, req, body; iofunction=iofunction, kw...)
end

const USER_AGENT = Ref{String}()
Expand Down
Loading

0 comments on commit 069ea7f

Please sign in to comment.