Skip to content

Commit 411cc73

Browse files
committed
Update solutions for Chapter 20
1 parent dc9f9e7 commit 411cc73

File tree

13 files changed

+105
-140
lines changed

13 files changed

+105
-140
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
/code/chapter/*
1717
/code/file_server.js
1818
/code/skillsharing.zip
19-
/code/solutions/20_4_a_public_space_on_the_web.zip
19+
/code/solutions/20_3_a_public_space_on_the_web.zip
2020
/code/skillsharing/*
2121
/node_modules
2222
.tern-port

20_node.md

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -794,12 +794,13 @@ it relative to the program's working directory.
794794
const {parse} = require("url");
795795
const {resolve} = require("path");
796796
797-
const baseDirectory = process.cwd() + "/";
797+
const baseDirectory = process.cwd();
798798
799799
function urlPath(url) {
800800
let {pathname} = parse(url);
801801
let path = resolve(decodeURIComponent(pathname).slice(1));
802-
if (!path.startsWith(baseDirectory)) {
802+
if (path != baseDirectory &&
803+
!path.startsWith(baseDirectory + "/")) {
803804
throw {status: 403, body: "Forbidden"};
804805
}
805806
return path;
@@ -862,7 +863,7 @@ and whether it is a ((directory)).
862863
```{includeCode: ">code/file_server.js"}
863864
const {createReadStream} = require("fs");
864865
const {stat, readdir} = require("mz/fs");
865-
const {getType} = require("mime");
866+
const mime = require("mime");
866867
867868
methods.GET = async function(request) {
868869
let path = urlPath(request.url);
@@ -874,10 +875,10 @@ methods.GET = async function(request) {
874875
else return {status: 404, body: "File not found"};
875876
}
876877
if (stats.isDirectory()) {
877-
return {body: await readdir(path).join("\n")};
878+
return {body: (await readdir(path)).join("\n")};
878879
} else {
879880
return {body: createReadStream(path),
880-
type: getType(path)};
881+
type: mime.getType(path)};
881882
}
882883
};
883884
```
@@ -1123,6 +1124,24 @@ widely used HTTP method, but it does exist for this same purpose in
11231124
the _((WebDAV))_ standard, which specifies a set of conventions on top
11241125
of ((HTTP)) that make it suitable for creating documents.
11251126

1127+
```{hidden: true, includeCode: ">code/file_server.js"}
1128+
const {mkdir} = require("mz/fs");
1129+
1130+
methods.MKCOL = async function(request) {
1131+
let path = urlPath(request.url);
1132+
let stats;
1133+
try {
1134+
stats = await stat(path);
1135+
} catch (error) {
1136+
if (error.code != "ENOENT") throw error;
1137+
await mkdir(path);
1138+
return {status: 204};
1139+
}
1140+
if (stats.isDirectory()) return {status: 204};
1141+
else return {status: 400, body: "Not a directory"};
1142+
};
1143+
```
1144+
11261145
{{hint
11271146

11281147
{{index "directory creation (exercise)", "file server example", "MKCOL method", "mkdir function", idempotency, "400 (HTTP status code)"}}

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ CHAPTERS := $(basename $(shell ls [0-9][0-9]_*.md) .md)
33
SVGS := $(wildcard img/*.svg)
44

55
html: $(foreach CHAP,$(CHAPTERS),html/$(CHAP).html) html/js/acorn_codemirror.js \
6-
code/skillsharing.zip code/solutions/20_4_a_public_space_on_the_web.zip html/js/chapter_info.js
6+
code/skillsharing.zip code/solutions/20_3_a_public_space_on_the_web.zip html/js/chapter_info.js
77

88
html/%.html: %.md
99
node src/render_html.js $< > $@
@@ -26,9 +26,9 @@ code/skillsharing.zip: html/21_skillsharing.html
2626
rm -f $@
2727
cd code; zip skillsharing.zip skillsharing/*.js* skillsharing/public/*.*
2828

29-
code/solutions/20_4_a_public_space_on_the_web.zip: $(wildcard code/solutions/20_4_a_public_space_on_the_web/*)
29+
code/solutions/20_3_a_public_space_on_the_web.zip: $(wildcard code/solutions/20_3_a_public_space_on_the_web/*)
3030
rm -f $@
31-
cd code/solutions; zip 20_4_a_public_space_on_the_web.zip 20_4_a_public_space_on_the_web/*
31+
cd code/solutions; zip 20_3_a_public_space_on_the_web.zip 20_3_a_public_space_on_the_web/*
3232

3333
test: html
3434
@for F in $(CHAPTERS); do echo Testing $$F:; node src/run_tests.js $$F.md; done

code/solutions/20_1_content_negotiation_again.js

Lines changed: 0 additions & 31 deletions
This file was deleted.

code/solutions/20_1_search_tool.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const {statSync, readdirSync, readFileSync} = require("fs");
2+
3+
let searchTerm = new RegExp(process.argv[2]);
4+
5+
for (let arg of process.argv.slice(3)) {
6+
search(arg, Math.min);
7+
}
8+
9+
function search(file) {
10+
let stats = statSync(file);
11+
if (stats.isDirectory()) {
12+
for (let f of readdirSync(file)) {
13+
search(file + "/" + f);
14+
}
15+
} else if (searchTerm.test(readFileSync(file, "utf8"))) {
16+
console.log(file);
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This code won't work on its own, but is also included in the
2+
// code/file_server.js file, which defines the whole system.
3+
4+
const {mkdir} = require("mz/fs");
5+
6+
methods.MKCOL = async function(request) {
7+
let path = urlPath(request.url);
8+
let stats;
9+
try {
10+
stats = await stat(path);
11+
} catch (error) {
12+
if (error.code != "ENOENT") throw error;
13+
await mkdir(path);
14+
return {status: 204};
15+
}
16+
if (stats.isDirectory()) return {status: 204};
17+
else return {status: 400, body: "Not a directory"};
18+
};

code/solutions/20_2_fixing_a_leak.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

code/solutions/20_4_a_public_space_on_the_web/index.html renamed to code/solutions/20_3_a_public_space_on_the_web/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<!doctype html>
2+
<meta charset=utf8>
23

34
<h1>A Public Space on the Web</h1>
45

Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
<!doctype html>
2+
<meta charset=utf8>
23

34
<h1>This is another file</h1>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Get a reference to the DOM nodes we need
2+
let filelist = document.querySelector("#filelist");
3+
let textarea = document.querySelector("#file");
4+
5+
// This loads the initial file list from the server
6+
fetch("/").then(resp => resp.text()).then(files => {
7+
for (let file of files.split("\n")) {
8+
let option = document.createElement("option");
9+
option.textContent = file;
10+
filelist.appendChild(option);
11+
}
12+
// Now that we have a list of files, make sure the textarea contains
13+
// the currently selected one.
14+
loadCurrentFile();
15+
});
16+
17+
// Fetch a file from the server and put it in the textarea.
18+
function loadCurrentFile() {
19+
fetch(filelist.value).then(resp => resp.text()).then(file => {
20+
textarea.value = file;
21+
});
22+
}
23+
24+
filelist.addEventListener("change", loadCurrentFile);
25+
26+
// Called by the button on the page. Makes a request to save the
27+
// currently selected file.
28+
function saveFile() {
29+
fetch(filelist.value, {method: "PUT",
30+
body: textarea.value});
31+
}

0 commit comments

Comments
 (0)