about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStephen Malina <stephenmalina@gmail.com>2016-09-21 05:27:56 -0700
committerStephen Malina <stephenmalina@gmail.com>2016-09-21 05:29:02 -0700
commit25cd8e9878b7b7325271773d4e4ec91b7462d0e6 (patch)
tree7c7a6952233abb3740171fd1803993535b73bc9d
parent6845c4d94a823f4de186b0cd1298f530ab6f7460 (diff)
downloadmu-25cd8e9878b7b7325271773d4e4ec91b7462d0e6.tar.gz
3403
Wrap $read-from-socket in a channel and fix the socket example
so that browser's display the response properly.
-rw-r--r--091socket.cc47
-rw-r--r--server-socket.mu54
2 files changed, 64 insertions, 37 deletions
diff --git a/091socket.cc b/091socket.cc
index 3ffca8f4..5caf9f79 100644
--- a/091socket.cc
+++ b/091socket.cc
@@ -16,7 +16,7 @@ struct socket_t {
 :(code)
 void server_socket(int portno, socket_t* server) {
   server->fd = socket(AF_INET, SOCK_STREAM, 0);
-  int dummy;
+  int dummy = 0;
   setsockopt(server->fd, SOL_SOCKET, SO_REUSEADDR, &dummy, sizeof(dummy));
   server->addr.sin_family = AF_INET;
   server->addr.sin_addr.s_addr = INADDR_ANY;
@@ -115,24 +115,20 @@ _READ_FROM_SOCKET,
 put(Recipe_ordinal, "$read-from-socket", _READ_FROM_SOCKET);
 :(before "End Primitive Recipe Checks")
 case _READ_FROM_SOCKET: {
-  if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end();
+  if (SIZE(inst.ingredients) != 2) {
+    raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly two ingredients, but got '" << inst.original_string << "'\n" << end();
     break;
   }
   if (!is_mu_number(inst.ingredients.at(0))) {
     raise << maybe(get(Recipe, r).name) << "first ingredient of '$read-from-socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
     break;
   }
-  if (SIZE(inst.products) != 2) {
-    raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly one product, but got '" << inst.original_string << "'\n" << end();
+  if (!is_mu_number(inst.ingredients.at(1))) {
+    raise << maybe(get(Recipe, r).name) << "second ingredient of '$read-from-socket' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
     break;
   }
-  if (!is_mu_character(inst.products.at(0))) {
-    raise << maybe(get(Recipe, r).name) << "first product of '$read-from-socket' should be a character, but got '" << to_string(inst.products.at(0)) << "'\n" << end();
-    break;
-  }
-  if (!is_mu_boolean(inst.products.at(1))) {
-    raise << maybe(get(Recipe, r).name) << "second product of '$read-from-socket' should be a boolean but got '" << to_string(inst.products.at(1)) << "'\n" << end();
+  if (SIZE(inst.products) != 2) {
+    raise << maybe(get(Recipe, r).name) << "'$read-from-socket' requires exactly two product, but got '" << inst.original_string << "'\n" << end();
     break;
   }
   break;
@@ -140,20 +136,16 @@ case _READ_FROM_SOCKET: {
 :(before "End Primitive Recipe Implementations")
 case _READ_FROM_SOCKET: {
   long long int x = static_cast<long long int>(ingredients.at(0).at(0));
+  int bytes = static_cast<int>(ingredients.at(1).at(0)); //? Should this be something with more bytes?
   socket_t* socket = reinterpret_cast<socket_t*>(x);
   int socket_fd = socket->fd;
-  char single_char[2];
-  bzero(single_char, 2);
-  int bytes_read = read(socket_fd, single_char, 1);
+  char contents[bytes];
+  bzero(contents, bytes);
+  int bytes_read = read(socket_fd, contents, bytes - 1 /* null-terminated */);
+  //?: cerr << "Read:\n" << string(contents) << "\n";
   products.resize(2);
-  if (single_char[0]== EOF || bytes_read == 0) {
-    products.at(1).push_back(1);  // eof
-  }
-  else {
-    products.at(1).push_back(0);
-  }
-  products.at(0).push_back(single_char[0]);
-  break;
+  products.at(0).push_back(new_mu_text(string(contents)));
+  products.at(1).push_back(bytes_read);
   break;
 }
 
@@ -190,11 +182,11 @@ _CLOSE_SOCKET,
 put(Recipe_ordinal, "$close-socket", _CLOSE_SOCKET);
 :(before "End Primitive Recipe Checks")
 case _CLOSE_SOCKET: {
-  if (SIZE(inst.ingredients) != 2) {
+  if (SIZE(inst.ingredients) != 1) {
     raise << maybe(get(Recipe, r).name) << "'$close-socket' requires exactly two ingredient, but got '" << inst.original_string << "'\n" << end();
     break;
   }
-  if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(0))) {
+  if (!is_mu_number(inst.ingredients.at(0))) {
     raise << maybe(get(Recipe, r).name) << "first ingredient of '$close-socket' should be a character, but got '" << to_string(inst.ingredients.at(0)) << "t\n" << end();
     break;
   }
@@ -202,9 +194,8 @@ case _CLOSE_SOCKET: {
 }
 :(before "End Primitive Recipe Implementations")
 case _CLOSE_SOCKET: {
-  double socket_fd = ingredients.at(0).at(0);
-  double session_fd = ingredients.at(1).at(0);
-  close(socket_fd);
-  close(session_fd);
+  long long int x = static_cast<long long int>(ingredients.at(0).at(0));
+  socket_t* socket = reinterpret_cast<socket_t*>(x);
+  close(socket->fd);
   break;
 }
diff --git a/server-socket.mu b/server-socket.mu
index dbdf1ef7..3da870ca 100644
--- a/server-socket.mu
+++ b/server-socket.mu
@@ -1,19 +1,33 @@
+# Example of reading from a socket using channels and writing back to it
+# directly. Running this file and navigating to <address of server>:8081
+# should result in your browser displaying "SUCCESS!".
+#
+# Unfortunately, the reading implementation has some redundant, inelegant
+# code to make up for my lack of insight into Linux's socket internal.
 def main [
   local-scope
-  socket:num <- $socket 8080/port
+  socket:num <- $socket 8081/port
   $print [Mu socket creation returned ], socket, 10/newline
   session:num <- $accept socket
-  write-to-socket session, [HTTP/1.0 200 OK
-
-OK]
+  contents:&:source:char, sink:&:sink:char <- new-channel 5096
+  sink <- start-running transmit-from-socket session, sink
+  buf:&:buffer <- new-buffer 30
   {
-    c:char, eof?:boolean <- $read-from-socket session
-    $print c
-    break-if eof?
+    c:char, done?:bool, contents <- read contents
+    break-if done?
+    buf <- append buf, c
     loop
   }
-  $print 10/newline, [Hit end of socket, closing...], 10/newline
-  $close-socket socket, session
+  socket-text:text <- buffer-to-array buf
+  $print [Done reading from socket.], 10/newline
+  write-to-socket session, [HTTP/1.0 200 OK
+Content-type: text/plain
+
+SUCCESS!
+]
+  $print 10/newline, [Wrote to and closing socket...], 10/newline
+  $close-socket session
+  $close-socket socket
 ]
 
 def write-to-socket session-socket:number, s:address:array:character [
@@ -25,8 +39,30 @@ def write-to-socket session-socket:number, s:address:array:character [
     done?:boolean <- greater-or-equal i, len
     break-if done?
     c:character <- index *s, i
+    $print [writing to socket: ], i, [ ], c, 10/newline
     $write-to-socket session-socket, c
     i <- add i, 1
     loop
   }
 ]
+
+def transmit-from-socket session:num, sink:&:sink:char -> sink:&:sink:char [
+  local-scope
+  load-ingredients
+  {
+    req:text, bytes-read:number  <- $read-from-socket session, 4096/bytes
+    $print [read ], bytes-read, [ bytes from socket], 10/newline
+    i:number <- copy 0
+    {
+      done?:boolean <- greater-or-equal i, bytes-read
+      break-if done?
+      c:char <- index *req, i
+      end-of-request?:bool <- equal c, 10/newline
+      break-if end-of-request? # To be safe, for now.
+      sink <- write sink, c
+      i <- add i, 1
+      loop
+    }
+  }
+  sink <- close sink
+]