/*
- * Copyright (C) 2018 Nick Downing <nick@ndcode.org>
+ * Copyright (C) 2018-2022 Nick Downing <nick@ndcode.org>
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
import jst_server from './index.mjs'
import url from 'url'
-let Server = function(resources, options/*, prev_server*/) {
- if (!this instanceof Server)
- throw new Error('Server is a constructor')
- this.resources = resources
- this.options = Object.assign(
- {
- caching: false,
- listeners: [
- {
- protocol: 'http:'
- },
- {
- protocol: 'https:'
- }
- ],
- hosts: {
- 'localhost': {
- type: 'site',
- root: 'site'
- },
- 'localhost.localdomain': {
- type: 'redirect',
- host: 'localhost'
- }
- }
- },
- options || {}
- )
-
- this.jst_cache = undefined
- this.site_roots = undefined
- this.listeners = undefined
- this.request_func =
- (request, response, listener) =>
- /*await*/ this.respond(request, response, listener.options.protocol)
- // returned Promise will be ignored
-}
-
-Server.prototype.start = async function() {
- assert(this.jst_cache === undefined)
- this.jst_cache = await this.resources.ref(
- 'jst_cache:.',
- async () => new JSTCache('.', {_jst_server: jst_server}, true)
- )
-
- assert(this.site_roots === undefined)
- this.site_roots = {}
- for (let i in this.options.hosts) {
- let host
- if (
- Object.prototype.hasOwnProperty.call(this.options.hosts, i) &&
- (host = this.options.hosts[i]).type === 'site' &&
- !Object.prototype.hasOwnProperty.call(this.site_roots, host.root)
- )
- this.site_roots[host.root] = await this.resources.ref(
- `site_root:${host.root}`,
- async () => (
+class Server {
+ constructor(resources, options/*, prev_server*/) {
+ this.resources = resources
+ this.options = Object.assign(
+ {
+ caching: false,
+ listeners: [
{
- jst_cache: await this.resources.ref(
- `jst_cache:${host.root}`,
- async () =>
- new JSTCache(host.root, {_jst_server: jst_server}, true)
- ),
- root: host.root,
- site: undefined
+ protocol: 'http:'
+ },
+ {
+ protocol: 'https:'
+ }
+ ],
+ hosts: {
+ 'localhost': {
+ type: 'site',
+ root: 'site'
+ },
+ 'localhost.localdomain': {
+ type: 'redirect',
+ host: 'localhost'
}
- ),
- async site_root => {
- await this.resources.unref(`jst_cache:${site_root.root}`)
- if (site_root.site !== undefined)
- await site_root.site.stop()
}
- )
+ },
+ options || {}
+ )
+
+ this.jst_cache = undefined
+ this.site_roots = undefined
+ this.listeners = undefined
+ this.request_func =
+ (request, response, listener) =>
+ /*await*/ this.respond(request, response, listener.options.protocol)
+ // returned Promise will be ignored
}
- assert(this.listeners === undefined)
- this.listeners = []
- for (let i = 0; i < this.options.listeners.length; ++i) {
- let options
- switch (this.options.listeners[i].protocol) {
- case 'http:':
- options = Object.assign(
- {
- protocol: 'https:',
- address: '0.0.0.0',
- port: 8080
- },
- this.options.listeners[i]
+ async start() {
+ assert(this.jst_cache === undefined)
+ this.jst_cache = await this.resources.ref(
+ 'jst_cache:.',
+ async () => new JSTCache('.', {_jst_server: jst_server}, true)
+ )
+
+ assert(this.site_roots === undefined)
+ this.site_roots = {}
+ for (let i in this.options.hosts) {
+ let host
+ if (
+ Object.prototype.hasOwnProperty.call(this.options.hosts, i) &&
+ (host = this.options.hosts[i]).type === 'site' &&
+ !Object.prototype.hasOwnProperty.call(this.site_roots, host.root)
)
- break
- case 'https:':
- options = Object.assign(
- {
- protocol: 'https:',
- address: '0.0.0.0',
- port: 8443,
- ssl_cert: '_ssl/localhost_cert_bundle.pem',
- ssl_key: '_ssl/localhost_key.pem'
- },
- this.options.listeners[i]
+ this.site_roots[host.root] = await this.resources.ref(
+ `site_root:${host.root}`,
+ async () => (
+ {
+ jst_cache: await this.resources.ref(
+ `jst_cache:${host.root}`,
+ async () =>
+ new JSTCache(host.root, {_jst_server: jst_server}, true)
+ ),
+ root: host.root,
+ site: undefined
+ }
+ ),
+ async site_root => {
+ await this.resources.unref(`jst_cache:${site_root.root}`)
+ if (site_root.site !== undefined)
+ await site_root.site.stop()
+ }
+ )
+ }
+
+ assert(this.listeners === undefined)
+ this.listeners = []
+ for (let i = 0; i < this.options.listeners.length; ++i) {
+ let options
+ switch (this.options.listeners[i].protocol) {
+ case 'http:':
+ options = Object.assign(
+ {
+ protocol: 'https:',
+ address: '0.0.0.0',
+ port: 8080
+ },
+ this.options.listeners[i]
+ )
+ break
+ case 'https:':
+ options = Object.assign(
+ {
+ protocol: 'https:',
+ address: '0.0.0.0',
+ port: 8443,
+ ssl_cert: '_ssl/localhost_cert_bundle.pem',
+ ssl_key: '_ssl/localhost_key.pem'
+ },
+ this.options.listeners[i]
+ )
+ break
+ default:
+ assert(false)
+ }
+ let listener = await this.resources.ref(
+ `listener:${JSON.stringify(options)}`,
+ async () => new Listener(undefined, options, true),
+ listener => /*await*/ listener.stop()
)
- break
- default:
- assert(false)
+ this.listeners.push(listener)
}
- let listener = await this.resources.ref(
- `listener:${JSON.stringify(options)}`,
- async () => new Listener(undefined, options, true),
- listener => /*await*/ listener.stop()
- )
- this.listeners.push(listener)
}
-}
-Server.prototype.stop = async function() {
- assert(this.jst_cache !== undefined)
- await this.resources.unref('jst_cache:.')
+ async stop() {
+ assert(this.jst_cache !== undefined)
+ await this.resources.unref('jst_cache:.')
- assert(this.site_roots !== undefined)
- for (let i in this.site_roots)
- if (Object.prototype.hasOwnProperty.call(this.site_roots, i))
- await this.resources.unref(`site_root:${i}`)
+ assert(this.site_roots !== undefined)
+ for (let i in this.site_roots)
+ if (Object.prototype.hasOwnProperty.call(this.site_roots, i))
+ await this.resources.unref(`site_root:${i}`)
- assert(this.listeners !== undefined)
- for (let i = 0; i < this.listeners.length; ++i)
- await this.resources.unref(
- `listener:${JSON.stringify(this.listeners[i].options)}`
- )
-}
-
-Server.prototype.kick = async function() {
- assert(this.jst_cache !== undefined)
- this.jst_cache.kick()
-
- assert(this.site_roots !== undefined)
- for (let i in this.site_roots)
- if (Object.prototype.hasOwnProperty.call(this.site_roots, i)) {
- let site_root = this.site_roots[i]
- let config = await site_root.jst_cache.get(
- `${i}/_config/site.jst`,
- true
+ assert(this.listeners !== undefined)
+ for (let i = 0; i < this.listeners.length; ++i)
+ await this.resources.unref(
+ `listener:${JSON.stringify(this.listeners[i].options)}`
)
- if (config !== undefined) {
- let prev_site = site_root.site
- let new_site = await config(this.resources, i, prev_site)
- await new_site.start() // exception here cancels site change
- site_root.site = new_site
- if (prev_site !== undefined)
- await prev_site.stop() // exception here doesn't cancel change
+ }
+
+ async kick() {
+ assert(this.jst_cache !== undefined)
+ this.jst_cache.kick()
+
+ assert(this.site_roots !== undefined)
+ for (let i in this.site_roots)
+ if (Object.prototype.hasOwnProperty.call(this.site_roots, i)) {
+ let site_root = this.site_roots[i]
+ let config = await site_root.jst_cache.get(
+ `${i}/_config/site.jst`,
+ true
+ )
+ if (config !== undefined) {
+ let prev_site = site_root.site
+ let new_site = await config(this.resources, i, prev_site)
+ await new_site.start() // exception here cancels site change
+ site_root.site = new_site
+ if (prev_site !== undefined)
+ await prev_site.stop() // exception here doesn't cancel change
+ }
+ await site_root.site.kick()
}
- await site_root.site.kick()
- }
- assert(this.listeners !== undefined)
- for (let i = 0; i < this.listeners.length; ++i) {
- this.listeners[i].request_func = this.request_func
- await this.listeners[i].start()
+ assert(this.listeners !== undefined)
+ for (let i = 0; i < this.listeners.length; ++i) {
+ this.listeners[i].request_func = this.request_func
+ await this.listeners[i].start()
+ }
}
-}
-Server.prototype.serve_internal = function(response, status, mime_type, data) {
- response.statusCode = status
- // no real need to cache errors and hostname redirects
- // (pathname redirects on the other hand will be cached, see Site.js)
- response.setHeader('Content-Type', mime_type)
- response.setHeader('Content-Length', data.length)
- response.end(data)
-}
+ serve_internal(response, status, mime_type, data) {
+ response.statusCode = status
+ // no real need to cache errors and hostname redirects
+ // (pathname redirects on the other hand will be cached, see Site.js)
+ response.setHeader('Content-Type', mime_type)
+ response.setHeader('Content-Length', data.length)
+ response.end(data)
+ }
-Server.prototype.die = function(response, pathname, message) {
- console.log(message)
- this.serve_internal(
- response,
- 404,
- 'text/html; charset=utf-8',
- Buffer.from(
- `<html>
+ die(response, pathname, message) {
+ console.log(message)
+ this.serve_internal(
+ response,
+ 404,
+ 'text/html; charset=utf-8',
+ Buffer.from(
+ `<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>404 Not Found</title>
</body>
</html>
`
+ )
)
- )
-}
+ }
-Server.prototype.redirect = function(response, location, message) {
- console.log(message)
- response.statusCode = 301
- response.setHeader('Location', location)
- this.serve_internal(
- response,
- 301,
- 'text/html; charset=utf-8',
- Buffer.from(
- `<html>
+ redirect(response, location, message) {
+ console.log(message)
+ response.statusCode = 301
+ response.setHeader('Location', location)
+ this.serve_internal(
+ response,
+ 301,
+ 'text/html; charset=utf-8',
+ Buffer.from(
+ `<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>301 Moved Permanently</title>
</body>
</html>
`
+ )
)
- )
-}
-
-Server.prototype.respond = async function(request, response, protocol) {
- try {
- let parsed_url = url.parse(
- protocol + '//' + (request.headers.host || 'localhost') + request.url,
- true
- )
- //console.log('parsed_url', parsed_url)
+ }
- if (
- !Object.prototype.hasOwnProperty.call(this.options.hosts, parsed_url.hostname)
- ) {
- this.die(
- response,
- parsed_url.pathname,
- 'nonexistent site: ' + parsed_url.hostname
+ async respond(request, response, protocol) {
+ try {
+ let parsed_url = url.parse(
+ protocol + '//' + (request.headers.host || 'localhost') + request.url,
+ true
)
- return
- }
+ //console.log('parsed_url', parsed_url)
- let host = this.options.hosts[parsed_url.hostname]
- switch (host.type) {
- case 'redirect':
- let new_host = host.host
- if (parsed_url.port !== null)
- new_host += ':' + parsed_url.port
- this.redirect(
+ if (
+ !Object.prototype.hasOwnProperty.call(this.options.hosts, parsed_url.hostname)
+ ) {
+ this.die(
+ response,
+ parsed_url.pathname,
+ 'nonexistent site: ' + parsed_url.hostname
+ )
+ return
+ }
+
+ let host = this.options.hosts[parsed_url.hostname]
+ switch (host.type) {
+ case 'redirect':
+ let new_host = host.host
+ if (parsed_url.port !== null)
+ new_host += ':' + parsed_url.port
+ this.redirect(
+ response,
+ `${parsed_url.protocol}//${new_host}${request.url}`,
+ `redirecting ${parsed_url.host} to ${new_host}`
+ )
+ break
+ case 'site':
+ await this.site_roots[host.root].site.respond(
+ {
+ parsed_url: parsed_url,
+ response: response,
+ request: request,
+ server: this
+ }
+ )
+ break
+ default:
+ assert(false)
+ }
+ }
+ catch (err) {
+ let message = (err.stack || err.message).toString()
+ console.error(message)
+ this.serve_internal(
response,
- `${parsed_url.protocol}//${new_host}${request.url}`,
- `redirecting ${parsed_url.host} to ${new_host}`
- )
- break
- case 'site':
- await this.site_roots[host.root].site.respond(
- {
- parsed_url: parsed_url,
- response: response,
- request: request,
- server: this
- }
+ 500,
+ 'text/html; charset=utf-8',
+ Buffer.from(`<html><body><pre>${message}</pre></body></html>`)
)
- break
- default:
- assert(false)
}
- }
- catch (err) {
- let message = (err.stack || err.message).toString()
- console.error(message)
- this.serve_internal(
- response,
- 500,
- 'text/html; charset=utf-8',
- Buffer.from(`<html><body><pre>${message}</pre></body></html>`)
- )
}
}
/*
- * Copyright (C) 2018-2021 Nick Downing <nick@ndcode.org>
+ * Copyright (C) 2018-2022 Nick Downing <nick@ndcode.org>
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
import fsPromises from 'fs/promises'
import jst_server from './index.mjs'
-let Site = function(resources, root, options/*, prev_site*/) {
- if (!this instanceof Site)
- throw new Error('Site is a constructor')
- this.resources = resources
- this.root = root
- this.options = Object.assign(
- {
- caching: false,
- mime_types: {
- '.css': 'text/css; charset=utf-8',
- '.html': 'text/html; charset=utf-8',
- '.ico': 'image/x-icon',
- '.jpg': 'image/jpeg',
- '.jpeg': 'image/jpeg',
- '.js': 'application/javascript; charset=utf-8',
- '.json': 'application/json; charset=utf-8',
- '.mp4': 'video/mp4',
- '.pdf': 'application/pdf',
- '.png': 'image/png',
- '.svg': 'image/svg+xml',
- '.ttf': 'application/octet-stream',
- '.woff': 'font/woff',
- '.woff2': 'font/woff2',
- '.xml': 'text/xml; charset=utf-8'
+class Site {
+ constructor(resources, root, options/*, prev_site*/) {
+ this.resources = resources
+ this.root = root
+ this.options = Object.assign(
+ {
+ caching: false,
+ mime_types: {
+ '.css': 'text/css; charset=utf-8',
+ '.html': 'text/html; charset=utf-8',
+ '.ico': 'image/x-icon',
+ '.jpg': 'image/jpeg',
+ '.jpeg': 'image/jpeg',
+ '.js': 'application/javascript; charset=utf-8',
+ '.json': 'application/json; charset=utf-8',
+ '.mp4': 'video/mp4',
+ '.pdf': 'application/pdf',
+ '.png': 'image/png',
+ '.svg': 'image/svg+xml',
+ '.ttf': 'application/octet-stream',
+ '.woff': 'font/woff',
+ '.woff2': 'font/woff2',
+ '.xml': 'text/xml; charset=utf-8'
+ },
+ certbot_webroot: '/var/www/html'
},
- certbot_webroot: '/var/www/html'
- },
- options || {}
- )
-
- this.json_cache = undefined
- this.json_cache_rw = undefined
- this.jst_cache = undefined
- this.less_css_cache = undefined
- this.menu_cache = undefined
- this.min_css_cache = undefined
- this.min_js_cache = undefined
- this.min_html_cache = undefined
- this.min_svg_cache = undefined
- this.text_cache = undefined
- this.zip_cache = undefined
-
- this.socket_io_connect_listeners = [] // later will use this for destruction
-}
+ options || {}
+ )
-Site.prototype.start = async function() {
- assert(this.json_cache === undefined)
- this.json_cache = await this.resources.ref(
- 'json_cache',
- async () => new JSONCache(true)
- )
-
- assert(this.json_cache_rw === undefined)
- this.json_cache_rw = await this.resources.ref(
- 'json_cache_rw',
- async () => new JSONCacheRW(true)
- )
-
- assert(this.jst_cache === undefined)
- this.jst_cache = await this.resources.ref(
- `jst_cache:${this.root}`,
- async () => new JSTCache(this.root, {_jst_server: jst_server}, true)
- )
-
- assert(this.less_css_cache === undefined)
- this.less_css_cache = await this.resources.ref(
- `less_css_cache:${this.root}`,
- async () => new LessCSSCache(this.root, true)
- )
-
- assert(this.menu_cache === undefined)
- this.menu_cache = await this.resources.ref(
- 'menu_cache',
- async () => new MenuCache(true)
- )
-
- assert(this.min_css_cache === undefined)
- this.min_css_cache = await this.resources.ref(
- 'min_css_cache',
- async () => new MinCSSCache(true)
- )
-
- assert(this.min_js_cache === undefined)
- this.min_js_cache = await this.resources.ref(
- 'min_js_cache',
- async () => new MinJSCache(true)
- )
-
- assert(this.min_html_cache === undefined)
- this.min_html_cache = await this.resources.ref(
- 'min_html_cache',
- async () => new MinHTMLCache(true)
- )
-
- assert(this.min_svg_cache === undefined)
- this.min_svg_cache = await this.resources.ref(
- 'min_svg_cache',
- async () => new MinSVGCache(true)
- )
-
- assert(this.sass_css_cache === undefined)
- this.sass_css_cache = await this.resources.ref(
- `sass_css_cache:${this.root}`,
- async () => new SassCSSCache(this.root, true)
- )
-
- assert(this.text_cache === undefined)
- this.text_cache = await this.resources.ref(
- 'text_cache',
- async () => new TextCache(true)
- )
-
- assert(this.zip_cache === undefined)
- this.zip_cache = await this.resources.ref(
- 'zip_cache',
- async () => new ZipCache(true)
- )
-}
+ this.json_cache = undefined
+ this.json_cache_rw = undefined
+ this.jst_cache = undefined
+ this.less_css_cache = undefined
+ this.menu_cache = undefined
+ this.min_css_cache = undefined
+ this.min_js_cache = undefined
+ this.min_html_cache = undefined
+ this.min_svg_cache = undefined
+ this.text_cache = undefined
+ this.zip_cache = undefined
+
+ this.socket_io_connect_listeners = [] // later will use this for destruction
+ }
-Site.prototype.stop = async function() {
- assert(this.json_cache !== undefined)
- await this.resources.unref('json_cache')
+ async start() {
+ assert(this.json_cache === undefined)
+ this.json_cache = await this.resources.ref(
+ 'json_cache',
+ async () => new JSONCache(true)
+ )
- assert(this.json_cache_rw !== undefined)
- await this.resources.unref('json_cache_rw')
+ assert(this.json_cache_rw === undefined)
+ this.json_cache_rw = await this.resources.ref(
+ 'json_cache_rw',
+ async () => new JSONCacheRW(true)
+ )
- assert(this.jst_cache !== undefined)
- await this.resources.unref(`jst_cache:${this.root}`)
+ assert(this.jst_cache === undefined)
+ this.jst_cache = await this.resources.ref(
+ `jst_cache:${this.root}`,
+ async () => new JSTCache(this.root, {_jst_server: jst_server}, true)
+ )
- assert(this.less_css_cache !== undefined)
- await this.resources.unref(`less_css_cache:${this.root}`)
+ assert(this.less_css_cache === undefined)
+ this.less_css_cache = await this.resources.ref(
+ `less_css_cache:${this.root}`,
+ async () => new LessCSSCache(this.root, true)
+ )
- assert(this.menu_cache !== undefined)
- await this.resources.unref('menu_cache')
+ assert(this.menu_cache === undefined)
+ this.menu_cache = await this.resources.ref(
+ 'menu_cache',
+ async () => new MenuCache(true)
+ )
- assert(this.min_css_cache !== undefined)
- await this.resources.unref('min_css_cache')
+ assert(this.min_css_cache === undefined)
+ this.min_css_cache = await this.resources.ref(
+ 'min_css_cache',
+ async () => new MinCSSCache(true)
+ )
- assert(this.min_js_cache !== undefined)
- await this.resources.unref('min_js_cache')
+ assert(this.min_js_cache === undefined)
+ this.min_js_cache = await this.resources.ref(
+ 'min_js_cache',
+ async () => new MinJSCache(true)
+ )
- assert(this.min_html_cache !== undefined)
- await this.resources.unref('min_html_cache')
+ assert(this.min_html_cache === undefined)
+ this.min_html_cache = await this.resources.ref(
+ 'min_html_cache',
+ async () => new MinHTMLCache(true)
+ )
- assert(this.min_svg_cache !== undefined)
- await this.resources.unref('min_svg_cache')
+ assert(this.min_svg_cache === undefined)
+ this.min_svg_cache = await this.resources.ref(
+ 'min_svg_cache',
+ async () => new MinSVGCache(true)
+ )
- assert(this.sass_css_cache !== undefined)
- await this.resources.unref(`sass_css_cache:${this.root}`)
+ assert(this.sass_css_cache === undefined)
+ this.sass_css_cache = await this.resources.ref(
+ `sass_css_cache:${this.root}`,
+ async () => new SassCSSCache(this.root, true)
+ )
- assert(this.text_cache !== undefined)
- await this.resources.unref('text_cache')
+ assert(this.text_cache === undefined)
+ this.text_cache = await this.resources.ref(
+ 'text_cache',
+ async () => new TextCache(true)
+ )
- assert(this.zip_cache !== undefined)
- await this.resources.unref('zip_cache')
-}
+ assert(this.zip_cache === undefined)
+ this.zip_cache = await this.resources.ref(
+ 'zip_cache',
+ async () => new ZipCache(true)
+ )
+ }
-Site.prototype.kick = async function() {
- assert(this.json_cache !== undefined)
- this.json_cache.kick()
+ async stop() {
+ assert(this.json_cache !== undefined)
+ await this.resources.unref('json_cache')
- assert(this.json_cache_rw !== undefined)
- this.json_cache_rw.kick()
+ assert(this.json_cache_rw !== undefined)
+ await this.resources.unref('json_cache_rw')
- assert(this.jst_cache !== undefined)
- this.jst_cache.kick()
+ assert(this.jst_cache !== undefined)
+ await this.resources.unref(`jst_cache:${this.root}`)
- assert(this.less_css_cache !== undefined)
- this.less_css_cache.kick()
+ assert(this.less_css_cache !== undefined)
+ await this.resources.unref(`less_css_cache:${this.root}`)
- assert(this.menu_cache !== undefined)
- this.menu_cache.kick()
+ assert(this.menu_cache !== undefined)
+ await this.resources.unref('menu_cache')
- assert(this.min_css_cache !== undefined)
- this.min_css_cache.kick()
+ assert(this.min_css_cache !== undefined)
+ await this.resources.unref('min_css_cache')
- assert(this.min_js_cache !== undefined)
- this.min_js_cache.kick()
+ assert(this.min_js_cache !== undefined)
+ await this.resources.unref('min_js_cache')
- assert(this.min_html_cache !== undefined)
- this.min_html_cache.kick()
+ assert(this.min_html_cache !== undefined)
+ await this.resources.unref('min_html_cache')
- assert(this.min_svg_cache !== undefined)
- this.min_svg_cache.kick()
+ assert(this.min_svg_cache !== undefined)
+ await this.resources.unref('min_svg_cache')
- assert(this.sass_css_cache !== undefined)
- this.sass_css_cache.kick()
+ assert(this.sass_css_cache !== undefined)
+ await this.resources.unref(`sass_css_cache:${this.root}`)
- assert(this.text_cache !== undefined)
- this.text_cache.kick()
+ assert(this.text_cache !== undefined)
+ await this.resources.unref('text_cache')
- assert(this.zip_cache !== undefined)
- this.zip_cache.kick()
-}
+ assert(this.zip_cache !== undefined)
+ await this.resources.unref('zip_cache')
+ }
-Site.prototype.serve_internal = function(response, status, mime_type, caching, data) {
- response.statusCode = status
- // html files will be direct recipient of links/bookmarks so can't have
- // a long lifetime, other files like css or images are often large files
- // and won't change frequently (but we'll need cache busting eventually)
- if (caching && mime_type !== this.options.mime_type['.html'])
- response.setHeader('Cache-Control', 'max-age=3600')
- response.setHeader('Content-Type', mime_type)
- response.setHeader('Content-Length', data.length)
- response.end(data)
-}
+ async kick() {
+ assert(this.json_cache !== undefined)
+ this.json_cache.kick()
-Site.prototype.serve = function(env, status, data, from) {
- console.log(
- `${env.parsed_url.host} serving ${env.parsed_url.pathname} size ${data.length} from ${from}`
- )
- this.serve_internal(env.response, status, env.mime_type, env.caching, data)
-}
+ assert(this.json_cache_rw !== undefined)
+ this.json_cache_rw.kick()
+
+ assert(this.jst_cache !== undefined)
+ this.jst_cache.kick()
+
+ assert(this.less_css_cache !== undefined)
+ this.less_css_cache.kick()
+
+ assert(this.menu_cache !== undefined)
+ this.menu_cache.kick()
+
+ assert(this.min_css_cache !== undefined)
+ this.min_css_cache.kick()
+
+ assert(this.min_js_cache !== undefined)
+ this.min_js_cache.kick()
+
+ assert(this.min_html_cache !== undefined)
+ this.min_html_cache.kick()
+
+ assert(this.min_svg_cache !== undefined)
+ this.min_svg_cache.kick()
+
+ assert(this.sass_css_cache !== undefined)
+ this.sass_css_cache.kick()
+
+ assert(this.text_cache !== undefined)
+ this.text_cache.kick()
-Site.prototype.die = function(env, message) {
- console.log(`${env.parsed_url.host} ${message}`)
- this.serve_internal(
- env.response,
- 404,
- this.options.mime_types['.html'],
- false,
- Buffer.from(
- `<html>
+ assert(this.zip_cache !== undefined)
+ this.zip_cache.kick()
+ }
+
+ serve_internal(response, status, mime_type, caching, data) {
+ response.statusCode = status
+ // html files will be direct recipient of links/bookmarks so can't have
+ // a long lifetime, other files like css or images are often large files
+ // and won't change frequently (but we'll need cache busting eventually)
+ if (caching && mime_type !== this.options.mime_type['.html'])
+ response.setHeader('Cache-Control', 'max-age=3600')
+ response.setHeader('Content-Type', mime_type)
+ response.setHeader('Content-Length', data.length)
+ response.end(data)
+ }
+
+ serve(env, status, data, from) {
+ console.log(
+ `${env.parsed_url.host} serving ${env.parsed_url.pathname} size ${data.length} from ${from}`
+ )
+ this.serve_internal(env.response, status, env.mime_type, env.caching, data)
+ }
+
+ die(env, message) {
+ console.log(`${env.parsed_url.host} ${message}`)
+ this.serve_internal(
+ env.response,
+ 404,
+ this.options.mime_types['.html'],
+ false,
+ Buffer.from(
+ `<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>404 Not Found</title>
</body>
</html>
`
+ )
)
- )
-}
+ }
-Site.prototype.redirect = function(env, pathname, message) {
- console.log(
- `${env.parsed_url.host} redirecting ${env.parsed_url.pathname} to ${pathname}`
- )
- let location = pathname + (env.parsed_url.search || '')
- env.response.statusCode = 301
- env.response.setHeader('Location', location)
- this.serve_internal(
- env.response,
- 301,
- this.options.mime_types['.html'],
- false,
- Buffer.from(
- `<html>
+ redirect(env, pathname, message) {
+ console.log(
+ `${env.parsed_url.host} redirecting ${env.parsed_url.pathname} to ${pathname}`
+ )
+ let location = pathname + (env.parsed_url.search || '')
+ env.response.statusCode = 301
+ env.response.setHeader('Location', location)
+ this.serve_internal(
+ env.response,
+ 301,
+ this.options.mime_types['.html'],
+ false,
+ Buffer.from(
+ `<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>301 Moved Permanently</title>
</body>
</html>
`
+ )
)
- )
-}
-
-Site.prototype.internal_ensure_dir = async function(pathname) {
- try {
- await fsPromises.mkdir(pathname)
- console.log('create directory', pathname)
}
- catch (err) {
- if (err.code !== 'EEXIST') // should check error type
- throw err
+
+ async internal_ensure_dir(pathname) {
+ try {
+ await fsPromises.mkdir(pathname)
+ console.log('create directory', pathname)
+ }
+ catch (err) {
+ if (err.code !== 'EEXIST') // should check error type
+ throw err
+ }
}
-}
-Site.prototype.get_json = function(pathname) {
- return /*await*/ this.json_cache.get(this.root + pathname)
-}
+ get_json(pathname) {
+ return /*await*/ this.json_cache.get(this.root + pathname)
+ }
-Site.prototype.get_jst = function(pathname) {
- return /*await*/ this.jst_cache.get(this.root + pathname)
-}
+ get_jst(pathname) {
+ return /*await*/ this.jst_cache.get(this.root + pathname)
+ }
-Site.prototype.get_less_css = function(pathname) {
- return /*await*/ this.less_css_cache.get(this.root + pathname)
-}
+ get_less_css(pathname) {
+ return /*await*/ this.less_css_cache.get(this.root + pathname)
+ }
-Site.prototype.get_menu = function(pathname) {
- return /*await*/ this.menu_cache.get(this.root + pathname)
-}
+ get_menu(pathname) {
+ return /*await*/ this.menu_cache.get(this.root + pathname)
+ }
-Site.prototype.get_min_css = function(pathname) {
- return /*await*/ this.min_css_cache.get(this.root + pathname)
-}
+ get_min_css(pathname) {
+ return /*await*/ this.min_css_cache.get(this.root + pathname)
+ }
-Site.prototype.get_min_html = function(pathname) {
- return /*await*/ this.min_html_cache.get(this.root + pathname)
-}
+ get_min_html(pathname) {
+ return /*await*/ this.min_html_cache.get(this.root + pathname)
+ }
-Site.prototype.get_min_js = function(pathname) {
- return /*await*/ this.min_js_cache.get(this.root + pathname)
-}
+ get_min_js(pathname) {
+ return /*await*/ this.min_js_cache.get(this.root + pathname)
+ }
-Site.prototype.get_min_svg = function(pathname) {
- return /*await*/ this.min_svg_cache.get(this.root + pathname)
-}
+ get_min_svg(pathname) {
+ return /*await*/ this.min_svg_cache.get(this.root + pathname)
+ }
-Site.prototype.get_sass_css = function(pathname) {
- return /*await*/ this.sass_css_cache.get(this.root + pathname)
-}
+ get_sass_css(pathname) {
+ return /*await*/ this.sass_css_cache.get(this.root + pathname)
+ }
-Site.prototype.get_text = function(pathname) {
- return /*await*/ this.text_cache.get(this.root + pathname)
-}
+ get_text(pathname) {
+ return /*await*/ this.text_cache.get(this.root + pathname)
+ }
-Site.prototype.get_zip = function(pathname) {
- return /*await*/ this.zip_cache.get(this.root + pathname)
-}
+ get_zip(pathname) {
+ return /*await*/ this.zip_cache.get(this.root + pathname)
+ }
-Site.prototype.ensure_dir = async function(pathname) {
- return /*await*/ this.internal_ensure_dir(this.root + pathname)
-}
+ async ensure_dir(pathname) {
+ return /*await*/ this.internal_ensure_dir(this.root + pathname)
+ }
-// this is for read/write JSON files
-// they will not be reloaded from disk if modified
-Site.prototype.read_json = async function(pathname, default_value) {
- return /*await*/ this.json_cache_rw.read(
- this.root + pathname,
- default_value
- )
-}
+ // this is for read/write JSON files
+ // they will not be reloaded from disk if modified
+ async read_json(pathname, default_value) {
+ return /*await*/ this.json_cache_rw.read(
+ this.root + pathname,
+ default_value
+ )
+ }
-Site.prototype.write_json = async function(pathname, value, timeout) {
- return /*await*/ this.json_cache_rw.write(
- this.root + pathname,
- value,
- timeout
- )
-}
+ async write_json(pathname, value, timeout) {
+ return /*await*/ this.json_cache_rw.write(
+ this.root + pathname,
+ value,
+ timeout
+ )
+ }
-Site.prototype.modify_json =
- async function(pathname, default_value, modify_func, timeout) {
+ async modify_json(pathname, default_value, modify_func, timeout) {
return /*await*/ this.json_cache_rw.modify(
this.root + pathname,
default_value,
)
}
-Site.prototype.serve_jst = async function(env, pathname, ...args) {
- let template
- try {
- template = await this.jst_cache.get(pathname)
- }
- catch (err) {
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- throw err
- return false
- }
- env.site = this
- await template(env, ...args)
- return true
-}
-
-Site.prototype.serve_less_css = async function(env, pathname) {
- if (pathname.slice(-9) !== '.css.less')
- return false
-
- let data
- try {
- data = await this.less_css_cache.get(pathname)
- }
- catch (err) {
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- throw err
- return false
+ async serve_jst(env, pathname, ...args) {
+ let template
+ try {
+ template = await this.jst_cache.get(pathname)
+ }
+ catch (err) {
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ throw err
+ return false
+ }
+ env.site = this
+ await template(env, ...args)
+ return true
}
- this.serve(env, 200, data, 'less_css')
- return true
-}
-Site.prototype.serve_min_css = async function(env, pathname) {
- if (pathname.slice(-8) !== '.css.min')
- return false
+ async serve_less_css(env, pathname) {
+ if (pathname.slice(-9) !== '.css.less')
+ return false
- let data
- try {
- data = await this.min_css_cache.get(pathname)
- }
- catch (err) {
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- throw err
- return false
+ let data
+ try {
+ data = await this.less_css_cache.get(pathname)
+ }
+ catch (err) {
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ throw err
+ return false
+ }
+ this.serve(env, 200, data, 'less_css')
+ return true
}
- this.serve(env, 200, data, 'min_css')
- return true
-}
-Site.prototype.serve_min_html = async function(env, pathname) {
- if (pathname.slice(-9) !== '.html.min')
- return false
+ async serve_min_css(env, pathname) {
+ if (pathname.slice(-8) !== '.css.min')
+ return false
- let data
- try {
- data = await this.min_html_cache.get(pathname)
- }
- catch (err) {
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- throw err
- return false
+ let data
+ try {
+ data = await this.min_css_cache.get(pathname)
+ }
+ catch (err) {
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ throw err
+ return false
+ }
+ this.serve(env, 200, data, 'min_css')
+ return true
}
- this.serve(env, 200, data, 'min_html')
- return true
-}
-Site.prototype.serve_min_js = async function(env, pathname) {
- if (pathname.slice(-7) !== '.js.min')
- return false
+ async serve_min_html(env, pathname) {
+ if (pathname.slice(-9) !== '.html.min')
+ return false
- let data
- try {
- data = await this.min_js_cache.get(pathname)
- }
- catch (err) {
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- throw err
- return false
+ let data
+ try {
+ data = await this.min_html_cache.get(pathname)
+ }
+ catch (err) {
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ throw err
+ return false
+ }
+ this.serve(env, 200, data, 'min_html')
+ return true
}
- this.serve(env, 200, data, 'min_js')
- return true
-}
-Site.prototype.serve_min_svg = async function(env, pathname) {
- if (pathname.slice(-8) !== '.svg.min')
- return false
+ async serve_min_js(env, pathname) {
+ if (pathname.slice(-7) !== '.js.min')
+ return false
- let data
- try {
- data = await this.min_svg_cache.get(pathname)
- }
- catch (err) {
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- throw err
- return false
+ let data
+ try {
+ data = await this.min_js_cache.get(pathname)
+ }
+ catch (err) {
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ throw err
+ return false
+ }
+ this.serve(env, 200, data, 'min_js')
+ return true
}
- this.serve(env, 200, data, 'min_svg')
- return true
-}
-
-Site.prototype.serve_sass_css = async function(env, pathname) {
- if (
- pathname.slice(-9) !== '.css.sass' &&
- pathname.slice(-9) !== '.css.scss'
- )
- return false
- let data
- try {
- data = await this.sass_css_cache.get(pathname)
- }
- catch (err) {
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- throw err
- return false
- }
- this.serve(env, 200, data, 'sass_css')
- return true
-}
+ async serve_min_svg(env, pathname) {
+ if (pathname.slice(-8) !== '.svg.min')
+ return false
-Site.prototype.serve_fs = async function(env, pathname) {
- // see serve_internal()
- // since the file may be huge we need to cache it for as long as reasonable
- if (this.options.caching)
- env.response.setHeader('Cache-Control', 'max-age=86400')
- env.response.setHeader('Content-Type', env.mime_type)
-
- // see https://dev.to/abdisalan_js/how-to-code-a-video-streaming-server-using-nodejs-2o0
- let stream
- let range = env.request.headers.range;
- if (range !== undefined) {
- let stats
+ let data
try {
- stats = await fsPromises.stat(pathname)
+ data = await this.min_svg_cache.get(pathname)
}
catch (err) {
if (!(err instanceof Error) || err.code !== 'ENOENT')
throw err
return false
}
+ this.serve(env, 200, data, 'min_svg')
+ return true
+ }
- // Parse Range
- // Example: "bytes=32324-"
- let start = Number(range.replace(/\D/g, ''))
- let end = Math.min(start + 1048576, stats.size)
-
- // see serve()
- console.log(
- `${env.parsed_url.host} streaming ${env.parsed_url.pathname} partial ${start}-${end}/${stats.size}`
- )
-
- // Create headers
- env.response.statusCode = 206 // partial content
- env.response.setHeader(
- 'Content-Range',
- `bytes ${start}-${end - 1}/${stats.size}`
+ async serve_sass_css(env, pathname) {
+ if (
+ pathname.slice(-9) !== '.css.sass' &&
+ pathname.slice(-9) !== '.css.scss'
)
- env.response.setHeader('Accept-Ranges', 'bytes')
- env.response.setHeader('Content-Length', end - start)
+ return false
- // create video read stream for this particular chunk
- stream = fs.createReadStream(pathname, {start: start, end: end - 1})
+ let data
+ try {
+ data = await this.sass_css_cache.get(pathname)
+ }
+ catch (err) {
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ throw err
+ return false
+ }
+ this.serve(env, 200, data, 'sass_css')
+ return true
}
- else {
- // see serve()
- console.log(
- `${env.parsed_url.host} streaming ${env.parsed_url.pathname}`
- )
+ async serve_fs(env, pathname) {
// see serve_internal()
- env.response.statusCode = 200
- stream = fs.createReadStream(pathname)
- }
-
- return /*await*/ new Promise(
- (resolve, reject) => {
- stream.on(
- 'error',
- err => {
- //console.log(`error: ${err.message}`)
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- reject(err)
- resolve(false)
- }
+ // since the file may be huge we need to cache it for as long as reasonable
+ if (this.options.caching)
+ env.response.setHeader('Cache-Control', 'max-age=86400')
+ env.response.setHeader('Content-Type', env.mime_type)
+
+ // see https://dev.to/abdisalan_js/how-to-code-a-video-streaming-server-using-nodejs-2o0
+ let stream
+ let range = env.request.headers.range;
+ if (range !== undefined) {
+ let stats
+ try {
+ stats = await fsPromises.stat(pathname)
+ }
+ catch (err) {
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ throw err
+ return false
+ }
+
+ // Parse Range
+ // Example: "bytes=32324-"
+ let start = Number(range.replace(/\D/g, ''))
+ let end = Math.min(start + 1048576, stats.size)
+
+ // see serve()
+ console.log(
+ `${env.parsed_url.host} streaming ${env.parsed_url.pathname} partial ${start}-${end}/${stats.size}`
)
- stream.on(
- 'data',
- data => {
- //console.log(`data: ${data.length} bytes`)
- env.response.write(data)
- }
+
+ // Create headers
+ env.response.statusCode = 206 // partial content
+ env.response.setHeader(
+ 'Content-Range',
+ `bytes ${start}-${end - 1}/${stats.size}`
)
- stream.on(
- 'end',
- () => {
- //console.log('end')
- env.response.end()
- resolve(true)
- }
+ env.response.setHeader('Accept-Ranges', 'bytes')
+ env.response.setHeader('Content-Length', end - start)
+
+ // create video read stream for this particular chunk
+ stream = fs.createReadStream(pathname, {start: start, end: end - 1})
+ }
+ else {
+ // see serve()
+ console.log(
+ `${env.parsed_url.host} streaming ${env.parsed_url.pathname}`
)
+
+ // see serve_internal()
+ env.response.statusCode = 200
+ stream = fs.createReadStream(pathname)
}
- )
-}
-Site.prototype.serve_zip = async function(env, zipname, pathname) {
- let zip
- try {
- zip = await this.zip_cache.get(zipname)
- }
- catch (err) {
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- throw err
- return false
+ return /*await*/ new Promise(
+ (resolve, reject) => {
+ stream.on(
+ 'error',
+ err => {
+ //console.log(`error: ${err.message}`)
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ reject(err)
+ resolve(false)
+ }
+ )
+ stream.on(
+ 'data',
+ data => {
+ //console.log(`data: ${data.length} bytes`)
+ env.response.write(data)
+ }
+ )
+ stream.on(
+ 'end',
+ () => {
+ //console.log('end')
+ env.response.end()
+ resolve(true)
+ }
+ )
+ }
+ )
}
- if (!Object.prototype.hasOwnProperty.call(zip, pathname))
- return false
- this.serve(env, 200, zip[pathname], 'zip')
- return true
-}
-Site.prototype.serve_file = async function(env, pathname) {
- //console.log(`serve_file ${pathname}`)
- if (await this.serve_jst(env, pathname + '.jst'))
- return
- if (await this.serve_less_css(env, pathname + '.less'))
- return
- if (await this.serve_min_css(env, pathname + '.min'))
- return
- if (await this.serve_min_html(env, pathname + '.min'))
- return
- if (await this.serve_min_js(env, pathname + '.min'))
- return
- if (await this.serve_min_svg(env, pathname + '.min'))
- return
- if (await this.serve_sass_css(env, pathname + '.sass'))
- return
- if (await this.serve_sass_css(env, pathname + '.scss'))
- return
- if (await this.serve_fs(env, pathname))
- return
- this.die(env, `file not found ${env.parsed_url.pathname}`)
-}
+ async serve_zip(env, zipname, pathname) {
+ let zip
+ try {
+ zip = await this.zip_cache.get(zipname)
+ }
+ catch (err) {
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ throw err
+ return false
+ }
+ if (!Object.prototype.hasOwnProperty.call(zip, pathname))
+ return false
+ this.serve(env, 200, zip[pathname], 'zip')
+ return true
+ }
-Site.prototype.serve_dir = async function(env, pathname, components) {
- if (await this.serve_jst(env, pathname + '.dir.jst', pathname, components))
- return
-
- let stats
- try {
- stats = await fsPromises.stat(pathname)
- }
- catch (err) {
- if (!(err instanceof Error) || err.code !== 'ENOENT')
- throw err
- this.die(env, `directory not found ${pathname}`)
- return
- }
- if (!stats.isDirectory()) {
- this.die(
- env,
- components.length > 1 ?
- `not directory ${pathname}` :
- `unknown file type in ${env.parsed_url.pathname}`
- )
- return
+ async serve_file(env, pathname) {
+ //console.log(`serve_file ${pathname}`)
+ if (await this.serve_jst(env, pathname + '.jst'))
+ return
+ if (await this.serve_less_css(env, pathname + '.less'))
+ return
+ if (await this.serve_min_css(env, pathname + '.min'))
+ return
+ if (await this.serve_min_html(env, pathname + '.min'))
+ return
+ if (await this.serve_min_js(env, pathname + '.min'))
+ return
+ if (await this.serve_min_svg(env, pathname + '.min'))
+ return
+ if (await this.serve_sass_css(env, pathname + '.sass'))
+ return
+ if (await this.serve_sass_css(env, pathname + '.scss'))
+ return
+ if (await this.serve_fs(env, pathname))
+ return
+ this.die(env, `file not found ${env.parsed_url.pathname}`)
}
- return /*await*/ this.serve_path(env, pathname, components)
-}
-Site.prototype.serve_path = async function(env, pathname, components) {
- //console.log(`serve_path ${pathname} ${components}`)
- if (components.length === 0) {
- // directory without trailing slash
- this.redirect(env, env.parsed_url.pathname + '/index.html')
- return
- }
-
- if (components[0].length === 0) {
- if (components.length > 1)
- this.die(env, `empty directory name in ${env.parsed_url.pathname}`)
- else
- // directory with trailing slash
- this.redirect(env, env.parsed_url.pathname + 'index.html')
- return
- }
-
- if (
- components[0].charAt(0) === '.' ||
- components[0].charAt(0) === '_'
- ) {
- this.die(env, `bad component "${components[0]}" in ${env.parsed_url.pathname}`)
- return
- }
-
- let i = components[0].lastIndexOf('.')
- if (i === -1)
- i = components[0].length
- let extension = components[0].slice(i)
-
- pathname = `${pathname}/${components[0]}`
- if (
- extension.length !== 0 &&
- Object.prototype.hasOwnProperty.call(this.options.mime_types, extension)
- ) {
- if (components.length > 1) {
- this.die(env, `non-directory extension "${extension}" in ${env.parsed_url.pathname}`)
+ async serve_dir(env, pathname, components) {
+ if (await this.serve_jst(env, pathname + '.dir.jst', pathname, components))
+ return
+
+ let stats
+ try {
+ stats = await fsPromises.stat(pathname)
+ }
+ catch (err) {
+ if (!(err instanceof Error) || err.code !== 'ENOENT')
+ throw err
+ this.die(env, `directory not found ${pathname}`)
+ return
+ }
+ if (!stats.isDirectory()) {
+ this.die(
+ env,
+ components.length > 1 ?
+ `not directory ${pathname}` :
+ `unknown file type in ${env.parsed_url.pathname}`
+ )
return
}
- return /*await*/ this.serve_file(env, pathname)
+ return /*await*/ this.serve_path(env, pathname, components)
}
- return /*await*/ this.serve_dir(env, pathname, components.slice(1))
-}
-Site.prototype.respond = async function(env) {
- env.mime_type = 'application/octet-stream'
- env.caching = this.options.caching
- let pathname = decodeURIComponent(env.parsed_url.pathname)
- let i = pathname.lastIndexOf('.')
- if (i !== -1) {
- let extension = pathname.slice(i)
+ async serve_path(env, pathname, components) {
+ //console.log(`serve_path ${pathname} ${components}`)
+ if (components.length === 0) {
+ // directory without trailing slash
+ this.redirect(env, env.parsed_url.pathname + '/index.html')
+ return
+ }
+
+ if (components[0].length === 0) {
+ if (components.length > 1)
+ this.die(env, `empty directory name in ${env.parsed_url.pathname}`)
+ else
+ // directory with trailing slash
+ this.redirect(env, env.parsed_url.pathname + 'index.html')
+ return
+ }
+
if (
+ components[0].charAt(0) === '.' ||
+ components[0].charAt(0) === '_'
+ ) {
+ this.die(env, `bad component "${components[0]}" in ${env.parsed_url.pathname}`)
+ return
+ }
+
+ let i = components[0].lastIndexOf('.')
+ if (i === -1)
+ i = components[0].length
+ let extension = components[0].slice(i)
+
+ pathname = `${pathname}/${components[0]}`
+ if (
+ extension.length !== 0 &&
Object.prototype.hasOwnProperty.call(this.options.mime_types, extension)
- )
- env.mime_type = this.options.mime_types[extension]
+ ) {
+ if (components.length > 1) {
+ this.die(env, `non-directory extension "${extension}" in ${env.parsed_url.pathname}`)
+ return
+ }
+ return /*await*/ this.serve_file(env, pathname)
+ }
+ return /*await*/ this.serve_dir(env, pathname, components.slice(1))
}
- if (
- await this.serve_zip(
- env,
- this.root + '/_favicon/favicons.zip',
- pathname
+
+ async respond(env) {
+ env.mime_type = 'application/octet-stream'
+ env.caching = this.options.caching
+ let pathname = decodeURIComponent(env.parsed_url.pathname)
+ let i = pathname.lastIndexOf('.')
+ if (i !== -1) {
+ let extension = pathname.slice(i)
+ if (
+ Object.prototype.hasOwnProperty.call(this.options.mime_types, extension)
+ )
+ env.mime_type = this.options.mime_types[extension]
+ }
+ if (
+ await this.serve_zip(
+ env,
+ this.root + '/_favicon/favicons.zip',
+ pathname
+ )
)
- )
- return
- let components = pathname.split('/')
- if (components.length) {
- assert(components[0].length == 0)
- components = components.slice(1)
- }
-
- // deal with ACME challenges for certbot (letsencrypt)
- if (components[0] === '.well-known') {
- // build path, ensuring that remaining components are safe
- /*let*/ pathname = `${this.options.certbot_webroot}/.well-known`
- for (let i = 1; i < components.length; ++i) {
- if (components[i].charAt(0) == '.') {
- this.die(env, `bad component "${components[i]}" in ${env.parsed_url.pathname}`)
- return
+ return
+ let components = pathname.split('/')
+ if (components.length) {
+ assert(components[0].length == 0)
+ components = components.slice(1)
+ }
+
+ // deal with ACME challenges for certbot (letsencrypt)
+ if (components[0] === '.well-known') {
+ // build path, ensuring that remaining components are safe
+ /*let*/ pathname = `${this.options.certbot_webroot}/.well-known`
+ for (let i = 1; i < components.length; ++i) {
+ if (components[i].charAt(0) == '.') {
+ this.die(env, `bad component "${components[i]}" in ${env.parsed_url.pathname}`)
+ return
+ }
+ pathname = `${pathname}/${components[i]}`
}
- pathname = `${pathname}/${components[i]}`
+
+ // use serve_fs() because challenge files have no extension
+ return /*await*/ this.serve_fs(env, pathname)
}
- // use serve_fs() because challenge files have no extension
- return /*await*/ this.serve_fs(env, pathname)
+ return /*await*/ this.serve_path(env, this.root, components)
}
-
- return /*await*/ this.serve_path(env, this.root, components)
}
export default Site