From: Adam Chlipala Date: Sun, 11 Jan 2004 00:30:34 +0000 (+0000) Subject: Changed handling of response headers to allow multiple values; added documentation... X-Git-Url: http://git.hcoop.net/bpt/mlt.git/commitdiff_plain/870af305c1ecdfeb14883690bee066e0b707757b Changed handling of response headers to allow multiple values; added documentation in code and manual --- diff --git a/doc/manual.tex b/doc/manual.tex index ada1809..a37f7d5 100644 --- a/doc/manual.tex +++ b/doc/manual.tex @@ -110,10 +110,18 @@ Anonymous {\tt fn} functions are available with the SML syntax. SML {\tt case} expressions are supported. +\subsubsection{\tt iff} + +SML {\tt if} expressions are supported, except that the keyword that introduces them is {\tt iff}, to disambiguate from {\tt if} statements. + \subsubsection{\tt raise} SML {\tt raise} expressions are supported. +\subsubsection{\tt let} + +SML {\tt let} expressions are supported. + \subsection{Patterns} Patterns are identical to SML patterns without support for user-defined infix constructors, though {\tt ::} is supported. Record patterns can include field names with no assigned patterns (the pattern for such a field is taken to be the field name) and "flex record" {\tt ...}'s to stand for unused fields. diff --git a/src/lib/main.sml b/src/lib/main.sml index 84c47b3..139686a 100644 --- a/src/lib/main.sml +++ b/src/lib/main.sml @@ -29,7 +29,7 @@ struct val cgiFields = Cgi.cgi_fieldnames () fun mapper name = (name, Cgi.cgi_field_strings name) in - Web.setHeader ("Content-type", "text/html"); + Web.addHeader ("Content-type", "text/html"); Web.pushParams (map mapper cgiFields); Templates.beforeFn (); case args of diff --git a/src/lib/web.sig b/src/lib/web.sig index 71a5881..2ed69e2 100644 --- a/src/lib/web.sig +++ b/src/lib/web.sig @@ -22,47 +22,99 @@ 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
'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 diff --git a/src/lib/web.sml b/src/lib/web.sml index ebd49fb..2394687 100644 --- a/src/lib/web.sml +++ b/src/lib/web.sml @@ -78,9 +78,17 @@ struct ((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) @@ -89,10 +97,12 @@ struct 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)))) @@ -188,7 +198,7 @@ struct else s in - setHeader ("Set-Cookie", s) + addHeader ("Set-Cookie", s) end fun getCookie n = @@ -211,6 +221,7 @@ struct 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) =