@@ -5,62 +5,86 @@ import Map from "map"
55import Option from "option"
66import File from "sys/file"
77import String from "string"
8+ import Result from "result"
89import Mediatype from "./lib/mediatype"
910import Stringutil from "./lib/stringutil"
1011
11- let serve = (abs_path) => {
12- // Trim the leading /
13- let path = String.slice(1, String.length(abs_path), abs_path)
14- File.fdWrite(File.stderr, "Fileserver: Loading file ")
15- File.fdWrite(File.stderr, path)
16- File.fdWrite(File.stderr, "\n")
12+ // Utility wrapper around a Result.expect that ignores the return value
13+ // so we don't need to worry about things returning non-Void types
14+ let validateResult = (msg, res) => {
15+ ignore(Result.expect(msg, res))
16+ }
17+
18+ let internalError = () => {
19+ validateResult(
20+ "Unexpected error when writing Internal Server Error response",
21+ File.fdWrite(File.stdout, "Status: 500\n\nInternal Server Error"),
22+ )
23+ }
1724
18- // Open file
19- // TODO: This throws an error when something goes wrong.
20- // But Grain 0.3 does not have a try/catch yet.
21- // When we figure out how, we need to catch the error here
22- // and do a 404 or 500.
23- let input = File.pathOpen(
24- File.pwdfd,
25- [],
26- path,
27- [],
28- [File.FdRead],
29- [],
30- [],
31- )
25+ let notFound = () => {
26+ validateResult(
27+ "Unexpected error when writing Not Found response",
28+ File.fdWrite(File.stdout, "Status: 404\n\nNot Found"),
29+ )
30+ }
3231
33- File.fdWrite(File.stdout, "Content-Type: ")
34- File.fdWrite(File.stdout, Mediatype.guess(path))
35- File.fdWrite(File.stdout, "\n\n" )
36-
37- // Pipe output to STDOUT
38- let rec pipe = (in, out ) => {
39- let (d, len) = File.fdRead(in, 1024 )
40- File.fdWrite(out, d)
41- if (len > 0) {
42- pipe(in, out)
43- }
44- }
45- pipe(input, File.stdout)
46- File.fdClose(input)
32+ // Pipe output to STDOUT
33+ let rec pipe = (in, out) => {
34+ let res = File.fdRead(in, 1024 )
35+ match (res) {
36+ Err(err) => Err(err),
37+ Ok((d, len) ) => {
38+ let res = File.fdWrite(out, d )
39+ if (len > 0) {
40+ pipe(in, out)
41+ } else {
42+ res
43+ }
44+ },
45+ }
4746}
4847
49- let guestpath = (env) => {
48+ let serve = abs_path => {
49+ // Trim the leading /
50+ let path = String.slice(1, String.length(abs_path), abs_path)
51+ // Explicitly ignoring any Ok or Err that happens on this log
52+ // The `ignore` can be removed if you don't want to be explicit about this behavior
53+ ignore(File.fdWrite(File.stderr, "Fileserver: Loading file " ++ path ++ "\n"))
54+
55+ // Open file
56+ let result = File.pathOpen(File.pwdfd, [], path, [], [File.FdRead], [], [])
5057
51- // Backward compat for an older version of Wagi that had PATH_INFO wrong.
52- // X_RELATIVE_PATH was removed before Wagi 0.4
53- match (Map.get("X_RELATIVE_PATH", env)) {
54- Some(p) => String.concat("/", p),
55- None => {
56- Option.unwrap(Map.get("PATH_INFO", env))
57- }
58- }
59-
58+ match (result) {
59+ Err(_err) => notFound(),
60+ Ok(input) => {
61+ validateResult(
62+ "Unexpected error when writing Content-Type",
63+ File.fdWrite(
64+ File.stdout,
65+ "Content-Type: " ++ Mediatype.guess(path) ++ "\n\n",
66+ ),
67+ )
68+
69+ validateResult(
70+ "Unexpected error when streaming file body",
71+ pipe(input, File.stdout),
72+ )
73+ // This validation may be able to be removed if it doesn't matter if the fdClose fails
74+ validateResult("Unexpected error when closing file", File.fdClose(input))
75+ },
76+ }
6077}
6178
62- let notFound = () => {
63- File.fdWrite(File.stdout, "Status: 404\n\nNot Found")
79+ let guestpath = env => {
80+ // Backward compat for an older version of Wagi that had PATH_INFO wrong.
81+ // X_RELATIVE_PATH was removed before Wagi 0.4
82+ match (Map.get("X_RELATIVE_PATH", env)) {
83+ Some(p) => String.concat("/", p),
84+ None => {
85+ Option.unwrap(Map.get("PATH_INFO", env))
86+ },
87+ }
6488}
6589
6690let kv = Env.envMap()
0 commit comments