Skip to content

Commit 4693a9b

Browse files
authored
HTTP.request(): infer socket_type_tls from sslconfig, if provided (#1106)
* Add NetworkOptions to test deps * Failing test for #1104 * infer socket_type_tls from sslconfig, if provided Fixes #1104. This PR only affects client requests, because those were the only ones broken. For now OpenSSL is not supported in HTTP.jl servers (and, honestly, maybe that's a good thing, OpenSSL is kinda crap). I didn't just revert the change to SOCKET_TYPE_TLS[] because that change has been released for months and users may now be relying on `sslconfig` accepting an `OpenSSL.SSLContext` value, just as I was relying on it accepting an `MbedTLS.SSLConfig` value. * Delete test/Manifest.toml
1 parent ec69a01 commit 4693a9b

File tree

3 files changed

+71
-6
lines changed

3 files changed

+71
-6
lines changed

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
3939
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
4040
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
4141
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
42+
NetworkOptions = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
4243

4344
[targets]
44-
test = ["BufferedStreams", "Deno_jll", "Distributed", "InteractiveUtils", "JSON", "Test", "Unitful"]
45+
test = ["BufferedStreams", "Deno_jll", "Distributed", "InteractiveUtils", "JSON", "Test", "Unitful", "NetworkOptions"]

src/clientlayers/ConnectionRequest.jl

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module ConnectionRequest
22

33
using URIs, Sockets, Base64, LoggingExtras, ConcurrentUtilities, ExceptionUnwrapping
4-
using MbedTLS: SSLContext, SSLConfig
5-
using OpenSSL: SSLStream
4+
import MbedTLS
5+
import OpenSSL
66
using ..Messages, ..IOExtras, ..Connections, ..Streams, ..Exceptions
77
import ..SOCKET_TYPE_TLS
88

@@ -55,7 +55,7 @@ Close the connection if the request throws an exception.
5555
Otherwise leave it open so that it can be reused.
5656
"""
5757
function connectionlayer(handler)
58-
return function connections(req; proxy=getproxy(req.url.scheme, req.url.host), socket_type::Type=TCPSocket, socket_type_tls::Type=SOCKET_TYPE_TLS[], readtimeout::Int=0, connect_timeout::Int=30, logerrors::Bool=false, logtag=nothing, kw...)
58+
return function connections(req; proxy=getproxy(req.url.scheme, req.url.host), socket_type::Type=TCPSocket, socket_type_tls::Union{Nothing, Type}=nothing, readtimeout::Int=0, connect_timeout::Int=30, logerrors::Bool=false, logtag=nothing, kw...)
5959
local io, stream
6060
if proxy !== nothing
6161
target_url = req.url
@@ -74,7 +74,7 @@ function connectionlayer(handler)
7474
end
7575

7676
connect_timeout = connect_timeout == 0 && readtimeout > 0 ? readtimeout : connect_timeout
77-
IOType = sockettype(url, socket_type, socket_type_tls)
77+
IOType = sockettype(url, socket_type, socket_type_tls, get(kw, :sslconfig, nothing))
7878
start_time = time()
7979
try
8080
io = newconnection(IOType, url.host, url.port; readtimeout=readtimeout, connect_timeout=connect_timeout, kw...)
@@ -148,7 +148,62 @@ function connectionlayer(handler)
148148
end
149149
end
150150

151-
sockettype(url::URI, tcp, tls) = url.scheme in ("wss", "https") ? tls : tcp
151+
function sockettype(url::URI, socket_type_tcp, socket_type_tls, sslconfig)
152+
if url.scheme in ("wss", "https")
153+
tls_socket_type(socket_type_tls, sslconfig)
154+
else
155+
socket_type_tcp
156+
end
157+
end
158+
159+
"""
160+
tls_socket_type(socket_type_tls, sslconfig)::Type
161+
162+
Find the best TLS socket type, given the values of these keyword arguments.
163+
164+
If both are `nothing` then we use the global default: `HTTP.SOCKET_TYPE_TLS[]`.
165+
If both are not `nothing` then they must agree:
166+
`sslconfig` must be of the right type to configure `socket_type_tls` or we throw an `ArgumentError`.
167+
"""
168+
function tls_socket_type(socket_type_tls::Union{Nothing, Type},
169+
sslconfig::Union{Nothing, MbedTLS.SSLConfig, OpenSSL.SSLContext}
170+
)::Type
171+
172+
socket_type_matching_sslconfig =
173+
if sslconfig isa MbedTLS.SSLConfig
174+
MbedTLS.SSLContext
175+
elseif sslconfig isa OpenSSL.SSLContext
176+
OpenSSL.SSLStream
177+
else
178+
nothing
179+
end
180+
181+
if socket_type_tls === socket_type_matching_sslconfig
182+
# Use the global default TLS socket if they're both nothing, or use
183+
# what they both specify if they're not nothing.
184+
isnothing(socket_type_tls) ? SOCKET_TYPE_TLS[] : socket_type_tls
185+
# If either is nothing, use the other one.
186+
elseif isnothing(socket_type_tls)
187+
socket_type_matching_sslconfig
188+
elseif isnothing(socket_type_matching_sslconfig)
189+
socket_type_tls
190+
else
191+
# If they specify contradictory types, throw an error.
192+
# Error thrown in noinline closure to avoid speed penalty in common case
193+
@noinline function err(socket_type_tls, sslconfig)
194+
msg = """
195+
Incompatible values for keyword args `socket_type_tls` and `sslconfig`:
196+
socket_type_tls=$socket_type_tls
197+
typeof(sslconfig)=$(typeof(sslconfig))
198+
199+
Make them match or provide only one of them.
200+
- the socket type MbedTLS.SSLContext is configured by MbedTLS.SSLConfig
201+
- the socket type OpenSSL.SSLStream is configured by OpenSSL.SSLContext"""
202+
throw(ArgumentError(msg))
203+
end
204+
err(socket_type_tls, sslconfig)
205+
end
206+
end
152207

153208
function connect_tunnel(io, target_url, req)
154209
target = "$(URIs.hoststring(target_url.host)):$(target_url.port)"

test/client.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ for x in (10, 12)
2424
@test getfield(HTTP.Connections.OPENSSL_POOL[], max_or_limit) == x
2525
end
2626

27+
@testset "sslconfig without explicit socket_type_tls #1104" begin
28+
# this was supported before 8f35185
29+
@test isok(HTTP.get("https://$httpbin/ip", sslconfig=MbedTLS.SSLConfig(false)))
30+
# The OpenSSL package doesn't have enough docs, but this is a valid way to initialise an SSLContext.
31+
@test isok(HTTP.get("https://$httpbin/ip", sslconfig=OpenSSL.SSLContext(OpenSSL.TLSClientMethod())))
32+
# Incompatible socket_type_tls and sslconfig should throw an error.
33+
@test_throws ArgumentError HTTP.get("https://$httpbin/ip", sslconfig=MbedTLS.SSLConfig(false), socket_type_tls=OpenSSL.SSLStream)
34+
end
35+
2736
@testset "@client macro" begin
2837
@eval module MyClient
2938
using HTTP

0 commit comments

Comments
 (0)