Skip to content

Commit 4d588c8

Browse files
committed
a set of extra test cases
Motivation: More unit tests are good and now that SwiftNIO shipd `NIOHTTP1TestServer`, writing integration tests for async-http-client is also more straightforward. Modification: Demonstrate some tests using NIOHTTP1TestServer. Result: More tests.
1 parent 018e139 commit 4d588c8

File tree

3 files changed

+188
-4
lines changed

3 files changed

+188
-4
lines changed

Diff for: Package.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@ let package = Package(
2121
.library(name: "AsyncHTTPClient", targets: ["AsyncHTTPClient"]),
2222
],
2323
dependencies: [
24-
.package(url: "https://github.com/apple/swift-nio.git", from: "2.8.0"),
24+
.package(url: "https://github.com/apple/swift-nio.git", from: "2.10.1"),
2525
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.0.0"),
2626
.package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.3.0"),
2727
],
2828
targets: [
2929
.target(
3030
name: "AsyncHTTPClient",
31-
dependencies: ["NIO", "NIOHTTP1", "NIOSSL", "NIOConcurrencyHelpers", "NIOHTTPCompression"]
31+
dependencies: ["NIO", "NIOHTTP1", "NIOSSL", "NIOConcurrencyHelpers", "NIOHTTPCompression", "NIOFoundationCompat"]
3232
),
3333
.testTarget(
3434
name: "AsyncHTTPClientTests",
35-
dependencies: ["NIO", "NIOConcurrencyHelpers", "NIOSSL", "AsyncHTTPClient", "NIOFoundationCompat"]
35+
dependencies: ["NIO", "NIOConcurrencyHelpers", "NIOSSL", "AsyncHTTPClient", "NIOFoundationCompat", "NIOTestUtils"]
3636
),
3737
]
3838
)

Diff for: Sources/AsyncHTTPClient/HTTPHandler.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import Foundation
1616
import NIO
1717
import NIOConcurrencyHelpers
18+
import NIOFoundationCompat
1819
import NIOHTTP1
1920
import NIOSSL
2021

@@ -758,11 +759,12 @@ extension TaskHandler: ChannelDuplexHandler {
758759
switch self.state {
759760
case .end:
760761
break
761-
default:
762+
case .body, .head, .idle, .redirected, .sent:
762763
self.state = .end
763764
let error = HTTPClientError.remoteConnectionClosed
764765
self.failTaskAndNotifyDelegate(error: error, self.delegate.didReceiveError)
765766
}
767+
context.fireChannelInactive()
766768
}
767769

