Skip to content

Commit ace3990

Browse files
authored
Merge pull request #1185 from weissi/jw-test-http-server-auto-port
2 parents 9d4ac8d + ff9aff8 commit ace3990

File tree

1 file changed

+59
-23
lines changed

1 file changed

+59
-23
lines changed

TestFoundation/HTTPServer.swift

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ struct _HTTPUtils {
3737
static let EMPTY = ""
3838
}
3939

40+
extension UInt16 {
41+
public init(networkByteOrder input: UInt16) {
42+
self.init(bigEndian: input)
43+
}
44+
}
45+
4046
class _TCPSocket {
4147

4248
private var listenSocket: Int32!
@@ -57,12 +63,15 @@ class _TCPSocket {
5763
return r
5864
}
5965

60-
init(port: UInt16) throws {
66+
public private(set) var port: UInt16
67+
68+
init(port: UInt16?) throws {
6169
#if os(Linux)
6270
let SOCKSTREAM = Int32(SOCK_STREAM.rawValue)
6371
#else
6472
let SOCKSTREAM = SOCK_STREAM
6573
#endif
74+
self.port = port ?? 0
6675
listenSocket = try attempt("socket", valid: isNotNegative, socket(AF_INET, SOCKSTREAM, Int32(IPPROTO_TCP)))
6776
var on: Int = 1
6877
_ = try attempt("setsockopt", valid: isZero, setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &on, socklen_t(MemoryLayout<Int>.size)))
@@ -72,16 +81,26 @@ class _TCPSocket {
7281
let addr = UnsafePointer<sockaddr>($0)
7382
_ = try attempt("bind", valid: isZero, bind(listenSocket, addr, socklen_t(MemoryLayout<sockaddr>.size)))
7483
})
84+
85+
var actualSA = sockaddr_in()
86+
withUnsafeMutablePointer(to: &actualSA) { ptr in
87+
ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) { (ptr: UnsafeMutablePointer<sockaddr>) in
88+
var len = socklen_t(MemoryLayout<sockaddr>.size)
89+
getsockname(listenSocket, ptr, &len)
90+
}
91+
}
92+
93+
self.port = UInt16(networkByteOrder: actualSA.sin_port)
7594
}
7695

77-
private func createSockaddr(_ port: UInt16) -> sockaddr_in {
96+
private func createSockaddr(_ port: UInt16?) -> sockaddr_in {
7897
// Listen on the loopback address so that OSX doesnt pop up a dialog
7998
// asking to accept incoming connections if the firewall is enabled.
8099
let addr = UInt32(INADDR_LOOPBACK).bigEndian
81100
#if os(Linux)
82-
return sockaddr_in(sin_family: sa_family_t(AF_INET), sin_port: htons(port), sin_addr: in_addr(s_addr: addr), sin_zero: (0,0,0,0,0,0,0,0))
101+
return sockaddr_in(sin_family: sa_family_t(AF_INET), sin_port: htons(port ?? 0), sin_addr: in_addr(s_addr: addr), sin_zero: (0,0,0,0,0,0,0,0))
83102
#else
84-
return sockaddr_in(sin_len: 0, sin_family: sa_family_t(AF_INET), sin_port: CFSwapInt16HostToBig(port), sin_addr: in_addr(s_addr: addr), sin_zero: (0,0,0,0,0,0,0,0))
103+
return sockaddr_in(sin_len: 0, sin_family: sa_family_t(AF_INET), sin_port: CFSwapInt16HostToBig(port ?? 0), sin_addr: in_addr(s_addr: addr), sin_zero: (0,0,0,0,0,0,0,0))
85104
#endif
86105
}
87106

@@ -135,20 +154,27 @@ class _TCPSocket {
135154
}
136155

137156
func shutdown() {
138-
close(connectionSocket)
157+
if let connectionSocket = self.connectionSocket {
158+
close(connectionSocket)
159+
}
139160
close(listenSocket)
140161
}
141162
}
142163

143164
class _HTTPServer {
144165

145-
let socket: _TCPSocket
166+
let socket: _TCPSocket
167+
var port: UInt16 {
168+
get {
169+
return self.socket.port
170+
}
171+
}
146172

147-
init(port: UInt16) throws {
173+
init(port: UInt16?) throws {
148174
socket = try _TCPSocket(port: port)
149175
}
150176

151-
public class func create(port: UInt16) throws -> _HTTPServer {
177+
public class func create(port: UInt16?) throws -> _HTTPServer {
152178
return try _HTTPServer(port: port)
153179
}
154180

@@ -161,7 +187,7 @@ class _HTTPServer {
161187
}
162188

163189
public func request() throws -> _HTTPRequest {
164-
return _HTTPRequest(request: try socket.readData())
190+
return try _HTTPRequest(request: socket.readData())
165191
}
166192

167193
public func respond(with response: _HTTPResponse, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
@@ -309,8 +335,13 @@ public class TestURLSessionServer {
309335
let startDelay: TimeInterval?
310336
let sendDelay: TimeInterval?
311337
let bodyChunks: Int?
338+
var port: UInt16 {
339+
get {
340+
return self.httpServer.port
341+
}
342+
}
312343

313-
public init (port: UInt16, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
344+
public init (port: UInt16?, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
314345
httpServer = try _HTTPServer.create(port: port)
315346
self.startDelay = startDelay
316347
self.sendDelay = sendDelay
@@ -406,23 +437,28 @@ public class ServerSemaphore {
406437
}
407438

408439
class LoopbackServerTest : XCTestCase {
409-
static var serverPort: Int = -1
440+
private static let staticSyncQ = DispatchQueue(label: "org.swift.TestFoundation.HTTPServer.StaticSyncQ")
441+
442+
private static var _serverPort: Int = -1
443+
static var serverPort: Int {
444+
get {
445+
return staticSyncQ.sync { _serverPort }
446+
}
447+
set {
448+
staticSyncQ.sync { _serverPort = newValue }
449+
}
450+
}
410451

411452
override class func setUp() {
412453
super.setUp()
413454
func runServer(with condition: ServerSemaphore, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
414-
let start = 21961
415-
for port in start...(start+100) { //we must find at least one port to bind
416-
do {
417-
serverPort = port
418-
let test = try TestURLSessionServer(port: UInt16(port), startDelay: startDelay, sendDelay: sendDelay, bodyChunks: bodyChunks)
419-
try test.start(started: condition)
420-
try test.readAndRespond()
421-
test.stop()
422-
} catch let e as ServerError {
423-
if e.operation == "bind" { continue }
424-
throw e
425-
}
455+
while true {
456+
let test = try TestURLSessionServer(port: nil, startDelay: startDelay, sendDelay: sendDelay, bodyChunks: bodyChunks)
457+
serverPort = Int(test.port)
458+
try test.start(started: condition)
459+
try test.readAndRespond()
460+
serverPort = -2
461+
test.stop()
426462
}
427463
}
428464

0 commit comments

Comments
 (0)