alms-0.4.9: lib/libsocketcap2.alms
(*
A typestate sockets library
This is a bit more involved than the example in the paper,
because we have error cases. We deal with this by raising
an exception which contains a witness that allows recovering
the capability if presented with the corresponding socket.
*)
#load "libsocket"
module type ASOCKET = sig
(* The representation of a socket *)
type 't socket
(* Socket capabilities and the socket states *)
type 't @@ 's qualifier A
type initial
type bound
type listening
type connected
(* Socket operations *)
val socket : unit -> ex 't. 't socket * 't@@initial
val bind : all 't. 't socket -> int -> 't@@initial -> 't@@bound
val connect : all 't. 't socket -> string -> string ->
't@@initial + 't@@bound -> 't@@connected
val listen : all 't. 't socket -> 't@@bound -> 't@@listening
val accept : all 't. 't socket -> 't@@listening ->
(ex 's. 's socket * 's@@connected) * 't@@listening
val send : all 't. 't socket -> string ->
't@@connected -> 't@@connected
val recv : all 't. 't socket -> int ->
't@@connected -> string * 't@@connected
val close : all 't. 't socket -> 't@@connected -> unit
(* When we raise an exception, we "freeze" the capability.
* We can thaw the frozen capability if we have the socket that
* it goes with. (This requires a dynamic check.) This lets us
* recover the capability with a type paramater that matches any
* extant sockets that go with it. *)
type 'a frozen qualifier A
val thaw : all 't. 't socket -> all 's. 's frozen -> 's frozen + 't@@'s
(* Operations for catching the error state associated with a given
socket. *)
val catchInitial : all 't `a. 't socket ->
(unit -o `a) -> ('t@@initial -o `a) -o `a
val catchBound : all 't `a. 't socket ->
(unit -o `a) -> ('t@@bound -o `a) -o `a
val catchListening : all 't `a. 't socket ->
(unit -o `a) -> ('t@@listening -o `a) -o `a
val catchConnected : all 't `a. 't socket ->
(unit -o `a) -> ('t@@connected -o `a) -o `a
(* Socket exceptions *)
type socketError = StillInitial of initial frozen
| StillBound of bound frozen
| StillListening of listening frozen
| StillConnected of connected frozen
| Disconnected
exception SocketError of socketError * string
end
module ASocket : ASOCKET = struct
module S = Socket
let getAddrByName = S.getAddrByName
type rep = S.socket
type 't socket = S.socket
type socketError = StillInitial of rep
| StillBound of rep
| StillListening of rep
| StillConnected of rep
| Disconnected
exception SocketError of socketError * string
let error (se: socketError) (msg: string) =
raise (SocketError (se, msg))
let socket () =
try (S.socket (), ())
with
IOError msg -> error Disconnected msg
let bind (sock: rep) (port: int) () =
try S.bind sock port
with
IOError msg -> error (StillInitial sock) msg
let connect (sock: rep) (host: string) (port: string)
(cap: unit + unit) =
try S.connect sock host port
with
IOError msg -> match cap with
| Left _ -> error (StillInitial sock) msg
| Right _ -> error (StillBound sock) msg
let listen (sock: rep) () =
try S.listen sock
with
IOError msg -> error (StillBound sock) msg
let accept (sock: rep) () =
try (S.accept sock, (), ())
with
IOError msg -> error (StillListening sock) msg
let send (sock: rep) (data: string) () =
try
S.send sock data;
()
with
IOError msg -> error Disconnected msg
let recv (sock: rep) (len: int) () =
try (S.recv sock len, ())
with
IOError msg -> error Disconnected msg
let close (sock: rep) () =
try S.close sock
with
IOError msg -> error Disconnected msg
(* Convenience functions for catching and thawing frozen socket
* capabilities. *)
let thaw (sock: rep) (sock': rep) =
if sock == sock'
then Right ()
else Left sock'
let catchInitial[`a] (sock: rep) (body: unit -o `a)
(handler: unit -o `a) =
try body () with
| SocketError (StillInitial frz, msg) ->
match thaw sock frz with
| Left frz -> error (StillInitial frz) msg
| Right cap -> handler cap
let catchBound[`a] (sock: rep) (body: unit -o `a)
(handler: unit -o `a) =
try body () with
| SocketError (StillBound frz, msg) ->
match thaw sock frz with
| Left frz -> error (StillBound frz) msg
| Right cap -> handler cap
let catchListening[`a] (sock: rep) (body: unit -o `a)
(handler: unit -o `a) =
try body () with
| SocketError (StillListening frz, msg) ->
match thaw sock frz with
| Left frz -> error (StillListening frz) msg
| Right cap -> handler cap
let catchConnected[`a] (sock: rep) (body: unit -o `a)
(handler: unit -o `a) =
try body () with
| SocketError (StillConnected frz, msg) ->
match thaw sock frz with
| Left frz -> error (StillConnected frz) msg
| Right cap -> handler cap
(* Types for the interface *)
type 't @@ 's = unit
type initial
type bound
type listening
type connected
type 's frozen = rep
end