768770
func errorCaught(context: ChannelHandlerContext, error: Error) {

Diff for: Tests/AsyncHTTPClientTests/HTTPClientTests.swift

+182
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,26 @@ import NIO
1717
import NIOFoundationCompat
1818
import NIOHTTP1
1919
import NIOHTTPCompression
20+
import NIOTestUtils
2021
import NIOSSL
2122
import XCTest
2223

2324
class HTTPClientTests: XCTestCase {
2425
typealias Request = HTTPClient.Request
2526

27+
var group: EventLoopGroup! = nil
28+
29+
override func setUp() {
30+
XCTAssertNil(self.group)
31+
self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
32+
}
33+
34+
override func tearDown() {
35+
XCTAssertNotNil(self.group)
36+
XCTAssertNoThrow(try self.group.syncShutdownGracefully())
37+
self.group = nil
38+
}
39+
2640
func testRequestURI() throws {
2741
let request1 = try Request(url: "https://someserver.com:8888/some/path?foo=bar")
2842
XCTAssertEqual(request1.url.host, "someserver.com")
@@ -658,4 +672,172 @@ class HTTPClientTests: XCTestCase {
658672
XCTAssertEqual(error as! HTTPClientError, HTTPClientError.redirectLimitReached)
659673
}
660674
}
675+
676+
func testWorksWith500Error() {
677+
let web = NIOHTTP1TestServer(group: self.group)
678+
defer {
679+
XCTAssertNoThrow(try web.stop())
680+
}
681+
682+
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.group))
683+
defer {
684+
XCTAssertNoThrow(try httpClient.syncShutdown())
685+
}
686+
let result = httpClient.get(url: "http://localhost:\(web.serverPort)/foo")
687+
688+
XCTAssertNoThrow(XCTAssertEqual(.head(.init(version: .init(major: 1, minor: 1),
689+
method: .GET,
690+
uri: "/foo",
691+
headers: HTTPHeaders([("Host", "localhost"),
692+
// The following line can be removed once we
693+
// have a connection pool.
694+
("Connection", "close"),
695+
("Content-Length", "0")]))),
696+
try web.readInbound()))
697+
XCTAssertNoThrow(XCTAssertEqual(.end(nil),
698+
try web.readInbound()))
699+
XCTAssertNoThrow(try web.writeOutbound(.head(.init(version: .init(major: 1, minor: 1),
700+
status: .internalServerError))))
701+
XCTAssertNoThrow(try web.writeOutbound(.end(nil)))
702+
703+
var response: HTTPClient.Response?
704+
XCTAssertNoThrow(response = try result.wait())
705+
XCTAssertEqual(.internalServerError, response?.status)
706+
XCTAssertNil(response?.body)
707+
}
708+
709+
func testWorksWithHTTP10Response() {
710+
let web = NIOHTTP1TestServer(group: self.group)
711+
defer {
712+
XCTAssertNoThrow(try web.stop())
713+
}
714+
715+
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.group))
716+
defer {
717+
XCTAssertNoThrow(try httpClient.syncShutdown())
718+
}
719+
let result = httpClient.get(url: "http://localhost:\(web.serverPort)/foo")
720+
721+
XCTAssertNoThrow(XCTAssertEqual(.head(.init(version: .init(major: 1, minor: 1),
722+
method: .GET,
723+
uri: "/foo",
724+
headers: HTTPHeaders([("Host", "localhost"),
725+
// The following line can be removed once we
726+
// have a connection pool.
727+
("Connection", "close"),
728+
("Content-Length", "0")]))),
729+
try web.readInbound()))
730+
XCTAssertNoThrow(XCTAssertEqual(.end(nil),
731+
try web.readInbound()))
732+
XCTAssertNoThrow(try web.writeOutbound(.head(.init(version: .init(major: 1, minor: 0),
733+
status: .internalServerError))))
734+
XCTAssertNoThrow(try web.writeOutbound(.end(nil)))
735+
736+
var response: HTTPClient.Response?
737+
XCTAssertNoThrow(response = try result.wait())
738+
XCTAssertEqual(.internalServerError, response?.status)
739+
XCTAssertNil(response?.body)
740+
}
741+
742+
func testWorksWhenServerClosesConnectionAfterReceivingRequest() {
743+
let web = NIOHTTP1TestServer(group: self.group)
744+
745+
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.group))
746+
defer {
747+
XCTAssertNoThrow(try httpClient.syncShutdown())
748+
}
749+
let result = httpClient.get(url: "http://localhost:\(web.serverPort)/foo")
750+
751+
XCTAssertNoThrow(XCTAssertEqual(.head(.init(version: .init(major: 1, minor: 1),
752+
method: .GET,
753+
uri: "/foo",
754+
headers: HTTPHeaders([("Host", "localhost"),
755+
// The following line can be removed once we
756+
// have a connection pool.
757+
("Connection", "close"),
758+
("Content-Length", "0")]))),
759+
try web.readInbound()))
760+
XCTAssertNoThrow(XCTAssertEqual(.end(nil),
761+
try web.readInbound()))
762+
XCTAssertNoThrow(try web.stop())
763+
764+
XCTAssertThrowsError(try result.wait()) { error in
765+
XCTAssertEqual(HTTPClientError.remoteConnectionClosed, error as? HTTPClientError)
766+
}
767+
}
768+
769+
func testSubsequentRequestsWorkWithServerSendingConnectionClose() {
770+
let web = NIOHTTP1TestServer(group: self.group)
771+
defer {
772+
XCTAssertNoThrow(try web.stop())
773+
}
774+
775+
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.group))
776+
defer {
777+
XCTAssertNoThrow(try httpClient.syncShutdown())
778+
}
779+
780+
for _ in 0..<10 {
781+
let result = httpClient.get(url: "http://localhost:\(web.serverPort)/foo")
782+
783+
XCTAssertNoThrow(XCTAssertEqual(.head(.init(version: .init(major: 1, minor: 1),
784+
method: .GET,
785+
uri: "/foo",
786+
headers: HTTPHeaders([("Host", "localhost"),
787+
// The following line can be removed once
788+
// we have a connection pool.
789+
("Connection", "close"),
790+
("Content-Length", "0")]))),
791+
try web.readInbound()))
792+
XCTAssertNoThrow(XCTAssertEqual(.end(nil),
793+
try web.readInbound()))
794+
XCTAssertNoThrow(try web.writeOutbound(.head(.init(version: .init(major: 1, minor: 0),
795+
status: .ok,
796+
headers: HTTPHeaders([("connection", "close")])))))
797+
XCTAssertNoThrow(try web.writeOutbound(.end(nil)))
798+
799+
var response: HTTPClient.Response?
800+
XCTAssertNoThrow(response = try result.wait())
801+
XCTAssertEqual(.ok, response?.status)
802+
XCTAssertNil(response?.body)
803+
}
804+
}
805+
806+
func testSubsequentRequestsWorkWithServerAlternatingBetweenKeepAliveAndClose() {
807+
let web = NIOHTTP1TestServer(group: self.group)
808+
defer {
809+
XCTAssertNoThrow(try web.stop())
810+
}
811+
812+
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.group))
813+
defer {
814+
XCTAssertNoThrow(try httpClient.syncShutdown())
815+
}
816+
817+
for i in 0..<10 {
818+
let result = httpClient.get(url: "http://localhost:\(web.serverPort)/foo")
819+
820+
XCTAssertNoThrow(XCTAssertEqual(.head(.init(version: .init(major: 1, minor: 1),
821+
method: .GET,
822+
uri: "/foo",
823+
headers: HTTPHeaders([("Host", "localhost"),
824+
// The following line can be removed once
825+
// we have a connection pool.
826+
("Connection", "close"),
827+
("Content-Length", "0")]))),
828+
try web.readInbound()))
829+
XCTAssertNoThrow(XCTAssertEqual(.end(nil),
830+
try web.readInbound()))
831+
XCTAssertNoThrow(try web.writeOutbound(.head(.init(version: .init(major: 1, minor: 0),
832+
status: .ok,
833+
headers: HTTPHeaders([("connection",
834+
i % 2 == 0 ? "close" : "keep-alive")])))))
835+
XCTAssertNoThrow(try web.writeOutbound(.end(nil)))
836+
837+
var response: HTTPClient.Response?
838+
XCTAssertNoThrow(response = try result.wait())
839+
XCTAssertEqual(.ok, response?.status)
840+
XCTAssertNil(response?.body)
841+
}
842+
}
661843
}

0 commit comments

Comments
 (0)