@@ -37,6 +37,12 @@ struct _HTTPUtils {
37
37
static let EMPTY = " "
38
38
}
39
39
40
+ extension UInt16 {
41
+ public init ( networkByteOrder input: UInt16 ) {
42
+ self . init ( bigEndian: input)
43
+ }
44
+ }
45
+
40
46
class _TCPSocket {
41
47
42
48
private var listenSocket : Int32 !
@@ -57,12 +63,15 @@ class _TCPSocket {
57
63
return r
58
64
}
59
65
60
- init ( port: UInt16 ) throws {
66
+ public private( set) var port : UInt16
67
+
68
+ init ( port: UInt16 ? ) throws {
61
69
#if os(Linux)
62
70
let SOCKSTREAM = Int32 ( SOCK_STREAM . rawValue)
63
71
#else
64
72
let SOCKSTREAM = SOCK_STREAM
65
73
#endif
74
+ self . port = port ?? 0
66
75
listenSocket = try attempt ( " socket " , valid: isNotNegative, socket ( AF_INET, SOCKSTREAM, Int32 ( IPPROTO_TCP) ) )
67
76
var on : Int = 1
68
77
_ = try attempt ( " setsockopt " , valid: isZero, setsockopt ( listenSocket, SOL_SOCKET, SO_REUSEADDR, & on, socklen_t ( MemoryLayout< Int> . size) ) )
@@ -72,16 +81,26 @@ class _TCPSocket {
72
81
let addr = UnsafePointer < sockaddr > ( $0)
73
82
_ = try attempt ( " bind " , valid: isZero, bind ( listenSocket, addr, socklen_t ( MemoryLayout< sockaddr> . size) ) )
74
83
} )
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)
75
94
}
76
95
77
- private func createSockaddr( _ port: UInt16 ) -> sockaddr_in {
96
+ private func createSockaddr( _ port: UInt16 ? ) -> sockaddr_in {
78
97
// Listen on the loopback address so that OSX doesnt pop up a dialog
79
98
// asking to accept incoming connections if the firewall is enabled.
80
99
let addr = UInt32 ( INADDR_LOOPBACK) . bigEndian
81
100
#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 ) )
83
102
#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 ) )
85
104
#endif
86
105
}
87
106
@@ -135,20 +154,27 @@ class _TCPSocket {
135
154
}
136
155
137
156
func shutdown( ) {
138
- close ( connectionSocket)
157
+ if let connectionSocket = self . connectionSocket {
158
+ close ( connectionSocket)
159
+ }
139
160
close ( listenSocket)
140
161
}
141
162
}
142
163
143
164
class _HTTPServer {
144
165
145
- let socket : _TCPSocket
166
+ let socket : _TCPSocket
167
+ var port : UInt16 {
168
+ get {
169
+ return self . socket. port
170
+ }
171
+ }
146
172
147
- init ( port: UInt16 ) throws {
173
+ init ( port: UInt16 ? ) throws {
148
174
socket = try _TCPSocket ( port: port)
149
175
}
150
176
151
- public class func create( port: UInt16 ) throws -> _HTTPServer {
177
+ public class func create( port: UInt16 ? ) throws -> _HTTPServer {
152
178
return try _HTTPServer ( port: port)
153
179
}
154
180
@@ -161,7 +187,7 @@ class _HTTPServer {
161
187
}
162
188
163
189
public func request( ) throws -> _HTTPRequest {
164
- return _HTTPRequest ( request: try socket. readData ( ) )
190
+ return try _HTTPRequest ( request: socket. readData ( ) )
165
191
}
166
192
167
193
public func respond( with response: _HTTPResponse , startDelay: TimeInterval ? = nil , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
@@ -309,8 +335,13 @@ public class TestURLSessionServer {
309
335
let startDelay : TimeInterval ?
310
336
let sendDelay : TimeInterval ?
311
337
let bodyChunks : Int ?
338
+ var port : UInt16 {
339
+ get {
340
+ return self . httpServer. port
341
+ }
342
+ }
312
343
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 {
314
345
httpServer = try _HTTPServer. create ( port: port)
315
346
self . startDelay = startDelay
316
347
self . sendDelay = sendDelay
@@ -406,23 +437,28 @@ public class ServerSemaphore {
406
437
}
407
438
408
439
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
+ }
410
451
411
452
override class func setUp( ) {
412
453
super. setUp ( )
413
454
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 ( )
426
462
}
427
463
}
428
464
0 commit comments