Skip to content

Commit c11f6b7

Browse files
authored
Merge pull request #146 from swhitty/WSCloseCode
Include reason within WSCloseCode
2 parents 7d98b0c + 9cd8705 commit c11f6b7

File tree

8 files changed

+54
-46
lines changed

8 files changed

+54
-46
lines changed

FlyingFox/Sources/WebSocket/WSCloseCode.swift

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,35 +31,37 @@
3131

3232
import Foundation
3333

34-
public struct WSCloseCode: RawRepresentable, Sendable, Hashable {
35-
public var rawValue: UInt16
36-
37-
public init(rawValue: UInt16) {
38-
self.rawValue = rawValue
39-
}
34+
public struct WSCloseCode: Sendable, Hashable {
35+
public var code: UInt16
36+
public var reason: String
4037

4138
public init(_ code: UInt16) {
42-
self.rawValue = code
39+
self.code = code
40+
self.reason = ""
41+
}
42+
public init(_ code: UInt16, reason: String) {
43+
self.code = code
44+
self.reason = reason
4345
}
4446
}
4547

4648
public extension WSCloseCode {
4749
// The following codes are based on:
4850
// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code
4951

50-
static let normalClosure = WSCloseCode(1000)
51-
static let goingAway = WSCloseCode(1001)
52-
static let protocolError = WSCloseCode(1002)
53-
static let unsupportedData = WSCloseCode(1003)
54-
static let noStatusReceived = WSCloseCode(1005)
55-
static let abnormalClosure = WSCloseCode(1006)
56-
static let invalidFramePayloadData = WSCloseCode(1007)
57-
static let policyViolation = WSCloseCode(1008)
58-
static let messageTooBig = WSCloseCode(1009)
59-
static let mandatoryExtensionMissing = WSCloseCode(1010)
60-
static let internalServerError = WSCloseCode(1011)
61-
static let serviceRestart = WSCloseCode(1012)
62-
static let tryAgainLater = WSCloseCode(1013)
63-
static let badGateway = WSCloseCode(1014)
64-
static let tlsHandshakeFailure = WSCloseCode(1015)
52+
static let normalClosure = WSCloseCode(1000)
53+
static let goingAway = WSCloseCode(1001, reason: "Going Away")
54+
static let protocolError = WSCloseCode(1002, reason: "Protocol Error")
55+
static let unsupportedData = WSCloseCode(1003, reason: "Unsupported Data")
56+
static let noStatusReceived = WSCloseCode(1005, reason: "No Status Received")
57+
static let abnormalClosure = WSCloseCode(1006, reason: "Abnormal Closure")
58+
static let invalidFramePayload = WSCloseCode(1007, reason: "Invalid Frame Payload")
59+
static let policyViolation = WSCloseCode(1008, reason: "Policy Violation")
60+
static let messageTooBig = WSCloseCode(1009, reason: "Message Too Big")
61+
static let mandatoryExtensionMissing = WSCloseCode(1010, reason: "Mandatory Extension Missing")
62+
static let internalServerError = WSCloseCode(1011, reason: "Internal Server Error")
63+
static let serviceRestart = WSCloseCode(1012, reason: "Service Restart")
64+
static let tryAgainLater = WSCloseCode(1013, reason: "Try Again Later")
65+
static let badGateway = WSCloseCode(1014, reason: "Bad Gateway")
66+
static let tlsHandshakeFailure = WSCloseCode(1015, reason: "TLS Handshake Failure")
6567
}

FlyingFox/Sources/WebSocket/WSFrame.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,14 @@ public struct WSFrame: Sendable, Hashable {
9393
public extension WSFrame {
9494
static func close(message: String = "", mask: Mask? = nil) -> Self {
9595
close(
96-
code: message.isEmpty ? .normalClosure : .protocolError,
97-
message: message,
96+
code: message.isEmpty ? .normalClosure : WSCloseCode(WSCloseCode.protocolError.code, reason: message),
9897
mask: mask
9998
)
10099
}
101100

102-
static func close(code: WSCloseCode, message: String, mask: Mask? = nil) -> Self {
103-
var payload = Data([UInt8(code.rawValue >> 8), UInt8(code.rawValue & 0xFF)])
104-
if let data = message.data(using: .utf8) {
101+
static func close(code: WSCloseCode, mask: Mask? = nil) -> Self {
102+
var payload = Data([UInt8(code.code >> 8), UInt8(code.code & 0xFF)])
103+
if let data = code.reason.data(using: .utf8) {
105104
payload.append(contentsOf: data)
106105
}
107106
return WSFrame(

FlyingFox/Sources/WebSocket/WSHandler.swift

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,18 @@ public struct MessageFrameWSHandler: WSHandler {
119119
}
120120
}
121121
group.addTask {
122-
for await message in messagesOut {
123-
for frame in makeFrames(for: message) {
124-
framesOut.yield(frame)
122+
do {
123+
for await message in messagesOut {
124+
for frame in makeFrames(for: message) {
125+
framesOut.yield(frame)
126+
if frame.opcode == .close {
127+
throw FrameError.closed(frame)
128+
}
129+
}
125130
}
131+
framesOut.finish(throwing: nil)
132+
} catch {
133+
framesOut.finish(throwing: nil)
126134
}
127135
}
128136
await group.next()!
@@ -140,23 +148,22 @@ public struct MessageFrameWSHandler: WSHandler {
140148
case .binary:
141149
return .data(frame.payload)
142150
case .close:
143-
let (code, reason) = try makeCloseCode(from: frame.payload)
144-
return .close(code: code, reason: reason)
151+
return try .close(makeCloseCode(from: frame.payload))
145152
default:
146153
return nil
147154
}
148155
}
149156

150-
func makeCloseCode(from payload: Data) throws -> (WSCloseCode, String) {
157+
func makeCloseCode(from payload: Data) throws -> WSCloseCode {
151158
guard payload.count >= 2 else {
152-
return (.noStatusReceived, "")
159+
return .noStatusReceived
153160
}
154161

155162
let statusCode = payload.withUnsafeBytes { $0.load(as: UInt16.self).bigEndian }
156163
guard let reason = String(data: payload.dropFirst(2), encoding: .utf8) else {
157164
throw FrameError.invalid("Invalid UTF8 Sequence")
158165
}
159-
return (WSCloseCode(statusCode), reason)
166+
return WSCloseCode(statusCode, reason: reason)
160167
}
161168

162169
func makeResponseFrames(for frame: WSFrame) throws -> WSFrame? {
@@ -178,8 +185,8 @@ public struct MessageFrameWSHandler: WSHandler {
178185
return Self.makeFrames(opcode: .text, payload: string.data(using: .utf8)!, size: frameSize)
179186
case let .data(data):
180187
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)]
188+
case let .close(code):
189+
return [WSFrame.close(code: code)]
183190
}
184191
}
185192

FlyingFox/Sources/WebSocket/WSMessage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import Foundation
3434
public enum WSMessage: @unchecked Sendable, Hashable {
3535
case text(String)
3636
case data(Data)
37-
case close(code: WSCloseCode = .normalClosure, reason: String = "")
37+
case close(WSCloseCode = .normalClosure)
3838
}
3939

4040
public protocol WSMessageHandler: Sendable {

FlyingFox/Tests/WebSocket/WSFrameTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ struct WSFrameTests {
7070
)
7171
)
7272
#expect(
73-
WSFrame.close(code: WSCloseCode(4999), message: "Err") == .make(
73+
WSFrame.close(code: WSCloseCode(4999, reason: "Err")) == .make(
7474
fin: true,
7575
opcode: .close,
7676
mask: nil,
7777
payload: Data([0x13, 0x87, .ascii("E"), .ascii("r"), .ascii("r")])
7878
)
7979
)
8080
#expect(
81-
WSFrame.close(code: WSCloseCode(4999), message: "Err", mask: .mock) == .make(
81+
WSFrame.close(code: WSCloseCode(4999, reason: "Err"), mask: .mock) == .make(
8282
fin: true,
8383
opcode: .close,
8484
mask: .mock,

FlyingFox/Tests/WebSocket/WSHandlerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ struct WSHandlerTests {
6363

6464
#expect(
6565
try handler.makeMessage(for: .make(fin: true, opcode: .close, payload: payload)) ==
66-
.close(code: WSCloseCode(4999), reason: "fish")
66+
.close(WSCloseCode(4999, reason: "fish"))
6767
)
6868
#expect(
6969
try handler.makeMessage(for: .make(fin: true, opcode: .close)) ==
70-
.close(code: .noStatusReceived, reason: "")
70+
.close(.noStatusReceived)
7171
)
7272
}
7373

FlyingFox/XCTests/WebSocket/WSFrameTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ final class WSFrameTests: XCTestCase {
6565
payload: Data([0x03, 0xEA, .ascii("E"), .ascii("r"), .ascii("r")]))
6666
)
6767
XCTAssertEqual(
68-
WSFrame.close(code: WSCloseCode(4999), message: "Err"),
68+
WSFrame.close(code: WSCloseCode(4999, reason: "Err")),
6969
.make(
7070
fin: true,
7171
opcode: .close,
@@ -74,7 +74,7 @@ final class WSFrameTests: XCTestCase {
7474
)
7575
)
7676
XCTAssertEqual(
77-
WSFrame.close(code: WSCloseCode(4999), message: "Err", mask: .mock),
77+
WSFrame.close(code: WSCloseCode(4999, reason: "Err"), mask: .mock),
7878
.make(
7979
fin: true,
8080
opcode: .close,

FlyingFox/XCTests/WebSocket/WSHandlerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ final class WSHandlerTests: XCTestCase {
6565

6666
XCTAssertEqual(
6767
try handler.makeMessage(for: .make(fin: true, opcode: .close, payload: payload)),
68-
.close(code: WSCloseCode(4999), reason: "fish")
68+
.close(WSCloseCode(4999, reason: "fish"))
6969
)
7070
XCTAssertEqual(
7171
try handler.makeMessage(for: .make(fin: true, opcode: .close)),
72-
.close(code: .noStatusReceived, reason: "")
72+
.close(.noStatusReceived)
7373
)
7474
}
7575

0 commit comments

Comments
 (0)