@@ -205,28 +205,42 @@ public class HTTPClient {
205205 switch eventLoop. preference {
206206 case . indifferent:
207207 return self . execute ( request: request, delegate: delegate, eventLoop: self . eventLoopGroup. next ( ) , deadline: deadline)
208- case . prefers( let preferred) :
209- precondition ( self . eventLoopGroup. makeIterator ( ) . contains { $0 === preferred } , " Provided EventLoop must be part of clients EventLoopGroup. " )
210- return self . execute ( request: request, delegate: delegate, eventLoop: preferred, deadline: deadline)
208+ case . delegate( on: let eventLoop) :
209+ precondition ( self . eventLoopGroup. makeIterator ( ) . contains { $0 === eventLoop } , " Provided EventLoop must be part of clients EventLoopGroup. " )
210+ return self . execute ( request: request, delegate: delegate, eventLoop: eventLoop, deadline: deadline)
211+ case . delegateAndChannel( on: let eventLoop) :
212+ precondition ( self . eventLoopGroup. makeIterator ( ) . contains { $0 === eventLoop } , " Provided EventLoop must be part of clients EventLoopGroup. " )
213+ return self . execute ( request: request, delegate: delegate, eventLoop: eventLoop, deadline: deadline)
214+ case . testOnly_exact( channelOn: let channelEL, delegateOn: let delegateEL) :
215+ return self . execute ( request: request,
216+ delegate: delegate,
217+ eventLoop: delegateEL,
218+ channelEL: channelEL,
219+ deadline: deadline)
211220 }
212221 }
213222
214223 private func execute< Delegate: HTTPClientResponseDelegate > ( request: Request ,
215224 delegate: Delegate ,
216- eventLoop: EventLoop ,
225+ eventLoop delegateEL: EventLoop ,
226+ channelEL: EventLoop ? = nil ,
217227 deadline: NIODeadline ? = nil ) -> Task < Delegate . Response > {
218228 let redirectHandler : RedirectHandler < Delegate . Response > ?
219229 if self . configuration. followRedirects {
220230 redirectHandler = RedirectHandler < Delegate . Response > ( request: request) { newRequest in
221- self . execute ( request: newRequest, delegate: delegate, eventLoop: eventLoop, deadline: deadline)
231+ self . execute ( request: newRequest,
232+ delegate: delegate,
233+ eventLoop: delegateEL,
234+ channelEL: channelEL,
235+ deadline: deadline)
222236 }
223237 } else {
224238 redirectHandler = nil
225239 }
226240
227- let task = Task < Delegate . Response > ( eventLoop: eventLoop )
241+ let task = Task < Delegate . Response > ( eventLoop: delegateEL )
228242
229- var bootstrap = ClientBootstrap ( group: eventLoop )
243+ var bootstrap = ClientBootstrap ( group: channelEL ?? delegateEL )
230244 . channelOption ( ChannelOptions . socket ( SocketOptionLevel ( IPPROTO_TCP) , TCP_NODELAY) , value: 1 )
231245 . channelInitializer { channel in
232246 let encoder = HTTPRequestEncoder ( )
@@ -262,9 +276,7 @@ public class HTTPClient {
262276 . flatMap { channel in
263277 channel. writeAndFlush ( request)
264278 }
265- . whenFailure { error in
266- task. fail ( error)
267- }
279+ . cascadeFailure ( to: task. promise)
268280
269281 return task
270282 }
@@ -351,8 +363,12 @@ public class HTTPClient {
351363 enum Preference {
352364 /// Event Loop will be selected by the library.
353365 case indifferent
354- /// Library will try to use provided event loop if possible.
355- case prefers( EventLoop )
366+ /// The delegate will be run on the specified EventLoop (and the Channel if possible).
367+ case delegate( on: EventLoop )
368+ /// The delegate and the `Channel` will be run on the specified EventLoop.
369+ case delegateAndChannel( on: EventLoop )
370+
371+ case testOnly_exact( channelOn: EventLoop , delegateOn: EventLoop )
356372 }
357373
358374 var preference : Preference
@@ -363,9 +379,28 @@ public class HTTPClient {
363379
364380 /// Event Loop will be selected by the library.
365381 public static let indifferent = EventLoopPreference ( . indifferent)
382+
366383 /// Library will try to use provided event loop if possible.
384+ @available ( * , deprecated, renamed: " delegate(on:) " )
367385 public static func prefers( _ eventLoop: EventLoop ) -> EventLoopPreference {
368- return EventLoopPreference ( . prefers( eventLoop) )
386+ return EventLoopPreference ( . delegate( on: eventLoop) )
387+ }
388+
389+ /// The delegate will be run on the specified EventLoop (and the Channel if possible).
390+ ///
391+ /// This will call the configured delegate on `eventLoop` and will try to use a `Channel` on the same
392+ /// `EventLoop` but will not establish a new network connection just to satisfy the `EventLoop` preference if
393+ /// another existing connection on a different `EventLoop` is readily available from a connection pool.
394+ public static func delegate( on eventLoop: EventLoop ) -> EventLoopPreference {
395+ return EventLoopPreference ( . delegate( on: eventLoop) )
396+ }
397+
398+ /// The delegate and the `Channel` will be run on the specified EventLoop.
399+ ///
400+ /// Use this for use-cases where you prefer a new connection to be established over re-using an existing
401+ /// connection that might be on a different `EventLoop`.
402+ public static func delegateAndChannel( on eventLoop: EventLoop ) -> EventLoopPreference {
403+ return EventLoopPreference ( . delegateAndChannel( on: eventLoop) )
369404 }
370405 }
371406}
0 commit comments