Skip to content

Commit fb105d0

Browse files
Merge pull request #4 from GNMoseke/feature/eventloop-auth
Update auth to allow eventloopfuture resolution, explicit eventloop initialization
2 parents e82bf7f + 2d6dfa8 commit fb105d0

File tree

2 files changed

+52
-13
lines changed

2 files changed

+52
-13
lines changed

Sources/GraphQLWS/Server.swift

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ public class Server<InitPayload: Equatable & Codable> {
1313

1414
let onExecute: (GraphQLRequest) -> EventLoopFuture<GraphQLResult>
1515
let onSubscribe: (GraphQLRequest) -> EventLoopFuture<SubscriptionResult>
16+
var auth: (InitPayload) throws -> EventLoopFuture<Void>
1617

17-
var auth: (InitPayload) throws -> Void = { _ in }
1818
var onExit: () -> Void = { }
1919
var onMessage: (String) -> Void = { _ in }
2020
var onOperationComplete: (String) -> Void = { _ in }
@@ -32,14 +32,17 @@ public class Server<InitPayload: Equatable & Codable> {
3232
/// - messenger: The messenger to bind the server to.
3333
/// - onExecute: Callback run during `start` resolution for non-streaming queries. Typically this is `API.execute`.
3434
/// - onSubscribe: Callback run during `start` resolution for streaming queries. Typically this is `API.subscribe`.
35+
/// - eventLoop: EventLoop on which to perform server operations.
3536
public init(
3637
messenger: Messenger,
3738
onExecute: @escaping (GraphQLRequest) -> EventLoopFuture<GraphQLResult>,
38-
onSubscribe: @escaping (GraphQLRequest) -> EventLoopFuture<SubscriptionResult>
39+
onSubscribe: @escaping (GraphQLRequest) -> EventLoopFuture<SubscriptionResult>,
40+
eventLoop: EventLoop
3941
) {
4042
self.messenger = messenger
4143
self.onExecute = onExecute
4244
self.onSubscribe = onSubscribe
45+
self.auth = { _ in eventLoop.makeSucceededVoidFuture() }
4346

4447
messenger.onReceive { message in
4548
guard let messenger = self.messenger else { return }
@@ -98,10 +101,10 @@ public class Server<InitPayload: Equatable & Codable> {
98101
}
99102
}
100103

101-
/// Define the callback run during `connection_init` resolution that allows authorization using the `payload`.
102-
/// Throw from this closure to indicate that authorization has failed.
104+
/// Define a custom callback run during `connection_init` resolution that allows authorization using the `payload`.
105+
/// Throw or fail the future from this closure to indicate that authorization has failed.
103106
/// - Parameter callback: The callback to assign
104-
public func auth(_ callback: @escaping (InitPayload) throws -> Void) {
107+
public func auth(_ callback: @escaping (InitPayload) throws -> EventLoopFuture<Void>) {
105108
self.auth = callback
106109
}
107110

@@ -136,14 +139,20 @@ public class Server<InitPayload: Equatable & Codable> {
136139
}
137140

138141
do {
139-
try self.auth(connectionInitRequest.payload)
142+
let authResult = try self.auth(connectionInitRequest.payload)
143+
authResult.whenSuccess {
144+
self.initialized = true
145+
self.sendConnectionAck()
146+
}
147+
authResult.whenFailure { error in
148+
self.error(.unauthorized())
149+
return
150+
}
140151
}
141152
catch {
142153
self.error(.unauthorized())
143154
return
144155
}
145-
initialized = true
146-
self.sendConnectionAck()
147156
// TODO: Should we send the `ka` message?
148157
}
149158

Tests/GraphQLWSTests/GraphQLWSTests.swift

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class GraphqlWsTests: XCTestCase {
1010
var clientMessenger: TestMessenger!
1111
var serverMessenger: TestMessenger!
1212
var server: Server<TokenInitPayload>!
13+
var eventLoop: EventLoop!
1314

1415
override func setUp() {
1516
// Point the client and server at each other
@@ -18,7 +19,7 @@ class GraphqlWsTests: XCTestCase {
1819
clientMessenger.other = serverMessenger
1920
serverMessenger.other = clientMessenger
2021

21-
let eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: 1).next()
22+
eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: 1).next()
2223
let api = TestAPI()
2324
let context = TestContext()
2425

@@ -28,16 +29,17 @@ class GraphqlWsTests: XCTestCase {
2829
api.execute(
2930
request: graphQLRequest.query,
3031
context: context,
31-
on: eventLoop
32+
on: self.eventLoop
3233
)
3334
},
3435
onSubscribe: { graphQLRequest in
3536
api.subscribe(
3637
request: graphQLRequest.query,
3738
context: context,
38-
on: eventLoop
39+
on: self.eventLoop
3940
)
40-
}
41+
},
42+
eventLoop: self.eventLoop
4143
)
4244
}
4345

@@ -71,7 +73,7 @@ class GraphqlWsTests: XCTestCase {
7173
}
7274

7375
/// Tests that throwing in the authorization callback forces an unauthorized error
74-
func testAuth() throws {
76+
func testAuthWithThrow() throws {
7577
server.auth { payload in
7678
throw TestError.couldBeAnything
7779
}
@@ -98,6 +100,34 @@ class GraphqlWsTests: XCTestCase {
98100
)
99101
}
100102

103+
/// Tests that failing a future in the authorization callback forces an unauthorized error
104+
func testAuthWithFailedFuture() throws {
105+
server.auth { payload in
106+
self.eventLoop.makeFailedFuture(TestError.couldBeAnything)
107+
}
108+
109+
var messages = [String]()
110+
let completeExpectation = XCTestExpectation()
111+
112+
let client = Client<TokenInitPayload>(messenger: clientMessenger)
113+
client.onMessage { message, _ in
114+
messages.append(message)
115+
completeExpectation.fulfill()
116+
}
117+
118+
client.sendConnectionInit(
119+
payload: TokenInitPayload(
120+
authToken: ""
121+
)
122+
)
123+
124+
wait(for: [completeExpectation], timeout: 2)
125+
XCTAssertEqual(
126+
messages,
127+
["\(ErrorCode.unauthorized): Unauthorized"]
128+
)
129+
}
130+
101131
/// Test single op message flow works as expected
102132
func testSingleOp() throws {
103133
let id = UUID().description

0 commit comments

Comments
 (0)