@@ -124,24 +124,33 @@ public class HTTPClient {
124124 var bootstrap = ClientBootstrap ( group: group)
125125 . channelOption ( ChannelOptions . socket ( SocketOptionLevel ( IPPROTO_TCP) , TCP_NODELAY) , value: 1 )
126126 . 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)
134145 }
135- } . flatMap {
136- channel. pipeline. addHandler ( TaskHandler ( task: task, delegate: delegate, promise: promise, redirectHandler: redirectHandler) )
137- }
138146 }
139147
140148 if let connectTimeout = timeout. connect {
141149 bootstrap = bootstrap. connectTimeout ( connectTimeout)
142150 }
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)
145154 . map { channel in
146155 task. setChannel ( channel)
147156 }
@@ -155,36 +164,33 @@ public class HTTPClient {
155164 return task
156165 }
157166
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)
170173 }
171174 }
172175
173176 public struct Configuration {
174177 public var tlsConfiguration : TLSConfiguration ?
175178 public var followRedirects : Bool
176179 public var timeout : Timeout
180+ public var proxy : Proxy ?
177181
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 ) {
179183 self . tlsConfiguration = tlsConfiguration
180184 self . followRedirects = followRedirects
181185 self . timeout = timeout
186+ self . proxy = proxy
182187 }
183188
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 ) {
185190 self . tlsConfiguration = TLSConfiguration . forClient ( certificateVerification: certificateVerification)
186191 self . followRedirects = followRedirects
187192 self . timeout = timeout
193+ self . proxy = proxy
188194 }
189195 }
190196
@@ -199,6 +205,37 @@ public class HTTPClient {
199205 }
200206}
201207
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+
202239public struct HTTPClientError : Error , Equatable , CustomStringConvertible {
203240 private enum Code : Equatable {
204241 case invalidURL
@@ -211,6 +248,7 @@ public struct HTTPClientError: Error, Equatable, CustomStringConvertible {
211248 case cancelled
212249 case identityCodingIncorrectlyPresent
213250 case chunkedSpecifiedMultipleTimes
251+ case invalidProxyResponse
214252 }
215253
216254 private var code : Code
@@ -233,4 +271,5 @@ public struct HTTPClientError: Error, Equatable, CustomStringConvertible {
233271 public static let cancelled = HTTPClientError ( code: . cancelled)
234272 public static let identityCodingIncorrectlyPresent = HTTPClientError ( code: . identityCodingIncorrectlyPresent)
235273 public static let chunkedSpecifiedMultipleTimes = HTTPClientError ( code: . chunkedSpecifiedMultipleTimes)
274+ public static let invalidProxyResponse = HTTPClientError ( code: . invalidProxyResponse)
236275}
0 commit comments