|
6 | 6 | // |
7 | 7 |
|
8 | 8 | import XCTest |
9 | | -import Swifter |
| 9 | +@testable import Swifter |
10 | 10 |
|
11 | 11 | class SwifterTestsHttpParser: XCTestCase { |
12 | 12 |
|
| 13 | + /// A specialized Socket which creates a linked socket pair with a pipe, and |
| 14 | + /// immediately writes in fixed data. This enables tests to static fixture |
| 15 | + /// data into the regular Socket flow. |
13 | 16 | class TestSocket: Socket { |
14 | | - var content = [UInt8]() |
15 | | - var offset = 0 |
16 | | - |
17 | 17 | init(_ content: String) { |
18 | | - super.init(socketFileDescriptor: -1) |
19 | | - self.content.append(contentsOf: [UInt8](content.utf8)) |
20 | | - } |
21 | | - |
22 | | - override func read() throws -> UInt8 { |
23 | | - if offset < content.count { |
24 | | - let value = self.content[offset] |
25 | | - offset = offset + 1 |
26 | | - return value |
| 18 | + /// Create an array to hold the read and write sockets that pipe creates |
| 19 | + var fds = [Int32](repeating: 0, count: 2) |
| 20 | + fds.withUnsafeMutableBufferPointer { ptr in |
| 21 | + let rv = pipe(ptr.baseAddress!) |
| 22 | + guard rv >= 0 else { fatalError("Pipe error!") } |
| 23 | + } |
| 24 | + |
| 25 | + // Extract the read and write handles into friendly variables |
| 26 | + let fdRead = fds[0] |
| 27 | + let fdWrite = fds[1] |
| 28 | + |
| 29 | + // Set non-blocking I/O on both sockets. This is required! |
| 30 | + _ = fcntl(fdWrite, F_SETFL, O_NONBLOCK) |
| 31 | + _ = fcntl(fdRead, F_SETFL, O_NONBLOCK) |
| 32 | + |
| 33 | + // Push the content bytes into the write socket. |
| 34 | + _ = content.withCString { stringPointer in |
| 35 | + // Count will be either >=0 to indicate bytes written, or -1 |
| 36 | + // if the bytes will be written later (non-blocking). |
| 37 | + let count = write(fdWrite, stringPointer, content.lengthOfBytes(using: .utf8) + 1) |
| 38 | + guard count != -1 || errno == EAGAIN else { fatalError("Write error!") } |
27 | 39 | } |
28 | | - throw SocketError.recvFailed("") |
| 40 | + |
| 41 | + // Close the write socket immediately. The OS will add an EOF byte |
| 42 | + // and the read socket will remain open. |
| 43 | + Darwin.close(fdWrite) // the super instance will close fdRead in deinit! |
| 44 | + |
| 45 | + super.init(socketFileDescriptor: fdRead) |
29 | 46 | } |
30 | 47 | } |
31 | | - |
| 48 | + |
32 | 49 | func testParser() { |
33 | 50 | let parser = HttpParser() |
34 | 51 |
|
@@ -89,6 +106,43 @@ class SwifterTestsHttpParser: XCTestCase { |
89 | 106 | let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 10\r\n\n")) |
90 | 107 | XCTAssert(false, "Parser should throw an error if request' body is too short.") |
91 | 108 | } catch { } |
| 109 | + |
| 110 | + do { // test payload less than 1 read segmant |
| 111 | + let contentLength = Socket.kBufferLength - 128 |
| 112 | + let bodyString = [String](repeating: "A", count: contentLength).joined(separator: "") |
| 113 | + |
| 114 | + let payload = "GET / HTTP/1.0\nContent-Length: \(contentLength)\n\n".appending(bodyString) |
| 115 | + let request = try parser.readHttpRequest(TestSocket(payload)) |
| 116 | + |
| 117 | + XCTAssert(bodyString.lengthOfBytes(using: .utf8) == contentLength, "Has correct request size") |
| 118 | + |
| 119 | + let unicodeBytes = bodyString.utf8.map { return $0 } |
| 120 | + XCTAssert(request.body == unicodeBytes, "Request body must be correct") |
| 121 | + } catch { } |
| 122 | + |
| 123 | + do { // test payload equal to 1 read segmant |
| 124 | + let contentLength = Socket.kBufferLength |
| 125 | + let bodyString = [String](repeating: "B", count: contentLength).joined(separator: "") |
| 126 | + let payload = "GET / HTTP/1.0\nContent-Length: \(contentLength)\n\n".appending(bodyString) |
| 127 | + let request = try parser.readHttpRequest(TestSocket(payload)) |
| 128 | + |
| 129 | + XCTAssert(bodyString.lengthOfBytes(using: .utf8) == contentLength, "Has correct request size") |
| 130 | + |
| 131 | + let unicodeBytes = bodyString.utf8.map { return $0 } |
| 132 | + XCTAssert(request.body == unicodeBytes, "Request body must be correct") |
| 133 | + } catch { } |
| 134 | + |
| 135 | + do { // test very large multi-segment payload |
| 136 | + let contentLength = Socket.kBufferLength * 4 |
| 137 | + let bodyString = [String](repeating: "C", count: contentLength).joined(separator: "") |
| 138 | + let payload = "GET / HTTP/1.0\nContent-Length: \(contentLength)\n\n".appending(bodyString) |
| 139 | + let request = try parser.readHttpRequest(TestSocket(payload)) |
| 140 | + |
| 141 | + XCTAssert(bodyString.lengthOfBytes(using: .utf8) == contentLength, "Has correct request size") |
| 142 | + |
| 143 | + let unicodeBytes = bodyString.utf8.map { return $0 } |
| 144 | + XCTAssert(request.body == unicodeBytes, "Request body must be correct") |
| 145 | + } catch { } |
92 | 146 |
|
93 | 147 | var r = try? parser.readHttpRequest(TestSocket("GET /open?link=https://www.youtube.com/watch?v=D2cUBG4PnOA HTTP/1.0\nContent-Length: 10\n\n1234567890")) |
94 | 148 |
|
|
0 commit comments