Skip to content

Commit 561c770

Browse files
committed
WSMessage.close
1 parent e004af7 commit 561c770

File tree

4 files changed

+54
-14
lines changed

4 files changed

+54
-14
lines changed

FlyingFox/Sources/WebSocket/WSHandler.swift

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,13 @@ public struct MessageFrameWSHandler: WSHandler {
106106
} else if let frame = try makeResponseFrames(for: frame) {
107107
framesOut.yield(frame)
108108
}
109+
if frame.opcode == .close {
110+
throw FrameError.closed(frame)
111+
}
109112
}
110113
framesOut.finish(throwing: nil)
111-
} catch FrameError.closed {
112-
framesOut.yield(.close(message: "Goodbye"))
114+
} catch FrameError.closed(let frame) {
115+
framesOut.yield(frame)
113116
framesOut.finish(throwing: nil)
114117
} catch {
115118
framesOut.finish(throwing: error)
@@ -136,11 +139,26 @@ public struct MessageFrameWSHandler: WSHandler {
136139
return .text(string)
137140
case .binary:
138141
return .data(frame.payload)
142+
case .close:
143+
let (code, reason) = try makeCloseCode(from: frame.payload)
144+
return .close(code: code, reason: reason)
139145
default:
140146
return nil
141147
}
142148
}
143149

150+
func makeCloseCode(from payload: Data) throws -> (UInt16, String) {
151+
guard payload.count >= 2 else {
152+
return (1005, "")
153+
}
154+
155+
let code = payload.withUnsafeBytes { $0.load(as: UInt16.self).bigEndian }
156+
guard let reason = String(data: payload.dropFirst(2), encoding: .utf8) else {
157+
throw FrameError.invalid("Invalid UTF8 Sequence")
158+
}
159+
return (code, reason)
160+
}
161+
144162
func makeResponseFrames(for frame: WSFrame) throws -> WSFrame? {
145163
switch frame.opcode {
146164
case .ping:
@@ -149,19 +167,19 @@ public struct MessageFrameWSHandler: WSHandler {
149167
return response
150168
case .pong:
151169
return nil
152-
case .close:
153-
throw FrameError.closed
154170
default:
155171
throw FrameError.invalid("Unexpected Frame")
156172
}
157173
}
158174

159175
func makeFrames(for message: WSMessage) -> [WSFrame] {
160176
switch message {
161-
case .text(let string):
177+
case let .text(string):
162178
return Self.makeFrames(opcode: .text, payload: string.data(using: .utf8)!, size: frameSize)
163-
case .data(let data):
179+
case let .data(data):
164180
return Self.makeFrames(opcode: .binary, payload: data, size: frameSize)
181+
case let .close(code: code, reason: message):
182+
return [WSFrame.close(code: code, message: message)]
165183
}
166184
}
167185

@@ -179,7 +197,7 @@ public struct MessageFrameWSHandler: WSHandler {
179197
extension MessageFrameWSHandler {
180198

181199
enum FrameError: Error {
182-
case closed
200+
case closed(WSFrame)
183201
case invalid(String)
184202
}
185203
}

FlyingFox/Sources/WebSocket/WSMessage.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import Foundation
3434
public enum WSMessage: @unchecked Sendable, Hashable {
3535
case text(String)
3636
case data(Data)
37+
case close(code: UInt16 = 1000, reason: String = "")
3738
}
3839

3940
public protocol WSMessageHandler: Sendable {

FlyingFox/Tests/WebSocket/WSHandlerTests.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,29 @@ struct WSHandlerTests {
4545
#expect(throws: (any Error).self) {
4646
try handler.makeMessage(for: .make(fin: true, opcode: .text, payload: Data([0x03, 0xE8])))
4747
}
48-
4948
#expect(
5049
try handler.makeMessage(for: .make(fin: true, opcode: .binary, payload: Data([0x01, 0x02]))) == .data(Data([0x01, 0x02]))
5150
)
52-
5351
#expect(
5452
try handler.makeMessage(for: .make(fin: true, opcode: .ping)) == nil
5553
)
5654
#expect(
5755
try handler.makeMessage(for: .make(fin: true, opcode: .pong)) == nil
5856
)
57+
}
58+
59+
@Test
60+
func frames_CreatesCloseMessage() throws {
61+
let handler = MessageFrameWSHandler.make()
62+
let payload = Data([0x13, 0x87, .ascii("f"), .ascii("i"), .ascii("s"), .ascii("h")])
63+
64+
#expect(
65+
try handler.makeMessage(for: .make(fin: true, opcode: .close, payload: payload)) ==
66+
.close(code: 4999, reason: "fish")
67+
)
5968
#expect(
60-
try handler.makeMessage(for: .make(fin: true, opcode: .close)) == nil
69+
try handler.makeMessage(for: .make(fin: true, opcode: .close)) ==
70+
.close(code: 1005, reason: "")
6171
)
6272
}
6373

@@ -114,7 +124,7 @@ struct WSHandlerTests {
114124
)
115125

116126
#expect(
117-
try await frames.collectAll() == [.pong, .close(message: "Goodbye")]
127+
try await frames.collectAll() == [.pong, .close]
118128
)
119129
}
120130

FlyingFox/XCTests/WebSocket/WSHandlerTests.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,19 @@ final class WSHandlerTests: XCTestCase {
5757
XCTAssertNil(
5858
try handler.makeMessage(for: .make(fin: true, opcode: .pong))
5959
)
60-
XCTAssertNil(
61-
try handler.makeMessage(for: .make(fin: true, opcode: .close))
60+
}
61+
62+
func testFrames_CreatesCloseMessage() throws {
63+
let handler = MessageFrameWSHandler.make()
64+
let payload = Data([0x13, 0x87, .ascii("f"), .ascii("i"), .ascii("s"), .ascii("h")])
65+
66+
XCTAssertEqual(
67+
try handler.makeMessage(for: .make(fin: true, opcode: .close, payload: payload)),
68+
.close(code: 4999, reason: "fish")
69+
)
70+
XCTAssertEqual(
71+
try handler.makeMessage(for: .make(fin: true, opcode: .close)),
72+
.close(code: 1005, reason: "")
6273
)
6374
}
6475

@@ -112,7 +123,7 @@ final class WSHandlerTests: XCTestCase {
112123

113124
await AsyncAssertEqual(
114125
try await frames.collectAll(),
115-
[.pong, .close(message: "Goodbye")]
126+
[.pong, .close]
116127
)
117128
}
118129

0 commit comments

Comments
 (0)