\ net2o connected commands
\ Copyright (C) 2011-2014 Bernd Paysan
\ This program is free software: you can redistribute it and/or modify
\ it under the terms of the GNU Affero General Public License as published by
\ the Free Software Foundation, either version 3 of the License, or
\ (at your option) any later version.
\ This program is distributed in the hope that it will be useful,
\ but WITHOUT ANY WARRANTY; without even the implied warranty of
\ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\ GNU Affero General Public License for more details.
\ You should have received a copy of the GNU Affero General Public License
\ along with this program. If not, see .
\ everything that follows here can assume to have a connection context
scope{ net2o-base
connect-table $@ inherit-table context-table
\ generic functions
\g
\g ### connection commands ###
\g
+net2o: disconnect ( -- ) \g close connection
o IF o elit, :>dispose-context true to closing? THEN ;
+net2o: set-ip ( $:string -- ) \g set address information
$> setip-xt ;
+net2o: get-ip ( -- ) \g request address information
>sockaddr $, set-ip [: $, set-ip ;] n2oaddrs ;
+net2o: set-blocksize ( n -- ) \g set blocksize to 2^n
64>n 1 swap max-block# umin lshift net2o:blocksizes! ;
+net2o: set-blockalign ( n -- ) \g set block alignment to 2^n
64>n 1 swap max-block# umin lshift blockalign ! ;
+net2o: close-all ( -- ) \g close all files
net2o:close-all ;
\ better slurping
+net2o: set-top ( utop flag -- ) \g set top, flag is true when all data is sent
>r 64>n r> data-rmap with mapc
over dest-top <> and false dest-end ?!@ drop \ atomic, replaces or!
dest-top!
endwith ;
+net2o: slurp ( -- ) \g slurp in tracked files
\ !!FIXME!! this should probably be asynchronous
net2o:slurp swap ulit, flag, set-top
['] do-track-seek net2o:track-all-seeks net2o:send-chunks ;
+net2o: ack-reset ( -- ) \g reset ack state
0 ack-state c! ;
\ object handles
\g
\g ### file commands ###
\g
$30 net2o: file-id ( uid -- o:file ) \g choose a file object
64>n state-addr n:>o ;
fs-table >table
reply-table $@ inherit-table fs-table
: file-start-req fs-id @ ulit, file-id ;
' file-start-req
file-classes file-classes# cells bounds
[DO] dup [I] @ to start-req cell [+LOOP]
drop
$20 net2o: open-file ( $:string mode -- ) \g open file with mode
parent .perm-mask @ >r r@ fs-perm?
64>n -2 and 4 umin dup r> ?rw-perm >r $> r> fs-open ;
+net2o: file-type ( n -- ) \g choose file type
64>n fs-class! ;
+net2o: close-file ( -- ) \g close file
fs-close ;
+net2o: set-size ( size -- ) \g set size attribute of current file
track( ." file <" fs-id @ 0 .r ." > size: " 64dup u64. forth:cr ) size! ;
+net2o: set-seek ( useek -- ) \g set seek attribute of current file
track( ." file <" fs-id @ 0 .r ." > seek: " 64dup u64. forth:cr ) seekto! ;
+net2o: set-limit ( ulimit -- ) \g set limit attribute of current file
track( ." file <" fs-id @ 0 .r ." > seek to: " 64dup u64. forth:cr ) limit-min! ;
+net2o: set-stat ( umtime umod -- ) \g set time and mode of current file
64>n fs-set-stat ;
+net2o: get-size ( -- ) \g request file size
fs-size lit, set-size ;
+net2o: get-stat ( -- ) \g request stat of current file
fs-get-stat >r lit, r> ulit, set-stat ;
+net2o: set-form ( w h -- ) \g if file is a terminal, set size
term-h ! term-w ! ;
+net2o: get-form ( -- ) \g if file is a terminal, request size
term-w @ ulit, term-h @ ulit, set-form ;
+net2o: poll-request ( ulimit -- ) \g poll a file to check for size changes
poll! lit, set-size ;
fs-table $save
' context-table is gen-table
:noname ( uid useek -- ) 64>r ulit, file-id
64r> lit, set-seek end-with ; is do-track-seek
\ flow control functions
\g
\g ### ack commands ###
\g
$31 net2o: ack ( -- o:acko ) \g ack object
ack@ n:>o ;
ack-table >table
reply-table $@ inherit-table ack-table
:noname ack ; ack-class to start-req
$20 net2o: ack-addrtime ( utime addr -- ) \g packet at addr received at time
net2o:ack-addrtime ;
+net2o: ack-resend ( flag -- ) \g set resend toggle flag
64>n parent .net2o:ack-resend ;
+net2o: set-rate ( urate udelta-t -- ) \g set rate
parent >o cookie? IF ack@ .net2o:set-rate
ELSE 64drop 64drop ack-order?
IF ack@ .ns/burst dup >r 64@ 64-2* 64-2* r> 64!
THEN
THEN o> ;
+net2o: resend-mask ( addr umask -- ) \g resend mask blocks starting at addr
2*64>n parent >o net2o:resend-mask net2o:send-chunks o> ;
+net2o: track-timing ( -- ) \g track timing
net2o:track-timing ;
+net2o: rec-timing ( $:string -- ) \g recorded timing
$> net2o:rec-timing ;
+net2o: send-timing ( -- ) \g request recorded timing
net2o:timing$ maxtiming umin tuck $, net2o:/timing rec-timing ;
+net2o: ack-b2btime ( utime addr -- ) \g burst-to-burst time at packet addr
net2o:ack-b2btime ;
+net2o: ack-resend# ( addr $:string -- ) \g resend numbers
64>n $>
ack-order? IF
parent .data-map .mapc:resend#? dup 0= IF
drop timeout( ." resend# don't match!" forth:cr
parent .net2o:see-me )
[ cookie-val 1 validated# lshift 1- xor ]L validated and!
ELSE
validated# lshift validated +! cookie-val validated or!
THEN
ELSE timeout( ." out of order arrival of ack" forth:cr )
2drop drop
THEN ;
+net2o: ack-flush ( addr -- ) \g flushed to addr
64>n parent .net2o:rewind-sender-partial ;
$2C net2o: set-rtdelay ( ticks -- ) \g set round trip delay only
rtdelay! ;
+net2o: seq# ( n -- ) \g set the ack number and check for smaller
64>n parent .data-map with mapc
dup send-ack# u> IF to send-ack# ack-order-val validated or! ELSE
drop [ ack-order-val invert ]L validated and!
THEN
endwith ;
ack-table $save
\ profiling, nat traversal
' context-table is gen-table
in net2o : gen-resend ( -- )
ack-receive invert resend-toggle# and ulit, ack-resend ;
in net2o : gen-reset ( -- )
ack-reset 0 to ack-receive ;
: rewind ( -- )
data-rmap .mapc:dest-back ulit, ack-flush ;
\ safe initialization
: lit< lit, push-lit ;
:noname ( throwcode -- )
remote? @ IF
?dup-IF init-reply
$error-id $@ dup IF $, error-id ELSE 2drop THEN
nlit, ko THEN
ELSE
error-id>o ?dup-IF
>o throw wait-task @ event> o>
ELSE throw THEN
THEN ; IS >throw
also }scope
: blocksize! ( n -- ) max-block# umin dup ulit, set-blocksize
1 swap lshift net2o:blocksizes! ;
: blockalign! ( n -- ) max-block# umin dup ulit, set-blockalign
1 swap lshift blockalign ! ;
: open-sized-file ( addr u mode --)
open-file get-size ;
: open-tracked-file ( addr u mode --)
open-sized-file get-stat ;
: n2o>file ( xt -- )
file-reg# @ ulit, file-id catch end-with
throw 1 file-reg# +! ;
in net2o : copy ( addrsrc us addrdest ud -- )
[: file( ." copy '" 2over forth:type ." ' -> '" 2dup forth:type
." '" forth:cr )
2swap $, r/o ulit, open-tracked-file
file-reg# @ save-to ;] n2o>file
1 file-count +! ;
in net2o : copy# ( addrhash u -- )
[: file( ." copy# " 2dup 85type forth:cr )
1 ulit, file-type 2dup $, r/o ulit, open-tracked-file
file-reg# @ save-to# ;] n2o>file
1 file-count +! ;
: seek! ( pos id -- ) >r d>64
64dup r@ state-addr >o to fs-seek o>
r> ulit, file-id lit, set-seek end-with ;
: limit! ( pos id -- ) >r d>64
r@ ulit, file-id 64dup lit, set-limit end-with
r> init-limit! ;
previous
\ client side timing
: ack-size ( -- ) 1 acks +!
ack@ .recv-tick 64@ 64dup lastb-ticks 64!@ 64- max-dticks 64max! ;
: ack-first ( -- )
lastb-ticks 64@ firstb-ticks 64@ 64- delta-ticks 64+!
ack@ .recv-tick 64@ 64dup firstb-ticks 64! 64dup lastb-ticks 64!
last-rtick 64! recv-addr 64@ last-raddr 64! ;
: ack-timing ( n -- )
b2b-toggle# and IF ack-first ELSE ack-size THEN ;
also net2o-base
: setrate-limit ( rate -- rate' )
\ do not change requested rate by more than a factor 2
last-rate 64@
64dup 64-0<> IF 64tuck 64-2* 64min 64swap 64-2/ 64max ELSE 64drop THEN
64dup last-rate 64! ;
: >rate ( -- ) delta-ticks 64@ 64-0= acks @ 0= or ?EXIT
ack@ .recv-tick 64@ 64dup burst-ticks 64!@ 64dup 64-0<> IF
64- max-dticks 64@ tick-init 1+ n>64 64* 64max 64>r
delta-ticks 64@ tick-init 1+ acks @ 64*/ setrate-limit
lit, 64r> lit, set-rate
ELSE
64drop 64drop
THEN
delta-ticks 64off max-dticks 64off acks off ;
in net2o : acktime ( -- )
recv-addr 64@ ack@ .recv-tick 64@ ack@ .time-offset 64@ 64-
timing( 64>r 64dup x64. 64r> 64dup u64. ." acktime" forth:cr )
lit, lit, ack-addrtime ;
in net2o : b2btime ( -- )
last-raddr 64@ last-rtick 64@ 64dup 64-0=
IF 64drop 64drop
ELSE ack@ .time-offset 64@ 64- lit, lit, ack-b2btime THEN ;
\ ack bits, new code
in net2o : ack-resend# ( -- ) data-rmap { map }
map .mapc:data-resend#-buf $@
bounds ?DO
I $@ over @ >r cell /string $FF -skip
dup >r $FF skip dup IF
r> over - r> + ulit, $, ack-resend#
ELSE 2drop rdrop rdrop THEN
cell +LOOP
map .mapc:data-resend#-buf $[]off ;
\ client side acknowledge
in net2o : genack ( -- )
net2o:ack-resend# net2o:b2btime net2o:acktime >rate ;
: !rdata-tail ( -- )
data-rmap with mapc
data-ack# @ bytes>addr
dest-head umin dest-top umin
dest-tail umax dup addr dest-tail !@ endwith
ack( ." tail: " over hex. dup hex. forth:cr )
u> IF net2o:save& 64#0 burst-ticks 64! THEN ;
$20 Value max-resend#
: prepare-resend ( flag -- end start acks ackm taibits backbits headbits )
data-rmap with mapc
ack( ." head/tail: " dup forth:. dest-head hex. dest-tail hex. forth:cr )
IF dest-head addr>bytes -4 and
ELSE dest-head 1- addr>bytes 1+ THEN 0 max
dest-tail addr>bytes -4 and \ dup data-ack# umin!
data-ackbits @ dest-size addr>bytes 1-
dest-tail addr>bits
dest-back dest-size + addr>bits
dest-head addr>bits
endwith ;
in net2o : do-resend ( flag -- )
o 0= IF drop EXIT THEN data-rmap 0= IF drop EXIT THEN
0 swap prepare-resend { acks ackm tailbits backbits headbits }
ack( ." ack loop: " over hex. dup hex. forth:cr )
+DO
acks I ackm and + l@
acks( ." acks[" I bytes>bits hex.
I data-rmap .mapc:data-ack# @ = IF '*' emit THEN
." ]=" dup hex. backbits hex. forth:cr )
I bytes>bits tailbits u< IF
-1 tailbits I bytes>bits - lshift invert $FFFFFFFF and or
THEN $FFFFFFFF xor
I bytes>bits $20 + headbits u> IF
$FFFFFFFF I bytes>bits $20 + headbits - rshift and
THEN
dup IF
resend( ." resend: " dup hex. over hex. forth:cr )
I ackm and bytes>addr data-rmap .mapc:>linear
ulit, ulit, resend-mask 1+
ELSE
drop dup 0= IF \ if we didn't have a resend yet, increase data-ack#
I 4 + bytes>bits backbits u<= IF \ no backbits, please
I 4 + data-rmap .mapc:data-ack# umax!
THEN
THEN
THEN
dup max-resend# >= ?LEAVE \ no more than x resends
4 +LOOP drop !rdata-tail ;
: do-expect-reply ( -- )
cmdbuf# @ 0> IF \ there's actuall something in the buffer
reply-index ulit, ok? end-cmd
net2o:expect-reply maxdata code+ \ don't reuse this buffer
THEN ['] end-cmd IS expect-reply? ;
: do-expect+slurp ( -- )
cmdbuf# @ 0> IF \ there's actuall something in the buffer
slurp next-request filereq# ! true data-rmap >o to mapc:dest-req o>
reply-index ulit, ok? end-cmd
net2o:expect-reply maxdata code+ \ don't reuse this buffer
THEN ['] end-cmd IS expect-reply? ;
: expect-reply-xt ( xt -- ) \ cmd( ." expect reply:" forth:cr )
['] do-expect-reply IS expect-reply?
cmd-reply-xt ! ;
: expect-reply ( -- )
['] drop expect-reply-xt ;
: expect+slurp-xt ( xt -- ) \ cmd( ." expect reply:" forth:cr )
['] do-expect+slurp IS expect-reply?
cmd-reply-xt ! ;
: expect+slurp ( -- )
['] drop expect+slurp-xt ;
UValue rec-ack-pos#
: seq#, ( -- )
cmdbuf# @ 1 = IF
data-rmap .mapc:rec-ack# ulit, seq#
cmdbuf# @ to rec-ack-pos#
THEN ;
: resend-all ( -- )
seq#,
false net2o:do-resend
ack@ .+timeouts resend-all-to 64! ;
0 Value request-stats?
: update-rtdelay ( -- )
ticks lit, push-lit push' set-rtdelay ;
: data-end? ( -- flag )
0 data-rmap .mapc:dest-end !@ ;
: expected@ ( -- head top )
o IF data-rmap with mapc
o IF dest-tail dest-top
msg( ." expected: " over hex. dup hex. forth:cr )
ELSE #0. msg( ." expected: no data-rmap" forth:cr ) THEN endwith
ELSE #0. msg( ." expected: no object" forth:cr ) THEN ;
: rewind-transfer ( -- flag )
data-end? IF filereq# @ net2o:request-done false
data-rmap >o dup to mapc:dest-req o>
ELSE data-rmap .mapc:dest-req THEN ;
: request-stats forth:true to request-stats? ack track-timing end-with ;
: expected? ( -- flag )
expected@ u>= IF
expect-reply
msg( ." check: " data-rmap with mapc
dest-back hex. dest-tail hex. dest-head hex.
data-ackbits @ data-ack# @ dup hex. + l@ hex.
endwith
forth:cr ." Block transfer done: " expected@ hex. hex. forth:cr )
seq#,
net2o:save&done net2o:ack-resend# rewind rewind-transfer
64#0 burst-ticks 64!
ELSE false THEN ;
cell 8 = [IF] 6 [ELSE] 5 [THEN] Constant cell>>
Create no-resend# bursts# 4 * 0 [DO] -1 c, [LOOP]
scope{ mapc
: +resend# ( bit -- ) >r
dest-addr 64@ 64>n [ min-size 1- ]L and
r@ [ bursts# 4 * 1- ]L and
r> [ bursts# -4 * ]L and \ one block per burst
data-resend#-buf $[]# 0 ?DO
dup I data-resend#-buf $[]@ drop @ = IF
drop I data-resend#-buf $[]@ drop cell+ + c!
UNLOOP EXIT THEN
LOOP
data-resend#-buf $[]# { w^ burstblock n }
burstblock cell data-resend#-buf $+[]!
no-resend# [ bursts# 4 * ]L n data-resend#-buf $[]+!
n data-resend#-buf $[]@ drop cell+ + c! ;
}scope
: +cookie ( -- )
data-rmap with mapc ack-bit# @ >r r@ +resend#
data-ackbits @ r> +bit@
endwith negate packetr2 +! ;
: resend-all? ( -- flag )
data-rmap with mapc
ack-advance? dest-head dest-top u>= and endwith
ticker 64@ resend-all-to 64@ 64u>= and
timeout( dup IF ." resend all" forth:cr THEN ) ;
: +expected ( -- flag )
resend-all? IF resend-all THEN expected? ;
\ higher level functions
: map-request, ( ucode udata -- )
net2o:new-map lit, swap ulit, ulit, map-request ;
also net2o-base
: nat-punch ( o:connection -- )
pings new-request false gen-punchload gen-punch ;
previous
: punch-reply ( addr u -- )
outflag @ >r cmdbuf-o @ >r
[: cmd0! cmdreset init-reply also net2o-base
[ also net2o-base ]
$, nest end-code ;] catch
r> cmdbuf-o ! r> outflag ! throw ;
: 0-resend? ( -- n )
resend0 @ IF
\ ." Resend to 0" cr
cmd0!
[:
resend( ." resend0: " resend0 $@ net2o:see forth:cr )
msg( ." resend0: " resend0 $@ swap hex. hex. forth:cr )
cmdreset init-reply resend0 $@ +cmdbuf
r0-address return-addr $10 move
cmdbuf$ rng64 send-cmd drop
1 packets2 +! ;]
cmdlock c-section 1
ELSE 0 THEN ;
: map-resend? ( -- n )
code-map ?dup-IF with mapc 0 outflag off
dest-replies
dest-size addr>replies bounds endwith U+DO
I action-of reply-xt IF
timeout( ." resend: " I action-of reply-xt .name I 2@ net2o:see forth:cr )
resend( ." resend: " I reply-dest 64@ x64. I 2@ net2o:see forth:cr )
msg( ." resend: " I reply-dest 64@ x64. I 2@ swap hex. hex. forth:cr )
ticks I reply-time 64!
I 2@ I reply-dest 64@
avalanche( ." resend cmd: " ftime 1000e fmod (.time) 64dup x64. 64>r dup hex. 64r> forth:cr )
send-cmd drop
1 packets2 +! 1+
THEN
reply +LOOP
ELSE 0 THEN ;
: cmd-resend? ( -- n )
0-resend? map-resend?
timeout( 2dup d0<> IF ." resend " over . dup . ." commands 0/map" cr THEN ) + ;
: .expected ( -- )
forth:.time ." expected/received: " recv-addr @ hex.
data-rmap .mapc:data-ack# @ hex.
expected@ hex. hex. forth:cr ;
\ acknowledge toplevel
in net2o : ack-code ( ackflag -- ackflag ) >r
false dup { slurp? stats? }
net2o-code
expect-reply ack 1 to rec-ack-pos#
ack( ." ack: " r@ hex. forth:cr )
r@ ack-toggle# and IF
seq#,
net2o:gen-resend net2o:genack
r@ resend-toggle# and IF
ack( ." ack: do-resend" forth:cr )
true net2o:do-resend
THEN
0 data-rmap .mapc:do-slurp !@
?dup-IF ulit, ack-flush
request-stats? to stats? true to slurp? THEN
THEN +expected
slurp? or to slurp?
stats? IF send-timing THEN
end-with cmdbuf# @ rec-ack-pos# 1+ stats? - = IF cmdbuf# off
ELSE 1 data-rmap with mapc +to rec-ack# endwith THEN
slurp? IF slurp THEN
end-code r> ( dup ack-toggle# and IF map-resend? THEN ) ;
in net2o : do-ack-rest ( ackflag -- )
dup resend-toggle# and IF
cmd-resend? drop
THEN
acks# and data-rmap .mapc:ack-advance?
IF net2o:ack-code THEN ack-timing ;
in net2o : do-ack ( -- )
dest-addr 64@ recv-addr 64! +cookie \ last received packet
inbuf 1+ c@ ack-receive over to ack-receive xor
+timeout0 resend-all-to 64!
net2o:do-ack-rest ;
: +flow-control ['] net2o:do-ack is ack-xt ;
\ keepalive
also net2o-base
: .keepalive ( -- ) ." transfer keepalive e/e h t b " expected@ hex. hex.
data-rmap with mapc dest-head hex. dest-tail hex. dest-back hex.
data-ackbits @ dest-size addr>bytes dump
endwith
forth:cr ;
: transfer-keepalive? ( -- flag )
o to connection
timeout( .keepalive )
data-rmap with mapc dest-req dup ack-advance? or to ack-advance? endwith
dup IF
!ticks ticker 64@ resend-all-to 64!
[ ack-toggle# resend-toggle# or ]L net2o:do-ack-rest THEN ;
previous
: cmd-timeout ( -- ) cmd-resend?
IF >next-timeout push-timeout ELSE ack@ .timeouts off THEN ;
: connected-timeout ( -- ) timeout( ." connected timeout " ack@ >o rtdelay 64@ u64. timeouts ? o> forth:cr )
cmd-resend?
IF
>next-timeout push-timeout
ELSE
transfer-keepalive? 0=
IF ack@ .timeouts off ELSE >next-timeout push-timeout THEN
THEN ;
\ : +connecting ['] connecting-timeout is timeout-xt ;
: +resend ( -- ) resend( ." +resend" cr )
['] connected-timeout is timeout-xt o+timeout
64#0 resend-all-to 64! ;
: +resend-cmd ( -- ) resend( ." +resend-cmd" cr )
['] cmd-timeout is timeout-xt o+timeout ;
: +get-time ['] get-tick is other ;
: reqsize! ( ucode udata -- ) to req-datasize to req-codesize ;
: connect-rest ( n -- )
clean-request -timeout tskc KEYBYTES erase context! ;
: end-code| ( -- ) ]] end-code client-loop [[ ; immediate compile-only
: gen-request ( -- )
setup! +resend-cmd gen-tmpkeys ['] connect-rest rqd?
cmd( ind-addr @ IF ." in" THEN ." direct connect" forth:cr )
ivs( ." gen request" forth:cr )
net2o-code0
net2o-version $, version? 0key,
tpkc keysize $, receive-tmpkey
nest[ cookie, gen-reply request, ]nest other
tmpkey-request
ind-addr @ IF punch? THEN
req-codesize req-datasize map-request, close-tmpnest
['] push-cmd IS expect-reply?
end-code| ;
in net2o : connect ( ucode udata -- ) reqsize! gen-request ;
previous
0 [IF]
Local Variables:
forth-local-words:
(
(("net2o:" "+net2o:") definition-starter (font-lock-keyword-face . 1)
"[ \t\n]" t name (font-lock-function-name-face . 3))
("[a-z\-0-9]+(" immediate (font-lock-comment-face . 1)
")" nil comment (font-lock-comment-face . 1))
)
forth-local-indent-words:
(
(("net2o:" "+net2o:") (0 . 2) (0 . 2) non-immediate)
(("[:") (0 . 1) (0 . 1) immediate)
((";]") (-1 . 0) (0 . -1) immediate)
)
End:
[THEN]