mirror of
https://github.com/logos-storage/nim-websock.git
synced 2026-01-07 16:13:08 +00:00
now the frame can choose to read from buffered payload if available or read from asyncstream if the buffer is empty
169 lines
6.0 KiB
Nim
169 lines
6.0 KiB
Nim
## Nim-Libp2p
|
|
## Copyright (c) 2021 Status Research & Development GmbH
|
|
## Licensed under either of
|
|
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
## at your option.
|
|
## This file may not be copied, modified, or distributed except according to
|
|
## those terms.
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
import std/tables
|
|
import pkg/[chronos, chronos/streams/tlsstream]
|
|
import ./utils
|
|
|
|
const
|
|
SHA1DigestSize* = 20
|
|
WSHeaderSize* = 12
|
|
WSDefaultVersion* = 13
|
|
WSDefaultFrameSize* = 1 shl 20 # 1mb
|
|
WSMaxMessageSize* = 20 shl 20 # 20mb
|
|
WSGuid* = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
|
|
type
|
|
ReadyState* {.pure.} = enum
|
|
Connecting = 0 # The connection is not yet open.
|
|
Open = 1 # The connection is open and ready to communicate.
|
|
Closing = 2 # The connection is in the process of closing.
|
|
Closed = 3 # The connection is closed or couldn't be opened.
|
|
|
|
Opcode* {.pure.} = enum
|
|
## 4 bits. Defines the interpretation of the "Payload data".
|
|
Cont = 0x0 ## Denotes a continuation frame.
|
|
Text = 0x1 ## Denotes a text frame.
|
|
Binary = 0x2 ## Denotes a binary frame.
|
|
# 3-7 are reserved for further non-control frames.
|
|
Close = 0x8 ## Denotes a connection close.
|
|
Ping = 0x9 ## Denotes a ping.
|
|
Pong = 0xa ## Denotes a pong.
|
|
# B-F are reserved for further control frames.
|
|
Reserved = 0xf
|
|
|
|
HeaderFlag* {.pure, size: sizeof(uint8).} = enum
|
|
rsv3
|
|
rsv2
|
|
rsv1
|
|
fin
|
|
|
|
HeaderFlags* = set[HeaderFlag]
|
|
|
|
MaskKey* = array[4, char]
|
|
|
|
Frame* = ref object
|
|
fin*: bool ## Indicates that this is the final fragment in a message.
|
|
rsv1*: bool ## MUST be 0 unless negotiated that defines meanings
|
|
rsv2*: bool ## MUST be 0
|
|
rsv3*: bool ## MUST be 0
|
|
opcode*: Opcode ## Defines the interpretation of the "Payload data".
|
|
mask*: bool ## Defines whether the "Payload data" is masked.
|
|
data*: seq[byte] ## Payload data
|
|
maskKey*: MaskKey ## Masking key
|
|
length*: uint64 ## Message size.
|
|
consumed*: uint64 ## how much has been consumed from the frame
|
|
offset*: int ## offset of buffered payload data
|
|
|
|
StatusCodes* = distinct range[0..4999]
|
|
|
|
ControlCb* = proc(data: openArray[byte] = [])
|
|
{.gcsafe, raises: [Defect].}
|
|
|
|
CloseResult* = tuple
|
|
code: StatusCodes
|
|
reason: string
|
|
|
|
CloseCb* = proc(code: StatusCodes, reason: string):
|
|
CloseResult {.gcsafe, raises: [Defect].}
|
|
|
|
WebSocket* = ref object of RootObj
|
|
extensions*: seq[Ext]
|
|
version*: uint
|
|
key*: string
|
|
readyState*: ReadyState
|
|
masked*: bool # send masked packets
|
|
binary*: bool # is payload binary?
|
|
flags*: set[TLSFlags]
|
|
rng*: Rng
|
|
frameSize*: int
|
|
onPing*: ControlCb
|
|
onPong*: ControlCb
|
|
onClose*: CloseCb
|
|
|
|
WSSession* = ref object of WebSocket
|
|
stream*: AsyncStream
|
|
frame*: Frame
|
|
proto*: string
|
|
|
|
Ext* = ref object of RootObj
|
|
name*: string
|
|
options*: Table[string, string]
|
|
session*: WSSession
|
|
|
|
ExtFactory* = proc(
|
|
name: string,
|
|
session: WSSession,
|
|
options: Table[string, string]): Ext {.raises: [Defect].}
|
|
|
|
WebSocketError* = object of CatchableError
|
|
WSMalformedHeaderError* = object of WebSocketError
|
|
WSFailedUpgradeError* = object of WebSocketError
|
|
WSVersionError* = object of WebSocketError
|
|
WSProtoMismatchError* = object of WebSocketError
|
|
WSMaskMismatchError* = object of WebSocketError
|
|
WSHandshakeError* = object of WebSocketError
|
|
WSOpcodeMismatchError* = object of WebSocketError
|
|
WSRsvMismatchError* = object of WebSocketError
|
|
WSWrongUriSchemeError* = object of WebSocketError
|
|
WSMaxMessageSizeError* = object of WebSocketError
|
|
WSClosedError* = object of WebSocketError
|
|
WSSendError* = object of WebSocketError
|
|
WSPayloadTooLarge* = object of WebSocketError
|
|
WSReservedOpcodeError* = object of WebSocketError
|
|
WSFragmentedControlFrameError* = object of WebSocketError
|
|
WSInvalidCloseCodeError* = object of WebSocketError
|
|
WSPayloadLengthError* = object of WebSocketError
|
|
WSInvalidOpcodeError* = object of WebSocketError
|
|
WSInvalidUTF8* = object of WebSocketError
|
|
|
|
const
|
|
StatusNotUsed* = (StatusCodes(0)..StatusCodes(999))
|
|
StatusFulfilled* = StatusCodes(1000)
|
|
StatusGoingAway* = StatusCodes(1001)
|
|
StatusProtocolError* = StatusCodes(1002)
|
|
StatusCannotAccept* = StatusCodes(1003)
|
|
StatusReserved* = StatusCodes(1004) # 1004 reserved
|
|
StatusNoStatus* = StatusCodes(1005) # use by clients
|
|
StatusClosedAbnormally* = StatusCodes(1006) # use by clients
|
|
StatusInconsistent* = StatusCodes(1007)
|
|
StatusPolicyError* = StatusCodes(1008)
|
|
StatusTooLarge* = StatusCodes(1009)
|
|
StatusNoExtensions* = StatusCodes(1010)
|
|
StatusUnexpectedError* = StatusCodes(1011)
|
|
StatusFailedTls* = StatusCodes(1015) # passed to applications to indicate TLS errors
|
|
StatusReservedProtocol* = StatusCodes(1016)..StatusCodes(2999) # reserved for this protocol
|
|
StatusLibsCodes* = (StatusCodes(3000)..StatusCodes(3999)) # 3000-3999 reserved for libs
|
|
StatusAppsCodes* = (StatusCodes(4000)..StatusCodes(4999)) # 4000-4999 reserved for apps
|
|
|
|
proc `<=`*(a, b: StatusCodes): bool = a.uint16 <= b.uint16
|
|
proc `>=`*(a, b: StatusCodes): bool = a.uint16 >= b.uint16
|
|
proc `<`*(a, b: StatusCodes): bool = a.uint16 < b.uint16
|
|
proc `>`*(a, b: StatusCodes): bool = a.uint16 > b.uint16
|
|
proc `==`*(a, b: StatusCodes): bool = a.uint16 == b.uint16
|
|
|
|
proc high*(a: HSlice[StatusCodes, StatusCodes]): uint16 = a.b.uint16
|
|
proc low*(a: HSlice[StatusCodes, StatusCodes]): uint16 = a.a.uint16
|
|
|
|
proc `$`*(a: StatusCodes): string = $(a.int)
|
|
|
|
proc `name=`*(self: Ext, name: string) =
|
|
raiseAssert "Can't change extensions name!"
|
|
|
|
method decode*(self: Ext, frame: Frame): Future[Frame] {.base, async.} =
|
|
raiseAssert "Not implemented!"
|
|
|
|
method encode*(self: Ext, frame: Frame): Future[Frame] {.base, async.} =
|
|
raiseAssert "Not implemented!"
|
|
|
|
method toHttpOptions*(self: Ext): string {.base.} =
|
|
raiseAssert "Not implemented!"
|