Make Site object independent of Server object, since it now uses Resources object...
authorNick Downing <nick@ndcode.org>
Tue, 4 Dec 2018 01:56:08 +0000 (12:56 +1100)
committerNick Downing <nick@ndcode.org>
Tue, 4 Dec 2018 01:56:08 +0000 (12:56 +1100)
Server.js
Site.js
site/_config/site.jst

index e4304d7..5ba15c4 100644 (file)
--- a/Server.js
+++ b/Server.js
@@ -264,16 +264,6 @@ Server.prototype.respond = async function(request, response, protocol) {
       return
     }
 
-    let mime_type = this.mime_type_default
-    let i = parsed_url.pathname.lastIndexOf('.')
-    if (i !== -1) {
-      let extension = parsed_url.pathname.slice(i)
-      if (
-        Object.prototype.hasOwnProperty.call(this.mime_types, extension)
-      )
-        mime_type = this.mime_types[extension]
-    }
-
     let host = this.hosts[parsed_url.hostname]
     switch (host.type) {
     case 'redirect':
@@ -305,12 +295,10 @@ Server.prototype.respond = async function(request, response, protocol) {
         true
       )
       if (config !== undefined)
-        root.site = await config(this, host.root, root.site)
+        root.site = await config(this.resources, host.root, root.site)
       return root.site.respond(
         {
-          mime_type: mime_type,
           parsed_url: parsed_url,
-          pathname: parsed_url.pathname,
           response: response,
           request: request,
           server: this
diff --git a/Site.js b/Site.js
index e5dd80f..b9901ec 100644 (file)
--- a/Site.js
+++ b/Site.js
@@ -18,50 +18,67 @@ let fs_mkdir = util.promisify(fs.mkdir)
 let fs_readFile = util.promisify(fs.readFile)
 let fs_stat = util.promisify(fs.stat)
 
-let Site = function(server, root) {
+let Site = function(resources, root, options) {
   if (!this instanceof Site)
     throw new Error('Site is a constructor')
-  this.server = server
-  this.socket_io_connect_listeners = [] // later will use this for destruction
+  this.resources = resources
   this.root = root
+  this.options = {
+    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',
+      '.png': 'image/png',
+      '.svg': 'image/svg+xml',
+      '.xml': 'text/xml; charset=utf-8'
+    }
+  }
+  if (options !== undefined)
+    Object.assign(this.options, options)
 
-  this.json_cache = server.resources.ref(
+  this.socket_io_connect_listeners = [] // later will use this for destruction
+  this.json_cache = resources.ref(
     'json_cache',
     () => new JSONCache(true)
   )
-  this.json_cache_rw = server.resources.ref(
+  this.json_cache_rw = resources.ref(
     'json_cache_rw',
     () => new JSONCacheRW(true)
   )
-  this.jst_cache = server.resources.ref(
+  this.jst_cache = resources.ref(
     `jst_cache:${root}`,
     () => new JSTCache(root, {_jst_server: jst_server}, true)
   )
-  this.less_css_cache = server.resources.ref(
+  this.less_css_cache = resources.ref(
     `less_css_cache:${root}`,
     () => new LessCSSCache(root, true)
   )
-  this.min_css_cache = server.resources.ref(
+  this.min_css_cache = resources.ref(
     'min_css_cache',
     () => new MinCSSCache(true)
   )
-  this.min_js_cache = server.resources.ref(
+  this.min_js_cache = resources.ref(
     'min_js_cache',
     () => new MinJSCache(true)
   )
-  this.min_html_cache = server.resources.ref(
+  this.min_html_cache = resources.ref(
     'min_html_cache',
     () => new MinHTMLCache(true)
   )
-  this.min_svg_cache = server.resources.ref(
+  this.min_svg_cache = resources.ref(
     'min_svg_cache',
     () => new MinSVGCache(true)
   )
-  this.text_cache = server.resources.ref(
+  this.text_cache = resources.ref(
     'text_cache',
     () => new TextCache(true)
   )
-  this.zip_cache = server.resources.ref(
+  this.zip_cache = resources.ref(
     'zip_cache',
     () => new ZipCache(true)
   )
@@ -69,29 +86,71 @@ let Site = function(server, root) {
 
 Site.prototype = Object.create(Site.prototype)
 
-Site.prototype.serve = function(env, status, data, message_from) {
-  this.server.serve(
-    env.response,
-    status,
-    env.mime_type,
-    data,
-    `${env.parsed_url.host} serving ${env.pathname} size ${data.length} from ${message_from}`
+Site.prototype.serve_internal = function(response, status, mime_type, 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 (this.options.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)
+}
+
+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, data)
 }
 
 Site.prototype.die = function(env, message) {
-  this.server.die(
+  console.log(`${env.parsed_url.host} ${message}`)
+  this.serve_internal(
     env.response,
-    env.pathname,
-    `${env.parsed_url.host} ${message}`
+    404,
+    this.options.mime_types['.html'],
+    Buffer.from(
+      `<html>
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title>404 Not Found</title>
+  </head>
+  <body style="font-family: sans-serif, serif, monospace; font-size: 16px;">
+    <h2>404 Not Found</h2>
+    <p>The document ${env.parsed_url.pathname} was not found.</p>
+  </body>
+</html>
+`
+    )
   )
 }
 
-Site.prototype.redirect = function(env, pathname) {
-  this.server.redirect(
+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 || '')
+  response.statusCode = 301
+  response.setHeader('Location', location)
+  this.serve_internal(
     env.response,
-    pathname + (env.parsed_url.search || ''),
-    `${env.parsed_url.host} redirecting ${env.pathname} to ${pathname}`
+    301,
+    this.options.mime_types['.html'],
+    Buffer.from(
+      `<html>
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title>301 Moved Permanently</title>
+  </head>
+  <body style="font-family: sans-serif, serif, monospace; font-size: 16px;">
+    <h2>301 Moved Permanently</h2>
+    <p>The document has moved <a href="${location}">here</a>.</p>
+  </body>
+</html>
+`
+    )
   )
 }
 
@@ -319,7 +378,7 @@ Site.prototype.serve_file = async function(env, pathname) {
     return
   if (await this.serve_fs(env, pathname))
     return
-  this.die(env, `file not found ${env.pathname}`)
+  this.die(env, `file not found ${env.parsed_url.pathname}`)
 }
 
 Site.prototype.serve_dir = async function(env, pathname, components) {
@@ -341,7 +400,7 @@ Site.prototype.serve_dir = async function(env, pathname, components) {
       env,
       components.length > 1 ?
         `not directory ${pathname}` :
-        `unknown extension "${extension}" in ${env.pathname}`
+        `unknown extension "${extension}" in ${env.parsed_url.pathname}`
     )
     return
   }
@@ -352,16 +411,16 @@ 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.pathname + '/index.html')
+    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.pathname}`)
+      this.die(env, `empty directory name in ${env.parsed_url.pathname}`)
     else
       // directory with trailing slash
-      this.redirect(env, env.pathname + 'index.html')
+      this.redirect(env, env.parsed_url.pathname + 'index.html')
     return
   }
 
@@ -369,7 +428,7 @@ Site.prototype.serve_path = async function(env, pathname, components) {
     components[0].charAt(0) === '.' ||
     components[0].charAt(0) === '_'
   ) {
-    this.die(env, `bad component "${components[0]}" in ${env.pathname}`)
+    this.die(env, `bad component "${components[0]}" in ${env.parsed_url.pathname}`)
     return
   }
 
@@ -381,10 +440,10 @@ Site.prototype.serve_path = async function(env, pathname, components) {
   pathname = `${pathname}/${components[0]}`
   if (
     extension.length !== 0 &&
-    Object.prototype.hasOwnProperty.call(this.server.mime_types, extension)
+    Object.prototype.hasOwnProperty.call(this.options.mime_types, extension)
   ) {
     if (components.length > 1) {
-      this.die(env, `non-directory extension "${extension}" in ${env.pathname}`)
+      this.die(env, `non-directory extension "${extension}" in ${env.parsed_url.pathname}`)
       return
     }
     return /*await*/ this.serve_file(env, pathname)
@@ -393,15 +452,24 @@ 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('.')
+  if (i !== -1) {
+    let extension = env.parsed_url.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',
-      env.pathname
+      env.parsed_url.pathname
     )
   )
     return
-  let components = env.pathname.split('/')
+  let components = env.parsed_url.pathname.split('/')
   if (components.length) {
     assert(components[0].length == 0)
     components = components.slice(1)
index 0240704..e0964a7 100644 (file)
@@ -1 +1 @@
-return async (server, root, prev_site) => new _jst_server.Site(server, root)
+return async (resources, root) => new _jst_server.Site(resources, root)