From c2d5610279fc5934ccdd82ff4d6b67054fb98468 Mon Sep 17 00:00:00 2001 From: Tobias Haeberle Date: Thu, 13 Dec 2018 08:02:35 +0100 Subject: [PATCH 1/3] adds test for pipelined requests --- Tests/HTTPTests/HTTPTests.swift | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Tests/HTTPTests/HTTPTests.swift b/Tests/HTTPTests/HTTPTests.swift index 9077d0af..1959d9a0 100644 --- a/Tests/HTTPTests/HTTPTests.swift +++ b/Tests/HTTPTests/HTTPTests.swift @@ -103,6 +103,61 @@ class HTTPTests: XCTestCase { XCTAssert(error is ChannelError) } } + + func testPipelining() throws { + struct SlowResponder: HTTPServerResponder { + func respond(to request: HTTPRequest, on worker: Worker) -> EventLoopFuture { + // the first request takes the longest + // in other words: the server will respond in reverse order + let timeout : Int = 5 - Int(request.url.lastPathComponent)! + let scheduled = worker.eventLoop.scheduleTask(in: .milliseconds(timeout * 50)) { () -> HTTPResponse in + let res = HTTPResponse( + status: .ok, + body: request.url.lastPathComponent + ) + return res + } + + return scheduled.futureResult + } + } + let worker = MultiThreadedEventLoopGroup(numberOfThreads: 1) + let server = try HTTPServer.start( + hostname: "localhost", + port: 8080, + responder: SlowResponder(), + supportPipelining: true, + on: worker + ) { error in + XCTFail("\(error)") + }.wait() + + let client = try HTTPClient.connect(hostname: "localhost", port: 8080, on: worker).wait() + + var responses = [String]() + var futures : [Future<()>] = [] + + for i in 0..<5 { + var req = HTTPRequest(method: .GET, url: "/\(i)") + req.headers.replaceOrAdd(name: .connection, value: "keep-alive") + let resFuture = client.send(req).map({ res in + let body = String(data: res.body.data!, encoding: .utf8)! + responses.append(body) + }) + futures.append(resFuture) + } + + assert(futures.count == 5) + + try Future<()>.andAll(futures, eventLoop: worker.eventLoop).wait() + + XCTAssertEqual([ "0", "1", "2", "3", "4" ], responses) + + try server.close().wait() + try server.onClose.wait() + } + + static let allTests = [ ("testCookieParse", testCookieParse), @@ -111,5 +166,6 @@ class HTTPTests: XCTestCase { ("testCookieIsSerializedCorrectly", testCookieIsSerializedCorrectly), ("testLargeResponseClose", testLargeResponseClose), ("testUpgradeFail", testUpgradeFail), + ("testPipelining", testPipelining), ] } From 61f1ab930916ebb31052455d57ac1f7981a9593f Mon Sep 17 00:00:00 2001 From: Tobias Haeberle Date: Thu, 13 Dec 2018 09:17:42 +0100 Subject: [PATCH 2/3] improves pipelining test by using two event loops and adds a failing (assertion failure) test --- Tests/HTTPTests/HTTPTests.swift | 64 ++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/Tests/HTTPTests/HTTPTests.swift b/Tests/HTTPTests/HTTPTests.swift index 1959d9a0..7c246f2b 100644 --- a/Tests/HTTPTests/HTTPTests.swift +++ b/Tests/HTTPTests/HTTPTests.swift @@ -121,24 +121,75 @@ class HTTPTests: XCTestCase { return scheduled.futureResult } } - let worker = MultiThreadedEventLoopGroup(numberOfThreads: 1) + let serverWorker = MultiThreadedEventLoopGroup(numberOfThreads: 1) + let clientWorker = MultiThreadedEventLoopGroup(numberOfThreads: 1) let server = try HTTPServer.start( hostname: "localhost", port: 8080, responder: SlowResponder(), supportPipelining: true, - on: worker + on: serverWorker ) { error in XCTFail("\(error)") }.wait() - let client = try HTTPClient.connect(hostname: "localhost", port: 8080, on: worker).wait() + let client = try HTTPClient.connect(hostname: "localhost", port: 8080, on: clientWorker).wait() + + var responses = [String]() + var futures : [Future<()>] = [] + + for i in 0..<5 { + var req = HTTPRequest(method: .GET, url: "/hello/\(i)") + req.headers.replaceOrAdd(name: .connection, value: "keep-alive") + let resFuture = client.send(req).map({ res in + let body = String(data: res.body.data!, encoding: .utf8)! + responses.append(body) + }) + futures.append(resFuture) + } + + assert(futures.count == 5) + + try Future<()>.andAll(futures, eventLoop: serverWorker.eventLoop).wait() + + XCTAssertEqual([ "0", "1", "2", "3", "4" ], responses) + + try server.close().wait() + try server.onClose.wait() + + try serverWorker.syncShutdownGracefully() + try clientWorker.syncShutdownGracefully() + } + + func testPipeliningWithoutDelay() throws { + struct FastResponder: HTTPServerResponder { + func respond(to request: HTTPRequest, on worker: Worker) -> EventLoopFuture { + // the server responds immediately + return worker.eventLoop.newSucceededFuture(result: HTTPResponse(status: .ok, + body: request.url.lastPathComponent)) + } + } + + let serverWorker = MultiThreadedEventLoopGroup(numberOfThreads: 1) + let clientWorker = MultiThreadedEventLoopGroup(numberOfThreads: 1) + + let server = try HTTPServer.start( + hostname: "localhost", + port: 8080, + responder: FastResponder(), + supportPipelining: true, + on: serverWorker + ) { error in + XCTFail("\(error)") + }.wait() + + let client = try HTTPClient.connect(hostname: "localhost", port: 8080, on: clientWorker).wait() var responses = [String]() var futures : [Future<()>] = [] for i in 0..<5 { - var req = HTTPRequest(method: .GET, url: "/\(i)") + var req = HTTPRequest(method: .GET, url: "/hello/\(i)") req.headers.replaceOrAdd(name: .connection, value: "keep-alive") let resFuture = client.send(req).map({ res in let body = String(data: res.body.data!, encoding: .utf8)! @@ -149,12 +200,15 @@ class HTTPTests: XCTestCase { assert(futures.count == 5) - try Future<()>.andAll(futures, eventLoop: worker.eventLoop).wait() + try Future<()>.andAll(futures, eventLoop: serverWorker.eventLoop).wait() XCTAssertEqual([ "0", "1", "2", "3", "4" ], responses) try server.close().wait() try server.onClose.wait() + + try serverWorker.syncShutdownGracefully() + try clientWorker.syncShutdownGracefully() } From 0359fdb6043737439e7baa338aa1c4582548781f Mon Sep 17 00:00:00 2001 From: Tobias Haeberle Date: Thu, 13 Dec 2018 09:21:31 +0100 Subject: [PATCH 3/3] enable the test... --- Tests/HTTPTests/HTTPTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/HTTPTests/HTTPTests.swift b/Tests/HTTPTests/HTTPTests.swift index 7c246f2b..a5d67d15 100644 --- a/Tests/HTTPTests/HTTPTests.swift +++ b/Tests/HTTPTests/HTTPTests.swift @@ -221,5 +221,6 @@ class HTTPTests: XCTestCase { ("testLargeResponseClose", testLargeResponseClose), ("testUpgradeFail", testUpgradeFail), ("testPipelining", testPipelining), + ("testPipeliningWithoutDelay", testPipeliningWithoutDelay), ] }