Skip to content

Commit 58a6db7

Browse files
authored
Try #401:
2 parents 1bd6737 + ed6b88a commit 58a6db7

File tree

4 files changed

+83
-6
lines changed

4 files changed

+83
-6
lines changed

src/utilities/request.jl

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
# this is used to allow custom dispatches to `_http_request`
2+
abstract type AbstractBackend end
3+
4+
"""
5+
AWS.HTTPBackend <: AWS.AbstractBackend
6+
7+
An `HTTPBackend` can hold default `http_options::AbstractDict{Symbol,<:Any}`
8+
to pass to HTTP.jl, which can be overwritten per-request by any `http_options`
9+
supplied there.
10+
"""
11+
struct HTTPBackend <: AbstractBackend
12+
http_options::AbstractDict{Symbol,<:Any}
13+
end
14+
15+
HTTPBackend(; kwargs...) = HTTPBackend(LittleDict(kwargs))
16+
17+
const DEFAULT_BACKEND = Ref{AbstractBackend}(HTTPBackend())
18+
119
Base.@kwdef mutable struct Request
220
service::String
321
api_version::String
@@ -13,6 +31,7 @@ Base.@kwdef mutable struct Request
1331
http_options::AbstractDict{Symbol,<:Any}=LittleDict{Symbol,String}()
1432
return_raw::Bool=false
1533
response_dict_type::Type{<:AbstractDict}=LittleDict
34+
backend::AbstractBackend=DEFAULT_BACKEND[]
1635
end
1736

1837

@@ -54,7 +73,7 @@ function submit_request(aws::AbstractAWSConfig, request::Request; return_headers
5473
@repeat 3 try
5574
credentials(aws) === nothing || sign!(aws, request)
5675

57-
response = @mock _http_request(request)
76+
response = @mock _http_request(request.backend, request)
5877

5978
if response.status in REDIRECT_ERROR_CODES
6079
if HTTP.header(response, "Location") != ""
@@ -141,23 +160,25 @@ function submit_request(aws::AbstractAWSConfig, request::Request; return_headers
141160
end
142161

143162

144-
function _http_request(request::Request)
163+
function _http_request(http_backend::HTTPBackend, request::Request)
164+
http_options = merge(http_backend.http_options, request.http_options)
165+
145166
@repeat 4 try
146167
http_stack = HTTP.stack(redirect=false, retry=false, aws_authorization=false)
147168

148169
if request.return_stream && request.response_stream === nothing
149170
request.response_stream = Base.BufferStream()
150171
end
151172

152-
return HTTP.request(
173+
return @mock HTTP.request(
153174
http_stack,
154175
request.request_method,
155176
HTTP.URI(request.url),
156177
HTTP.mkheaders(request.headers),
157178
request.content;
158179
require_ssl_verification=false,
159180
response_stream=request.response_stream,
160-
request.http_options...
181+
http_options...
161182
)
162183
catch e
163184
# Base.IOError is needed because HTTP.jl can often have errors that aren't

src/utilities/utilities.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ function _extract_common_kw_args(service, args)
6969
headers=LittleDict{String, String}(_pop!(args, "headers", [])),
7070
http_options=_pop!(args, "http_options", LittleDict{Symbol, String}()),
7171
response_dict_type=_pop!(args, "response_dict_type", LittleDict),
72+
backend=_pop!(args, "backend", DEFAULT_BACKEND[]),
7273
)
7374
end
7475

test/AWS.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,50 @@ end
351351
end
352352
end
353353

354+
@testset "HTTPBackend" begin
355+
request = Request(
356+
service="s3",
357+
api_version="api_version",
358+
request_method="GET",
359+
url="https://s3.us-east-1.amazonaws.com/sample-bucket",
360+
)
361+
apply(Patches._http_options_patch) do
362+
# No default options
363+
@test isempty(AWS._http_request(request.backend, request))
364+
365+
# We can pass options via the backend
366+
custom_backend = AWS.HTTPBackend(Dict(:connection_limit => 5))
367+
@test custom_backend isa AWS.AbstractBackend
368+
@test AWS._http_request(custom_backend, request) == Dict(:connection_limit => 5)
369+
370+
# We can pass options per-request
371+
request.http_options = Dict(:pipeline_limit => 20)
372+
@test AWS._http_request(request.backend, request) == Dict(:pipeline_limit => 20)
373+
@test AWS._http_request(custom_backend, request) == Dict(:pipeline_limit => 20, :connection_limit => 5)
374+
375+
# per-request options override backend options:
376+
custom_backend = AWS.HTTPBackend(Dict(:pipeline_limit => 5))
377+
@test AWS._http_request(custom_backend, request) == Dict(:pipeline_limit => 20)
378+
end
379+
380+
# Let's test setting the default backend
381+
try
382+
AWS.DEFAULT_BACKEND[] = AWS.HTTPBackend(; connection_limit = 3)
383+
request = Request(
384+
service="s3",
385+
api_version="api_version",
386+
request_method="GET",
387+
url="https://s3.us-east-1.amazonaws.com/sample-bucket",
388+
)
389+
@test request.backend.http_options == Dict(:connection_limit => 3)
390+
apply(Patches._http_options_patch) do
391+
@test AWS._http_request(request.backend, request) == Dict(:connection_limit => 3)
392+
end
393+
finally
394+
AWS.DEFAULT_BACKEND[] = AWS.HTTPBackend()
395+
end
396+
end
397+
354398
@testset "_generate_rest_resource" begin
355399
request_uri = "/{Bucket}/{Key+}"
356400
args = Dict{String, Any}(

test/patch.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ end
5656

5757

5858
function _aws_http_request_patch(response::HTTP.Messages.Response=_response())
59-
return @patch AWS._http_request(request::Request) = response
59+
return @patch AWS._http_request(::AWS.HTTPBackend, request::Request) = response
6060
end
6161

6262
_cred_file_patch = @patch function dot_aws_credentials_file()
@@ -73,7 +73,7 @@ _web_identity_patch = function (;
7373
session_token="web_session_token",
7474
role_arn="arn:aws:sts:::assumed-role/role-name",
7575
)
76-
@patch function AWS._http_request(request)
76+
@patch function AWS._http_request(::AWS.HTTPBackend, request)
7777
params = Dict(split.(split(request.content, '&'), '='))
7878
creds = Dict(
7979
"AccessKeyId" => access_key,
@@ -107,4 +107,15 @@ _instance_metadata_timeout_patch = @patch function HTTP.request(method::String,
107107
throw(HTTP.ConnectionPool.ConnectTimeout("169.254.169.254", "80"))
108108
end
109109

110+
# This patch causes `HTTP.request` to return all of its keyword arguments
111+
# except `require_ssl_verification` and `response_stream`. This is used to
112+
# test which other options are being passed to `HTTP.Request` inside of
113+
# `_http_request`.
114+
_http_options_patch = @patch function HTTP.request(args...; kwargs...)
115+
options = Dict(kwargs)
116+
delete!(options, :require_ssl_verification)
117+
delete!(options, :response_stream)
118+
return options
119+
end
120+
110121
end

0 commit comments

Comments
 (0)