Fix a bug of missing let in ACME challenges, add URI decoding to serve filenames...
authorNick Downing <nick@ndcode.org>
Sat, 4 Sep 2021 13:01:36 +0000 (23:01 +1000)
committerNick Downing <nick@ndcode.org>
Sat, 4 Sep 2021 13:01:36 +0000 (23:01 +1000)
Site.js

diff --git a/Site.js b/Site.js
index 57e1f02..0ae12f2 100644 (file)
--- a/Site.js
+++ b/Site.js
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 Nick Downing <nick@ndcode.org>
+ * Copyright (C) 2018-2021 Nick Downing <nick@ndcode.org>
  * SPDX-License-Identifier: MIT
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -38,7 +38,6 @@ let jst_server = require('./index')
 let util = require('util')
 
 let fs_mkdir = util.promisify(fs.mkdir)
-let fs_readFile = util.promisify(fs.readFile)
 let fs_stat = util.promisify(fs.stat)
 
 let Site = function(resources, root, options/*, prev_site*/) {
@@ -57,6 +56,8 @@ let Site = function(resources, root, options/*, prev_site*/) {
         '.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',
@@ -456,17 +457,47 @@ Site.prototype.serve_min_svg = async function(env, pathname) {
 }
 
 Site.prototype.serve_fs = async function(env, pathname) {
-  let data 
-  try {
-    data = await fs_readFile(pathname)
-  }
-  catch (err) {
-    if (!(err instanceof Error) || err.code !== 'ENOENT')
-      throw err
-    return false
-  }
-  this.serve(env, 200, data, 'fs')
-  return true
+  // see serve()
+  console.log(
+    `${env.parsed_url.host} streaming ${env.parsed_url.pathname}`
+  )
+
+  // see serve_internal()
+  env.response.statusCode = 200
+  // 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)
+
+  let 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)
+        }
+      )
+      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)
+        }
+      )
+    }
+  )
 }
 
 Site.prototype.serve_zip = async function(env, zipname, pathname) {
@@ -576,9 +607,10 @@ Site.prototype.serve_path = async function(env, pathname, components) {
 
 Site.prototype.respond = async function(env) {
   env.mime_type = 'application/octet-stream'
-  let i = env.parsed_url.pathname.lastIndexOf('.')
+  let pathname = decodeURIComponent(env.parsed_url.pathname)
+  let i = pathname.lastIndexOf('.')
   if (i !== -1) {
-    let extension = env.parsed_url.pathname.slice(i)
+    let extension = pathname.slice(i)
     if (
       Object.prototype.hasOwnProperty.call(this.options.mime_types, extension)
     )
@@ -588,11 +620,11 @@ Site.prototype.respond = async function(env) {
     await this.serve_zip(
       env,
       this.root + '/_favicon/favicons.zip',
-      env.parsed_url.pathname
+      pathname
     )
   )
     return
-  let components = env.parsed_url.pathname.split('/')
+  let components = pathname.split('/')
   if (components.length) {
     assert(components[0].length == 0)
     components = components.slice(1)
@@ -601,7 +633,7 @@ Site.prototype.respond = async function(env) {
   // deal with ACME challenges for certbot (letsencrypt)
   if (components[0] === '.well-known') {
     // build path, ensuring that remaining components are safe
-    pathname = `${this.options.certbot_webroot}/.well-known`
+    /*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}`)