Skip to content

add NIO event loop as an argument for execute #79

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Aug 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions Sources/AsyncHTTPClient/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ public class HTTPClient {
return self.execute(request: request, delegate: accumulator, deadline: deadline).futureResult
}

/// Execute arbitrary HTTP request using specified URL.
///
/// - parameters:
/// - request: HTTP request to execute.
/// - eventLoop: NIO Event Loop preference.
/// - deadline: Point in time by which the request must complete.
public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response> {
let accumulator = ResponseAccumulator(request: request)
return self.execute(request: request, delegate: accumulator, eventLoop: eventLoop, deadline: deadline).futureResult
}

/// Execute arbitrary HTTP request and handle response processing using provided delegate.
///
/// - parameters:
Expand All @@ -180,7 +191,27 @@ public class HTTPClient {
/// - deadline: Point in time by which the request must complete.
public func execute<T: HTTPClientResponseDelegate>(request: Request, delegate: T, deadline: NIODeadline? = nil) -> Task<T.Response> {
let eventLoop = self.eventLoopGroup.next()
return self.execute(request: request, delegate: delegate, eventLoop: eventLoop, deadline: deadline)
}

/// Execute arbitrary HTTP request and handle response processing using provided delegate.
///
/// - parameters:
/// - request: HTTP request to execute.
/// - delegate: Delegate to process response parts.
/// - eventLoop: NIO Event Loop preference.
/// - deadline: Point in time by which the request must complete.
public func execute<T: HTTPClientResponseDelegate>(request: Request, delegate: T, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> Task<T.Response> {
switch eventLoop.preference {
case .indifferent:
return self.execute(request: request, delegate: delegate, eventLoop: self.eventLoopGroup.next(), deadline: deadline)
case .prefers(let preferred):
precondition(self.eventLoopGroup.makeIterator().contains { $0 === preferred }, "Provided EventLoop must be part of clients EventLoopGroup.")
return self.execute(request: request, delegate: delegate, eventLoop: preferred, deadline: deadline)
}
}

private func execute<T: HTTPClientResponseDelegate>(request: Request, delegate: T, eventLoop: EventLoop, deadline: NIODeadline? = nil) -> Task<T.Response> {
let redirectHandler: RedirectHandler<T.Response>?
if self.configuration.followRedirects {
redirectHandler = RedirectHandler<T.Response>(request: request) { newRequest in
Expand Down Expand Up @@ -312,6 +343,29 @@ public class HTTPClient {
case createNew
}

/// Specifies how the library will treat event loop passed by the user.
public struct EventLoopPreference {
enum Preference {
/// Event Loop will be selected by the library.
case indifferent
/// Library will try to use provided event loop if possible.
case prefers(EventLoop)
}

var preference: Preference

init(_ preference: Preference) {
self.preference = preference
}

/// Event Loop will be selected by the library.
public static let indifferent = EventLoopPreference(.indifferent)
/// Library will try to use provided event loop if possible.
public static func prefers(_ eventLoop: EventLoop) -> EventLoopPreference {
return EventLoopPreference(.prefers(eventLoop))
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preference only has one 'r'.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, fixed, thanks!


/// Timeout configuration
public struct Timeout {
/// Specifies connect timeout.
Expand Down
1 change: 1 addition & 0 deletions Tests/AsyncHTTPClientTests/HTTPClientTests+XCTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ extension HTTPClientTests {
("testNoResponseWithIgnoreErrorForSSLUncleanShutdown", testNoResponseWithIgnoreErrorForSSLUncleanShutdown),
("testWrongContentLengthForSSLUncleanShutdown", testWrongContentLengthForSSLUncleanShutdown),
("testWrongContentLengthWithIgnoreErrorForSSLUncleanShutdown", testWrongContentLengthWithIgnoreErrorForSSLUncleanShutdown),
("testEventLoopArgument", testEventLoopArgument),
]
}
}
37 changes: 37 additions & 0 deletions Tests/AsyncHTTPClientTests/HTTPClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -484,4 +484,41 @@ class HTTPClientTests: XCTestCase {
}
}
}

func testEventLoopArgument() throws {
let httpBin = HttpBin()
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup))
defer {
try! eventLoopGroup.syncShutdownGracefully()
httpBin.shutdown()
}

class EventLoopValidatingDelegate: HTTPClientResponseDelegate {
typealias Response = Bool

let eventLoop: EventLoop
var result = false

init(eventLoop: EventLoop) {
self.eventLoop = eventLoop
}

func didReceiveHead(task: HTTPClient.Task<Bool>, _ head: HTTPResponseHead) -> EventLoopFuture<Void> {
self.result = task.eventLoop === self.eventLoop
return task.eventLoop.makeSucceededFuture(())
}

func didFinishRequest(task: HTTPClient.Task<Bool>) throws -> Bool {
return self.result
}
}

let eventLoop = eventLoopGroup.next()
let delegate = EventLoopValidatingDelegate(eventLoop: eventLoop)
let request = try HTTPClient.Request(url: "http://localhost:\(httpBin.port)/get")
let response = try httpClient.execute(request: request, delegate: delegate, eventLoop: .prefers(eventLoop)).wait()

XCTAssertEqual(true, response)
}
}