- Notifications
You must be signed in to change notification settings - Fork 9
Implement a get request #14
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
Changes from 1 commit
87d1682 186088c d772994 1bdd64d e3a764c c2e774f bafe676 923cf2b 423d8f9 48772a6 79b653f 9056f40 118b029 6bad1ff 437783a bfb446a bc1b03b 4288843 File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -42,6 +42,25 @@ struct MemcachedRequestEncoder: MessageToByteEncoder { | |
| out.writeBuffer(&command.value) | ||
| out.writeInteger(UInt8.carriageReturn) | ||
| out.writeInteger(UInt8.newline) | ||
| | ||
| case .get(let command): | ||
| precondition(!command.key.isEmpty, "Key must not be empty") | ||
| | ||
| // write command and key | ||
| out.writeInteger(UInt8.m) | ||
| out.writeInteger(UInt8.g) | ||
| out.writeInteger(UInt8.whitespace) | ||
| out.writeBytes(command.key.utf8) | ||
| | ||
| // write flags if there are any | ||
| for flag in command.flags { | ||
| ||
| out.writeInteger(UInt8.whitespace) | ||
| out.writeInteger(flag.bytes) | ||
| } | ||
| | ||
| // write separator | ||
| out.writeInteger(UInt8.carriageReturn) | ||
| out.writeInteger(UInt8.newline) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -12,6 +12,8 @@ | |
| // | ||
| //===----------------------------------------------------------------------===// | ||
| | ||
| import NIOCore | ||
| | ||
| struct MemcachedResponse { | ||
| enum ReturnCode { | ||
| case HD | ||
| | @@ -40,4 +42,6 @@ struct MemcachedResponse { | |
| | ||
| var returnCode: ReturnCode | ||
| var dataLength: UInt64? | ||
| var flags: [MemcachedFlag]? | ||
| ||
| var value: ByteBuffer? | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -56,12 +56,10 @@ struct MemcachedResponseDecoder: NIOSingleStepByteToMessageDecoder { | |
| /// This error is thrown when EOF is encountered but there are still | ||
| /// readable bytes in the buffer, which can indicate a bad message. | ||
| case unexpectedEOF | ||
| | ||
| /// This error is thrown when EOF is encountered but there is still an expected next step | ||
| /// in the decoder's state machine. This error suggests that the message ended prematurely, | ||
| /// possibly indicating a bad message. | ||
| case unexpectedNextStep(NextStep) | ||
| | ||
| /// This error is thrown when an unexpected character is encountered in the buffer | ||
| /// during the decoding process. | ||
| case unexpectedCharacter(UInt8) | ||
| | @@ -77,6 +75,7 @@ struct MemcachedResponseDecoder: NIOSingleStepByteToMessageDecoder { | |
| /// Decode the next flag | ||
| case decodeNextFlag(MemcachedResponse.ReturnCode, UInt64?) | ||
| // TODO: Add a next step for decoding the response data if the return code is VA | ||
| case decodeValue(MemcachedResponse.ReturnCode, UInt64, [MemcachedFlag]) | ||
| } | ||
| | ||
| /// The action that the decoder will take in response to the current state of the ByteBuffer and the `NextStep`. | ||
| | @@ -120,8 +119,24 @@ struct MemcachedResponseDecoder: NIOSingleStepByteToMessageDecoder { | |
| | ||
| case .dataLengthOrFlag(let returnCode): | ||
| ||
| if returnCode == .VA { | ||
| // TODO: Implement decoding of data length | ||
| fatalError("Decoding for VA return code is not yet implemented") | ||
| guard let dataLength = buffer.readInteger(as: UInt64.self) else { | ||
| ||
| return .waitForMoreBytes | ||
| } | ||
| | ||
| let flags: [MemcachedFlag] = [] | ||
| while let nextByte = buffer.readInteger(as: UInt8.self), nextByte != UInt8.whitespace { | ||
| // TODO: Implement decoding of flags | ||
| } | ||
| | ||
| guard let nextByte = buffer.readInteger(as: UInt8.self), | ||
| nextByte == UInt8.carriageReturn, | ||
| let nextNextByte = buffer.readInteger(as: UInt8.self), | ||
| nextNextByte == UInt8.newline else { | ||
| return .waitForMoreBytes | ||
| } | ||
| | ||
| self.nextStep = .decodeValue(returnCode, dataLength, flags) | ||
| return .continueDecodeLoop | ||
| } | ||
| | ||
| guard let nextByte = buffer.readInteger(as: UInt8.self) else { | ||
| | @@ -151,6 +166,28 @@ struct MemcachedResponseDecoder: NIOSingleStepByteToMessageDecoder { | |
| let response = MemcachedResponse(returnCode: returnCode, dataLength: dataLength) | ||
| self.nextStep = .returnCode | ||
| return .returnDecodedResponse(response) | ||
| | ||
| case .decodeValue(let returnCode, let dataLength, let flags): | ||
| guard buffer.readableBytes >= dataLength else { | ||
| ||
| return .waitForMoreBytes | ||
| } | ||
| | ||
| var data: ByteBuffer = .init() | ||
| for _ in 0..<dataLength { | ||
| let byte = buffer.readInteger(as: UInt8.self) | ||
| data.writeInteger(byte!) | ||
| ||
| } | ||
| | ||
| guard let nextByte = buffer.readInteger(as: UInt8.self), | ||
| nextByte == UInt8.carriageReturn, | ||
| let nextNextByte = buffer.readInteger(as: UInt8.self), | ||
| nextNextByte == UInt8.newline else { | ||
| return .waitForMoreBytes | ||
| ||
| } | ||
| | ||
| let response = MemcachedResponse(returnCode: returnCode, dataLength: dataLength, flags: flags, value: data) | ||
| self.nextStep = .returnCode | ||
| return .returnDecodedResponse(response) | ||
| } | ||
| } | ||
| | ||
| | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -45,10 +45,26 @@ final class MemcachedResponseDecoderTests: XCTestCase { | |
| | ||
| buffer.writeInteger(returnCode) | ||
| | ||
| // If there's a data length, write it to the buffer. | ||
| // Write the data length <size> to the buffer. | ||
| if let dataLength = response.dataLength, response.returnCode == .VA { | ||
| buffer.writeInteger(UInt8.whitespace, as: UInt8.self) | ||
| buffer.writeInteger(dataLength, as: UInt64.self) | ||
| buffer.writeInteger(dataLength) | ||
| buffer.writeInteger(UInt8.whitespace) | ||
| | ||
| // Add flags after dataLength | ||
| for flag in response.flags ?? [] { | ||
| buffer.writeInteger(flag.bytes) | ||
| buffer.writeInteger(UInt8.whitespace) | ||
| } | ||
| | ||
| buffer.writeBytes([UInt8.carriageReturn, UInt8.newline]) | ||
| | ||
| // Write the value <data block> to the buffer if it exists | ||
| if let value = response.value { | ||
| var mutableValue = value | ||
| buffer.writeBuffer(&mutableValue) | ||
| } | ||
| | ||
| buffer.writeBytes([UInt8.carriageReturn, UInt8.newline]) | ||
| } | ||
| | ||
| buffer.writeBytes([UInt8.carriageReturn, UInt8.newline]) | ||
| | @@ -94,4 +110,34 @@ final class MemcachedResponseDecoderTests: XCTestCase { | |
| var buffer = self.makeMemcachedResponseByteBuffer(from: notFoundResponse) | ||
| try self.testDecodeResponse(buffer: &buffer, expectedReturnCode: .NF) | ||
| } | ||
| | ||
| func testDecodeValueResponse() throws { | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah we need a couple of more tests here that are trying to pass partial responses and assert that we return the right things. | ||
| let allocator = ByteBufferAllocator() | ||
| var valueBuffer = allocator.buffer(capacity: 8) | ||
| valueBuffer.writeString("hi") | ||
| | ||
| let valueResponse = MemcachedResponse(returnCode: .VA, dataLength: 2, flags: [], value: valueBuffer) | ||
| var buffer = self.makeMemcachedResponseByteBuffer(from: valueResponse) | ||
| | ||
| // Pass our response through the decoder | ||
| var output: MemcachedResponse? = nil | ||
| do { | ||
| output = try self.decoder.decode(buffer: &buffer) | ||
| } catch { | ||
| XCTFail("Decoding failed with error: \(error)") | ||
| } | ||
| // Check the decoded response | ||
| if let decoded = output { | ||
| XCTAssertEqual(decoded.returnCode, .VA) | ||
| XCTAssertEqual(decoded.dataLength, 2) | ||
| if let value = decoded.value { | ||
| var copiedBuffer = value | ||
| XCTAssertEqual(copiedBuffer.readString(length: Int(decoded.dataLength!)), "hi") | ||
| } else { | ||
| XCTFail("Decoded value was not found.") | ||
| } | ||
| } else { | ||
| XCTFail("Failed to decode the inbound response.") | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want to use something more specialised than an array here. We probably should create a
MemcachedFlagsstruct instead that holds all of the flags inline. We know what flags exists so we don't need the dynamism that array offers us.