|
@@ -9,96 +9,192 @@ import XCTest
|
|
|
|
|
|
|
|
class SwifterTestsHttpParser: XCTestCase {
|
|
class SwifterTestsHttpParser: XCTestCase {
|
|
|
|
|
|
|
|
- class TestSocket: Socket {
|
|
|
|
|
- var content = [UInt8]()
|
|
|
|
|
- var offset = 0
|
|
|
|
|
-
|
|
|
|
|
- init(_ content: String) {
|
|
|
|
|
- super.init(socketFileDescriptor: -1)
|
|
|
|
|
- self.content.append(contentsOf: [UInt8](content.utf8))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override func read() throws -> UInt8 {
|
|
|
|
|
- if offset < content.count {
|
|
|
|
|
- let value = self.content[offset]
|
|
|
|
|
- offset = offset + 1
|
|
|
|
|
- return value
|
|
|
|
|
- }
|
|
|
|
|
- throw SocketError.recvFailed("")
|
|
|
|
|
|
|
+ func testRandomStuff() {
|
|
|
|
|
+ do {
|
|
|
|
|
+ let data = [UInt8]("1231245".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ XCTAssert(false, "Http processor should not return a request object for invalid data .")
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- func testParser() {
|
|
|
|
|
- let parser = HttpParser()
|
|
|
|
|
-
|
|
|
|
|
|
|
+ func testInvalidStatusLineChunk() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket(""))
|
|
|
|
|
- XCTAssert(false, "Parser should throw an error if socket is empty.")
|
|
|
|
|
- } catch { }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ let data = [UInt8]("GET HTTP/1.0".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ XCTAssert(false, "Http processor should not return a request object for invalid status line.")
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testValidStatusLineChunk() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("12345678"))
|
|
|
|
|
- XCTAssert(false, "Parser should throw an error if status line has single token.")
|
|
|
|
|
- } catch { }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ let data = [UInt8]("GET / HTTP/1.0".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ XCTAssert(false, "Http processor should not return a request object for valid status line only.")
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testStatusLineWithSingleNextLine() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("GET HTTP/1.0"))
|
|
|
|
|
- XCTAssert(false, "Parser should throw an error if status line has not enough tokens.")
|
|
|
|
|
- } catch { }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ let data = [UInt8]("GET / HTTP/1.0\r\n".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ XCTAssert(false, "Http processor should not return if there is no double next line symbol.")
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testStatusLineWithDoubleNextLine() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0"))
|
|
|
|
|
- XCTAssert(false, "Parser should throw an error if there is no next line symbol.")
|
|
|
|
|
- } catch { }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ var request: Request? = nil
|
|
|
|
|
+ let data = [UInt8]("GET / HTTP/1.0\r\n\r\n".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ request = item
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ XCTAssertEqual(request?.path, "/")
|
|
|
|
|
+ XCTAssertEqual(request?.method, "GET")
|
|
|
|
|
+ XCTAssertEqual(request?.httpVersion, .http10)
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ XCTAssert(false, "There should be no crash for valid http request.")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testContentLengthZero() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0"))
|
|
|
|
|
- XCTAssert(false, "Parser should throw an error if there is no next line symbol.")
|
|
|
|
|
- } catch { }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ var request: Request? = nil
|
|
|
|
|
+ let data = [UInt8]("GET / HTTP/1.0\r\nContent-Length: 0\r\n\r\n".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ request = item
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ XCTAssertEqual(request?.path, "/")
|
|
|
|
|
+ XCTAssertEqual(request?.method, "GET")
|
|
|
|
|
+ XCTAssertEqual(request?.body.count, 0)
|
|
|
|
|
+ XCTAssertEqual(request?.httpVersion, .http10)
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testContentLengthNonZero() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\r"))
|
|
|
|
|
- XCTAssert(false, "Parser should throw an error if there is no next line symbol.")
|
|
|
|
|
- } catch { }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ var request: Request? = nil
|
|
|
|
|
+ let data = [UInt8]("GET / HTTP/1.0\r\nContent-Length: 5\r\n\r\n12345".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ request = item
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ XCTAssertEqual(request?.path, "/")
|
|
|
|
|
+ XCTAssertEqual(request?.method, "GET")
|
|
|
|
|
+ XCTAssertEqual(request?.body.count, 5)
|
|
|
|
|
+ XCTAssertEqual((request?.body)!, [49, 50, 51, 52, 53])
|
|
|
|
|
+ XCTAssertEqual(request?.httpVersion, .http10)
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testContentLengthWithBodyChunk() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\n"))
|
|
|
|
|
- XCTAssert(false, "Parser should throw an error if there is no 'Content-Length' header.")
|
|
|
|
|
- } catch { }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ var request: Request? = nil
|
|
|
|
|
+ let data = [UInt8]("GET / HTTP/1.0\r\nContent-Length: 10\r\n\r\n123".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ request = item
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ XCTAssertNil(request)
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testContentProcessedInChunks() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\r\nContent-Length: 0\r\n\r\n"))
|
|
|
|
|
|
|
+ var request: Request? = nil
|
|
|
|
|
+
|
|
|
|
|
+ let processor = HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ request = item
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ let chunk1 = [UInt8]("GET /chunk HTTP/1.0\r\nContent-Length: 20\r\n\r\n123".utf8)
|
|
|
|
|
+ try processor.process(chunk1[0..<chunk1.count])
|
|
|
|
|
+
|
|
|
|
|
+ XCTAssertNil(request)
|
|
|
|
|
+
|
|
|
|
|
+ let chunk2 = [UInt8]("1234567890".utf8)
|
|
|
|
|
+ try processor.process(chunk2[0..<chunk2.count])
|
|
|
|
|
+
|
|
|
|
|
+ XCTAssertNil(request)
|
|
|
|
|
+
|
|
|
|
|
+ let chunk3 = [UInt8]("1234567".utf8)
|
|
|
|
|
+ try processor.process(chunk3[0..<chunk3.count])
|
|
|
|
|
+
|
|
|
|
|
+ XCTAssertEqual(request?.path, "/chunk")
|
|
|
|
|
+ XCTAssertEqual(request?.method, "GET")
|
|
|
|
|
+ XCTAssertEqual(request?.body.count, 20)
|
|
|
|
|
+ XCTAssertEqual(request?.httpVersion, .http10)
|
|
|
|
|
+
|
|
|
} catch {
|
|
} catch {
|
|
|
- XCTAssert(false, "Parser should not throw any errors if there is a valid 'Content-Length' header.")
|
|
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testHeaders() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 0\r\n\n"))
|
|
|
|
|
|
|
+ var request: Request? = nil
|
|
|
|
|
+ let data = [UInt8]("GET / HTTP/1.0\r\na: b\r\nc: d\r\nContent-Length: 0\r\n\r\n".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ request = item
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ XCTAssertNotNil(request)
|
|
|
|
|
+ XCTAssertEqual(request?.headers.first?.0, "a")
|
|
|
|
|
+ XCTAssertEqual(request?.headers.first?.1, "b")
|
|
|
|
|
+ XCTAssertEqual(request?.headers[1].0, "c")
|
|
|
|
|
+ XCTAssertEqual(request?.headers[1].1, "d")
|
|
|
} catch {
|
|
} catch {
|
|
|
- XCTAssert(false, "Parser should not throw any errors if there is a valid 'Content-Length' header.")
|
|
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testPath() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 5\n\n12345"))
|
|
|
|
|
|
|
+ var request: Request? = nil
|
|
|
|
|
+ let data = [UInt8]("GET /a/b/c/d?1345678=1231 HTTP/1.0\r\nContent-Length: 0\r\n\r\n".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ request = item
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ XCTAssertEqual(request?.path, "/a/b/c/d")
|
|
|
|
|
+ XCTAssertEqual(request?.method, "GET")
|
|
|
|
|
+ XCTAssertEqual(request?.body.count, 0)
|
|
|
|
|
+ XCTAssertEqual(request?.httpVersion, .http10)
|
|
|
} catch {
|
|
} catch {
|
|
|
- XCTAssert(false, "Parser should not throw any errors if there is a valid 'Content-Length' header.")
|
|
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func testPathWithComplexQuery() {
|
|
|
do {
|
|
do {
|
|
|
- let _ = try parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 10\r\n\n"))
|
|
|
|
|
- XCTAssert(false, "Parser should throw an error if request' body is too short.")
|
|
|
|
|
- } catch { }
|
|
|
|
|
-
|
|
|
|
|
- var r = try? parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nContent-Length: 10\n\n1234567890"))
|
|
|
|
|
- XCTAssertEqual(r?.method, "GET", "Parser should extract HTTP method name from the status line.")
|
|
|
|
|
- XCTAssertEqual(r?.path, "/", "Parser should extract HTTP path value from the status line.")
|
|
|
|
|
- XCTAssertEqual(r?.headers["content-length"], "10", "Parser should extract Content-Length header value.")
|
|
|
|
|
-
|
|
|
|
|
- r = try? parser.readHttpRequest(TestSocket("POST / HTTP/1.0\nContent-Length: 10\n\n1234567890"))
|
|
|
|
|
- XCTAssertEqual(r?.method, "POST", "Parser should extract HTTP method name from the status line.")
|
|
|
|
|
-
|
|
|
|
|
- r = try? parser.readHttpRequest(TestSocket("GET / HTTP/1.0\nHeader1: 1\nHeader2: 2\nContent-Length: 0\n\n"))
|
|
|
|
|
- XCTAssertEqual(r?.headers["header1"], "1", "Parser should extract multiple headers from the request.")
|
|
|
|
|
- XCTAssertEqual(r?.headers["header2"], "2", "Parser should extract multiple headers from the request.")
|
|
|
|
|
|
|
+ var request: Request? = nil
|
|
|
|
|
+ let data = [UInt8]("GET /a/b/c/d?key=value1?&key=???s HTTP/1.0\r\nContent-Length: 0\r\n\r\n".utf8)
|
|
|
|
|
+ try HttpIncomingDataPorcessor(0, { item in
|
|
|
|
|
+ request = item
|
|
|
|
|
+ }).process(data[0..<data.count])
|
|
|
|
|
+ XCTAssertEqual(request?.path, "/a/b/c/d")
|
|
|
|
|
+ XCTAssertEqual(request?.method, "GET")
|
|
|
|
|
+ XCTAssertEqual(request?.query[0].0, "key")
|
|
|
|
|
+ XCTAssertEqual(request?.query[0].1, "value1?")
|
|
|
|
|
+ XCTAssertEqual(request?.query[1].0, "key")
|
|
|
|
|
+ XCTAssertEqual(request?.query[1].1, "???s")
|
|
|
|
|
+ XCTAssertEqual(request?.body.count, 0)
|
|
|
|
|
+ XCTAssertEqual(request?.httpVersion, .http10)
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ XCTAssert(false, "No exception")
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|