container local-network [
data:&:@:port-connection
]
container port-connection [
port:num
contents:text
]
def new-port-connection port:num, contents:text -> p:&:port-connection [
local-scope
load-ingredients
p:&:port-connection <- new port-connection:type
*p <- merge port, contents
]
def new-fake-network -> n:&:local-network [
local-scope
load-ingredients
n:&:local-network <- new local-network:type
local-network-ports:&:@:port-connection <- new port-connection:type, 0
*n <- put *n, data:offset, local-network-ports
]
scenario write-to-fake-socket [
local-scope
single-port-network:&:local-network <- new-fake-network
sink:&:sink:char, writer:num/routine <- start-writing-socket single-port-network, 8080
sink <- write sink, 120/x
close sink
wait-for-routine writer
tested-port-connections:&:@:port-connection <- get *single-port-network, data:offset
tested-port-connection:port-connection <- index *tested-port-connections, 0
contents:text <- get tested-port-connection, contents:offset
10:@:char/raw <- copy *contents
memory-should-contain [
10:array:character <- [x]
]
]
def start-reading-from-network resources:&:resources, uri:text -> contents:&:source:char [
local-scope
load-ingredients
{
break-if resources
host:text, path:text <- split-at uri, 47/slash
socket:num <- $open-client-socket host, 80/http-port
assert socket, [contents]
req:text <- interpolate [GET _ HTTP/1.1], path
request-socket socket, req
contents:&:source:char, sink:&:sink:char <- new-channel 10000
start-running receive-from-socket socket, sink
return
}
return 0/not-found
]
def request-socket socket:num, s:text -> socket:num [
local-scope
load-ingredients
write-to-socket socket, s
$write-to-socket socket, 13/cr
$write-to-socket socket, 10/lf
$write-to-socket socket, 13/cr
$write-to-socket socket, 10/lf
]
def start-writing-socket network:&:local-network, port:num -> sink:&:sink:char, routine-id:num [
local-scope
load-ingredients
source:&:source:char, sink:&:sink:char <- new-channel 30
{
break-if network
socket:num <- $open-server-socket port
session:num <- $accept socket
return sink, 0/routine-id
}
routine-id <- start-running transmit-to-fake-socket network, port, source
]
def transmit-to-fake-socket network:&:local-network, port:num, source:&:source:char -> network:&:local-network, source:&:source:char [
local-scope
load-ingredients
buf:&:buffer <- new-buffer 30
{
c:char, done?:bool, source <- read source
break-unless c
buf <- append buf, c
break-if done?
loop
}
contents:text <- buffer-to-array buf
new-port-connection:&:port-connection <- new-port-connection port, contents
i:num <- copy 0
port-connections:&:@:port-connection <- get *network, data:offset
len:num <- length *port-connections
{
done?:bool <- greater-or-equal i, len
break-if done?
current:port-connection <- index *port-connections, i
current-port:num <- get current, port:offset
ports-match?:bool <- equal current-port, port
i <- add i, 1
loop-unless ports-match?
put-index *port-connections, i, *new-port-connection
reply
}
new-len:num <- add len, 1
new-port-connections:&:@:port-connection <- new port-connection:type, new-len
put *network, data:offset, new-port-connections
i:num <- copy 0
{
done?:bool <- greater-or-equal i, len
break-if done?
tmp:port-connection <- index *port-connections, i
put-index *new-port-connections, i, tmp
}
put-index *new-port-connections, len, *new-port-connection
]
def receive-from-socket socket:num, sink:&:sink:char -> sink:&:sink:char [
local-scope
load-ingredients
{
req:text, eof?:bool <- $read-from-socket socket, 4096/bytes
bytes-read:num <- length *req
i:num <- copy 0
{
done?:bool <- greater-or-equal i, bytes-read
break-if done?
c:char <- index *req, i
sink <- write sink, c
i <- add i, 1
loop
}
loop-unless eof?
}
sink <- close sink
]
def write-to-socket socket:num, s:text [
local-scope
load-ingredients
len:num <- length *s
i:num <- copy 0
{
done?:bool <- greater-or-equal i, len
break-if done?
c:char <- index *s, i
$write-to-socket socket, c
i <- add i, 1
loop
}
]
def split-at text:text, delim:char -> x:text, y:text [
local-scope
load-ingredients
len:num <- length *text
{
empty?:bool <- equal len, 0
break-unless empty?
x:text <- new []
y:text <- new []
return
}
idx:num <- find-next text, delim, 0
x:text <- copy-range text, 0, idx
y:text <- copy-range text, idx, len
]
scenario text-split-at [
local-scope
x:text <- new [a/b]
run [
y:text, z:text <- split-at x, 47/slash
10:@:char/raw <- copy *y
20:@:char/raw <- copy *z
]
memory-should-contain [
10:array:character <- [a]
20:array:character <- [/b]
]
]