signature WEB =
sig
val for : (int -> unit) -> int * int -> unit
+ (* (for f (min, max)) runs f on i for every i from min to max, inclusive.
+ * min may also be greater than max, for backward order. *)
val print : string -> unit
+ (* Add to the web output buffer *)
val clear : unit -> unit
+ (* Clear web output buffer *)
val noOutput : unit -> bool
+ (* True if there is no output in the buffer *)
val output : unit -> unit
+ (* Flush the buffer *)
val setParam : string * string list -> unit
+ (* Set the values associated with a single URL parameter.
+ * Parameters acquire multiple values when things like the following
+ * appear in URL's: ...&x=1&x=2&...
+ *)
val setSingleParam : string * string -> unit
+ (* Set a parameter with a single value *)
val getParam : string -> string
+ (* Get the first value set for a parameter, or "" if none is set *)
val getMultiParam : string -> string list
+ (* Get all values of a parameter *)
val pushParams : (string * string list) list -> unit
+ (* Save the current parameter map and set the new ones given *)
val popParams : unit -> unit
+ (* Restore the last parameter map saved with pushParams *)
val withParams : (unit -> 'a) -> (string * string list) list -> 'a
+ (* (withParams f params) executes f with params added to the parameter
+ * map, removing them afterwards *)
val getCgi : string -> string option
+ (* Get a named value defined by the CGI protocol, such as HTTP_COOKIE *)
val html : string -> string
+ (* Escape a plaintext string to appear in HTML *)
val htmlNl : string -> string
+ (* Like html, but changes newlines to <br>'s *)
val urlEncode : string -> string
+ (* Escape a plaintext string to appear in a URL *)
exception Format of string
val stoi : string -> int
+ (* Convert a string to an int, raising Format on error *)
val stor : string -> real
+ (* Convert a string to a real, raising Format on error *)
val summary : unit -> string
+ (* Debugging info on CGI state *)
val getExn : unit -> exn
+ (* Get the current exception when in an exception handler template *)
val setExn : exn -> unit
+ (* Set the exception to be returned by getExn *)
- val setHeader : string * string -> unit
- val getHeader : string -> string option
+ val setHeader : string * string list -> unit
+ (* Set the values of an HTTP response header *)
+ val getHeader : string -> string list option
+ (* Get the values of an HTTP response header *)
+ val addHeader : string * string -> unit
+ (* Add a value for a header *)
+ val getSingleHeader : string -> string option
+ (* Get the first value of a header *)
type cookie = {name : string, value : string, expires : Date.date option,
domain : string option, path : string option, secure : bool}
+ (* name: Key
+ * value: Data to associate with name
+ * expires: When browser should discard this cookie.
+ * NONE clears the cookie immediately. SOME of a date clears it at
+ * that date. Use a very far-off date for "non-expiring" cookies.
+ * domain: If NONE, cookie is valid in current domain.
+ * If SOME, specifies a particular domain pattern for the cookie.
+ * path: Like domain, but specifies path on a server
+ * secure: If set, cookie may only be transferred over HTTPS
+ *)
val setCookie : cookie -> unit
+ (* Add a cookie set request to the HTTP response *)
val getCookie : string -> string option
+ (* Get a cookie's value by looking up its name *)
val remoteHost : unit -> string option
+ (* Get REMOTE_HOST CGI variable *)
+ val plusSeconds : Time.time * int -> Time.time
+ (* Add seconds to a time *)
val minusSeconds : Time.time * int -> Time.time
+ (* Subtract seconds from a time *)
val replaceUrlVar : string * string * string -> string
+ (* (replaceUrlVar (url, name, value)) replaces the value of
+ * URL variable name in url by value. If name is not set in
+ * url, it is added.
+ *)
end
\ No newline at end of file
((f ()) handle ex => (popParams (); raise ex))
before popParams ())
- val headers = ref (StringMap.empty : string StringMap.map)
+ val headers = ref (StringMap.empty : string list StringMap.map)
fun setHeader (n, v) = headers := StringMap.insert (!headers, n, v)
fun getHeader n = StringMap.find (!headers, n)
+ fun addHeader (n, v) =
+ headers := StringMap.insert (!headers, n, case getHeader n of
+ NONE => [v]
+ | SOME vs => vs @ [v])
+ fun getSingleHeader n =
+ (case StringMap.find (!headers, n) of
+ SOME (v::_) => SOME v
+ | _ => NONE)
val text = ref ([] : string list)
fun noOutput () = !text = []
fun output () =
(TextIO.print "Status: 200\n";
- StringMap.appi (fn (n, v) => (TextIO.print n;
- TextIO.print ": ";
- TextIO.print v;
- TextIO.print "\n")) (!headers);
+ StringMap.appi (fn (n, vs) =>
+ app (fn v => (TextIO.print n;
+ TextIO.print ": ";
+ TextIO.print v;
+ TextIO.print "\n"))
+ vs) (!headers);
TextIO.print "\n";
TextIO.print (String.concat (List.rev (!text))))
else
s
in
- setHeader ("Set-Cookie", s)
+ addHeader ("Set-Cookie", s)
end
fun getCookie n =
NONE => getCgi "REMOTE_ADDR"
| h => h
+ fun plusSeconds (t, s) = Time.+ (t, Time.fromSeconds (LargeInt.fromInt s))
fun minusSeconds (t, s) = Time.- (t, Time.fromSeconds (LargeInt.fromInt s))
fun split (s, ch) =