@@ -124,24 +124,33 @@ public class HTTPClient {
124
124
var bootstrap = ClientBootstrap ( group: group)
125
125
. channelOption ( ChannelOptions . socket ( SocketOptionLevel ( IPPROTO_TCP) , TCP_NODELAY) , value: 1 )
126
126
. channelInitializer { channel in
127
- channel. pipeline. addHTTPClientHandlers ( ) . flatMap {
128
- self . configureSSL ( channel: channel, useTLS: request. useTLS, hostname: request. host)
129
- } . flatMap {
130
- if let readTimeout = timeout. read {
131
- return channel. pipeline. addHandler ( IdleStateHandler ( readTimeout: readTimeout) )
132
- } else {
133
- return channel. eventLoop. makeSucceededFuture ( ( ) )
127
+ let encoder = HTTPRequestEncoder ( )
128
+ let decoder = ByteToMessageHandler ( HTTPResponseDecoder ( leftOverBytesStrategy: . forwardBytes) )
129
+ return channel. pipeline. addHandlers ( [ encoder, decoder] , position: . first) . flatMap {
130
+ switch self . configuration. proxy {
131
+ case . none:
132
+ return channel. pipeline. addSSLHandlerIfNeeded ( for: request, tlsConfiguration: self . configuration. tlsConfiguration)
133
+ case . some:
134
+ return channel. pipeline. addProxyHandler ( for: request, decoder: decoder, encoder: encoder, tlsConfiguration: self . configuration. tlsConfiguration)
135
+ }
136
+ } . flatMap {
137
+ if let readTimeout = timeout. read {
138
+ return channel. pipeline. addHandler ( IdleStateHandler ( readTimeout: readTimeout) )
139
+ } else {
140
+ return channel. eventLoop. makeSucceededFuture ( ( ) )
141
+ }
142
+ } . flatMap {
143
+ let taskHandler = TaskHandler ( task: task, delegate: delegate, promise: promise, redirectHandler: redirectHandler)
144
+ return channel. pipeline. addHandler ( taskHandler)
134
145
}
135
- } . flatMap {
136
- channel. pipeline. addHandler ( TaskHandler ( task: task, delegate: delegate, promise: promise, redirectHandler: redirectHandler) )
137
- }
138
146
}
139
147
140
148
if let connectTimeout = timeout. connect {
141
149
bootstrap = bootstrap. connectTimeout ( connectTimeout)
142
150
}
143
-
144
- bootstrap. connect ( host: request. host, port: request. port)
151
+
152
+ let address = self . resolveAddress ( request: request, proxy: self . configuration. proxy)
153
+ bootstrap. connect ( host: address. host, port: address. port)
145
154
. map { channel in
146
155
task. setChannel ( channel)
147
156
}
@@ -155,36 +164,33 @@ public class HTTPClient {
155
164
return task
156
165
}
157
166
158
- private func configureSSL( channel: Channel , useTLS: Bool , hostname: String ) -> EventLoopFuture < Void > {
159
- if useTLS {
160
- do {
161
- let tlsConfiguration = self . configuration. tlsConfiguration ?? TLSConfiguration . forClient ( )
162
- let context = try NIOSSLContext ( configuration: tlsConfiguration)
163
- return channel. pipeline. addHandler ( try NIOSSLClientHandler ( context: context, serverHostname: hostname) ,
164
- position: . first)
165
- } catch {
166
- return channel. eventLoop. makeFailedFuture ( error)
167
- }
168
- } else {
169
- return channel. eventLoop. makeSucceededFuture ( ( ) )
167
+ private func resolveAddress( request: Request , proxy: Proxy ? ) -> ( host: String , port: Int ) {
168
+ switch self . configuration. proxy {
169
+ case . none:
170
+ return ( request. host, request. port)
171
+ case . some( let proxy) :
172
+ return ( proxy. host, proxy. port)
170
173
}
171
174
}
172
175
173
176
public struct Configuration {
174
177
public var tlsConfiguration : TLSConfiguration ?
175
178
public var followRedirects : Bool
176
179
public var timeout : Timeout
180
+ public var proxy : Proxy ?
177
181
178
- public init ( tlsConfiguration: TLSConfiguration ? = nil , followRedirects: Bool = false , timeout: Timeout = Timeout ( ) ) {
182
+ public init ( tlsConfiguration: TLSConfiguration ? = nil , followRedirects: Bool = false , timeout: Timeout = Timeout ( ) , proxy : Proxy ? = nil ) {
179
183
self . tlsConfiguration = tlsConfiguration
180
184
self . followRedirects = followRedirects
181
185
self . timeout = timeout
186
+ self . proxy = proxy
182
187
}
183
188
184
- public init ( certificateVerification: CertificateVerification , followRedirects: Bool = false , timeout: Timeout = Timeout ( ) ) {
189
+ public init ( certificateVerification: CertificateVerification , followRedirects: Bool = false , timeout: Timeout = Timeout ( ) , proxy : Proxy ? = nil ) {
185
190
self . tlsConfiguration = TLSConfiguration . forClient ( certificateVerification: certificateVerification)
186
191
self . followRedirects = followRedirects
187
192
self . timeout = timeout
193
+ self . proxy = proxy
188
194
}
189
195
}
190
196
@@ -199,6 +205,37 @@ public class HTTPClient {
199
205
}
200
206
}
201
207
208
+ private extension ChannelPipeline {
209
+ func addProxyHandler( for request: HTTPClient . Request , decoder: ByteToMessageHandler < HTTPResponseDecoder > , encoder: HTTPRequestEncoder , tlsConfiguration: TLSConfiguration ? ) -> EventLoopFuture < Void > {
210
+ let handler = HTTPClientProxyHandler ( host: request. host, port: request. port, onConnect: { channel in
211
+ return channel. pipeline. removeHandler ( decoder) . flatMap {
212
+ return channel. pipeline. addHandler (
213
+ ByteToMessageHandler ( HTTPResponseDecoder ( leftOverBytesStrategy: . forwardBytes) ) ,
214
+ position: . after( encoder)
215
+ )
216
+ } . flatMap {
217
+ return channel. pipeline. addSSLHandlerIfNeeded ( for: request, tlsConfiguration: tlsConfiguration)
218
+ }
219
+ } )
220
+ return self . addHandler ( handler)
221
+ }
222
+
223
+ func addSSLHandlerIfNeeded( for request: HTTPClient . Request , tlsConfiguration: TLSConfiguration ? ) -> EventLoopFuture < Void > {
224
+ guard request. useTLS else {
225
+ return self . eventLoop. makeSucceededFuture ( ( ) )
226
+ }
227
+
228
+ do {
229
+ let tlsConfiguration = tlsConfiguration ?? TLSConfiguration . forClient ( )
230
+ let context = try NIOSSLContext ( configuration: tlsConfiguration)
231
+ return self . addHandler ( try NIOSSLClientHandler ( context: context, serverHostname: request. host) ,
232
+ position: . first)
233
+ } catch {
234
+ return self . eventLoop. makeFailedFuture ( error)
235
+ }
236
+ }
237
+ }
238
+
202
239
public struct HTTPClientError : Error , Equatable , CustomStringConvertible {
203
240
private enum Code : Equatable {
204
241
case invalidURL
@@ -211,6 +248,7 @@ public struct HTTPClientError: Error, Equatable, CustomStringConvertible {
211
248
case cancelled
212
249
case identityCodingIncorrectlyPresent
213
250
case chunkedSpecifiedMultipleTimes
251
+ case invalidProxyResponse
214
252
}
215
253
216
254
private var code : Code
@@ -233,4 +271,5 @@ public struct HTTPClientError: Error, Equatable, CustomStringConvertible {
233
271
public static let cancelled = HTTPClientError ( code: . cancelled)
234
272
public static let identityCodingIncorrectlyPresent = HTTPClientError ( code: . identityCodingIncorrectlyPresent)
235
273
public static let chunkedSpecifiedMultipleTimes = HTTPClientError ( code: . chunkedSpecifiedMultipleTimes)
274
+ public static let invalidProxyResponse = HTTPClientError ( code: . invalidProxyResponse)
236
275
}
0 commit comments