-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.js
154 lines (142 loc) · 6.15 KB
/
server.js
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Run a node.js web server for local development of a static web site.
// Start with "node server.js" and put pages in a "public" sub-folder.
// Visit the site at the address printed on the console.
// The server is configured to be platform independent. URLs are made lower
// case, so the server is case insensitive even on Linux, and paths containing
// upper case letters are banned so that the file system is treated as case
// sensitive even on Windows. All .html files are delivered as
// application/xhtml+xml for instant feedback on XHTML errors. To improve the
// server, either add content negotiation (so old browsers can be tested) or
// switch to text/html and do validity checking another way (e.g. with vnu.jar).
// Choose a port, e.g. change the port to the default 80, if there are no
// privilege issues and port number 80 isn't already in use. Choose verbose to
// list banned files (with upper case letters) on startup.
var port = 5000;
var verbose = true;
// Load the library modules, and define the global constants.
// See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes.
// Start the server:
var http = require("http");
var fs = require("fs");
var OK = 200, NotFound = 404, BadType = 415, Error = 500;
var types, banned;
start();
// Start the http service. Accept only requests from localhost, for security.
function start() {
if (! checkSite()) return;
types = defineTypes();
banned = [];
banUpperCase("./public/", "");
var service = http.createServer(handle);
//service.listen(port, "localhost");
service.listen(process.env.PORT || port); //removed requirement for localhost requests
var address = "http://localhost";
if (port != 80) address = address + ":" + port;
console.log("Server running at", address);
}
// Check that the site folder and index page exist.
function checkSite() {
var path = "./public";
var ok = fs.existsSync(path);
if (ok) path = "./public/index.html";
if (ok) ok = fs.existsSync(path);
if (! ok) console.log("Can't find", path);
return ok;
}
// Serve a request by delivering a file.
function handle(request, response) {
var url = request.url.toLowerCase();
if (url.endsWith("/")) url = url + "index.html";
if (isBanned(url)) return fail(response, NotFound, "URL has been banned");
var type = findType(url);
if (type == null) return fail(response, BadType, "File type unsupported");
var file = "./public" + url;
fs.readFile(file, ready);
function ready(err, content) { deliver(response, type, err, content); }
}
// Forbid any resources which shouldn't be delivered to the browser.
function isBanned(url) {
for (var i=0; i<banned.length; i++) {
var b = banned[i];
if (url.startsWith(b)) return true;
}
return false;
}
// Find the content type to respond with, or undefined.
function findType(url) {
var dot = url.lastIndexOf(".");
var extension = url.substring(dot + 1);
return types[extension];
}
// Deliver the file that has been read in to the browser.
function deliver(response, type, err, content) {
if (err) return fail(response, NotFound, "File not found");
var typeHeader = { "Content-Type": type };
response.writeHead(OK, typeHeader);
response.write(content);
response.end();
}
// Give a minimal failure response to the browser
function fail(response, code, text) {
var textTypeHeader = { "Content-Type": "text/plain" };
response.writeHead(code, textTypeHeader);
response.write(text, "utf8");
response.end();
}
// Check a folder for files/subfolders with non-lowercase names. Add them to
// the banned list so they don't get delivered, making the site case sensitive,
// so that it can be moved from Windows to Linux, for example. Synchronous I/O
// is used because this function is only called during startup. This avoids
// expensive file system operations during normal execution. A file with a
// non-lowercase name added while the server is running will get delivered, but
// it will be detected and banned when the server is next restarted.
function banUpperCase(root, folder) {
var folderBit = 1 << 14;
var names = fs.readdirSync(root + folder);
for (var i=0; i<names.length; i++) {
var name = names[i];
var file = folder + "/" + name;
if (name != name.toLowerCase()) {
if (verbose) console.log("Banned:", file);
banned.push(file.toLowerCase());
}
var mode = fs.statSync(root + file).mode;
if ((mode & folderBit) == 0) continue;
banUpperCase(root, file);
}
}
// The most common standard file extensions are supported, and html is
// delivered as "application/xhtml+xml". Some common non-standard file
// extensions are explicitly excluded. This table is defined using a function
// rather than just a global variable, because otherwise the table would have
// to appear before calling start(). NOTE: add entries as needed or, for a more
// complete list, install the mime module and adapt the list it provides.
function defineTypes() {
var types = {
html : "application/xhtml+xml", // this enables file being sent as xhtml
css : "text/css",
js : "application/javascript",
mjs : "application/javascript", // for ES6 modules
png : "image/png",
gif : "image/gif", // for images copied unchanged
jpeg : "image/jpeg", // for images copied unchanged
jpg : "image/jpeg", // for images copied unchanged
svg : "image/svg+xml",
json : "application/json",
pdf : "application/pdf",
txt : "text/plain",
ttf : "application/x-font-ttf",
woff : "application/font-woff",
aac : "audio/aac",
mp3 : "audio/mpeg",
mp4 : "video/mp4",
webm : "video/webm",
ico : "image/x-icon", // just for favicon.ico
xhtml: undefined, // non-standard, use .html
htm : undefined, // non-standard, use .html
rar : undefined, // non-standard, platform dependent, use .zip
doc : undefined, // non-standard, platform dependent, use .pdf
docx : undefined, // non-standard, platform dependent, use .pdf
}
return types;
}