Non gemtext static files

* Static file handler now attempts to use "file-type" if it is available 
to read the magic bytes of a file and get Mime type
* Fixed a crash if the client closed the socket prematurely
* Move README to gmi
This commit is contained in:
MatthiasSaihttam 2021-10-09 21:05:56 -04:00
parent 7761c18fd5
commit 2dabbc0969
8 changed files with 317 additions and 10 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.pem
node_modules

View File

@ -220,6 +220,9 @@ export default class GeminiServer {
}
}
});
// This can be just ECONNRESET
socket.on("error", console.error);
});
server.listen(1965);
}

View File

@ -23,7 +23,7 @@ A `relativePath` is the difference between a `basePath` and a `urlPath`. It's no
Sometimes these paths will include their hostnames
A path in the pathRegistry is a `p`, which is an object with a hostname, a basePath, and a handler
A path in the pathRegistry is a `p`, which is an object with a hostname, a basePath, and a handler
@ -44,4 +44,4 @@ openssl x509 -req -in csr.pem -signkey private-key.pem -out public-cert.pem
It's possible to imagine a situation where astronomical theater is behind a proxy and the proxy doesn't do address translation, passing the raw gemini request to us.
In this case, our behavior is undefined.
Similarly, SNI is not supported with IP addresses.
Similarly, SNI is not supported with IP addresses.

View File

@ -57,8 +57,8 @@ server.registerPath("localhost/allFiles", new ExampleHandler());
// We will provide some convenient handlers for static, CGI, and reverse proxy
//if the passed file is a single file, it's a file. If it's a directory, all sub-files are auto-included
server.registerPath("localhost/static/about.gmi", new StaticHandler("/tmp/content/about.gmi" /*, {options}*/));
server.registerPath("localhost/static", new StaticHandler("/tmp/content/" /*, {options}*/));
server.registerPath("localhost/static/about.gmi", new StaticHandler("README.gmi" /*, {options}*/));
server.registerPath("localhost/static", new StaticHandler("static/" /*, {options}*/));
// server.registerPath("localhost/static", new StaticHandler("/tmp/yourmom/" /*, {options}*/));
//The file passed to CGI handler must exist and be executable at run time

View File

