LengthPrefixedMessageReaderTests.swift 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import Foundation
  2. import XCTest
  3. import SwiftGRPCNIO
  4. import NIO
  5. class LengthPrefixedMessageReaderTests: XCTestCase {
  6. static var allTests: [(String, (LengthPrefixedMessageReaderTests) -> () throws -> Void)] {
  7. return [
  8. ("testNextMessageReturnsNilWhenNoBytesAppended", testNextMessageReturnsNilWhenNoBytesAppended),
  9. ("testNextMessageReturnsMessageIsAppendedInOneBuffer", testNextMessageReturnsMessageIsAppendedInOneBuffer),
  10. ("testNextMessageReturnsMessageForZeroLengthMessage", testNextMessageReturnsMessageForZeroLengthMessage),
  11. ("testNextMessageDeliveredAcrossMultipleByteBuffers", testNextMessageDeliveredAcrossMultipleByteBuffers),
  12. ("testNextMessageWhenMultipleMessagesAreBuffered", testNextMessageWhenMultipleMessagesAreBuffered),
  13. ("testNextMessageReturnsNilWhenNoMessageLengthIsAvailable", testNextMessageReturnsNilWhenNoMessageLengthIsAvailable),
  14. ("testNextMessageReturnsNilWhenNotAllMessageLengthIsAvailable", testNextMessageReturnsNilWhenNotAllMessageLengthIsAvailable),
  15. ("testNextMessageReturnsNilWhenNoMessageBytesAreAvailable", testNextMessageReturnsNilWhenNoMessageBytesAreAvailable),
  16. ("testNextMessageReturnsNilWhenNotAllMessageBytesAreAvailable", testNextMessageReturnsNilWhenNotAllMessageBytesAreAvailable),
  17. ("testNextMessageThrowsWhenCompressionMechanismIsNotSupported", testNextMessageThrowsWhenCompressionMechanismIsNotSupported),
  18. ("testNextMessageThrowsWhenCompressionFlagIsSetButNotExpected", testNextMessageThrowsWhenCompressionFlagIsSetButNotExpected),
  19. ("testNextMessageDoesNotThrowWhenCompressionFlagIsExpectedButNotSet", testNextMessageDoesNotThrowWhenCompressionFlagIsExpectedButNotSet),
  20. ("testAppendReadsAllBytes", testAppendReadsAllBytes),
  21. ]
  22. }
  23. var reader = LengthPrefixedMessageReader(mode: .client, compressionMechanism: .none)
  24. var allocator = ByteBufferAllocator()
  25. func byteBuffer(withBytes bytes: [UInt8]) -> ByteBuffer {
  26. var buffer = allocator.buffer(capacity: bytes.count)
  27. buffer.writeBytes(bytes)
  28. return buffer
  29. }
  30. final let twoByteMessage: [UInt8] = [0x01, 0x02]
  31. func lengthPrefixedTwoByteMessage(withCompression compression: Bool = false) -> [UInt8] {
  32. return [
  33. compression ? 0x01 : 0x00, // 1-byte compression flag
  34. 0x00, 0x00, 0x00, 0x02, // 4-byte message length (2)
  35. ] + twoByteMessage
  36. }
  37. func assertMessagesEqual(expected expectedBytes: [UInt8], actual buffer: ByteBuffer?, file: StaticString = #file, line: UInt = #line) {
  38. guard let buffer = buffer else {
  39. XCTFail("buffer is nil", file: file, line: line)
  40. return
  41. }
  42. guard let bytes = buffer.getBytes(at: buffer.readerIndex, length: expectedBytes.count) else {
  43. XCTFail("Expected \(expectedBytes.count) bytes, but only \(buffer.readableBytes) bytes are readable", file: file, line: line)
  44. return
  45. }
  46. XCTAssertEqual(expectedBytes, bytes, file: file, line: line)
  47. }
  48. func testNextMessageReturnsNilWhenNoBytesAppended() throws {
  49. XCTAssertNil(try reader.nextMessage())
  50. }
  51. func testNextMessageReturnsMessageIsAppendedInOneBuffer() throws {
  52. var buffer = byteBuffer(withBytes: lengthPrefixedTwoByteMessage())
  53. reader.append(buffer: &buffer)
  54. self.assertMessagesEqual(expected: twoByteMessage, actual: try reader.nextMessage())
  55. }
  56. func testNextMessageReturnsMessageForZeroLengthMessage() throws {
  57. let bytes: [UInt8] = [
  58. 0x00, // 1-byte compression flag
  59. 0x00, 0x00, 0x00, 0x00, // 4-byte message length (0)
  60. // 0-byte message
  61. ]
  62. var buffer = byteBuffer(withBytes: bytes)
  63. reader.append(buffer: &buffer)
  64. self.assertMessagesEqual(expected: [], actual: try reader.nextMessage())
  65. }
  66. func testNextMessageDeliveredAcrossMultipleByteBuffers() throws {
  67. let firstBytes: [UInt8] = [
  68. 0x00, // 1-byte compression flag
  69. 0x00, 0x00, 0x00, // first 3 bytes of 4-byte message length
  70. ]
  71. let secondBytes: [UInt8] = [
  72. 0x02, // fourth byte of 4-byte message length (2)
  73. 0xf0, 0xba, // 2-byte message
  74. ]
  75. var firstBuffer = byteBuffer(withBytes: firstBytes)
  76. reader.append(buffer: &firstBuffer)
  77. var secondBuffer = byteBuffer(withBytes: secondBytes)
  78. reader.append(buffer: &secondBuffer)
  79. self.assertMessagesEqual(expected: [0xf0, 0xba], actual: try reader.nextMessage())
  80. }
  81. func testNextMessageWhenMultipleMessagesAreBuffered() throws {
  82. let bytes: [UInt8] = [
  83. // 1st message
  84. 0x00, // 1-byte compression flag
  85. 0x00, 0x00, 0x00, 0x02, // 4-byte message length (2)
  86. 0x0f, 0x00, // 2-byte message
  87. // 2nd message
  88. 0x00, // 1-byte compression flag
  89. 0x00, 0x00, 0x00, 0x04, // 4-byte message length (4)
  90. 0xde, 0xad, 0xbe, 0xef, // 4-byte message
  91. // 3rd message
  92. 0x00, // 1-byte compression flag
  93. 0x00, 0x00, 0x00, 0x01, // 4-byte message length (1)
  94. 0x01, // 1-byte message
  95. ]
  96. var buffer = byteBuffer(withBytes: bytes)
  97. reader.append(buffer: &buffer)
  98. self.assertMessagesEqual(expected: [0x0f, 0x00], actual: try reader.nextMessage())
  99. self.assertMessagesEqual(expected: [0xde, 0xad, 0xbe, 0xef], actual: try reader.nextMessage())
  100. self.assertMessagesEqual(expected: [0x01], actual: try reader.nextMessage())
  101. }
  102. func testNextMessageReturnsNilWhenNoMessageLengthIsAvailable() throws {
  103. let bytes: [UInt8] = [
  104. 0x00, // 1-byte compression flag
  105. ]
  106. var buffer = byteBuffer(withBytes: bytes)
  107. reader.append(buffer: &buffer)
  108. XCTAssertNil(try reader.nextMessage())
  109. // Ensure we can read a message when the rest of the bytes are delivered
  110. let restOfBytes: [UInt8] = [
  111. 0x00, 0x00, 0x00, 0x01, // 4-byte message length (1)
  112. 0x00, // 1-byte message
  113. ]
  114. var secondBuffer = byteBuffer(withBytes: restOfBytes)
  115. reader.append(buffer: &secondBuffer)
  116. self.assertMessagesEqual(expected: [0x00], actual: try reader.nextMessage())
  117. }
  118. func testNextMessageReturnsNilWhenNotAllMessageLengthIsAvailable() throws {
  119. let bytes: [UInt8] = [
  120. 0x00, // 1-byte compression flag
  121. 0x00, 0x00, // 2-bytes of message length (should be 4)
  122. ]
  123. var buffer = byteBuffer(withBytes: bytes)
  124. reader.append(buffer: &buffer)
  125. XCTAssertNil(try reader.nextMessage())
  126. // Ensure we can read a message when the rest of the bytes are delivered
  127. let restOfBytes: [UInt8] = [
  128. 0x00, 0x01, // 4-byte message length (1)
  129. 0x00, // 1-byte message
  130. ]
  131. var secondBuffer = byteBuffer(withBytes: restOfBytes)
  132. reader.append(buffer: &secondBuffer)
  133. self.assertMessagesEqual(expected: [0x00], actual: try reader.nextMessage())
  134. }
  135. func testNextMessageReturnsNilWhenNoMessageBytesAreAvailable() throws {
  136. let bytes: [UInt8] = [
  137. 0x00, // 1-byte compression flag
  138. 0x00, 0x00, 0x00, 0x02, // 4-byte message length (2)
  139. ]
  140. var buffer = byteBuffer(withBytes: bytes)
  141. reader.append(buffer: &buffer)
  142. XCTAssertNil(try reader.nextMessage())
  143. // Ensure we can read a message when the rest of the bytes are delivered
  144. var secondBuffer = byteBuffer(withBytes: twoByteMessage)
  145. reader.append(buffer: &secondBuffer)
  146. self.assertMessagesEqual(expected: twoByteMessage, actual: try reader.nextMessage())
  147. }
  148. func testNextMessageReturnsNilWhenNotAllMessageBytesAreAvailable() throws {
  149. let bytes: [UInt8] = [
  150. 0x00, // 1-byte compression flag
  151. 0x00, 0x00, 0x00, 0x02, // 4-byte message length (2)
  152. 0x00, // 1-byte of message
  153. ]
  154. var buffer = byteBuffer(withBytes: bytes)
  155. reader.append(buffer: &buffer)
  156. XCTAssertNil(try reader.nextMessage())
  157. // Ensure we can read a message when the rest of the bytes are delivered
  158. let restOfBytes: [UInt8] = [
  159. 0x01 // final byte of message
  160. ]
  161. var secondBuffer = byteBuffer(withBytes: restOfBytes)
  162. reader.append(buffer: &secondBuffer)
  163. self.assertMessagesEqual(expected: [0x00, 0x01], actual: try reader.nextMessage())
  164. }
  165. func testNextMessageThrowsWhenCompressionMechanismIsNotSupported() throws {
  166. // Unknown should never be supported.
  167. reader.compressionMechanism = .unknown
  168. XCTAssertFalse(reader.compressionMechanism.supported)
  169. var buffer = byteBuffer(withBytes: lengthPrefixedTwoByteMessage(withCompression: true))
  170. reader.append(buffer: &buffer)
  171. XCTAssertThrowsError(try reader.nextMessage()) { error in
  172. XCTAssertEqual(.unsupportedCompressionMechanism("unknown"), (error as? GRPCError)?.error as? GRPCCommonError)
  173. }
  174. }
  175. func testNextMessageThrowsWhenCompressionFlagIsSetButNotExpected() throws {
  176. // Default compression mechanism is `.none` which requires that no
  177. // compression flag is set as it indicates a lack of message encoding header.
  178. XCTAssertFalse(reader.compressionMechanism.requiresFlag)
  179. var buffer = byteBuffer(withBytes: lengthPrefixedTwoByteMessage(withCompression: true))
  180. reader.append(buffer: &buffer)
  181. XCTAssertThrowsError(try reader.nextMessage()) { error in
  182. XCTAssertEqual(.unexpectedCompression, (error as? GRPCError)?.error as? GRPCCommonError)
  183. }
  184. }
  185. func testNextMessageDoesNotThrowWhenCompressionFlagIsExpectedButNotSet() throws {
  186. // `.identity` should always be supported and requires a flag.
  187. reader.compressionMechanism = .identity
  188. XCTAssertTrue(reader.compressionMechanism.supported)
  189. XCTAssertTrue(reader.compressionMechanism.requiresFlag)
  190. var buffer = byteBuffer(withBytes: lengthPrefixedTwoByteMessage())
  191. reader.append(buffer: &buffer)
  192. self.assertMessagesEqual(expected: twoByteMessage, actual: try reader.nextMessage())
  193. }
  194. func testAppendReadsAllBytes() throws {
  195. var buffer = byteBuffer(withBytes: lengthPrefixedTwoByteMessage())
  196. reader.append(buffer: &buffer)
  197. XCTAssertEqual(0, buffer.readableBytes)
  198. }
  199. }