@@ -35,6 +35,12 @@ struct _HTTPUtils {
35
35
static let EMPTY = " "
36
36
}
37
37
38
+ extension UInt16 {
39
+ public init ( networkByteOrder input: UInt16 ) {
40
+ self . init ( bigEndian: input)
41
+ }
42
+ }
43
+
38
44
class _TCPSocket {
39
45
40
46
private var listenSocket : Int32 !
@@ -55,12 +61,15 @@ class _TCPSocket {
55
61
return r
56
62
}
57
63
58
- init ( port: UInt16 ) throws {
64
+ public private( set) var port : UInt16
65
+
66
+ init ( port: UInt16 ? ) throws {
59
67
#if os(Linux)
60
68
let SOCKSTREAM = Int32 ( SOCK_STREAM . rawValue)
61
69
#else
62
70
let SOCKSTREAM = SOCK_STREAM
63
71
#endif
72
+ self . port = port ?? 0
64
73
listenSocket = try attempt ( " socket " , valid: isNotNegative, socket ( AF_INET, SOCKSTREAM, Int32 ( IPPROTO_TCP) ) )
65
74
var on : Int = 1
66
75
_ = try attempt ( " setsockopt " , valid: isZero, setsockopt ( listenSocket, SOL_SOCKET, SO_REUSEADDR, & on, socklen_t ( MemoryLayout< Int> . size) ) )
@@ -70,16 +79,26 @@ class _TCPSocket {
70
79
let addr = UnsafePointer < sockaddr > ( $0)
71
80
_ = try attempt ( " bind " , valid: isZero, bind ( listenSocket, addr, socklen_t ( MemoryLayout< sockaddr> . size) ) )
72
81
} )
82
+
83
+ var actualSA = sockaddr_in ( )
84
+ withUnsafeMutablePointer ( to: & actualSA) { ptr in
85
+ ptr. withMemoryRebound ( to: sockaddr. self, capacity: 1 ) { ( ptr: UnsafeMutablePointer < sockaddr > ) in
86
+ var len = socklen_t ( MemoryLayout< sockaddr> . size)
87
+ getsockname ( listenSocket, ptr, & len)
88
+ }
89
+ }
90
+
91
+ self . port = UInt16 ( networkByteOrder: actualSA. sin_port)
73
92
}
74
93
75
- private func createSockaddr( _ port: UInt16 ) -> sockaddr_in {
94
+ private func createSockaddr( _ port: UInt16 ? ) -> sockaddr_in {
76
95
// Listen on the loopback address so that OSX doesnt pop up a dialog
77
96
// asking to accept incoming connections if the firewall is enabled.
78
97
let addr = UInt32 ( INADDR_LOOPBACK) . bigEndian
79
98
#if os(Linux)
80
- 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 ) )
99
+ 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 ) )
81
100
#else
82
- 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 ) )
101
+ 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 ) )
83
102
#endif
84
103
}
85
104
@@ -133,20 +152,27 @@ class _TCPSocket {
133
152
}
134
153
135
154
func shutdown( ) {
136
- close ( connectionSocket)
155
+ if let connectionSocket = self . connectionSocket {
156
+ close ( connectionSocket)
157
+ }
137
158
close ( listenSocket)
138
159
}
139
160
}
140
161
141
162
class _HTTPServer {
142
163
143
- let socket : _TCPSocket
164
+ let socket : _TCPSocket
165
+ var port : UInt16 {
166
+ get {
167
+ return self . socket. port
168
+ }
169
+ }
144
170
145
- init ( port: UInt16 ) throws {
171
+ init ( port: UInt16 ? ) throws {
146
172
socket = try _TCPSocket ( port: port)
147
173
}
148
174
149
- public class func create( port: UInt16 ) throws -> _HTTPServer {
175
+ public class func create( port: UInt16 ? ) throws -> _HTTPServer {
150
176
return try _HTTPServer ( port: port)
151
177
}
152
178
@@ -159,7 +185,7 @@ class _HTTPServer {
159
185
}
160
186
161
187
public func request( ) throws -> _HTTPRequest {
162
- return _HTTPRequest ( request: try socket. readData ( ) )
188
+ return try _HTTPRequest ( request: socket. readData ( ) )
163
189
}
164
190
165
191
public func respond( with response: _HTTPResponse , startDelay: TimeInterval ? = nil , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
@@ -307,8 +333,13 @@ public class TestURLSessionServer {
307
333
let startDelay : TimeInterval ?
308
334
let sendDelay : TimeInterval ?
309
335
let bodyChunks : Int ?
336
+ var port : UInt16 {
337
+ get {
338
+ return self . httpServer. port
339
+ }
340
+ }
310
341
311
- public init ( port: UInt16 , startDelay: TimeInterval ? = nil , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
342
+ public init ( port: UInt16 ? , startDelay: TimeInterval ? = nil , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
312
343
httpServer = try _HTTPServer. create ( port: port)
313
344
self . startDelay = startDelay
314
345
self . sendDelay = sendDelay
@@ -404,23 +435,28 @@ public class ServerSemaphore {
404
435
}
405
436
406
437
class LoopbackServerTest : XCTestCase {
407
- static var serverPort : Int = - 1
438
+ private static let staticSyncQ = DispatchQueue ( label: " org.swift.TestFoundation.HTTPServer.StaticSyncQ " )
439
+
440
+ private static var _serverPort : Int = - 1
441
+ static var serverPort : Int {
442
+ get {
443
+ return staticSyncQ. sync { _serverPort }
444
+ }
445
+ set {
446
+ staticSyncQ. sync { _serverPort = newValue }
447
+ }
448
+ }
408
449
409
450
override class func setUp( ) {
410
451
super. setUp ( )
411
452
func runServer( with condition: ServerSemaphore , startDelay: TimeInterval ? = nil , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
412
- let start = 21961
413
- for port in start... ( start+ 100 ) { //we must find at least one port to bind
414
- do {
415
- serverPort = port
416
- let test = try TestURLSessionServer ( port: UInt16 ( port) , startDelay: startDelay, sendDelay: sendDelay, bodyChunks: bodyChunks)
417
- try test. start ( started: condition)
418
- try test. readAndRespond ( )
419
- test. stop ( )
420
- } catch let e as ServerError {
421
- if e. operation == " bind " { continue }
422
- throw e
423
- }
453
+ while true {
454
+ let test = try TestURLSessionServer ( port: nil , startDelay: startDelay, sendDelay: sendDelay, bodyChunks: bodyChunks)
455
+ serverPort = Int ( test. port)
456
+ try test. start ( started: condition)
457
+ try test. readAndRespond ( )
458
+ serverPort = - 2
459
+ test. stop ( )
424
460
}
425
461
}
426
462
0 commit comments