about summary refs log tree commit diff stats
path: root/092socket.mu
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-10-22 16:56:07 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-10-22 16:56:07 -0700
commit9a81d7460fdb16f4e77712e5381d9db8781f5ae6 (patch)
tree43b05169535fe33e65ecbf61f3fb3ada5f75ed52 /092socket.mu
parent22f4b76344b2d639cbfcaad56ed681670d436548 (diff)
downloadmu-9a81d7460fdb16f4e77712e5381d9db8781f5ae6.tar.gz
3561
Diffstat (limited to '092socket.mu')
-rw-r--r--092socket.mu230
1 files changed, 125 insertions, 105 deletions
diff --git a/092socket.mu b/092socket.mu
index 6a5c427a..1fd2586b 100644
--- a/092socket.mu
+++ b/092socket.mu
@@ -1,69 +1,87 @@
-# Wrappers around socket primitives that take a 'local-network' object and are
-# thus easier to test.
+# Wrappers around socket primitives that are easier to test.
 #
-# The current semantics of fake port-connections don't match UNIX socket ones,
-# but we'll improve them as we learn more.
-
-container local-network [
-  data:&:@:port-connection
-]
-
-# Port connections represent connections to ports on localhost.
-# Before passing a local-network object to network functions
-# `start-reading-socket` and `start-writing-socket`, add port-connections to
-# the local-network.
-#
-# For reading, `receive-from-socket` will check for a
-# port-connection on the port parameter that's been passed in. If there's
-# no port-connection for that port, it will return nothing and log an error.
-# If there is a port-connection for that port, it will transmit the contents
-# to the provided sink.
+# To test client operations, use `assume-resources` with a filename that
+# begins with a hostname. (Filenames starting with '/' are assumed to be
+# local.)
 #
-# For writing, `start-writing-socket` returns a sink connecting the
-# caller to the socket on the passed-in port.
-container port-connection [
-  port:num
-  contents:text
-]
+# To test server operations, just run a real client against localhost.
 
-def new-port-connection port:num, contents:text -> p:&:port-connection [
+scenario example-server-test [
   local-scope
-  load-ingredients
-  p:&:port-connection <- new port-connection:type
-  *p <- merge port, contents
+  # test server without a fake on a random (real) port
+  # that way repeatedly running the test will give ports time to timeout and
+  # close before reusing them
+  make-random-nondeterministic
+  port:num <- random-in-range 0/real-random-numbers, 8000, 8100
+  run [
+    socket:num <- $open-server-socket port
+    $print [server socket: ], socket, 10/newline
+    assert socket, [ 
+F - example-server-test: $open-server-socket failed]
+    $print [starting up server routine], 10/newline
+    handler-routine:number <- start-running serve-one-request socket, example-handler
+  ]
+  $print [starting to read from port ], port, 10/newline
+  source:&:source:char <- start-reading-from-network 0/real-resources, [localhost], [/], port
+  response:text <- drain source
+  10:@:char/raw <- copy *response
+  memory-should-contain [
+    10:array:character <- [abc]
+  ]
 ]
-
-def new-fake-network -> n:&:local-network [
+# helper just for this scenario
+def example-handler query:text -> response:text [
   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
+  reply [abc]
 ]
 
-scenario write-to-fake-socket [
+#? scenario example-client-test [
+#?   local-scope
+#?   assume-resources [
+#?     [example.com/] -> [abc]
+#?   ]
+#?   run [
+#?     source:&:source:char <- start-reading-from-network resources, [example.com], [/]
+#?   ]
+#?   contents:text <- drain source
+#?   10:@:char/raw <- copy *contents
+#?   memory-should-contain [
+#?     10:address:character <- [abc]
+#?   ]
+#? ]
+
+type request-handler = (recipe text -> text)
+
+def serve-one-request socket:num, request-handler:request-handler [
   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]
-  ]
+  load-ingredients
+  session:num <- $accept socket
+  $print [server session socket: ], session, 10/newline
+  assert session, [ 
+F - example-server-test: $accept failed]
+  contents:&:source:char, sink:&:sink:char <- new-channel 30
+  sink <- start-running receive-from-socket session, sink
+  query:text <- drain contents
+  response:text <- call request-handler, query
+  write-to-socket session, response
+  $close-socket session
 ]
 
 def start-reading-from-network resources:&:resources, host:text, path:text -> contents:&:source:char [
   local-scope
   load-ingredients
+  $print [running start-reading-from-network], 10/newline
+  {
+    port:num, port-found?:boolean <- next-ingredient
+    break-if port-found?
+    port <- copy 80/http-port
+  }
   {
     break-if resources
     # real network
-    socket:num <- $open-client-socket host, 80/http-port
+    socket:num <- $open-client-socket host, port
+    $print [client socket: ], socket, 10/newline
     assert socket, [contents]
     req:text <- interpolate [GET _ HTTP/1.1], path
     request-socket socket, req
@@ -102,70 +120,72 @@ def request-socket socket:num, s:text -> socket:num [
   $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
-    # TODO Create channel implementation of write-to-socket.
-    return sink, 0/routine-id
-  }
-  # fake network
-  routine-id <- start-running transmit-to-fake-socket network, port, source
-]
+#? 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
+#?     # TODO Create channel implementation of write-to-socket.
+#?     return sink, 0/routine-id
+#?   }
+#?   # fake network
+#?   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
-  # compute new port connection contents
-  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
-  # Got the contents of the channel, time to write to fake port.
-  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?
-    # Found an existing connection on this port, overwrite.
-    put-index *port-connections, i, *new-port-connection
-    reply
-  }
-  # Couldn't find an existing connection on this port, initialize a new one.
-  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 transmit-to-fake-socket network:&:local-network, port:num, source:&:source:char -> network:&:local-network, source:&:source:char [
+#?   local-scope
+#?   load-ingredients
+#?   # compute new port connection contents
+#?   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
+#?   # Got the contents of the channel, time to write to fake port.
+#?   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?
+#?     # Found an existing connection on this port, overwrite.
+#?     put-index *port-connections, i, *new-port-connection
+#?     reply
+#?   }
+#?   # Couldn't find an existing connection on this port, initialize a new one.
+#?   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
   {
+    $print [read-from-socket ], socket, 10/newline
     req:text, eof?:bool <- $read-from-socket socket, 4096/bytes
+    loop-unless req
     bytes-read:num <- length *req
     i:num <- copy 0
     {