mirror of
https://github.com/status-im/news.git
synced 2025-02-22 00:28:10 +00:00
fix connection and validation of server responce (#11)
* fix connection and validation of server responce * fix typo, cleanup * fix typo, better validation * more valid validation ;)
This commit is contained in:
parent
c178f8c628
commit
09af7f0d97
66
src/news.nim
66
src/news.nim
@ -1,5 +1,5 @@
|
|||||||
import strutils, streams, random, base64, uri, strformat, nativesockets, oids,
|
import strutils, streams, random, base64, uri, strformat, nativesockets, oids,
|
||||||
strtabs, std/sha1, net
|
strtabs, std/sha1, net, httpcore
|
||||||
|
|
||||||
when not declaredInScope(newsUseChronos):
|
when not declaredInScope(newsUseChronos):
|
||||||
# Currently chronos is second class citizen. To use this library in chronos-based
|
# Currently chronos is second class citizen. To use this library in chronos-based
|
||||||
@ -9,7 +9,7 @@ when not declaredInScope(newsUseChronos):
|
|||||||
const newsUseChronos = false
|
const newsUseChronos = false
|
||||||
|
|
||||||
when newsUseChronos:
|
when newsUseChronos:
|
||||||
import chronos, chronos/streams/[asyncstream, tlsstream], httpcore
|
import chronos, chronos/streams/[asyncstream, tlsstream]
|
||||||
type Transport = object
|
type Transport = object
|
||||||
reader: AsyncStreamReader
|
reader: AsyncStreamReader
|
||||||
writer: AsyncStreamWriter
|
writer: AsyncStreamWriter
|
||||||
@ -36,6 +36,8 @@ else:
|
|||||||
import httpcore, asyncdispatch, asyncnet, asynchttpserver
|
import httpcore, asyncdispatch, asyncnet, asynchttpserver
|
||||||
type Transport = AsyncSocket
|
type Transport = AsyncSocket
|
||||||
|
|
||||||
|
const CRLF = "\c\l"
|
||||||
|
const GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||||
|
|
||||||
type
|
type
|
||||||
ReadyState* = enum
|
ReadyState* = enum
|
||||||
@ -118,23 +120,55 @@ when not newsUseChronos:
|
|||||||
if req.headers.hasKey("sec-webSocket-protocol"):
|
if req.headers.hasKey("sec-webSocket-protocol"):
|
||||||
ws.protocol = req.headers["sec-webSocket-protocol"].strip()
|
ws.protocol = req.headers["sec-webSocket-protocol"].strip()
|
||||||
|
|
||||||
let sh = secureHash(ws.key & "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
let sh = secureHash(ws.key & GUID)
|
||||||
let acceptKey = base64.encode(decodeBase16($sh))
|
let acceptKey = base64.encode(decodeBase16($sh))
|
||||||
|
|
||||||
var responce = "HTTP/1.1 101 Web Socket Protocol Handshake\c\L"
|
var response = "HTTP/1.1 101 Web Socket Protocol Handshake" & CRLF
|
||||||
responce.add("Sec-WebSocket-Accept: " & acceptKey & "\c\L")
|
response.add("Sec-WebSocket-Accept: " & acceptKey & CRLF)
|
||||||
responce.add("Connection: Upgrade\c\L")
|
response.add("Connection: Upgrade" & CRLF)
|
||||||
responce.add("Upgrade: webSocket\c\L")
|
response.add("Upgrade: webSocket" & CRLF)
|
||||||
if not ws.protocol.len == 0:
|
if not ws.protocol.len == 0:
|
||||||
responce.add("Sec-WebSocket-Protocol: " & ws.protocol & "\c\L")
|
response.add("Sec-WebSocket-Protocol: " & ws.protocol & CRLF)
|
||||||
responce.add "\c\L"
|
response.add CRLF
|
||||||
|
|
||||||
ws.transp = req.client
|
ws.transp = req.client
|
||||||
# await ws.transp.connect(uri.hostname, port)
|
# await ws.transp.connect(uri.hostname, port)
|
||||||
await ws.transp.send(responce)
|
await ws.transp.send(response)
|
||||||
ws.readyState = Open
|
ws.readyState = Open
|
||||||
return ws
|
return ws
|
||||||
|
|
||||||
|
proc validateServerResponse(resp, secKey: string): string =
|
||||||
|
let respLines = resp.splitLines()
|
||||||
|
block statusCode:
|
||||||
|
const k = "HTTP/1.1 "
|
||||||
|
let i = respLines[0].find(k) + k.len
|
||||||
|
let v = respLines[0][i .. i + 2]
|
||||||
|
if v != "101":
|
||||||
|
return respLines[0][i ..< respLines[0].len]
|
||||||
|
|
||||||
|
var validatedHeaders: array[3, bool]
|
||||||
|
for i in 1 ..< respLines.len:
|
||||||
|
let h = parseHeader(respLines[i])
|
||||||
|
if h.key == "Upgrade":
|
||||||
|
if h.value[0].toLowerAscii != "websocket":
|
||||||
|
return "Upgrade header is invalid"
|
||||||
|
validatedHeaders[0] = true
|
||||||
|
|
||||||
|
elif h.key == "Connection":
|
||||||
|
if h.value[0].toLowerAscii != "upgrade":
|
||||||
|
return "Connection header is invalid"
|
||||||
|
validatedHeaders[1] = true
|
||||||
|
|
||||||
|
elif h.key == "Sec-WebSocket-Accept":
|
||||||
|
let sh = decodeBase16($secureHash(secKey & GUID))
|
||||||
|
if h.value[0].toLowerAscii != base64.encode(sh).toLowerAscii:
|
||||||
|
return "Secret key invalid"
|
||||||
|
validatedHeaders[2] = true
|
||||||
|
|
||||||
|
if not validatedHeaders[0]: return "Missing Upgrade header"
|
||||||
|
if not validatedHeaders[1]: return "Missing Connection header"
|
||||||
|
if not validatedHeaders[2]: return "Missing Sec-WebSocket-Accept header"
|
||||||
|
|
||||||
proc newWebSocket*(url: string, headers: StringTableRef = nil,
|
proc newWebSocket*(url: string, headers: StringTableRef = nil,
|
||||||
sslContext: SSLContext = getDefaultSslContext()): Future[WebSocket] {.async.} =
|
sslContext: SSLContext = getDefaultSslContext()): Future[WebSocket] {.async.} =
|
||||||
## Creates a client
|
## Creates a client
|
||||||
@ -170,8 +204,12 @@ proc newWebSocket*(url: string, headers: StringTableRef = nil,
|
|||||||
raise newException(WebSocketError, "SSL support is not available. Compile with -d:ssl to enable.")
|
raise newException(WebSocketError, "SSL support is not available. Compile with -d:ssl to enable.")
|
||||||
await ws.transp.connect(uri.hostname, port)
|
await ws.transp.connect(uri.hostname, port)
|
||||||
|
|
||||||
|
var urlPath = uri.path
|
||||||
|
if uri.query.len > 0:
|
||||||
|
urlPath.add("?" & uri.query)
|
||||||
|
|
||||||
let secKey = encode($genOid())[16..^1]
|
let secKey = encode($genOid())[16..^1]
|
||||||
let requestLine = &"GET {url} HTTP/1.1"
|
let requestLine = &"GET {urlPath} HTTP/1.1"
|
||||||
let predefinedHeaders = [
|
let predefinedHeaders = [
|
||||||
&"Host: {uri.hostname}:{$port}",
|
&"Host: {uri.hostname}:{$port}",
|
||||||
"Connection: Upgrade",
|
"Connection: Upgrade",
|
||||||
@ -179,7 +217,7 @@ proc newWebSocket*(url: string, headers: StringTableRef = nil,
|
|||||||
"Sec-WebSocket-Version: 13",
|
"Sec-WebSocket-Version: 13",
|
||||||
&"Sec-WebSocket-Key: {secKey}"
|
&"Sec-WebSocket-Key: {secKey}"
|
||||||
]
|
]
|
||||||
const CRLF = "\c\l"
|
|
||||||
var customHeaders = ""
|
var customHeaders = ""
|
||||||
if not headers.isNil:
|
if not headers.isNil:
|
||||||
for k, v in headers:
|
for k, v in headers:
|
||||||
@ -195,7 +233,9 @@ proc newWebSocket*(url: string, headers: StringTableRef = nil,
|
|||||||
while not output.endsWith(static(CRLF & CRLF)):
|
while not output.endsWith(static(CRLF & CRLF)):
|
||||||
output.add await ws.transp.recv(1)
|
output.add await ws.transp.recv(1)
|
||||||
|
|
||||||
# TODO: Validate server reply
|
let error = validateServerResponse(output, secKey)
|
||||||
|
if error.len > 0:
|
||||||
|
raise newException(WebSocketError, "WebSocket connection error: " & error)
|
||||||
|
|
||||||
ws.readyState = Open
|
ws.readyState = Open
|
||||||
ws.maskFrames = true
|
ws.maskFrames = true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user