Skip to content

[Test][NSURLSession] Chunked response #913

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 56 additions & 11 deletions TestFoundation/HTTPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,33 @@ class _TCPSocket {
_ = try attempt("read", valid: isNotNegative, CInt(read(connectionSocket, &buffer, 4096)))
return String(cString: &buffer)
}

func split(_ str: String, _ count: Int) -> [String] {
return stride(from: 0, to: str.characters.count, by: count).map { i -> String in
let startIndex = str.index(str.startIndex, offsetBy: i)
let endIndex = str.index(startIndex, offsetBy: count, limitedBy: str.endIndex) ?? str.endIndex
return str[startIndex..<endIndex]
}
}

func writeData(data: String) throws {
var bytes = Array(data.utf8)
_ = try attempt("write", valid: isNotNegative, CInt(write(connectionSocket, &bytes, data.utf8.count)))
func writeData(header: String, body: String, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
var header = Array(header.utf8)
_ = try attempt("write", valid: isNotNegative, CInt(write(connectionSocket, &header, header.count)))

if let sendDelay = sendDelay, let bodyChunks = bodyChunks {
let count = max(1, Int(Double(body.utf8.count) / Double(bodyChunks)))
let texts = split(body, count)

for item in texts {
sleep(UInt32(sendDelay))
var bytes = Array(item.utf8)
print(item)
_ = try attempt("write", valid: isNotNegative, CInt(write(connectionSocket, &bytes, bytes.count)))
}
} else {
var bytes = Array(body.utf8)
_ = try attempt("write", valid: isNotNegative, CInt(write(connectionSocket, &bytes, bytes.count)))
}
}

func shutdown() {
Expand Down Expand Up @@ -128,8 +151,24 @@ class _HTTPServer {
return _HTTPRequest(request: try socket.readData())
}

public func respond(with response: _HTTPResponse) throws {
try socket.writeData(data: response.description)
public func respond(with response: _HTTPResponse, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
let semaphore = DispatchSemaphore(value: 0)
let deadlineTime: DispatchTime

if let startDelay = startDelay {
deadlineTime = .now() + .seconds(Int(startDelay))
} else {
deadlineTime = .now()
}

DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
do {
try self.socket.writeData(header: response.header, body: response.body, sendDelay: sendDelay, bodyChunks: bodyChunks)
semaphore.signal()
} catch { }
}
semaphore.wait()

}
}

Expand Down Expand Up @@ -160,17 +199,17 @@ struct _HTTPResponse {
}
private let responseCode: Response
private let headers: String
private let body: String
public let body: String

public init(response: Response, headers: String = _HTTPUtils.EMPTY, body: String) {
self.responseCode = response
self.headers = headers
self.body = body
}

public var description: String {
public var header: String {
let statusLine = _HTTPUtils.VERSION + _HTTPUtils.SPACE + "\(responseCode.rawValue)" + _HTTPUtils.SPACE + "\(responseCode)"
return statusLine + (headers != _HTTPUtils.EMPTY ? _HTTPUtils.CRLF + headers : _HTTPUtils.EMPTY) + _HTTPUtils.CRLF2 + body
return statusLine + (headers != _HTTPUtils.EMPTY ? _HTTPUtils.CRLF + headers : _HTTPUtils.EMPTY) + _HTTPUtils.CRLF2
}
}

Expand All @@ -181,18 +220,24 @@ public class TestURLSessionServer {
"USA":"Washington, D.C.",
"country.txt": "A country is a region that is identified as a distinct national entity in political geography"]
let httpServer: _HTTPServer
let startDelay: TimeInterval?
let sendDelay: TimeInterval?
let bodyChunks: Int?

public init (port: UInt16) throws {
public init (port: UInt16, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
httpServer = try _HTTPServer.create(port: port)
self.startDelay = startDelay
self.sendDelay = sendDelay
self.bodyChunks = bodyChunks
}
public func start(started: ServerSemaphore) throws {
started.signal()
try httpServer.listen(notify: started)
}

public func readAndRespond() throws {
try httpServer.respond(with: process(request: httpServer.request()))
}
try httpServer.respond(with: process(request: httpServer.request()), startDelay: self.startDelay, sendDelay: self.sendDelay, bodyChunks: self.bodyChunks)
}

func process(request: _HTTPRequest) -> _HTTPResponse {
if request.method == .GET {
Expand Down
31 changes: 28 additions & 3 deletions TestFoundation/TestNSURLSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@ class TestURLSession : XCTestCase {
("test_taskError", test_taskError),
("test_taskCopy", test_taskCopy),
("test_cancelTask", test_cancelTask),
// ("test_taskTimeout", test_taskTimeout),
]
}

private func runServer(with condition: ServerSemaphore) throws {
private func runServer(with condition: ServerSemaphore, startDelay: TimeInterval? = nil, sendDelay: TimeInterval? = nil, bodyChunks: Int? = nil) throws {
let start = 21961
for port in start...(start+100) { //we must find at least one port to bind
do {
serverPort = port
let test = try TestURLSessionServer(port: UInt16(port))
let test = try TestURLSessionServer(port: UInt16(port), startDelay: startDelay, sendDelay: sendDelay, bodyChunks: bodyChunks)
try test.start(started: condition)
try test.readAndRespond()
test.stop()
Expand Down Expand Up @@ -317,7 +318,31 @@ class TestURLSession : XCTestCase {
d.cancel()
waitForExpectations(timeout: 12)
}


func test_taskTimeout() {
let serverReady = ServerSemaphore()
globalDispatchQueue.async {
do {
try self.runServer(with: serverReady, startDelay: 3, sendDelay: 3, bodyChunks: 3)
} catch {
XCTAssertTrue(true)
return
}
}
serverReady.wait()
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 5
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
var expect = expectation(description: "download task with handler")
let req = URLRequest(url: URL(string: "http://127.0.0.1:\(serverPort)/Peru")!)
var task = session.dataTask(with: req) { (data, _, error) -> Void in
defer { expect.fulfill() }
XCTAssertNil(error)
}
task.resume()

waitForExpectations(timeout: 30)
}
}

class SessionDelegate: NSObject, URLSessionDelegate {
Expand Down