Skip to content

Instantly share code, notes, and snippets.

@ingoogni
Last active February 26, 2025 14:17
Show Gist options
  • Save ingoogni/459c79707065a8492651c0130954e5db to your computer and use it in GitHub Desktop.
Save ingoogni/459c79707065a8492651c0130954e5db to your computer and use it in GitHub Desktop.
SSE Client Nim experiment Server Sent Events
# Compile:
# nim c --gc:orc --experimental:strictFuncs -d:ssl -d:nimStressOrc --import:std/httpcore sseClient.nim
# based on Harpoon https://github.com/juancarlospaco/harpoon
import std/[net, macros, strutils, times, json, httpcore, uri]
type
SSE = object
comment: string
retry: string
id: string
event: string
data: string
func toString(url: Uri; metod: static[HttpMethod]; headers: openArray[(string, string)]; body: string): string {.inline, raises: [].} =
var it: char
var temp: string = url.path
macro unrollStringOps(x: ForLoopStmt) =
result = newStmtList()
for chara in x[^2][^2].strVal:
result.add nnkAsgn.newTree(x[^2][^1], chara.newLit)
result.add x[^1]
if unlikely(temp.len == 0): temp = "/"
if url.query.len > 0:
temp.add '?'
temp.add url.query
for _ in unrollStringOps("GET ", it):
result.add it
result.add temp
for _ in unrollStringOps(" HTTP/1.1\r\nHost: ", it): result.add it
result.add url.hostname
for _ in unrollStringOps("\r\n", it): result.add it
temp.setLen 0
for header in headers:
temp.add header[0]
for _ in unrollStringOps(": ", it): temp.add it
temp.add header[1]
for _ in unrollStringOps("\r\n", it): temp.add it
for _ in unrollStringOps("\r\n", it): temp.add it
result.add temp
result.add body
iterator sse*(socket: Socket; url: Uri; metod: static[HttpMethod];
headers: openArray[(string, string)] = [("User-Agent", "x"), ("Content-Type", "text/plain"), ("Accept", "text/event-stream"), ("Dnt", "1")];
body = ""; timeout = -1; port: static[Port] = 80.Port; portSsl: static[Port] = 443.Port;
parseHeader = true; parseStatus = true; parseBody = true; bodyOnly: static[bool] = false):auto =
var
res: string
sse: bool
retry = "3000"
sseobj = SSE()
if likely(url.scheme == "https"):
var ctx =
try: newContext(verifyMode = CVerifyNone)
except: raise newException(IOError, getCurrentExceptionMsg())
socket.connect(url.hostname, portSsl, timeout)
try: ctx.wrapConnectedSocket(socket, handshakeAsClient, url.hostname)
except: raise newException(IOError, getCurrentExceptionMsg())
else: socket.connect(url.hostname, port, timeout)
socket.send(toString(url, metod, headers, body))
while true:
let line = socket.recvLine(timeout)
res.add line
res.add '\r'
res.add '\n'
let lineLower = line.toLowerAscii()
if line == "\r\n":
break
elif linelower.startsWith("content-type: text/event-stream"):
sse = true
if sse:
var ignore = false
while true:
#yield socket.recvLine(timeout)
let inmsg = socket.recvLine(timeout)
let msg = inmsg.split(":", 1)
echo inmsg
case msg[0]:
of "retry":
retry = msg[1].strip(leading = true)
sseObj.retry = msg[1].strip(leading = true)
of "id":
sseObj.id = msg[1].strip(leading = true)
of "event":
sseObj.event = msg[1].strip(leading = true)
of "data":
if sseObj.data.len() > 0:
sseObj.data &= "\n" & msg[1].strip(leading = true)
else:
sseObj.data &= msg[1].strip(leading = true)
of "":
if msg[1] == "":
ignore = true
else:
sseObj.comment = msg[1].strip(leading = true)
of "\r\n", "\n\n", "\r\r", "\n\r":
if ignore:
ignore = false
continue
yield sseObj
sseObj = SSE()
else:
#ignore = true
continue
else:
echo "Eeeek"
close socket
let socket: Socket = newSocket()
for msg in socket.sse(parseUri"https://stream.wikimedia.org/v2/stream/mediawiki.recentchange", HttpGet, port = 8088.Port):
echo msg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment