changeset 741:f7e2026dd5ae

Returning a blob as page result
author Adam Chlipala <adamc@hcoop.net>
date Sun, 26 Apr 2009 09:02:17 -0400
parents b302b6e35f93
children 43553c93dd8c
files include/types.h include/urweb.h lib/ur/basis.urs src/c/driver.c src/c/urweb.c src/cjr.sml src/cjr_print.sml src/cjrize.sml src/jscomp.sml src/mono.sml src/mono_opt.sig src/mono_opt.sml src/mono_print.sml src/mono_reduce.sml src/mono_util.sml src/monoize.sml src/prepare.sml src/scriptcheck.sml tests/echoBlob.ur tests/echoBlob.urp tests/echoBlob.urs
diffstat 21 files changed, 144 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/include/types.h	Sat Apr 25 14:47:16 2009 -0400
+++ b/include/types.h	Sun Apr 26 09:02:17 2009 -0400
@@ -33,7 +33,7 @@
   uw_Basis_blob data;
 } uw_Basis_file;
 
-typedef enum { SUCCESS, FATAL, BOUNDED_RETRY, UNLIMITED_RETRY } failure_kind;
+typedef enum { SUCCESS, FATAL, BOUNDED_RETRY, UNLIMITED_RETRY, RETURN_BLOB } failure_kind;
 
 
 #define INTS_MAX 50
--- a/include/urweb.h	Sat Apr 25 14:47:16 2009 -0400
+++ b/include/urweb.h	Sun Apr 26 09:02:17 2009 -0400
@@ -157,6 +157,7 @@
 uw_Basis_client uw_Basis_self(uw_context, uw_unit);
 
 uw_Basis_string uw_Basis_bless(uw_context, uw_Basis_string);
+uw_Basis_string uw_Basis_blessMime(uw_context, uw_Basis_string);
 
 uw_Basis_string uw_unnull(uw_Basis_string);
 uw_Basis_string uw_Basis_makeSigString(uw_context, uw_Basis_string);
@@ -166,3 +167,4 @@
 uw_Basis_string uw_Basis_fileMimeType(uw_context, uw_Basis_file);
 uw_Basis_blob uw_Basis_fileData(uw_context, uw_Basis_file);
 
+__attribute__((noreturn)) void uw_return_blob(uw_context, uw_Basis_blob, uw_Basis_string mimeType);
--- a/lib/ur/basis.urs	Sat Apr 25 14:47:16 2009 -0400
+++ b/lib/ur/basis.urs	Sun Apr 26 09:02:17 2009 -0400
@@ -521,6 +521,10 @@
 
 val upload : formTag file [] [Value = string, Size = int]
 
+type mimeType
+val blessMime : string -> mimeType
+val returnBlob : t ::: Type -> blob -> mimeType -> transaction t
+
 con radio = [Body, Radio]
 val radio : formTag string radio []
 val radioOption : unit -> tag [Value = string] radio [] [] []
--- a/src/c/driver.c	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/c/driver.c	Sun Apr 26 09:02:17 2009 -0400
@@ -194,7 +194,7 @@
 
       if (s = strstr(buf, "\r\n\r\n")) {
         failure_kind fk;
-        int is_post = 0;
+        int is_post = 0, do_normal_send = 1;
         char *boundary = NULL;
         size_t boundary_len;
         char *cmd, *path, *headers, path_copy[uw_bufsize+1], *inputs, *after_headers;
@@ -433,7 +433,7 @@
 
           strcpy(path_copy, path);
           fk = uw_begin(ctx, path_copy);
-          if (fk == SUCCESS) {
+          if (fk == SUCCESS || fk == RETURN_BLOB) {
             uw_commit(ctx);
             break;
           } else if (fk == BOUNDED_RETRY) {
--- a/src/c/urweb.c	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/c/urweb.c	Sun Apr 26 09:02:17 2009 -0400
@@ -1,4 +1,4 @@
-#define _XOPEN_SOURCE
+#define _XOPEN_SOURCE 500
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -8,6 +8,7 @@
 #include <setjmp.h>
 #include <stdarg.h>
 #include <assert.h>
+#include <ctype.h>
 
 #include <pthread.h>
 
@@ -2104,6 +2105,16 @@
   return s;
 }
 
+uw_Basis_string uw_Basis_blessMime(uw_context ctx, uw_Basis_string s) {
+  char *s2;
+
+  for (s2 = s; *s2; ++s2)
+    if (!isalnum(*s2) && *s2 != '/' && *s2 != '-' && *s2 != '.')
+      uw_error(ctx, FATAL, "MIME type \"%s\" contains invalid character %c\n", s, *s2);
+  
+  return s;
+}
+
 uw_Basis_string uw_unnull(uw_Basis_string s) {
   return s ? s : "";
 }
@@ -2135,3 +2146,28 @@
 uw_Basis_blob uw_Basis_fileData(uw_context ctx, uw_Basis_file f) {
   return f.data;
 }
+
+__attribute__((noreturn)) void uw_return_blob(uw_context ctx, uw_Basis_blob b, uw_Basis_string mimeType) {
+  cleanup *cl;
+  int len;
+
+  buf_reset(&ctx->outHeaders);
+  buf_reset(&ctx->page);
+
+  uw_write_header(ctx, "HTTP/1.1 200 OK\r\nContent-Type: ");
+  uw_write_header(ctx, mimeType);
+  uw_write_header(ctx, "\r\nContent-Length: ");
+  buf_check(&ctx->outHeaders, INTS_MAX);
+  sprintf(ctx->outHeaders.front, "%d%n", b.size, &len);
+  ctx->outHeaders.front += len;
+  uw_write_header(ctx, "\r\n");  
+
+  buf_append(&ctx->page, b.data, b.size);
+
+  for (cl = ctx->cleanup; cl < ctx->cleanup_front; ++cl)
+    cl->func(cl->arg);
+
+  ctx->cleanup_front = ctx->cleanup;
+
+  longjmp(ctx->jmp_buf, RETURN_BLOB);
+}
--- a/src/cjr.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/cjr.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -75,6 +75,7 @@
        | ECase of exp * (pat * exp) list * { disc : typ, result : typ }
 
        | EError of exp * typ
+       | EReturnBlob of {blob : exp, mimeType : exp, t : typ}
 
        | EWrite of exp
        | ESeq of exp * exp
--- a/src/cjr_print.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/cjr_print.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -1276,8 +1276,26 @@
              string "tmp;",
              newline,
              string "})"]
+      | EReturnBlob {blob, mimeType, t} =>
+        box [string "({",
+             newline,
+             p_typ env t,
+             space,
+             string "tmp;",
+             newline,
+             string "uw_return_blob(ctx, ",
+             p_exp env blob,
+             string ", ",
+             p_exp env mimeType,
+             string ");",
+             newline,
+             string "tmp;",
+             newline,
+             string "})"]
       | EApp ((EError (e, (TFun (_, ran), _)), loc), _) =>
         p_exp env (EError (e, ran), loc)
+      | EApp ((EReturnBlob {blob, mimeType, t = (TFun (_, ran), _)}, loc), _) =>
+        p_exp env (EReturnBlob {blob = blob, mimeType = mimeType, t = ran}, loc)
 
       | EFfiApp (m, x, es) => box [string "uw_",
                                    p_ident m,
--- a/src/cjrize.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/cjrize.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -319,6 +319,14 @@
         in
             ((L'.EError (e, t), loc), sm)
         end
+      | L.EReturnBlob {blob, mimeType, t} =>
+        let
+            val (blob, sm) = cifyExp (blob, sm)
+            val (mimeType, sm) = cifyExp (mimeType, sm)
+            val (t, sm) = cifyTyp (t, sm)
+        in
+            ((L'.EReturnBlob {blob = blob, mimeType = mimeType, t = t}, loc), sm)
+        end
 
       | L.EStrcat (e1, e2) =>
         let
--- a/src/jscomp.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/jscomp.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -101,6 +101,7 @@
         (map (fn (p, e) => E.patBindsN p + varDepth e) pes)
       | EStrcat (e1, e2) => Int.max (varDepth e1, varDepth e2)
       | EError (e, _) => varDepth e
+      | EReturnBlob {blob = e1, mimeType = e2, ...} => Int.max (varDepth e1, varDepth e2)
       | EWrite e => varDepth e
       | ESeq (e1, e2) => Int.max (varDepth e1, varDepth e2)
       | ELet (_, _, e1, e2) => Int.max (varDepth e1, 1 + varDepth e2)
@@ -141,6 +142,7 @@
                 andalso List.all (fn (p, e) => cu (inner + E.patBindsN p) e) pes
               | EStrcat (e1, e2) => cu inner e1 andalso cu inner e2
               | EError (e, _) => cu inner e
+              | EReturnBlob {blob = e1, mimeType = e2, ...} => cu inner e1 andalso cu inner e2
               | EWrite e => cu inner e
               | ESeq (e1, e2) => cu inner e1 andalso cu inner e2
               | ELet (_, _, e1, e2) => cu inner e1 andalso cu (inner + 1) e2
@@ -915,6 +917,7 @@
                           | EDml _ => unsupported "DML"
                           | ENextval _ => unsupported "Nextval"
                           | EUnurlify _ => unsupported "EUnurlify"
+                          | EReturnBlob _ => unsupported "EUnurlify"
                           | EJavaScript (_, e, _) =>
                             let
                                 val (e, st) = jsE inner (e, st)
--- a/src/mono.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/mono.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -88,6 +88,7 @@
        | EStrcat of exp * exp
 
        | EError of exp * typ
+       | EReturnBlob of {blob : exp, mimeType : exp, t : typ}
 
        | EWrite of exp
        | ESeq of exp * exp
--- a/src/mono_opt.sig	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/mono_opt.sig	Sun Apr 26 09:02:17 2009 -0400
@@ -31,5 +31,6 @@
     val optExp : Mono.exp -> Mono.exp
 
     val bless : (string -> bool) ref
+    val blessMime : (string -> bool) ref
 
 end
--- a/src/mono_opt.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/mono_opt.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -31,6 +31,7 @@
 structure U = MonoUtil
 
 val bless = ref (fn _ : string => true)
+val blessMime = ref (CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"-" orelse ch = #"/" orelse ch = #"."))
 
 fun typ t = t
 fun decl d = d
@@ -386,6 +387,12 @@
          else
              ErrorMsg.errorAt loc "Invalid URL passed to 'bless'";
          se)
+      | EFfiApp ("Basis", "blessMime", [(se as EPrim (Prim.String s), loc)]) =>
+        (if !blessMime s then
+             ()
+         else
+             ErrorMsg.errorAt loc "Invalid string passed to 'blessMime'";
+         se)
 
       | EFfiApp ("Basis", "checkString", [(EPrim (Prim.String s), loc)]) => 
         let
--- a/src/mono_print.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/mono_print.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -211,6 +211,18 @@
                               space,
                               p_typ env t,
                               string ")"]
+      | EReturnBlob {blob, mimeType, t} => box [string "(blob",
+                                                space,
+                                                p_exp env blob,
+                                                space,
+                                                string "in",
+                                                space,
+                                                p_exp env mimeType,
+                                                space,
+                                                string ":",
+                                                space,
+                                                p_typ env t,
+                                                string ")"]
 
       | EStrcat (e1, e2) => parenIf par (box [p_exp' true env e1,
                                               space,
--- a/src/mono_reduce.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/mono_reduce.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -79,6 +79,7 @@
       | ECase (e, pes, _) => impure e orelse List.exists (fn (_, e) => impure e) pes
 
       | EError (e, _) => impure e
+      | EReturnBlob {blob = e1, mimeType = e2, ...} => impure e1 orelse impure e2
 
       | EStrcat (e1, e2) => impure e1 orelse impure e2
 
@@ -349,6 +350,7 @@
                       | EStrcat (e1, e2) => summarize d e1 @ summarize d e2
 
                       | EError (e, _) => summarize d e @ [Unsure]
+                      | EReturnBlob {blob = e1, mimeType = e2, ...} => summarize d e1 @ summarize d e2 @ [Unsure]
 
                       | EWrite e => summarize d e @ [WritePage]
                                     
--- a/src/mono_util.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/mono_util.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -247,7 +247,15 @@
                             S.map2 (mft t,
                                     fn t' =>
                                        (EError (e', t'), loc)))
-
+              | EReturnBlob {blob, mimeType, t} =>
+                S.bind2 (mfe ctx blob,
+                         fn blob' =>
+                            S.bind2 (mfe ctx mimeType,
+                                  fn mimeType' =>
+                                     S.map2 (mft t,
+                                          fn t' =>
+                                             (EReturnBlob {blob = blob', mimeType = mimeType', t = t'}, loc))))
+                            
               | EStrcat (e1, e2) =>
                 S.bind2 (mfe ctx e1,
                       fn e1' =>
--- a/src/monoize.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/monoize.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -128,6 +128,7 @@
                     readType (mt env dtmap t, loc)
 
                   | L.CFfi ("Basis", "url") => (L'.TFfi ("Basis", "string"), loc)
+                  | L.CFfi ("Basis", "mimeType") => (L'.TFfi ("Basis", "string"), loc)
                   | L.CApp ((L.CApp ((L.CApp ((L.CFfi ("Basis", "xml"), _), _), _), _), _), _) =>
                     (L'.TFfi ("Basis", "string"), loc)
                   | L.CApp ((L.CApp ((L.CFfi ("Basis", "xhtml"), _), _), _), _) =>
@@ -2560,6 +2561,20 @@
                            (L'.EError ((L'.ERel 0, loc), t), loc)), loc),
                  fm)
             end
+          | L.ECApp ((L.EFfi ("Basis", "returnBlob"), _), t) =>
+            let
+                val t = monoType env t
+                val un = (L'.TRecord [], loc)
+            in
+                ((L'.EAbs ("b", (L'.TFfi ("Basis", "blob"), loc),
+                           (L'.TFun ((L'.TFfi ("Basis", "string"), loc), (L'.TFun (un, t), loc)), loc),
+                           (L'.EAbs ("mt", (L'.TFfi ("Basis", "string"), loc), (L'.TFun (un, t), loc),
+                                     (L'.EAbs ("_", un, t,
+                                               (L'.EReturnBlob {blob = (L'.ERel 2, loc),
+                                                                mimeType = (L'.ERel 1, loc),
+                                                                t = t}, loc)), loc)), loc)), loc),
+                 fm)
+            end
 
           | L.EApp (e1, e2) =>
             let
--- a/src/prepare.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/prepare.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -156,6 +156,14 @@
             ((EError (e, t), loc), sns)
         end
 
+      | EReturnBlob {blob, mimeType, t} =>
+        let
+            val (blob, sns) = prepExp (blob, sns)
+            val (mimeType, sns) = prepExp (mimeType, sns)
+        in
+            ((EReturnBlob {blob = blob, mimeType = mimeType, t = t}, loc), sns)
+        end
+
       | EWrite e =>
         let
             val (e, sns) = prepExp (e, sns)
--- a/src/scriptcheck.sml	Sat Apr 25 14:47:16 2009 -0400
+++ b/src/scriptcheck.sml	Sun Apr 26 09:02:17 2009 -0400
@@ -86,6 +86,7 @@
                       | EField (e, _) => hasClient e
                       | ECase (e, pes, _) => hasClient e orelse List.exists (hasClient o #2) pes
                       | EError (e, _) => hasClient e
+                      | EReturnBlob {blob = e1, mimeType = e2, ...} => hasClient e1 orelse hasClient e2
                       | EWrite e => hasClient e
                       | ESeq (e1, e2) => hasClient e1 orelse hasClient e2
                       | ELet (_, _, e1, e2) => hasClient e1 orelse hasClient e2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/echoBlob.ur	Sun Apr 26 09:02:17 2009 -0400
@@ -0,0 +1,8 @@
+fun echo r = returnBlob (fileData r.Data) (blessMime (fileMimeType r.Data))
+
+fun main () = return <xml><body>
+  <form>
+    <upload{#Data}/>
+    <submit action={echo}/>
+  </form>
+</body></xml>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/echoBlob.urp	Sun Apr 26 09:02:17 2009 -0400
@@ -0,0 +1,3 @@
+debug
+
+echoBlob
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/echoBlob.urs	Sun Apr 26 09:02:17 2009 -0400
@@ -0,0 +1,1 @@
+val main : unit -> transaction page