Skip to content

Commit 342a579

Browse files
authored
Record what reserved bits are valid (#8)
1 parent 1d60de6 commit 342a579

File tree

4 files changed

+28
-0
lines changed

4 files changed

+28
-0
lines changed

Sources/WSCompression/PerMessageDeflateExtension.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,9 @@ struct PerMessageDeflateExtension: WebSocketExtension {
296296
}
297297
return frame
298298
}
299+
300+
/// Reserved bits extension uses
301+
var reservedBits: WebSocketFrame.ReservedBits { .rsv1 }
299302
}
300303

301304
extension WebSocketExtensionFactory {

Sources/WSCore/Extensions/WebSocketExtension.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ public protocol WebSocketExtension: Sendable {
3535
func processReceivedFrame(_ frame: WebSocketFrame, context: WebSocketExtensionContext) async throws -> WebSocketFrame
3636
/// Process frame about to be sent to websocket
3737
func processFrameToSend(_ frame: WebSocketFrame, context: WebSocketExtensionContext) async throws -> WebSocketFrame
38+
/// Reserved bits extension uses
39+
var reservedBits: WebSocketFrame.ReservedBits { get }
3840
/// shutdown extension
3941
func shutdown() async
4042
}
43+
44+
extension WebSocketExtension {
45+
/// Reserved bits extension uses (default is none)
46+
public var reservedBits: WebSocketFrame.ReservedBits { .init() }
47+
}

Sources/WSCore/WebSocketHandler.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,16 @@ public struct WebSocketCloseFrame: Sendable {
7171
let extensions: [any WebSocketExtension]
7272
let autoPing: AutoPingSetup
7373
let validateUTF8: Bool
74+
let reservedBits: WebSocketFrame.ReservedBits
7475

7576
@_spi(WSInternal) public init(extensions: [any WebSocketExtension], autoPing: AutoPingSetup, validateUTF8: Bool) {
7677
self.extensions = extensions
7778
self.autoPing = autoPing
7879
self.validateUTF8 = validateUTF8
80+
// store reserved bits used by this handler
81+
self.reservedBits = extensions.reduce(.init()) { partialResult, `extension` in
82+
partialResult.union(`extension`.reservedBits)
83+
}
7984
}
8085
}
8186

@@ -301,6 +306,16 @@ public struct WebSocketCloseFrame: Sendable {
301306
}
302307

303308
func receivedClose(_ frame: WebSocketFrame) async throws {
309+
guard frame.reservedBits.isEmpty else {
310+
try await self.sendClose(code: .protocolError, reason: nil)
311+
// Only server should initiate a connection close. Clients should wait for the
312+
// server to close the connection when it receives the WebSocket close packet
313+
// See https://www.rfc-editor.org/rfc/rfc6455#section-7.1.1
314+
if self.type == .server {
315+
self.outbound.finish()
316+
}
317+
return
318+
}
304319
switch self.stateMachine.receivedClose(frameData: frame.unmaskedData, validateUTF8: self.configuration.validateUTF8) {
305320
case .sendClose(let errorCode):
306321
try await self.sendClose(code: errorCode, reason: nil)

Sources/WSCore/WebSocketInboundStream.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ public final class WebSocketInboundStream: AsyncSequence, Sendable {
6767
case .pong:
6868
try await self.handler.onPong(frame)
6969
case .text, .binary, .continuation:
70+
guard self.handler.configuration.reservedBits.contains(frame.reservedBits) else {
71+
throw WebSocketHandler.InternalError.close(.protocolError)
72+
}
7073
// apply extensions
7174
var frame = frame
7275
for ext in self.handler.configuration.extensions.reversed() {

0 commit comments

Comments
 (0)