@@ -77,6 +77,7 @@ class _TCPSocket {
77
77
return sockaddr_in ( sin_len: 0 , sin_family: sa_family_t ( AF_INET) , sin_port: CFSwapInt16HostToBig ( port) , sin_addr: in_addr ( s_addr: INADDR_ANY) , sin_zero: ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) )
78
78
#endif
79
79
}
80
+
80
81
func acceptConnection( notify: ServerSemaphore ) throws {
81
82
_ = try attempt ( " listen " , valid: isZero, listen ( listenSocket, SOMAXCONN) )
82
83
try socketAddress. withMemoryRebound ( to: sockaddr. self, capacity: MemoryLayout< sockaddr> . size, {
@@ -92,10 +93,32 @@ class _TCPSocket {
92
93
_ = try attempt ( " read " , valid: isNotNegative, CInt ( read ( connectionSocket, & buffer, 4096 ) ) )
93
94
return String ( cString: & buffer)
94
95
}
96
+
97
+ func split( _ str: String , _ count: Int ) -> [ String ] {
98
+ return stride ( from: 0 , to: str. characters. count, by: count) . map { i -> String in
99
+ let startIndex = str. index ( str. startIndex, offsetBy: i)
100
+ let endIndex = str. index ( startIndex, offsetBy: count, limitedBy: str. endIndex) ?? str. endIndex
101
+ return str [ startIndex..< endIndex]
102
+ }
103
+ }
95
104
96
- func writeData( data: String ) throws {
97
- var bytes = Array ( data. utf8)
98
- _ = try attempt ( " write " , valid: isNotNegative, CInt ( write ( connectionSocket, & bytes, data. utf8. count) ) )
105
+ func writeData( header: String , body: String , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
106
+ var header = Array ( header. utf8)
107
+ _ = try attempt ( " write " , valid: isNotNegative, CInt ( write ( connectionSocket, & header, header. count) ) )
108
+
109
+ if let sendDelay = sendDelay, let bodyChunks = bodyChunks {
110
+ let count = max ( 1 , Int ( Double ( body. utf8. count) / Double( bodyChunks) ) )
111
+ let texts = split ( body, count)
112
+
113
+ for item in texts {
114
+ sleep ( UInt32 ( sendDelay) )
115
+ var bytes = Array ( item. utf8)
116
+ _ = try attempt ( " write " , valid: isNotNegative, CInt ( write ( connectionSocket, & bytes, bytes. count) ) )
117
+ }
118
+ } else {
119
+ var bytes = Array ( body. utf8)
120
+ _ = try attempt ( " write " , valid: isNotNegative, CInt ( write ( connectionSocket, & bytes, bytes. count) ) )
121
+ }
99
122
}
100
123
101
124
func shutdown( ) {
@@ -128,8 +151,24 @@ class _HTTPServer {
128
151
return _HTTPRequest ( request: try socket. readData ( ) )
129
152
}
130
153
131
- public func respond( with response: _HTTPResponse ) throws {
132
- try socket. writeData ( data: response. description)
154
+ public func respond( with response: _HTTPResponse , startDelay: TimeInterval ? = nil , sendDelay: TimeInterval ? = nil , bodyChunks: Int ? = nil ) throws {
155
+ let semaphore = DispatchSemaphore ( value: 0 )
156
+ let deadlineTime : DispatchTime
157
+
158
+ if let startDelay = startDelay {
159
+ deadlineTime = . now( ) + . seconds( Int ( startDelay) )
160
+ } else {
161
+ deadlineTime = . now( )
162
+ }
163
+
164
+ DispatchQueue . main. asyncAfter ( deadline: deadlineTime) {
165
+ do {
166
+ try self . socket. writeData ( header: response. header, body: response. body, sendDelay: sendDelay, bodyChunks: bodyChunks)
167
+ semaphore. signal ( )
168
+ } catch { }
169
+ }
170
+ semaphore. wait ( )
171
+
133
172
}
134
173
}
135
174
@@ -152,6 +191,14 @@ struct _HTTPRequest {
152
191
body = lines. last!
153
192
}
154
193
194
+ public func getCommaSeparatedHeaders( ) -> String {
195
+ var allHeaders = " "
196
+ for header in headers {
197
+ allHeaders += header + " , "
198
+ }
199
+ return allHeaders
200
+ }
201
+
155
202
}
156
203
157
204
struct _HTTPResponse {
@@ -160,17 +207,17 @@ struct _HTTPResponse {
160
207
}
161
208
private let responseCode : Response
162
209
private let headers : String
163
- private let body : String
210
+ public let body : String
164
211
165
212
public init ( response: Response , headers: String = _HTTPUtils. EMPTY, body: String ) {
166
213
self . responseCode = response
167
214
self . headers = headers
168
215
self . body = body
169
216
}
170
217
171
- public var description : String {
218
+ public var header : String {
172
219
let statusLine = _HTTPUtils. VERSION + _HTTPUtils. SPACE + " \( responseCode. rawValue) " + _HTTPUtils. SPACE + " \( responseCode) "
173
- return statusLine + ( headers != _HTTPUtils. EMPTY ? _HTTPUtils. CRLF + headers : _HTTPUtils. EMPTY) + _HTTPUtils. CRLF2 + body
220
+ return statusLine + ( headers != _HTTPUtils. EMPTY ? _HTTPUtils. CRLF + headers : _HTTPUtils. EMPTY) + _HTTPUtils. CRLF2
174
221
}
175
222
}
176
223
@@ -181,32 +228,44 @@ public class TestURLSessionServer {
181
228
" USA " : " Washington, D.C. " ,
182
229
" country.txt " : " A country is a region that is identified as a distinct national entity in political geography " ]
183
230
let httpServer : _HTTPServer
231
+ let startDelay : TimeInterval ?
232
+ let sendDelay : TimeInterval ?
233
+ let bodyChunks : Int ?
184
234
185
- public init ( port: UInt16 ) throws {
235
+ public init ( port: UInt16 , startDelay : TimeInterval ? = nil , sendDelay : TimeInterval ? = nil , bodyChunks : Int ? = nil ) throws {
186
236
httpServer = try _HTTPServer. create ( port: port)
237
+ self . startDelay = startDelay
238
+ self . sendDelay = sendDelay
239
+ self . bodyChunks = bodyChunks
187
240
}
188
241
public func start( started: ServerSemaphore ) throws {
189
242
started. signal ( )
190
243
try httpServer. listen ( notify: started)
191
244
}
192
245
193
246
public func readAndRespond( ) throws {
194
- try httpServer. respond ( with: process ( request: httpServer. request ( ) ) )
195
- }
247
+ try httpServer. respond ( with: process ( request: httpServer. request ( ) ) , startDelay : self . startDelay , sendDelay : self . sendDelay , bodyChunks : self . bodyChunks )
248
+ }
196
249
197
250
func process( request: _HTTPRequest ) -> _HTTPResponse {
198
- if request. method == . GET {
199
- return getResponse ( uri : request. uri )
251
+ if request. method == . GET || request . method == . POST {
252
+ return getResponse ( request : request)
200
253
} else {
201
254
fatalError ( " Unsupported method! " )
202
255
}
203
256
}
204
257
205
- func getResponse( uri: String ) -> _HTTPResponse {
258
+ func getResponse( request: _HTTPRequest ) -> _HTTPResponse {
259
+ let uri = request. uri
206
260
if uri == " /country.txt " {
207
261
let text = capitals [ String ( uri. characters. dropFirst ( ) ) ] !
208
262
return _HTTPResponse ( response: . OK, headers: " Content-Length: \( text. characters. count) " , body: text)
209
263
}
264
+
265
+ if uri == " /requestHeaders " {
266
+ let text = request. getCommaSeparatedHeaders ( )
267
+ return _HTTPResponse ( response: . OK, headers: " Content-Length: \( text. characters. count) " , body: text)
268
+ }
210
269
return _HTTPResponse ( response: . OK, body: capitals [ String ( uri. characters. dropFirst ( ) ) ] !)
211
270
}
212
271
0 commit comments