M .gitignore => .gitignore +1 -0
@@ 1,3 1,4 @@
/.direnv
/.tup
/nim.cfg
+*.spt
M Tuprules.tup => Tuprules.tup +2 -0
@@ 1,3 1,5 @@
NIM = $(DIRENV) $(NIM)
NIM_GROUPS += $(TUP_CWD)/<lock>
+NIM_FLAGS += --path:$(TUP_CWD)/../cps
NIM_FLAGS += --path:$(TUP_CWD)/../getdns/src
+NIM_FLAGS += --path:$(TUP_CWD)/../solo5_dispatcher/pkg
A examples/minimal_example/Tupfile => examples/minimal_example/Tupfile +2 -0
@@ 0,0 1,2 @@
+include_rules
+: foreach *.nim |> !nim |>
A lock.json => lock.json +87 -0
@@ 0,0 1,87 @@
+{
+ "depends": [
+ {
+ "method": "fetchzip",
+ "packages": [
+ "cps"
+ ],
+ "path": "/nix/store/8gbhwni0akqskdb3qhn5nfgv6gkdz0vz-source",
+ "rev": "c90530ac57f98a842b7be969115c6ef08bdcc564",
+ "sha256": "0h8ghs2fqg68j3jdcg7grnxssmllmgg99kym2w0a3vlwca1zvr62",
+ "srcDir": "",
+ "url": "https://github.com/ehmry/cps/archive/c90530ac57f98a842b7be969115c6ef08bdcc564.tar.gz"
+ },
+ {
+ "method": "fetchzip",
+ "packages": [
+ "getdns"
+ ],
+ "path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
+ "rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
+ "sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
+ "srcDir": "src",
+ "url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
+ },
+ {
+ "method": "fetchzip",
+ "packages": [
+ "getdns"
+ ],
+ "path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
+ "rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
+ "sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
+ "srcDir": "src",
+ "url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
+ },
+ {
+ "method": "fetchzip",
+ "packages": [
+ "sys"
+ ],
+ "path": "/nix/store/vf9ls2wip6d8xhsi3rjh0dqsqg597i6b-source",
+ "rev": "c117ee60542f084525f254e6ade590675a6a2ed6",
+ "sha256": "12qzx2lnh84xqfgypy0pka8nflq0y8n1izfwx8mb4zya5nzawmyf",
+ "srcDir": "src",
+ "url": "https://github.com/alaviss/nim-sys/archive/c117ee60542f084525f254e6ade590675a6a2ed6.tar.gz"
+ },
+ {
+ "date": "2024-04-02T15:38:57+01:00",
+ "deepClone": false,
+ "fetchLFS": false,
+ "fetchSubmodules": true,
+ "hash": "sha256-iZb9aAgYr4FGkqfIg49QWiCqeizIi047kFhugHiP8o0=",
+ "leaveDotGit": false,
+ "method": "git",
+ "packages": [
+ "solo5_dispatcher"
+ ],
+ "path": "/nix/store/sf5dgj2ljvahcm6my7d61ibda51vnrii-solo5_dispatcher",
+ "rev": "a7a894a96a2221284012800e6fd32923d83d20bd",
+ "sha256": "13gjixw80vjqj0xlx2y85ixal82sa27q7j57j9383bqq11lgv5l9",
+ "srcDir": "pkg",
+ "url": "https://git.sr.ht/~ehmry/solo5_dispatcher"
+ },
+ {
+ "method": "fetchzip",
+ "packages": [
+ "cps"
+ ],
+ "path": "/nix/store/phdf6siqbhj7vx4qq507lzla81si60iz-source",
+ "rev": "58772ff9ddb38a4b2ec52da142d8532ba2fe7039",
+ "sha256": "1lph7v27nqwgm3a0ssi8q348gjrkjwgqc50agw38j7xif6wj80cw",
+ "srcDir": "",
+ "url": "https://github.com/ehmry/cps/archive/58772ff9ddb38a4b2ec52da142d8532ba2fe7039.tar.gz"
+ },
+ {
+ "method": "fetchzip",
+ "packages": [
+ "stew"
+ ],
+ "path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
+ "rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
+ "sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
+ "srcDir": "",
+ "url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
+ }
+ ]
+}
M shell.nix => shell.nix +2 -2
@@ 1,6 1,6 @@
let pkgs = import <nixpkgs> { };
in pkgs.buildNimPackage {
name = "dummy";
- buildInputs = [ pkgs.getdns ];
- nativeBuildInputs = [ pkgs.pkg-config ];
+ buildInputs = builtins.attrValues { inherit (pkgs) getdns solo5; };
+ nativeBuildInputs = builtins.attrValues { inherit (pkgs) pkg-config solo5; };
}
M src/taps/lwip.nim => src/taps/lwip.nim +4 -1
@@ 3,12 3,15 @@
## Meta-module for building LwIP
+when defined(nimPreviewSlimSystem):
+ import std/assertions
+
const
ipv4Enabled* {.booldefine.}: bool = false
ipv6Enabled* {.booldefine.}: bool = false
when not (ipv4Enabled or ipv6Enabled):
- {.error: "neither ipv4 or ipv6 enabled".}
+ {.error: "neither ipv4Enabled or ipv6Enabled defined".}
{.passC: "-DIPV6_FRAG_COPYHEADER=1".}
M src/taps/lwip/include/lwipopts.h => src/taps/lwip/include/lwipopts.h +1 -0
@@ 38,6 38,7 @@
#define SYS_DEBUG LWIP_DBG_OFF
#define SYS_LIGHTWEIGHT_PROT 0
#define TCP_DEBUG LWIP_DBG_OFF
+#define TCP_MSS 1220 // Default for IPv6
#define TIMERS_DEBUG LWIP_DBG_OFF
#define UDP_DEBUG LWIP_DBG_OFF
M src/taps/lwip_implementation.nim => src/taps/lwip_implementation.nim +154 -95
@@ 3,9 3,6 @@
import lwip
-when defined(solo5):
- import solo5/solo5
-
# *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
# Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
@@ 15,7 12,9 @@ type Pcg32 = object
proc initPcg32*: Pcg32 =
when defined(solo5):
Pcg32(
- state: 0x853c49e6748fea9b'u64 xor uint64 solo5.clock_wall(),
+ state: 0x853c49e6748fea9b'u64 xor
+ solo5_clock_wall().uint64 xor
+ solo5_clock_monotonic().uint64,
inc: 0xda3e39cb94b95bdb'u64)
elif defined(genode):
Pcg32(
@@ 39,6 38,7 @@ type
var
ERR_OK {.importc, nodecl.}: err_t
ERR_VAL {.importc, nodecl.}: err_t
+ ERR_RTE {.importc, nodecl.}: err_t
ERR_WOULDBLOCK {.importc, nodecl.}: err_t
template isOk(e: err_t): bool = e == ERR_OK
@@ 107,7 107,6 @@ proc toLwipIp(ip: IpAddress): ip_addr_t =
ip.address_v4[0], ip.address_v4[1], ip.address_v4[2], ip.address_v4[3])
else:
raiseAssert "IPv4 is disabled"
- assert result.toIpAddress == ip, $result.toIpAddress
type
pbuf_layer {.importc, header: "lwip/pbuf.h".} = enum
@@ 134,10 133,7 @@ when ipv4Enabled:
when ipv6Enabled:
type Netif_output_ip6 = proc (netif: pointer; p: Pbuf; ipaddr: ptr ip6_addr_t): err_t {.cdecl.}
-when defined(solo5):
- discard
-
-elif defined(genode):
+when defined(genode):
import std/tables, genode/constructibles
type
@@ 186,15 182,15 @@ type
name: string
when defined(solo5):
netif: Netif
- handle: devices.Handle
+ handle: NetHandle
info: NetInfo
+ buf: seq[byte]
elif defined(genode):
heap: Heap
nic: NicNetif
-when defined(solo5):
- type NetifRegistry = HandleRegistry[TapsNetifRef]
-elif defined(genode):
+
+when defined(genode):
proc netif(nic: NicNetif): ptr Netif {.importcpp: "&(#->lwip_netif())".}
var ethernet_input {.importc, nodecl, header: "netif/ethernet.h".}: Netif_input
@@ 253,20 249,22 @@ type
TcpRecv = proc(arg: pointer; pcb: TcpPcb; p: Pbuf; err: err_t): err_t {.cdecl.}
TcpSent = proc(arg: pointer; pcb: TcpPcb; len: uint16): err_t {.cdecl.}
-proc tcp_new(): TcpPcb {.importc, tcpH.}
-proc tcp_bind(pcb: TcpPcb, ipaddr: ptr ip_addr_t, port: uint16): err_t {.importc, tcpH.}
-proc tcp_connect(pcb: TcpPcb, ipaddr: ptr ip_addr_t, port: uint16, connected: TcpConnected): err_t {.importc, tcpH.}
-proc tcp_close(pcb: TcpPcb): err_t {.importc, tcpH.}
proc tcp_abort(pcb: TcpPcb) {.importc, tcpH.}
-proc tcp_listen(pcb: TcpPcb): TcpPcb {.importc, tcpH.}
-proc tcp_arg(pcb: TcpPcb; arg: pointer) {.importc, tcpH.}
proc tcp_accept(pcb: TcpPcb; accept: TcpAccept) {.importc, tcpH.}
+proc tcp_arg(pcb: TcpPcb; arg: pointer) {.importc, tcpH.}
+proc tcp_bind(pcb: TcpPcb, ipaddr: ptr ip_addr_t, port: uint16): err_t {.importc, tcpH.}
+proc tcp_close(pcb: TcpPcb): err_t {.importc, tcpH.}
+proc tcp_connect(pcb: TcpPcb, ipaddr: ptr ip_addr_t, port: uint16, connected: TcpConnected): err_t {.importc, tcpH.}
proc tcp_err(pcb: TcpPcb; err: TcpErr) {.importc, tcpH.}
+proc tcp_listen(pcb: TcpPcb): TcpPcb {.importc, tcpH.}
+proc tcp_new(): TcpPcb {.importc, tcpH.}
+proc tcp_output(pcb: TcpPcb): err_t {.importc, tcpH.}
proc tcp_recv(pcb: TcpPcb; recv: TcpRecv) {.importc, tcpH.}
proc tcp_recved(pcb: TcpPcb; len: uint16) {.importc, tcpH.}
-proc tcp_write(pcb: TcpPcb; arg: pointer; len: uint16; apiFlags: uint8): err_t {.importc, tcpH.}
proc tcp_sent(pcb: TcpPcb; sent: TcpSent) {.importc, tcpH.}
+proc tcp_shutdown(pcb: TcpPcb; shut_rx, shut_tx: cint): err_t {.importc, tcpH.}
proc tcp_tcp_get_tcp_addrinfo(pcb: TcpPcb; local: cint; ipAddr: ptr ip_addr_t; port: ptr uint16): err_t {.importc, tcpH.}
+proc tcp_write(pcb: TcpPcb; arg: pointer; len: uint16; apiFlags: uint8 = 0): err_t {.importc, tcpH.}
proc receiveBuffered(conn: Connection | ptr ConnectionObj) =
assert(not conn.received.isNil)
@@ 291,28 289,36 @@ proc receiveBuffered(conn: Connection | ptr ConnectionObj) =
ctx.remote = conn.remote
conn.platform.recvPending = false
tapsEcho "Connection -> Received<messageData, messageContext>"
- conn.received(buf, ctx)
+ conn.receivedPartial(buf, ctx, conn.platform.remoteFinished)
assert(buf.len < 0x1_00_00)
tcp_recved(conn.platform.tcpPcb, uint16 buf.len)
proc tapsTcpError(arg: pointer; err: err_t) {.cdecl} =
var conn = cast[ptr ConnectionObj](arg)
assert not err.isOk
- conn.connectionError(err.toException)
+ conn.callConnectionError(err.toException)
proc tapsTcpRecv(arg: pointer; pcb: TcpPcb; p: Pbuf; err: err_t): err_t {.cdecl} =
var conn = cast[ptr ConnectionObj](arg)
assert not conn.isNil
- assert err.isOk, "TODO: receiveError callback"
- if p.isNil:
- conn.platform.tcpPcb = nil
- result = tcp_close(pcb)
- if not conn.closed.isNil: conn.closed()
+ if not err.isOk:
+ var ctx: MessageContext # TODO
+ conn.callReceiveError(ctx, newException(IOError, $err))
+ elif p.isNil:
+ conn.platform.remoteFinished = true
+ if conn.platform.localFinished:
+ checkErr tcp_close(pcb)
+ conn.platform.pbuf = nil
+ elif not conn.closed.isNil:
+ conn.closed()
+ else:
+ checkErr tcp_close(pcb)
+ conn.platform.pbuf = nil
else:
if conn.platform.pbuf.isNil: conn.platform.pbuf = p
else: pbuf_cat(conn.platform.pbuf, p)
if conn.platform.recvPending:
- assert not conn.received.isNil
+ assert not conn.receivedPartial.isNil
receiveBuffered(conn)
proc tapsTcpSent(arg: pointer; pcb: TcpPcb; len: uint16): err_t {.cdecl} =
@@ 354,13 360,21 @@ proc tapsTcpAccept(arg: pointer; newPcb: TcpPcb; err: err_t): err_t {.cdecl.} =
proc tapsTcpConnected(arg: pointer; pcb: TcpPcb; err: err_t): err_t {.cdecl.} =
var conn = cast[ptr ConnectionObj](arg)
- assert not conn.isNil
- if err.isOk:
- tapsEcho "Connection -> Ready"
- conn.ready()
+ if not conn.platform.tcpPcb.isNil:
+ if err.isOk:
+ # Close redundant TCP stream if Connection is ready.
+ tcp_abort(pcb)
else:
- tapsEcho "Connection -> InitiateError<reason?>"
- conn.initiateError(err.toException)
+ if not err.isOk:
+ tapsEcho "Connection -> InitiateError<reason?>"
+ conn.callInitiateError(err.toException)
+ else:
+ tapsEcho "Connection -> Ready"
+ conn.platform.tcpPcb = pcb
+ tcp_err(conn.platform.tcpPcb, tapsTcpError)
+ tcp_recv(conn.platform.tcpPcb, tapsTcpRecv)
+ tcp_sent(conn.platform.tcpPcb, tapsTcpSent)
+ conn.ready()
{.pragma: udpH, header: "lwip/udp.h".}
@@ 369,7 383,7 @@ proc udp_remove(pcb: UdpPcb) {.importc, udpH.}
when defined(solo5):
type GlobalState = object
- netifs: NetifRegistry
+ netifs: seq[TapsNetifRef]
listeners: Deque[tuple[pc: Preconnection, ls: Listener]]
elif defined(genode):
@@ 379,11 393,19 @@ elif defined(genode):
var globalState: GlobalState
+type IpAddrCallback* = proc (device: string; ip: IpAddress) {.closure.}
+var ipAddrCallback: IpAddrCallback
+
+proc onInterfaceUp*(cb: IpAddrCallback) = ipAddrCallback = cb
+
proc tapsStatusCallback(netif: ptr Netif) {.cdecl.} =
when defined(solo5):
var state = netif.state
for ip in state.ipAddresses:
- echo state.name, " interface address ", ip
+ if not ipAddrCallback.isNil:
+ ipAddrCallback(state.name, ip)
+ else:
+ echo state.name, " interface address ", ip
proc tapsLinkOutput(netif: ptr Netif; p: Pbuf): err_t {.cdecl.} =
when defined(genode):
@@ 391,16 413,21 @@ proc tapsLinkOutput(netif: ptr Netif; p: Pbuf): err_t {.cdecl.} =
elif defined(solo5):
var
state = netif.state
- writeTotal: csize_t
- q = p
- result = ERR_OK
- while not q.isNil and result == ERR_OK and writeTotal < p.tot_len:
- result = case net_write(state.handle, cast[ptr uint8](q.payload), csize_t q.len)
- of SOLO5_R_OK: ERR_OK
- of SOLO5_R_AGAIN: ERR_WOULDBLOCK
- else: ERR_VAL
- writeTotal = writeTotal + csize_t q.len
- q = q.next
+ res: Solo5Result
+ if p.len == p.tot_len:
+ res = solo5_net_write(state.handle, cast[ptr uint8](p.payload), csize_t p.len)
+ # unchained buffer
+ else:
+ let n = int p.tot_len
+ if state.buf.len < n:
+ state.buf.setLen n
+ if pbuf_copy_partial(p, addr state.buf[0], p.tot_len, 0) != p.tot_len:
+ return ERR_VAL
+ res = solo5_net_write(state.handle, addr state.buf[0], csize_t n)
+ case res
+ of SOLO5_R_OK: ERR_OK
+ of SOLO5_R_AGAIN: ERR_WOULDBLOCK
+ else: ERR_VAL
else:
{.error: "link output proc not implemented".}
@@ 430,6 457,7 @@ proc initTapsNetif(netif: ptr Netif): err_t {.cdecl.} =
when defined(solo5):
import std/[endians, strformat]
+ import solo5_dispatcher
type Frame {.packed.} = object
dst, src: MacAddress
@@ 440,30 468,39 @@ when defined(solo5):
bigEndian16(addr t, unsafeAddr fr.etherType)
fmt"""[{fr.dst}][{fr.src}][{t.toHex}]"""
- proc solo5NetHandler(h: Handle) =
- ## Handler invoked by asyncdispatcher to read a network packet.
- var state = globalState.netifs[h]
- var p = pbuf_alloc(PBUF_RAW, state.info.mtu.uint16, PBUF_POOL)
- var q = p
- var totRead: csize_t
- while not q.isNil:
+ proc netInputLoop(state: TapsNetifRef; h: NetHandle) {.cps: Continuation.} =
+ ## Continuation loop for the `h` net device.
+ var p = pbuf_alloc(PBUF_RAW, state.info.mtu.uint16, PBUF_RAM)
+ assert p.len == p.tot_len, "p.len:" & $p.len & " p.tot_len:" & $p.tot_len
+ assert p.next.isNil
+ while true:
var readSize: csize_t
- if net_read(h, cast[ptr uint8](q.payload), q.len, addr readSize) != SOLO5_R_OK:
- q = nil
- pbuf_free(p)
+ var res = solo5_net_read(h, cast[ptr uint8](p.payload), p.len, addr readSize)
+ case res
+ of SOLO5_R_AGAIN:
+ await(h)
+ of SOLO5_R_OK:
+ assert readSize > 0
+ pbuf_realloc(p, uint16 readSize)
+ assert p.tot_len == readSize
+ let res = state.netif.input(p, addr state.netif)
+ if res != ERR_OK:
+ pbuf_free(p)
+ raise newException(IOError, $res)
+ p = pbuf_alloc(PBUF_RAW, state.info.mtu.uint16, PBUF_RAM)
else:
- totRead = totRead + readSize
- if readSize < q.len.csize_t: q = nil
- else: q = q.next
- pbuf_realloc(p, totRead.uint16)
- if totRead > 0 and state.netif.input(p, addr state.netif) == ERR_OK: discard
- else: pbuf_free(p)
-
- proc netAcquireHook*(name: string; h: Handle, ni: NetInfo) {.nimcall.} =
- var state = TapsNetifRef(name: name, handle: h, info: ni)
- globalState.netifs[h] = state
- registerHandler(h, solo5NetHandler)
+ pbuf_free(p)
+ raise newException(IOError, $res)
+
+ proc netAcquireHook*(name: string; h: NetHandle, ni: NetInfo) {.nimcall.} =
+ let state = TapsNetifRef(name: name, handle: h, info: ni)
+ var i = int h
+ if globalState.netifs.high < i:
+ globalState.netifs.setLen(succ i)
+ globalState.netifs[i] = state
discard netif_add_noaddr(addr state.netif, addr(state[]), initTapsNetif)
+ discard trampoline:
+ whelp netInputLoop(state, h)
elif defined(genode):
proc acquireNic*(env: GenodeEnvPtr; label = "") =
@@ 476,8 513,7 @@ elif defined(genode):
# TODO: destructible Nics
-proc sys_check_timeouts*() {.importc, header: "lwip/timeouts.h".}
- # TODO: do no export this, register it with the async dispatcher
+proc sys_check_timeouts() {.importc, header: "lwip/timeouts.h".}
proc stop*(lis: Listener) =
case lis.platform.transport
@@ 493,8 529,12 @@ proc stop*(lis: Listener) =
proc close*(conn: Connection) =
case conn.platform.transport
of lwipTcp:
- checkErr tcp_close(conn.platform.tcpPcb)
- conn.platform.tcpPcb = nil
+ conn.platform.localFinished = true
+ if conn.platform.remoteFinished:
+ checkErr tcp_close(conn.platform.tcpPcb)
+ conn.platform.tcpPcb = nil
+ else:
+ checkErr tcp_shutdown(conn.platform.tcpPcb, shut_rx=0, shut_tx=1)
of lwipUdp:
udp_remove(conn.platform.udpPcb)
conn.platform.udpPcb = nil
@@ 515,23 555,30 @@ proc initiateUDP(preconn: Preconnection; result: Connection) =
result.platform.udp_pcb = udp_new()
raiseAssert "not implemented"
-proc initiateTCP(preconn: Preconnection; conn: Connection) =
- # see ./lwip/upstream/src/apps/lwiperf/lwiperf.c:428
- conn.platform.tcp_pcb = tcp_new()
- tcp_arg(conn.platform.tcpPcb, addr conn[])
- tcp_err(conn.platform.tcpPcb, tapsTcpError)
- tcp_recv(conn.platform.tcpPcb, tapsTcpRecv)
- tcp_sent(conn.platform.tcpPcb, tapsTcpSent)
- var err: err_t
- for remote in preconn.remotes:
- var
+when defined(solo5):
+
+ proc initiateTCP(conn: Connection; remote: RemoteSpecifier) {.solo5dispatch.} =
+ let
+ pcb = tcp_new()
ipAddr = remote.ip.toLwipIp
port = remote.port.uint16
- err = tcp_connect(conn.platform.tcpPcb, addr ipAddr, port, tapsTcpConnected)
- if err.isOk:
- conn.remote = some remote
- break
- if not err.isOk: conn.initiateError(err.toException)
+ tcp_arg(pcb, addr conn[])
+ while conn.platform.tcpPcb.isNil:
+ let err = tcp_connect(pcb, addr ipAddr, port, tapsTcpConnected)
+ if err == ERR_OK:
+ conn.remote = some remote
+ return
+ elif err == ERR_RTE:
+ # Route not ready?
+ yieldFor initDuration(seconds = 1)
+ tcp_abort(pcb)
+ # Already connected.
+
+ proc initiateTCP(preconn: Preconnection; conn: Connection) =
+ # see ./lwip/upstream/src/apps/lwiperf/lwiperf.c:428
+ for remote in preconn.remotes:
+ discard trampoline:
+ whelp initiateTCP(conn, remote)
proc initiate*(preconn: var Preconnection; timeout = none(
Duration)): Connection =
@@ 598,9 645,7 @@ proc send*(
conn: Connection; msg: pointer; msgLen: int;
ctx = MessageContext(); endOfMessage = true) =
assert msgLen < 0x1_00_00
- var err = tcp_write(conn.platform.tcpPcb, msg, uint16 msgLen,
- TCP_WRITE_FLAG_COPY or (if endOfMessage: 0'u8 else: TCP_WRITE_FLAG_MORE))
- # TODO: no-copy, accept a seq[byte] and hold it until ACKed
+ var err = tcp_write(conn.platform.tcpPcb, msg, uint16 msgLen, TCP_WRITE_FLAG_COPY)
if err.isOk:
ctx.len = msgLen
conn.outgoing.addLast ctx
@@ 608,17 653,31 @@ proc send*(
else:
conn.callSendError(ctx, err.toException)
+proc startBatch*(conn: Connection) =
+ discard # nothing to do
+
+proc endBatch*(conn: Connection) =
+ if not conn.platform.tcpPcb.isNil:
+ var err = tcp_output(conn.platform.tcpPcb)
+ # Flush the buffered TCP pbuf.
+ if not err.isOk:
+ conn.callConnectionError(err.toException)
+
proc receive*(conn: Connection;
minIncompleteLength = -1; maxLength = -1) =
assert maxLength != 0
(conn.platform.recvMinIncompleteLength, conn.platform.recvMaxLength) =
(minIncompleteLength, maxLength)
conn.platform.recvPending = true
- callSoon:
+ if not conn.platform.pbuf.isNil:
receiveBuffered(conn)
-proc startBatch*(conn: Connection) = discard
-proc endBatch*(conn: Connection) = discard
-
-addTimer(initDuration(seconds=2), oneshot=false) do:
- sys_check_timeouts()
+when defined(solo5):
+ proc checkTimeouts() {.solo5dispatch.} =
+ const period = initDuration(milliseconds = 500)
+ while true:
+ yieldFor(period)
+ sys_check_timeouts()
+
+ discard trampoline:
+ whelp checkTimeouts()
M src/taps/lwip_types.nim => src/taps/lwip_types.nim +2 -1
@@ 2,7 2,7 @@
# SPDX-License-Identifier: Unlicense
when defined(solo5):
- import solo5/devices
+ import solo5
type
Pbuf {.importc: "struct pbuf", header: "lwip/pbuf.h".} = ptr object
@@ 29,6 29,7 @@ type
tcpPcb: TcpPcb
pbuf: Pbuf
pbufOff: uint16
+ localFinished, remoteFinished: bool
of lwipUdp:
udpPcb: UdpPcb
recvMinIncompleteLength, recvMaxLength: int
M taps.nimble => taps.nimble +2 -2
@@ 1,6 1,6 @@
# Package
-version = "20240318"
+version = "20240402"
author = "Emery Hemingway"
description = "Transport Services Interface"
license = "Unlicense"
@@ 10,4 10,4 @@ srcDir = "src"
# Dependencies
-requires "nim >= 2.0.0", "https://git.sr.ht/~ehmry/getdns-nim >= 20220928", "https://github.com/alaviss/nim-sys.git >= 0.0.4", "https://github.com/ehmry/cps#c90530ac57f98a842b7be969115c6ef08bdcc564", "https://git.sr.ht/~ehmry/getdns-nim"
+requires "nim >= 2.0.0", "https://git.sr.ht/~ehmry/getdns-nim >= 20220928", "https://github.com/alaviss/nim-sys.git >= 0.0.4", "https://github.com/ehmry/cps#c90530ac57f98a842b7be969115c6ef08bdcc564", "https://git.sr.ht/~ehmry/getdns-nim", "https://git.sr.ht/~ehmry/solo5_dispatcher"
A tests/Tupfile => tests/Tupfile +4 -0
@@ 0,0 1,4 @@
+include_rules
+
+# : solo5*test.nim |> !nim_solo5_hvt |>
+: solo5*test.nim | ../<sources> |> !nim_solo5_spt |>
A tests/config.nims => tests/config.nims +1 -0
@@ 0,0 1,1 @@
+switch("path", "$projectDir/../src")
A tests/solo5_test.nim => tests/solo5_test.nim +44 -0
@@ 0,0 1,44 @@
+# SPDX-FileCopyrightText: ☭ Emery Hemingway
+# SPDX-License-Identifier: Unlicense
+
+import std/options
+import taps
+import solo5, solo5_dispatcher
+
+proc `$`(b: seq[byte]): string = cast[string](b)
+
+proc connectionHandler(conn: Connection) =
+ echo "Received new Connection."
+ conn.onClosed do ():
+ conn.close()
+ conn.onReceivedPartial do (data: seq[byte]; ctx: MessageContext; eom: bool):
+ echo "Received partial message of ", data.len, " bytes"
+ conn.send(data)
+ conn.receive()
+ conn.onSent do (ctx: MessageContext):
+ echo "Sent cb received, message ", ctx, " has been sent."
+ conn.onReceiveError do (ctx: MessageContext; reason: ref Exception):
+ echo "connection error: ", reason.msg
+ conn.receive()
+
+proc main =
+ var lp = newLocalEndpoint()
+ lp.with Port(1024)
+
+ var tp = newTransportProperties()
+ tp.require "reliability"
+ tp.ignore "congestion-control"
+ tp.ignore "preserve-order"
+
+ let preconn = newPreconnection(
+ local=[lp], transport=tp.some)
+ var listener = preconn.listen()
+ listener.onListenError do (err: ref Exception):
+ echo "Listen Error occcured, ", err.msg, "."
+ quit -1
+ listener.onConnectionReceived(connectionHandler)
+
+ run()
+
+acquireDevices([("echoserver", netBasic)], netAcquireHook)
+main()
A tests/solo5_test.nim.cfg => tests/solo5_test.nim.cfg +1 -0
@@ 0,0 1,1 @@
+define:ipv6Enabled