14
14
15
15
import NIOSSL
16
16
17
+ #if canImport(Darwin)
18
+ import Darwin. C
19
+ #elseif os(Linux) || os(FreeBSD) || os(Android)
20
+ import Glibc
21
+ #else
22
+ #error("unsupported target operating system")
23
+ #endif
24
+
25
+ extension String {
26
+ var isIPAddress : Bool {
27
+ var ipv4Address = in_addr ( )
28
+ var ipv6Address = in6_addr ( )
29
+ return self . withCString { host in
30
+ inet_pton ( AF_INET, host, & ipv4Address) == 1 ||
31
+ inet_pton ( AF_INET6, host, & ipv6Address) == 1
32
+ }
33
+ }
34
+ }
35
+
17
36
enum ConnectionPool {
18
37
/// Used by the `ConnectionPool` to index its `HTTP1ConnectionProvider`s
19
38
///
@@ -24,15 +43,18 @@ enum ConnectionPool {
24
43
var scheme : Scheme
25
44
var connectionTarget : ConnectionTarget
26
45
private var tlsConfiguration : BestEffortHashableTLSConfiguration ?
46
+ var serverNameIndicatorOverride : String ?
27
47
28
48
init (
29
49
scheme: Scheme ,
30
50
connectionTarget: ConnectionTarget ,
31
- tlsConfiguration: BestEffortHashableTLSConfiguration ? = nil
51
+ tlsConfiguration: BestEffortHashableTLSConfiguration ? = nil ,
52
+ serverNameIndicatorOverride: String ?
32
53
) {
33
54
self . scheme = scheme
34
55
self . connectionTarget = connectionTarget
35
56
self . tlsConfiguration = tlsConfiguration
57
+ self . serverNameIndicatorOverride = serverNameIndicatorOverride
36
58
}
37
59
38
60
var description : String {
@@ -48,26 +70,44 @@ enum ConnectionPool {
48
70
case . unixSocket( let socketPath) :
49
71
hostDescription = socketPath
50
72
}
51
- return " \( self . scheme) :// \( hostDescription) TLS-hash: \( hash) "
73
+ return " \( self . scheme) :// \( hostDescription) \( self . serverNameIndicatorOverride . map { " SNI: \( $0 ) " } ?? " " ) TLS-hash: \( hash) "
52
74
}
53
75
}
54
76
}
55
77
78
+ extension DeconstructedURL {
79
+ func applyDNSOverride( _ dnsOverride: [ String : String ] ) -> ( ConnectionTarget , serverNameIndicatorOverride: String ? ) {
80
+ guard
81
+ let originalHost = self . connectionTarget. host,
82
+ let hostOverride = dnsOverride [ originalHost]
83
+ else {
84
+ return ( self . connectionTarget, nil )
85
+ }
86
+ return (
87
+ . init( remoteHost: hostOverride, port: self . connectionTarget. port ?? self . scheme. defaultPort) ,
88
+ serverNameIndicatorOverride: originalHost. isIPAddress ? nil : originalHost
89
+ )
90
+ }
91
+ }
92
+
56
93
extension ConnectionPool . Key {
57
- init ( url: DeconstructedURL , tlsConfiguration: TLSConfiguration ? ) {
94
+ init ( url: DeconstructedURL , tlsConfiguration: TLSConfiguration ? , dnsOverride: [ String : String ] ) {
95
+ let ( connectionTarget, serverNameIndicatorOverride) = url. applyDNSOverride ( dnsOverride)
58
96
self . init (
59
97
scheme: url. scheme,
60
- connectionTarget: url . connectionTarget,
98
+ connectionTarget: connectionTarget,
61
99
tlsConfiguration: tlsConfiguration. map {
62
100
BestEffortHashableTLSConfiguration ( wrapping: $0)
63
- }
101
+ } ,
102
+ serverNameIndicatorOverride: serverNameIndicatorOverride
64
103
)
65
104
}
66
105
67
- init ( _ request: HTTPClient . Request ) {
106
+ init ( _ request: HTTPClient . Request , dnsOverride : [ String : String ] = [ : ] ) {
68
107
self . init (
69
108
url: request. deconstructedURL,
70
- tlsConfiguration: request. tlsConfiguration
109
+ tlsConfiguration: request. tlsConfiguration,
110
+ dnsOverride: dnsOverride
71
111
)
72
112
}
73
113
}
0 commit comments