This repository was archived by the owner on Jul 10, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathfileserver.gr
More file actions
139 lines (123 loc) · 3.91 KB
/
fileserver.gr
File metadata and controls
139 lines (123 loc) · 3.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// This is a simple Wagi static file server.
import Env from "./lib/env"
import Map from "map"
import Option from "option"
import File from "sys/file"
import String from "string"
import Result from "result"
import Mediatype from "./lib/mediatype"
import Stringutil from "./lib/stringutil"
// Utility wrapper around a Result.expect that ignores the return value
// so we don't need to worry about things returning non-Void types
let validateResult = (msg, res) => {
ignore(Result.expect(msg, res))
}
let internalError = () => {
validateResult(
"Unexpected error when writing Internal Server Error response",
File.fdWrite(File.stdout, "Status: 500\n\nInternal Server Error"),
)
}
let notFound = () => {
validateResult(
"Unexpected error when writing Not Found response",
File.fdWrite(File.stdout, "Status: 404\n\nNot Found"),
)
}
// Pipe output to STDOUT
let rec pipe = (in, out) => {
let res = File.fdRead(in, 1024)
match (res) {
Err(err) => Err(err),
Ok((d, len)) => {
let res = File.fdWrite(out, d)
if (len > 0) {
pipe(in, out)
} else {
res
}
},
}
}
// Determine cache control values.
let cache_duration = (env, mtype) => {
let default_cache_control = match (Map.get("CACHE_CONTROL", env)) {
Some(val) => val,
None => "no-cache",
}
if (String.indexOf("image/", mtype) == Some(0)) {
match (Map.get("IMAGE_CACHE_CONTROL", env)) {
Some(val) => val,
None => default_cache_control,
}
} else if (String.indexOf("font/", mtype) == Some(0)) {
match (Map.get("FONT_CACHE_CONTROL", env)) {
Some(val) => val,
None => default_cache_control,
}
} else if (String.indexOf("text/css", mtype) == Some(0)) {
match (Map.get("CSS_CACHE_CONTROL", env)) {
Some(val) => val,
None => default_cache_control,
}
} else if (String.indexOf("text/javascript", mtype) == Some(0)) {
match (Map.get("JS_CACHE_CONTROL", env)) {
Some(val) => val,
None => default_cache_control,
}
} else {
default_cache_control
}
}
let headers = (env, path) => {
let mtype = Mediatype.guess(path);
"Content-Type: " ++ mtype ++ "\nCache-Control: " ++ cache_duration(env, mtype) ++ "\n\n"
}
let serve = (abs_path, env) => {
// If PATH_PREFIX is set, then the path prefix is prepended onto the incoming path.
// This allows you to map to a directory that does not match the directory name in the URL.
let path = match (Map.get("PATH_PREFIX", env)) {
Some(prefix) => {
let tmp = String.slice(1, String.length(abs_path), abs_path)
String.concat(prefix, tmp)
},
// If no env var, trim off just the leading /
None => String.slice(1, String.length(abs_path), abs_path)
}
// Explicitly ignoring any Ok or Err that happens on this log
// The `ignore` can be removed if you don't want to be explicit about this behavior
ignore(File.fdWrite(File.stderr, "Fileserver: Loading file " ++ path ++ "\n"))
// Open file
let result = File.pathOpen(File.pwdfd, [], path, [], [File.FdRead], [], [])
match (result) {
Err(_err) => notFound(),
Ok(input) => {
validateResult(
"Unexpected error when writing Content-Type",
File.fdWrite(
File.stdout,
headers(env, path),
),
)
validateResult(
"Unexpected error when streaming file body",
pipe(input, File.stdout),
)
// This validation may be able to be removed if it doesn't matter if the fdClose fails
validateResult("Unexpected error when closing file", File.fdClose(input))
},
}
}
let guestpath = env => {
// Backward compat for an older version of Wagi that had PATH_INFO wrong.
// X_RELATIVE_PATH was removed before Wagi 0.4
match (Map.get("X_RELATIVE_PATH", env)) {
Some(p) => String.concat("/", p),
None => {
Option.unwrap(Map.get("PATH_INFO", env))
},
}
}
let kv = Env.envMap()
let pathInfo = guestpath(kv)
serve(pathInfo, kv)