Skip to content

Commit 7b1cc20

Browse files
authored
Merge pull request #2968 from spevans/pr_test_httpserver_fixes1
Test HTTPServer: Minor improvements and fix for errors on Ubuntu20.04
2 parents 1fbd1a3 + 5f4d143 commit 7b1cc20

File tree

2 files changed

+84
-82
lines changed

2 files changed

+84
-82
lines changed

Tests/Foundation/FTPServer.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ import Dispatch
1717
import Darwin
1818
#endif
1919

20+
public class ServerSemaphore {
21+
let dispatchSemaphore = DispatchSemaphore(value: 0)
22+
23+
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult {
24+
return dispatchSemaphore.wait(timeout: timeout)
25+
}
26+
27+
public func signal() {
28+
dispatchSemaphore.signal()
29+
}
30+
}
2031

2132
class _FTPSocket {
2233

Tests/Foundation/HTTPServer.swift

Lines changed: 73 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ import Dispatch
2727
typealias SOCKET = Int32
2828
#endif
2929

30+
private let serverDebug = (ProcessInfo.processInfo.environment["SCLF_HTTP_SERVER_DEBUG"] == "YES")
31+
32+
private func debugLog(_ msg: String) {
33+
if serverDebug {
34+
NSLog(msg)
35+
}
36+
}
3037

3138
public let globalDispatchQueue = DispatchQueue.global()
3239
public let dispatchQueueMake: (String) -> DispatchQueue = { DispatchQueue.init(label: $0) }
@@ -47,7 +54,7 @@ extension UInt16 {
4754
}
4855

4956
// _TCPSocket wraps one socket that is used either to listen()/accept() new connections, or for the client connection itself.
50-
class _TCPSocket {
57+
class _TCPSocket: CustomStringConvertible {
5158
#if !os(Windows)
5259
#if os(Linux) || os(Android) || os(FreeBSD)
5360
private let sendFlags = CInt(MSG_NOSIGNAL)
@@ -56,6 +63,10 @@ class _TCPSocket {
5663
#endif
5764
#endif
5865

66+
var description: String {
67+
return "_TCPSocket @ 0x" + String(unsafeBitCast(self, to: UInt.self), radix: 16)
68+
}
69+
5970
let listening: Bool
6071
private var _socket: SOCKET!
6172
private var socketAddress = UnsafeMutablePointer<sockaddr_in>.allocate(capacity: 1)
@@ -139,7 +150,7 @@ class _TCPSocket {
139150
#endif
140151
}
141152

142-
func acceptConnection(notify: ServerSemaphore) throws -> _TCPSocket {
153+
func acceptConnection() throws -> _TCPSocket {
143154
guard listening else { fatalError("Trying to listen on a client connection socket") }
144155
let connection: SOCKET = try socketAddress.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout<sockaddr>.size, {
145156
let addr = UnsafeMutablePointer<sockaddr>($0)
@@ -157,6 +168,7 @@ class _TCPSocket {
157168
throw ServerError.init(operation: "setsockopt", errno: errno, file: #file, line: #line)
158169
}
159170
#endif
171+
debugLog("\(self) acceptConnection: accepted: \(connectionSocket)")
160172
return connectionSocket
161173
})
162174
return _TCPSocket(socket: connection)
@@ -187,7 +199,6 @@ class _TCPSocket {
187199
guard let connectionSocket = _socket else {
188200
throw InternalServerError.socketAlreadyClosed
189201
}
190-
191202
#if os(Windows)
192203
_ = try data.withUnsafeBytes {
193204
var dwNumberOfBytesSent: DWORD = 0
@@ -196,47 +207,16 @@ class _TCPSocket {
196207
}
197208
#else
198209
_ = try data.withUnsafeBytes { ptr in
199-
try attempt("send", valid: isNotNegative, CInt(send(connectionSocket, ptr.baseAddress!, data.count, sendFlags)))
200-
}
201-
#endif
202-
}
203-
204-
private func _send(_ bytes: [UInt8]) throws -> Int {
205-
guard let connectionSocket = _socket else {
206-
throw InternalServerError.socketAlreadyClosed
207-
}
208-
209-
#if os(Windows)
210-
return try bytes.withUnsafeBytes {
211-
var dwNumberOfBytesSent: DWORD = 0
212-
var wsaBuffer: WSABUF = WSABUF(len: ULONG(bytes.count), buf: UnsafeMutablePointer<CHAR>(mutating: $0.bindMemory(to: CHAR.self).baseAddress))
213-
return try Int(attempt("WSASend", valid: { $0 != SOCKET_ERROR }, WSASend(connectionSocket, &wsaBuffer, 1, &dwNumberOfBytesSent, 0, nil, nil)))
214-
}
215-
#else
216-
return try bytes.withUnsafeBufferPointer {
217-
try attempt("send", valid: { $0 >= 0 }, send(connectionSocket, $0.baseAddress, $0.count, sendFlags))
210+
try attempt("send", valid: { $0 == data.count }, CInt(send(connectionSocket, ptr.baseAddress!, data.count, sendFlags)))
218211
}
219212
#endif
213+
debugLog("wrote \(data.count) bytes")
220214
}
221215

222-
func writeData(header: String, bodyData: Data, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
223-
_ = try _send(Array(header.utf8))
224-
225-
if let sendDelay = sendDelay, let bodyChunks = bodyChunks {
226-
let count = max(1, Int(Double(bodyData.count) / Double(bodyChunks)))
227-
for startIndex in stride(from: 0, to: bodyData.count, by: count) {
228-
Thread.sleep(forTimeInterval: sendDelay)
229-
let endIndex = min(startIndex + count, bodyData.count)
230-
try bodyData.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> Void in
231-
let chunk = UnsafeRawBufferPointer(rebasing: ptr[startIndex..<endIndex])
232-
_ = try _send(Array(chunk.bindMemory(to: UInt8.self)))
233-
}
234-
}
235-
} else {
236-
try bodyData.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> Void in
237-
_ = try _send(Array(ptr.bindMemory(to: UInt8.self)))
238-
}
239-
}
216+
func writeData(header: String, bodyData: Data) throws {
217+
var totalData = Data(header.utf8)
218+
totalData.append(bodyData)
219+
try writeRawData(totalData)
240220
}
241221

242222
func closeSocket() throws {
@@ -252,11 +232,17 @@ class _TCPSocket {
252232
}
253233

254234
deinit {
235+
debugLog("\(self) closing socket")
255236
try? closeSocket()
256237
}
257238
}
258239

259-
class _HTTPServer {
240+
241+
class _HTTPServer: CustomStringConvertible {
242+
243+
var description: String {
244+
return "_HTTPServer @ 0x" + String(unsafeBitCast(self, to: UInt.self), radix: 16)
245+
}
260246

261247
// Provide Data() blocks from the socket either separated by a given separator or of a requested block size.
262248
struct _SocketDataReader {
@@ -271,6 +257,7 @@ class _HTTPServer {
271257
var range = buffer.range(of: separatorData)
272258
while range == nil {
273259
guard let data = try tcpSocket.readData() else { break }
260+
debugLog("read \(data.count) bytes")
274261
buffer.append(data)
275262
range = buffer.range(of: separatorData)
276263
}
@@ -284,6 +271,7 @@ class _HTTPServer {
284271
mutating func readBytes(count: Int) throws -> Data {
285272
while buffer.count < count {
286273
guard let data = try tcpSocket.readData() else { break }
274+
debugLog("read \(data.count) bytes")
287275
buffer.append(data)
288276
}
289277
guard buffer.count >= count else {
@@ -296,6 +284,10 @@ class _HTTPServer {
296284
}
297285
}
298286

287+
deinit {
288+
debugLog("_HTTPServer \(self) stopping")
289+
}
290+
299291
let tcpSocket: _TCPSocket
300292
var port: UInt16 { tcpSocket.port }
301293

@@ -311,8 +303,9 @@ class _HTTPServer {
311303
return try _HTTPServer(port: port)
312304
}
313305

314-
public func listen(notify: ServerSemaphore) throws -> _HTTPServer {
315-
let connection = try tcpSocket.acceptConnection(notify: notify)
306+
public func listen() throws -> _HTTPServer {
307+
let connection = try tcpSocket.acceptConnection()
308+
debugLog("\(self) accepted: \(connection)")
316309
return _HTTPServer(socket: connection)
317310
}
318311

@@ -381,14 +374,8 @@ class _HTTPServer {
381374
return request
382375
}
383376

384-
public func respond(with response: _HTTPResponse, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
385-
if let delay = startDelay {
386-
Thread.sleep(forTimeInterval: delay)
387-
}
388-
do {
389-
try tcpSocket.writeData(header: response.header, bodyData: response.bodyData, sendDelay: sendDelay, bodyChunks: bodyChunks)
390-
} catch {
391-
}
377+
public func respond(with response: _HTTPResponse) throws {
378+
try tcpSocket.writeData(header: response.header, bodyData: response.bodyData)
392379
}
393380

394381
func respondWithBrokenResponses(uri: String) throws {
@@ -490,7 +477,7 @@ class _HTTPServer {
490477
}
491478
}
492479

493-
struct _HTTPRequest {
480+
struct _HTTPRequest: CustomStringConvertible {
494481
enum Method : String {
495482
case HEAD
496483
case GET
@@ -511,6 +498,9 @@ struct _HTTPRequest {
511498
private(set) var parameters: [String: String] = [:]
512499
var messageBody: String?
513500
var messageData: Data?
501+
var description: String {
502+
return "\(method.rawValue) \(uri)"
503+
}
514504

515505

516506
public init(header: String) throws {
@@ -644,7 +634,12 @@ struct _HTTPResponse {
644634
}
645635
}
646636

647-
public class TestURLSessionServer {
637+
public class TestURLSessionServer: CustomStringConvertible {
638+
639+
public var description: String {
640+
return "TestURLSessionServer @ 0x" + String(unsafeBitCast(self, to: UInt.self), radix: 16)
641+
}
642+
648643
let capitals: [String:String] = ["Nepal": "Kathmandu",
649644
"Peru": "Lima",
650645
"Italy": "Rome",
@@ -654,19 +649,15 @@ public class TestURLSessionServer {
654649
"UK": "London",
655650
"country.txt": "A country is a region that is identified as a distinct national entity in political geography"]
656651
let httpServer: _HTTPServer
657-
let startDelay: TimeInterval?
658-
let sendDelay: TimeInterval?
659-
let bodyChunks: Int?
660652

661653
internal init(httpServer: _HTTPServer) {
662654
self.httpServer = httpServer
663-
self.startDelay = nil
664-
self.sendDelay = nil
665-
self.bodyChunks = nil
655+
debugLog("\(self) - server \(httpServer)")
666656
}
667657

668658
public func readAndRespond() throws {
669659
let req = try httpServer.request()
660+
debugLog("request: \(req)")
670661
if let value = req.getHeader(for: "x-pause") {
671662
if let wait = Double(value), wait > 0 {
672663
Thread.sleep(forTimeInterval: wait)
@@ -691,7 +682,9 @@ public class TestURLSessionServer {
691682
} else if req.uri.hasPrefix("/unauthorized") {
692683
try httpServer.respondWithUnauthorizedHeader()
693684
} else {
694-
try httpServer.respond(with: getResponse(request: req))
685+
let response = try getResponse(request: req)
686+
try httpServer.respond(with: response)
687+
debugLog("response: \(response)")
695688
}
696689
}
697690

@@ -918,23 +911,11 @@ enum InternalServerError : Error {
918911
case badBody
919912
}
920913

921-
public class ServerSemaphore {
922-
let dispatchSemaphore = DispatchSemaphore(value: 0)
923-
924-
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult {
925-
return dispatchSemaphore.wait(timeout: timeout)
926-
}
927-
928-
public func signal() {
929-
dispatchSemaphore.signal()
930-
}
931-
}
932914

933915
class LoopbackServerTest : XCTestCase {
934916
private static let staticSyncQ = DispatchQueue(label: "org.swift.TestFoundation.HTTPServer.StaticSyncQ")
935917

936918
private static var _serverPort: Int = -1
937-
private static let serverReady = ServerSemaphore()
938919
private static var _serverActive = false
939920
private static var testServer: _HTTPServer? = nil
940921

@@ -955,18 +936,26 @@ class LoopbackServerTest : XCTestCase {
955936

956937
override class func setUp() {
957938
super.setUp()
958-
func runServer(with condition: ServerSemaphore) throws {
939+
940+
var _serverPort = 0
941+
let dispatchGroup = DispatchGroup()
942+
943+
func runServer() throws {
959944
testServer = try _HTTPServer(port: nil)
960-
serverPort = Int(testServer!.port)
961-
serverReady.signal()
945+
_serverPort = Int(testServer!.port)
962946
serverActive = true
947+
dispatchGroup.leave()
963948

964949
while serverActive {
965950
do {
966-
let httpServer = try testServer!.listen(notify: condition)
951+
let httpServer = try testServer!.listen()
967952
globalDispatchQueue.async {
968953
let subServer = TestURLSessionServer(httpServer: httpServer)
969-
try? subServer.readAndRespond()
954+
do {
955+
try subServer.readAndRespond()
956+
} catch {
957+
NSLog("reandAndRespond: \(error)")
958+
}
970959
}
971960
} catch {
972961
if (serverActive) { // Ignore errors thrown on shutdown
@@ -977,20 +966,22 @@ class LoopbackServerTest : XCTestCase {
977966
serverPort = -2
978967
}
979968

969+
dispatchGroup.enter()
970+
980971
globalDispatchQueue.async {
981972
do {
982-
try runServer(with: serverReady)
973+
try runServer()
983974
} catch {
975+
NSLog("runServer: \(error)")
984976
}
985977
}
986978

987979
let timeout = DispatchTime(uptimeNanoseconds: DispatchTime.now().uptimeNanoseconds + 2_000_000_000)
988980

989-
while serverPort == -1 {
990-
guard serverReady.wait(timeout: timeout) == .success else {
991-
fatalError("Timedout waiting for server to be ready")
992-
}
981+
guard dispatchGroup.wait(timeout: timeout) == .success, _serverPort > 0 else {
982+
fatalError("Timedout waiting for server to be ready")
993983
}
984+
serverPort = _serverPort
994985
}
995986

996987
override class func tearDown() {

0 commit comments

Comments
 (0)