@@ -40,7 +40,8 @@ extension UInt16 {
4040}
4141
4242class _TCPSocket {
43-
43+
44+ private let sendFlags : CInt
4445 private var listenSocket : Int32 !
4546 private var socketAddress = UnsafeMutablePointer< sockaddr_in> . allocate( capacity: 1 )
4647 private var connectionSocket : Int32 !
@@ -55,27 +56,37 @@ class _TCPSocket {
5556
5657 private func attempt( _ name: String , file: String = #file, line: UInt = #line, valid: ( CInt ) -> Bool , _ b: @autoclosure ( ) -> CInt ) throws -> CInt {
5758 let r = b ( )
58- guard valid ( r) else { throw ServerError ( operation: name, errno: r, file: file, line: line) }
59+ guard valid ( r) else {
60+ throw ServerError ( operation: name, errno: errno, file: file, line: line)
61+ }
5962 return r
6063 }
6164
6265 public private( set) var port : UInt16
6366
6467 init ( port: UInt16 ? ) throws {
65- #if os(Linux) && !os(Android)
68+ #if canImport(Darwin)
69+ sendFlags = 0
70+ #else
71+ sendFlags = CInt ( MSG_NOSIGNAL)
72+ #endif
73+
74+ #if os(Linux) && !os(Android)
6675 let SOCKSTREAM = Int32 ( SOCK_STREAM . rawValue)
67- #else
76+ #else
6877 let SOCKSTREAM = SOCK_STREAM
69- #endif
78+ #endif
7079 self . port = port ?? 0
7180 listenSocket = try attempt ( " socket " , valid: isNotNegative, socket ( AF_INET, SOCKSTREAM, Int32 ( IPPROTO_TCP) ) )
72- var on : Int = 1
73- _ = try attempt ( " setsockopt " , valid: isZero, setsockopt ( listenSocket, SOL_SOCKET, SO_REUSEADDR, & on, socklen_t ( MemoryLayout< Int> . size) ) )
81+ var on : CInt = 1
82+ _ = try attempt ( " setsockopt " , valid: isZero, setsockopt ( listenSocket, SOL_SOCKET, SO_REUSEADDR, & on, socklen_t ( MemoryLayout< CInt> . size) ) )
83+
7484 let sa = createSockaddr ( port)
7585 socketAddress. initialize ( to: sa)
7686 try socketAddress. withMemoryRebound ( to: sockaddr. self, capacity: MemoryLayout< sockaddr> . size, {
7787 let addr = UnsafePointer < sockaddr > ( $0)
7888 _ = try attempt ( " bind " , valid: isZero, bind ( listenSocket, addr, socklen_t ( MemoryLayout< sockaddr> . size) ) )
89+ _ = try attempt ( " listen " , valid: isZero, listen ( listenSocket, SOMAXCONN) )
7990 } )
8091
8192 var actualSA = sockaddr_in ( )
@@ -104,18 +115,21 @@ class _TCPSocket {
104115 }
105116
106117 func acceptConnection( notify: ServerSemaphore ) throws {
107- _ = try attempt ( " listen " , valid: isZero, listen ( listenSocket, SOMAXCONN) )
108118 try socketAddress. withMemoryRebound ( to: sockaddr. self, capacity: MemoryLayout< sockaddr> . size, {
109119 let addr = UnsafeMutablePointer < sockaddr > ( $0)
110120 var sockLen = socklen_t ( MemoryLayout< sockaddr> . size)
111- notify. signal ( )
112121 connectionSocket = try attempt ( " accept " , valid: isNotNegative, accept ( listenSocket, addr, & sockLen) )
122+ #if canImport(Dawin)
123+ // Disable SIGPIPEs when writing to closed sockets
124+ var on : CInt = 1
125+ _ = try attempt ( " setsockopt " , valid: isZero, setsockopt ( connectionSocket, SOL_SOCKET, SO_NOSIGPIPE, & on, socklen_t ( MemoryLayout< CInt> . size) ) )
126+ #endif
113127 } )
114128 }
115129
116130 func readData( ) throws -> String {
117131 var buffer = [ UInt8] ( repeating: 0 , count: 4096 )
118- _ = try attempt ( " read " , valid: isNotNegative, CInt ( read ( connectionSocket, & buffer, 4096 ) ) )
132+ _ = try attempt ( " read " , valid: isNotNegative, CInt ( read ( connectionSocket, & buffer, buffer . count ) ) )
119133 return String ( cString: & buffer)
120134 }
121135
@@ -129,33 +143,39 @@ class _TCPSocket {
129143
130144 func writeRawData( _ data: Data ) throws {
131145 _ = try data. withUnsafeBytes { ptr in
132- try attempt ( " write " , valid: isNotNegative, CInt ( write ( connectionSocket, ptr, data. count) ) )
146+ try attempt ( " send " , valid: isNotNegative, CInt ( send ( connectionSocket, ptr, data. count, sendFlags ) ) )
133147 }
134148 }
135149
136150 func writeData( header: String , body: String , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
137- var header = Array ( header. utf8)
138- _ = try attempt ( " write " , valid: isNotNegative, CInt ( write ( connectionSocket, & header , header . count) ) )
139-
151+ var _header = Array ( header. utf8)
152+ _ = try attempt ( " send " , valid: isNotNegative, CInt ( send ( connectionSocket, & _header , _header . count, sendFlags ) ) )
153+
140154 if let sendDelay = sendDelay, let bodyChunks = bodyChunks {
141155 let count = max ( 1 , Int ( Double ( body. utf8. count) / Double( bodyChunks) ) )
142156 let texts = split ( body, count)
143157
144158 for item in texts {
145159 sleep ( UInt32 ( sendDelay) )
146160 var bytes = Array ( item. utf8)
147- _ = try attempt ( " write " , valid: isNotNegative, CInt ( write ( connectionSocket, & bytes, bytes. count) ) )
161+ _ = try attempt ( " send " , valid: isNotNegative, CInt ( send ( connectionSocket, & bytes, bytes. count, sendFlags ) ) )
148162 }
149163 } else {
150164 var bytes = Array ( body. utf8)
151- _ = try attempt ( " write " , valid: isNotNegative, CInt ( write ( connectionSocket, & bytes, bytes. count) ) )
165+ _ = try attempt ( " send " , valid: isNotNegative, CInt ( send ( connectionSocket, & bytes, bytes. count, sendFlags ) ) )
152166 }
153167 }
154168
155- func shutdown ( ) {
169+ func closeClient ( ) {
156170 if let connectionSocket = self . connectionSocket {
157171 close ( connectionSocket)
172+ self . connectionSocket = nil
158173 }
174+ }
175+
176+ func shutdownListener( ) {
177+ closeClient ( )
178+ shutdown ( listenSocket, CInt ( SHUT_RDWR) )
159179 close ( listenSocket)
160180 }
161181}
@@ -182,32 +202,23 @@ class _HTTPServer {
182202 }
183203
184204 public func stop( ) {
185- socket. shutdown ( )
205+ socket. closeClient ( )
206+ socket. shutdownListener ( )
186207 }
187208
188209 public func request( ) throws -> _HTTPRequest {
189210 return try _HTTPRequest ( request: socket. readData ( ) )
190211 }
191212
192213 public func respond( with response: _HTTPResponse , startDelay: TimeInterval ? = nil , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
193- let semaphore = DispatchSemaphore ( value: 0 )
194- let deadlineTime : DispatchTime
195-
196- if let startDelay = startDelay {
197- deadlineTime = . now( ) + . seconds( Int ( startDelay) )
198- } else {
199- deadlineTime = . now( )
214+ if let delay = startDelay {
215+ Thread . sleep ( forTimeInterval: delay)
200216 }
201-
202- DispatchQueue . global ( ) . asyncAfter ( deadline: deadlineTime) {
203- do {
204- try self . socket. writeData ( header: response. header, body: response. body, sendDelay: sendDelay, bodyChunks: bodyChunks)
205- semaphore. signal ( )
206- } catch { }
217+ do {
218+ try self . socket. writeData ( header: response. header, body: response. body, sendDelay: sendDelay, bodyChunks: bodyChunks)
219+ } catch {
207220 }
208- semaphore. wait ( )
209-
210- }
221+ }
211222
212223 func respondWithBrokenResponses( uri: String ) throws {
213224 let responseData : Data
@@ -285,11 +296,13 @@ struct _HTTPRequest {
285296 let headers : [ String ]
286297
287298 public init ( request: String ) {
288- let lines = request. components ( separatedBy: _HTTPUtils. CRLF2) [ 0 ] . components ( separatedBy: _HTTPUtils. CRLF)
289- headers = Array ( lines [ 0 ... lines. count- 2 ] )
290- method = Method ( rawValue: headers [ 0 ] . components ( separatedBy: " " ) [ 0 ] ) !
291- uri = headers [ 0 ] . components ( separatedBy: " " ) [ 1 ]
292- body = lines. last!
299+ let headerEnd = ( request as NSString ) . range ( of: _HTTPUtils. CRLF2)
300+ let header = ( request as NSString ) . substring ( to: headerEnd. location)
301+ headers = header. components ( separatedBy: _HTTPUtils. CRLF)
302+ let action = headers [ 0 ]
303+ method = Method ( rawValue: action. components ( separatedBy: " " ) [ 0 ] ) !
304+ uri = action. components ( separatedBy: " " ) [ 1 ]
305+ body = ( request as NSString ) . substring ( from: headerEnd. location + headerEnd. length)
293306 }
294307
295308 public func getCommaSeparatedHeaders( ) -> String {
@@ -349,11 +362,7 @@ public class TestURLSessionServer {
349362 self . sendDelay = sendDelay
350363 self . bodyChunks = bodyChunks
351364 }
352- public func start( started: ServerSemaphore ) throws {
353- started. signal ( )
354- try httpServer. listen ( notify: started)
355- }
356-
365+
357366 public func readAndRespond( ) throws {
358367 let req = try httpServer. request ( )
359368 if req. uri. hasPrefix ( " /LandOfTheLostCities/ " ) {
@@ -400,6 +409,16 @@ public class TestURLSessionServer {
400409 return _HTTPResponse ( response: . OK, headers: " Content-Length: \( text. data ( using: . utf8) !. count) " , body: text)
401410 }
402411
412+ if uri == " /requestCookies " {
413+ let text = request. getCommaSeparatedHeaders ( )
414+ return _HTTPResponse ( response: . OK, headers: " Content-Length: \( text. data ( using: . utf8) !. count) \r \n Set-Cookie: fr=anjd&232; Max-Age=7776000; path=/ \r \n Set-Cookie: nm=sddf&232; Max-Age=7776000; path=/; domain=.swift.org; secure; httponly \r \n " , body: text)
415+ }
416+
417+ if uri == " /setCookies " {
418+ let text = request. getCommaSeparatedHeaders ( )
419+ return _HTTPResponse ( response: . OK, headers: " Content-Length: \( text. data ( using: . utf8) !. count) " , body: text)
420+ }
421+
403422 if uri == " /UnitedStates " {
404423 let value = capitals [ String ( uri. dropFirst ( ) ) ] !
405424 let text = request. getCommaSeparatedHeaders ( )
@@ -472,8 +491,8 @@ extension ServerError : CustomStringConvertible {
472491public class ServerSemaphore {
473492 let dispatchSemaphore = DispatchSemaphore ( value: 0 )
474493
475- public func wait( ) {
476- dispatchSemaphore. wait ( )
494+ public func wait( timeout : DispatchTime ) -> DispatchTimeoutResult {
495+ return dispatchSemaphore. wait ( timeout : timeout )
477496 }
478497
479498 public func signal( ) {
@@ -485,6 +504,11 @@ class LoopbackServerTest : XCTestCase {
485504 private static let staticSyncQ = DispatchQueue ( label: " org.swift.TestFoundation.HTTPServer.StaticSyncQ " )
486505
487506 private static var _serverPort : Int = - 1
507+ private static let serverReady = ServerSemaphore ( )
508+ private static var _serverActive = false
509+ private static var testServer : TestURLSessionServer ? = nil
510+
511+
488512 static var serverPort : Int {
489513 get {
490514 return staticSyncQ. sync { _serverPort }
@@ -494,30 +518,56 @@ class LoopbackServerTest : XCTestCase {
494518 }
495519 }
496520
521+ static var serverActive : Bool {
522+ get { return staticSyncQ. sync { _serverActive } }
523+ set { staticSyncQ. sync { _serverActive = newValue } }
524+ }
525+
526+ static func terminateServer( ) {
527+ serverActive = false
528+ testServer? . stop ( )
529+ testServer = nil
530+ }
531+
497532 override class func setUp( ) {
498533 super. setUp ( )
499534 func runServer( with condition: ServerSemaphore , startDelay: TimeInterval ? = nil , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
500- while true {
501- let test = try TestURLSessionServer ( port: nil , startDelay: startDelay, sendDelay: sendDelay, bodyChunks: bodyChunks)
502- serverPort = Int ( test. port)
503- try test. start ( started: condition)
504- try test. readAndRespond ( )
505- serverPort = - 2
506- test. stop ( )
535+ let server = try TestURLSessionServer ( port: nil , startDelay: startDelay, sendDelay: sendDelay, bodyChunks: bodyChunks)
536+ testServer = server
537+ serverPort = Int ( server. port)
538+ serverReady. signal ( )
539+ serverActive = true
540+
541+ while serverActive {
542+ do {
543+ try server. httpServer. listen ( notify: condition)
544+ try server. readAndRespond ( )
545+ server. httpServer. socket. closeClient ( )
546+ } catch {
547+ }
507548 }
549+ serverPort = - 2
550+
508551 }
509552
510- let serverReady = ServerSemaphore ( )
511553 globalDispatchQueue. async {
512554 do {
513555 try runServer ( with: serverReady)
514-
515556 } catch {
516- XCTAssertTrue ( true )
517- return
518557 }
519558 }
520559
521- serverReady. wait ( )
560+ let timeout = DispatchTime ( uptimeNanoseconds: DispatchTime . now ( ) . uptimeNanoseconds + 2_000_000_000 )
561+
562+ while serverPort == - 2 {
563+ guard serverReady. wait ( timeout: timeout) == . success else {
564+ fatalError ( " Timedout waiting for server to be ready " )
565+ }
566+ }
567+ }
568+
569+ override class func tearDown( ) {
570+ super. tearDown ( )
571+ terminateServer ( )
522572 }
523573}
0 commit comments