mirror of
https://github.com/codex-storage/nim-websock.git
synced 2025-02-08 16:55:19 +00:00
Implement web socket handshake
This commit is contained in:
commit
3b69187007
5
.editorconfig
Normal file
5
.editorconfig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
21
Readme.md
Normal file
21
Readme.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Websocket for Nim
|
||||||
|
|
||||||
|
We're working towards an implementation of the
|
||||||
|
[Websocket](https://tools.ietf.org/html/rfc6455) protocol for
|
||||||
|
[Nim](https://nim-lang.org/). This is very much a work in progress, and not yet
|
||||||
|
in a usable state.
|
||||||
|
|
||||||
|
Building and testing
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nimble install -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nimble test
|
||||||
|
```
|
14
lint.nims
Normal file
14
lint.nims
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env nim
|
||||||
|
import std/strutils
|
||||||
|
|
||||||
|
proc lintFile*(file: string) =
|
||||||
|
if file.endsWith(".nim"):
|
||||||
|
exec "nimpretty " & file
|
||||||
|
|
||||||
|
proc lintDir*(dir: string) =
|
||||||
|
for file in listFiles(dir):
|
||||||
|
lintFile(file)
|
||||||
|
for subdir in listDirs(dir):
|
||||||
|
lintDir(subdir)
|
||||||
|
|
||||||
|
lintDir(projectDir())
|
8
test/server.nim
Normal file
8
test/server.nim
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import ../ws, chronos, asynchttpserver
|
||||||
|
|
||||||
|
proc cb(req: Request) {.async.} =
|
||||||
|
var ws = await newWebSocket(req)
|
||||||
|
ws.close()
|
||||||
|
|
||||||
|
var server = newAsyncHttpServer()
|
||||||
|
waitFor server.serve(Port(9001), cb)
|
11
ws.nimble
Normal file
11
ws.nimble
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
packageName = "ws"
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Status Research & Development GmbH"
|
||||||
|
description = "WS protocol implementation"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
requires "nim >= 1.2.6"
|
||||||
|
requires "chronos >= 2.5.2 & < 3.0.0"
|
||||||
|
|
||||||
|
task lint, "format source files according to the official style guide":
|
||||||
|
exec "./lint.nims"
|
86
ws/ws.nim
Normal file
86
ws/ws.nim
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import chronos, asyncdispatch, asynchttpserver, base64, nativesockets
|
||||||
|
|
||||||
|
type HeaderVerificationError* {.pure.} = enum
|
||||||
|
None
|
||||||
|
## No error.
|
||||||
|
UnsupportedVersion
|
||||||
|
## The Sec-Websocket-Version header gave an unsupported version.
|
||||||
|
## The only currently supported version is 13.
|
||||||
|
NoKey
|
||||||
|
## No Sec-Websocket-Key was provided.
|
||||||
|
ProtocolAdvertised
|
||||||
|
## A protocol was advertised but the server gave no protocol.
|
||||||
|
NoProtocolsSupported
|
||||||
|
## None of the advertised protocols match the server protocol.
|
||||||
|
NoProtocolAdvertised
|
||||||
|
## Server asked for a protocol but no protocol was advertised.
|
||||||
|
|
||||||
|
proc `$`*(error: HeaderVerificationError): string =
|
||||||
|
const errorTable: array[HeaderVerificationError, string] = [
|
||||||
|
"no error",
|
||||||
|
"the only supported sec-websocket-version is 13",
|
||||||
|
"no sec-websocket-key provided",
|
||||||
|
"server does not support protocol negotation",
|
||||||
|
"no advertised protocol supported",
|
||||||
|
"no protocol advertised"
|
||||||
|
]
|
||||||
|
result = errorTable[error]
|
||||||
|
|
||||||
|
type
|
||||||
|
ReadyState* = 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.
|
||||||
|
|
||||||
|
WebSocket* = ref object
|
||||||
|
tcpSocket*: AsyncSocket
|
||||||
|
version*: int
|
||||||
|
key*: string
|
||||||
|
protocol*: string
|
||||||
|
readyState*: ReadyState
|
||||||
|
masked*: bool # send masked packets
|
||||||
|
|
||||||
|
WebSocketError* = object of IOError
|
||||||
|
|
||||||
|
proc handshake*(ws: WebSocket, headers: HttpHeaders): Future[error: HeaderVerificationError] {.async.} =
|
||||||
|
ws.version = parseInt(headers["Sec-WebSocket-Version"])
|
||||||
|
ws.key = headers["Sec-WebSocket-Key"].strip()
|
||||||
|
if headers.hasKey("Sec-WebSocket-Protocol"):
|
||||||
|
let wantProtocol = headers["Sec-WebSocket-Protocol"].strip()
|
||||||
|
if ws.protocol != wantProtocol:
|
||||||
|
return NoProtocolsSupported
|
||||||
|
|
||||||
|
let
|
||||||
|
sh = secureHash(ws.key & "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||||
|
acceptKey = base64.encode(decodeBase16($sh))
|
||||||
|
|
||||||
|
var response = "HTTP/1.1 101 Web Socket Protocol Handshake\c\L"
|
||||||
|
response.add("Sec-WebSocket-Accept: " & acceptKey & "\c\L")
|
||||||
|
response.add("Connection: Upgrade\c\L")
|
||||||
|
response.add("Upgrade: webSocket\c\L")
|
||||||
|
|
||||||
|
if ws.protocol != "":
|
||||||
|
response.add("Sec-WebSocket-Protocol: " & ws.protocol & "\c\L")
|
||||||
|
response.add "\c\L"
|
||||||
|
|
||||||
|
await ws.tcpSocket.send(response)
|
||||||
|
ws.readyState = Open
|
||||||
|
|
||||||
|
proc newWebSocket*(req: Request, protocol: string = ""): Future[tuple[ws: AsyncWebSocket, error: HeaderVerificationError]] {.async.} =
|
||||||
|
if not req.headers.hasKey("Sec-WebSocket-Version"):
|
||||||
|
return ("", UnsupportedVersion)
|
||||||
|
|
||||||
|
var ws = WebSocket()
|
||||||
|
ws.masked = false
|
||||||
|
ws.tcpSocket = req.client
|
||||||
|
ws.protocol = protocol
|
||||||
|
let (ws, error) = await ws.handshake(req.headers)
|
||||||
|
return ws, error
|
||||||
|
|
||||||
|
proc close*(ws: WebSocket) =
|
||||||
|
ws.readyState = Closed
|
||||||
|
proc close() {.async.} =
|
||||||
|
await ws.send("", Close)
|
||||||
|
ws.tcpSocket.close()
|
||||||
|
asyncCheck close()
|
Loading…
x
Reference in New Issue
Block a user