@ -3,6 +3,9 @@ import * as fs from "fs";
import DefaultHandler from "./default.js";
let fileTypeFromFile;
import("file-type").then(ft => fileTypeFromFile = ft.default.fromFile).catch(console.error);
export default class StaticHandler extends DefaultHandler {
constructor(basePath, directoryList = false) {
super();
@ -18,7 +21,7 @@ export default class StaticHandler extends DefaultHandler {
// This is the handler
// url is the URL object, with url.pathname being the path. p is the 'client-side' basepath
handle (url, p) {
async handle (url, p, socket) {
const relativePath = path.relative(p, url.pathname);
//Concat and normalize the passed URL as being relative to the base path
const toServe = path.join(this.basePath, relativePath);
@ -32,7 +35,7 @@ export default class StaticHandler extends DefaultHandler {
if (fs.statSync(toServe).isDirectory()) {
console.log(`File is a directory and directoryList is ${this.directoryList}.`);
if (this.directoryList) {
//TODO: Static content directory listing
} else {
return "51 Not Found\r\n";
}
@ -43,9 +46,31 @@ export default class StaticHandler extends DefaultHandler {
return "51 Not Found\r\n";
}
//TODO: import mmmagic
let mimeType = "text/gemini";
if (fileTypeFromFile) {
const type = await fileTypeFromFile(toServe);
// `type` will be undefined if the file is plaintext
// In which case we want to fall down to serving plaintext
if (type) {
mimeType = type.mime;
}
}
//TODO: some file-ending based logic, please
//So we can serve md and txt
//TODO: convert line endings
const data = fs.readFileSync(toServe);
return "20 text/gemini\r\n" + data;
// I read somewhere that `readFileSync` on every request is a bad idea, but I don't care
socket.write(`20 ${mimeType}\r\n`);
const fileReader = fs.createReadStream(toServe);
//This function (handle) needs to return after we're done writing the file to the stream
//Because the place that called this is going to handle .end() the socket
//So we don't end the socket here, and we make a promise to wait for the reader to end before returning
//It would be great if we could just not return from this function but I don't think that's how this works
fileReader.pipe(socket, {end: false});
await new Promise((res, rej) => fileReader.on("end", res));
return "";
}
}

275
package-lock.json generated Normal file
View File

@ -0,0 +1,275 @@
{
"name": "astronomical-theater",
"version": "v2.0.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "astronomical-theater",
"version": "v2.0.5",
"license": "CC0-1.0",
"optionalDependencies": {
"file-type": "^16.5.3"
}
},
"node_modules/@tokenizer/token": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
"optional": true
},
"node_modules/file-type": {
"version": "16.5.3",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.3.tgz",
"integrity": "sha512-uVsl7iFhHSOY4bEONLlTK47iAHtNsFHWP5YE4xJfZ4rnX7S1Q3wce09XgqSC7E/xh8Ncv/be1lNoyprlUH/x6A==",
"optional": true,
"dependencies": {
"readable-web-to-node-stream": "^3.0.0",
"strtok3": "^6.2.4",
"token-types": "^4.1.1"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"optional": true
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"optional": true
},
"node_modules/peek-readable": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.0.1.tgz",
"integrity": "sha512-7qmhptnR0WMSpxT5rMHG9bW/mYSR1uqaPFj2MHvT+y/aOUu6msJijpKt5SkTDKySwg65OWG2JwTMBlgcbwMHrQ==",
"optional": true,
"engines": {
"node": ">=8"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"optional": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/readable-web-to-node-stream": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
"integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
"optional": true,
"dependencies": {
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"optional": true
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"optional": true,
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/strtok3": {
"version": "6.2.4",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.2.4.tgz",
"integrity": "sha512-GO8IcFF9GmFDvqduIspUBwCzCbqzegyVKIsSymcMgiZKeCfrN9SowtUoi8+b59WZMAjIzVZic/Ft97+pynR3Iw==",
"optional": true,
"dependencies": {
"@tokenizer/token": "^0.3.0",
"peek-readable": "^4.0.1"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/token-types": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-4.1.1.tgz",
"integrity": "sha512-hD+QyuUAyI2spzsI0B7gf/jJ2ggR4RjkAo37j3StuePhApJUwcWDjnHDOFdIWYSwNR28H14hpwm4EI+V1Ted1w==",
"optional": true,
"dependencies": {
"@tokenizer/token": "^0.3.0",
"ieee754": "^1.2.1"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"optional": true
}
},
"dependencies": {
"@tokenizer/token": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
"optional": true
},
"file-type": {
"version": "16.5.3",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.3.tgz",
"integrity": "sha512-uVsl7iFhHSOY4bEONLlTK47iAHtNsFHWP5YE4xJfZ4rnX7S1Q3wce09XgqSC7E/xh8Ncv/be1lNoyprlUH/x6A==",
"optional": true,
"requires": {
"readable-web-to-node-stream": "^3.0.0",
"strtok3": "^6.2.4",
"token-types": "^4.1.1"
}
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"optional": true
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"optional": true
},
"peek-readable": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.0.1.tgz",
"integrity": "sha512-7qmhptnR0WMSpxT5rMHG9bW/mYSR1uqaPFj2MHvT+y/aOUu6msJijpKt5SkTDKySwg65OWG2JwTMBlgcbwMHrQ==",
"optional": true
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"optional": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"readable-web-to-node-stream": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
"integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
"optional": true,
"requires": {
"readable-stream": "^3.6.0"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"optional": true
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"optional": true,
"requires": {
"safe-buffer": "~5.2.0"
}
},
"strtok3": {
"version": "6.2.4",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.2.4.tgz",
"integrity": "sha512-GO8IcFF9GmFDvqduIspUBwCzCbqzegyVKIsSymcMgiZKeCfrN9SowtUoi8+b59WZMAjIzVZic/Ft97+pynR3Iw==",
"optional": true,
"requires": {
"@tokenizer/token": "^0.3.0",
"peek-readable": "^4.0.1"
}
},
"token-types": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-4.1.1.tgz",
"integrity": "sha512-hD+QyuUAyI2spzsI0B7gf/jJ2ggR4RjkAo37j3StuePhApJUwcWDjnHDOFdIWYSwNR28H14hpwm4EI+V1Ted1w==",
"optional": true,
"requires": {
"@tokenizer/token": "^0.3.0",
"ieee754": "^1.2.1"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"optional": true
}
}
}

View File

@ -1,11 +1,14 @@
{
"name": "astronomical-theater",
"version": "v2.0.5",
"version": "v2.1.0",
"author": "Matthias",
"license": "CC0-1.0",
"type": "module",
"main": "main.js",
"scripts": {
"start": "node main.js"
},
"optionalDependencies": {
"file-type": "^16.5.3"
}
}

BIN
static/small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB