[MLton] BUG in Socket.Ctl.setLINGER

Ray Racine rracine@adelphia.net
Sat, 19 Mar 2005 12:51:31 -0500


I have completed a port of the Swerve HTTP server to MLton to the point
where it a) compiles and b) serves simple html.  Its capable of much
more but I haven't tested various aspects yet.  [a) was last weekend and
b) was 20 minutes ago]

The purpose of the port was so I can embed a small, yet robust http
server in an SML application.  Code release coming soon.  

The following bug was the issue in preventing the server from serving
simple html that I found this morning.  A call to Socket.Ctl.setLinger
errors with a Subscript exception.  I used D Wang's Shootout submission
of a simple TCP echo program as the test case to reproduce the bug.  

Consider this a bug report without a fix at this time.  I'm done'fer the
weekend and may get to tracking down the cause sporadically during the
upcoming work week.  If the issue is still open by next weekend I'll try
and track it down and get a fix.

Ray

(* -*- mode: sml -*-
 : echo-smlnj.code,v 1.4 2004/11/13 07:41:31 bfulgham Exp $
 * http://www.bagley.org/~doug/shootout/
 * from Daniel Wang
 *)

structure Test : sig
    
    val main : (string * string list) -> OS.Process.status
					 
end =
struct
  exception Error of string
		     
  val data = "Hello there sailor\n"
	     
  fun mkSocks () = let
      val server = INetSock.TCP.socket()
      val client = INetSock.TCP.socket()
      val _ = Socket.bind(server,INetSock.any 0)
      val saddr = INetSock.fromAddr(Socket.Ctl.getSockName server)
      val _ = Socket.listen(server,2)
      val _ = Socket.connect(client,INetSock.toAddr saddr)
      val _ = INetSock.TCP.setNODELAY(server,true)
      val _ = INetSock.TCP.setNODELAY(client,true)
  in 
      { client=client,server=server }
  end
		   
  fun readString (s,n) = let
      fun loop(0) = []
	| loop(n) = let
              val data = Byte.unpackStringVec (Word8VectorSlice.full
(Socket.recvVec(s,n)))
              val len = String.size data
          in if len = 0 then []
             else (data::(loop(n - len)))
          end
  in 
      String.concat (loop n)
  end
			 
  fun writeString (out,str) =
      Socket.sendVec(out, Word8VectorSlice.full( Byte.stringToBytes
str))
  (*     Socket.sendVec(out,{buf=Byte.stringToBytes str,i=0,sz=NONE}) *)
      
  fun closeSock s =
      (Socket.shutdown(s,Socket.NO_RECVS_OR_SENDS);
       Socket.close s)
      
  fun main (_,args) = let
      val num =
	  case args of
              nil => 1
	    | n::_ => valOf (Int.fromString n)
      val {client=client_sock,server=server_sock} = mkSocks()
      fun server () = let
	  val (sock,_) = Socket.accept(server_sock)
	(* COMMENT OUT LINGER TO WORK *)
	  val _ = Socket.Ctl.setLINGER (sock, SOME (Time.fromSeconds 2 ))
	  fun s b =
              case readString(sock,19) of
		  "" => (Posix.Process.wait ();
			 TextIO.output(TextIO.stdOut,
                                       concat ["server processed ",
                                               Int.toString b,
                                               " bytes\n"]);
			 TextIO.flushOut(TextIO.stdOut))
		| i =>(writeString(sock,i);
                       s (b + 19))
      in s 0
      end
      fun client () = let
	  fun c 0 = closeSock(client_sock)
            | c n = let
		  val _ = writeString(client_sock,data);
		  val reply = readString(client_sock,19)
              in if reply = data then c(n - 1)
		 else raise Error "Didn't receive the same data"
              end
      in c num
      end
  in
      case Posix.Process.fork () of
	  SOME pid => server ()
	| NONE => client ();
      OS.Process.success
  end
end

val _ = Test.main (CommandLine.name (), CommandLine.arguments ())