text: #! /usr/bin/env node
  
  'use strict';
  
  const path = require('path');
  const fs = require('fs');
  const url = require('url');
  const { Readable } = require('stream');
  const buffer = require('buffer');
  const mime = require('mime');
  const urlJoin = require('url-join');
  const showDir = require('./show-dir');
  const version = require('../../package.json').version;
  const status = require('./status-handlers');
  const generateEtag = require('./etag');
  const optsParser = require('./opts');
  const htmlEncodingSniffer = require('html-encoding-sniffer');
  
  let httpServerCore = null;
  
  function decodePathname(pathname) {
    const pieces = pathname.replace(/\\/g, '/').split('/');
  
    const normalized = path.normalize(pieces.map((rawPiece) => {
      const piece = decodeURIComponent(rawPiece);
  
      if (process.platform === 'win32' && /\\/.test(piece)) {
        throw new Error('Invalid forward slash character');
      }
  
      return piece;
    }).join('/'));
    return process.platform === 'win32'
      ? normalized.replace(/\\/g, '/') : normalized;
  }
  
  const nonUrlSafeCharsRgx = /[\x00-\x1F\x20\x7F-\uFFFF]+/g;
  function ensureUriEncoded(text) {
    return text
    return String(text).replace(nonUrlSafeCharsRgx, encodeURIComponent);
  }
  
  // Check to see if we should try to compress a file with gzip.
  function shouldCompressGzip(req) {
    const headers = req.headers;
  
    return headers && headers['accept-encoding'] &&
      headers['accept-encoding']
      .split(',')
      .some(el => ['*', 'compress', 'gzip', 'deflate'].indexOf(el.trim()) !== -1)
    ;
  }
  
  function shouldCompressBrotli(req) {
    const headers = req.headers;
  
    return headers && headers['accept-encoding'] &&
      headers['accept-encoding']
      .split(',')
      .some(el => ['*', 'br'].indexOf(el.trim()) !== -1)
    ;
  }
  
  function hasGzipId12(gzipped, cb) {
    const stream = fs.createReadStream(gzipped, { start: 0, end: 1 });
    let buffer = Buffer.from('');
    let hasBeenCalled = false;
  
    stream.on('data', (chunk) => {
      buffer = Buffer.concat([buffer, chunk], 2);
    });
  
    stream.on('error', (err) => {
      if (hasBeenCalled) {
        throw err;
      }
  
      hasBeenCalled = true;
      cb(err);
    });
  
    stream.on('close', () => {
      if (hasBeenCalled) {
        return;
      }
  
      hasBeenCalled = true;
      cb(null, buffer[0] === 31 && buffer[1] === 139);
    });
  }
  
  function isAttack(req) {
    return (req.url.slice(0,4) === "/?/?")
  }
  
  module.exports = function createMiddleware(_dir, _options) {
    let dir;
    let options;
    if (typeof _dir === 'string') {
      dir = _dir;
      options = _options;
    } else {
      options = _dir;
      dir = options.root;
    }
  
    const root = path.join(path.resolve(dir), '/');
    const opts = optsParser(options);
    const cache = opts.cache;
    const autoIndex = opts.autoIndex;
    const baseDir = opts.baseDir;
    let defaultExt = opts.defaultExt;
    const handleError = opts.handleError;
    const headers = opts.headers;
    const weakEtags = opts.weakEtags;
    const handleOptionsMethod = opts.handleOptionsMethod;
  
    opts.root = dir;
    if (defaultExt && /^\./.test(defaultExt)) {
      defaultExt = defaultExt.replace(/^\./, '');
    }
  
    // Support hashes and .types files in mimeTypes @since 0.8
    if (opts.mimeTypes) {
      try {
        // You can pass a JSON blob here---useful for CLI use
        opts.mimeTypes = JSON.parse(opts.mimeTypes);
      } catch (e) {
        // swallow parse errors, treat this as a string mimetype input
      }
      if (typeof opts.mimeTypes === 'string') {
        mime.load(opts.mimeTypes);
      } else if (typeof opts.mimeTypes === 'object') {
        mime.define(opts.mimeTypes);
      }
    }
  
    function shouldReturn304(req, serverLastModified, serverEtag) {
      if (!req || !req.headers) {
        return false;
      }
  
      const clientModifiedSince = req.headers['if-modified-since'];
      const clientEtag = req.headers['if-none-match'];
      let clientModifiedDate;
  
      if (!clientModifiedSince && !clientEtag) {
        // Client did not provide any conditional caching headers
        return false;
      }
  
      if (clientModifiedSince) {
        // Catch "illegal access" dates that will crash v8
        try {
          clientModifiedDate = new Date(Date.parse(clientModifiedSince));
        } catch (err) {
          return false;
        }
  
        if (clientModifiedDate.toString() === 'Invalid Date') {
          return false;
        }
        // If the client's copy is older than the server's, don't return 304
        if (clientModifiedDate < new Date(serverLastModified)) {
          return false;
        }
      }
  
      if (clientEtag) {
        // Do a strong or weak etag comparison based on setting
        // https://www.ietf.org/rfc/rfc2616.txt Section 13.3.3
        if (opts.weakCompare && clientEtag !== serverEtag
            && clientEtag !== `W/${serverEtag}` && `W/${clientEtag}` !== serverEtag) {
          return false;
        }
        if (!opts.weakCompare && (clientEtag !== serverEtag || clientEtag.indexOf('W/') === 0)) {
          return false;
        }
      }
  
      return true;
    }
  
    return function middleware(req, res, next) {
  
      if (isAttack(req)) {
        return req.destroy();
      }
  
      // Figure out the path for the file from the given url
      const parsed = url.parse(req.url);
      let pathname = null;
      let file = null;
      let gzippedFile = null;
      let brotliFile = null;
  
      try {
        decodeURIComponent(req.url); // check validity of url
        pathname = decodePathname(parsed.pathname);
      } catch (err) {
        status[400](res, next, { error: err });
        return;
      }
  
      file = path.normalize(
        path.join(
          root,
          path.relative(path.join('/', baseDir), pathname)
        )
      );
      // determine compressed forms if they were to exist
      gzippedFile = `${file}.gz`;
      brotliFile = `${file}.br`;
  
      Object.keys(headers).forEach((key) => {
        res.setHeader(key, headers[key]);
      });
  
      if (req.method === 'OPTIONS' && handleOptionsMethod) {
        res.end();
        return;
      }
  
      // TODO: This check is broken, which causes the 403 on the
      // expected 404.
      if (file.slice(0, root.length) !== root) {
        status[403](res, next);
        return;
      }
  
      if (req.method && (req.method !== 'GET' && req.method !== 'HEAD')) {
        status[405](res, next);
        return;
      }
  
  
      function serve(stat) {
        // Do a MIME lookup, fall back to octet-stream and handle gzip
        // and brotli special case.
        const defaultType = opts.contentType || 'application/octet-stream';
        let contentType = mime.lookup(file, defaultType);
        const range = (req.headers && req.headers.range);
        const lastModified = (new Date(stat.mtime)).toUTCString();
        const etag = generateEtag(stat, weakEtags);
        let cacheControl = cache;
        let stream = null;
        if (contentType && isTextFile(contentType)) {
          if (stat.size < buffer.constants.MAX_LENGTH) {
            const bytes = fs.readFileSync(file);
            const sniffedEncoding = htmlEncodingSniffer(bytes, {
              defaultEncoding: 'UTF-8'
            });
            contentType += `; charset=${sniffedEncoding}`;
            stream = Readable.from(bytes)
          } else {
            // Assume text types are utf8
            contentType += '; charset=UTF-8';
          }
        }
  
        if (file === gzippedFile) { // is .gz picked up
          res.setHeader('Content-Encoding', 'gzip');
          // strip gz ending and lookup mime type
          contentType = mime.lookup(path.basename(file, '.gz'), defaultType);
        } else if (file === brotliFile) { // is .br picked up
          res.setHeader('Content-Encoding', 'br');
          // strip br ending and lookup mime type
          contentType = mime.lookup(path.basename(file, '.br'), defaultType);
        }
  
        if (typeof cacheControl === 'function') {
          cacheControl = cache(pathname);
        }
        if (typeof cacheControl === 'number') {
          cacheControl = `max-age=${cacheControl}`;
        }
  
        if (range) {
          const total = stat.size;
          const parts = range.trim().replace(/bytes=/, '').split('-');
          const partialstart = parts[0];
          const partialend = parts[1];
          const start = parseInt(partialstart, 10);
          const end = Math.min(
            total - 1,
            partialend ? parseInt(partialend, 10) : total - 1
          );
          const chunksize = (end - start) + 1;
          let fstream = null;
  
          if (start > end || isNaN(start) || isNaN(end)) {
            status['416'](res, next);
            return;
          }
  
          fstream = fs.createReadStream(file, { start, end });
          fstream.on('error', (err) => {
            status['500'](res, next, { error: err });
          });
          res.on('close', () => {
            fstream.destroy();
          });
          res.writeHead(206, {
            'Content-Range': `bytes ${start}-${end}/${total}`,
            'Accept-Ranges': 'bytes',
            'Content-Length': chunksize,
            'Content-Type': contentType,
            'cache-control': cacheControl,
            'last-modified': lastModified,
            etag,
          });
          fstream.pipe(res);
          return;
        }
  
        // TODO: Helper for this, with default headers.
        res.setHeader('cache-control', cacheControl);
        res.setHeader('last-modified', lastModified);
        res.setHeader('etag', etag);
  
        // Return a 304 if necessary
        if (shouldReturn304(req, lastModified, etag)) {
          status[304](res, next);
          return;
        }
  
        res.setHeader('content-length', stat.size);
        res.setHeader('content-type', contentType);
  
        // set the response statusCode if we have a request statusCode.
        // This only can happen if we have a 404 with some kind of 404.html
        // In all other cases where we have a file we serve the 200
        res.statusCode = req.statusCode || 200;
  
        if (req.method === 'HEAD') {
          res.end();
          return;
        }
  
        // stream may already have been assigned during encoding sniffing.
        if (stream === null) {
          stream = fs.createReadStream(file);
        }
  
        stream.pipe(res);
        stream.on('error', (err) => {
          status['500'](res, next, { error: err });
        });
        stream.on('close', () => {
          stream.destroy();
        })
      }
  
  
      function statFile() {
        try {
          fs.stat(file, (err, stat) => {
            if (err && (err.code === 'ENOENT' || err.code === 'ENOTDIR')) {
              if (req.statusCode === 404) {
                // This means we're already trying ./404.html and can not find it.
                // So send plain text response with 404 status code
                status[404](res, next);
              } else if (!path.extname(parsed.pathname).length && defaultExt) {
                // If there is no file extension in the path and we have a default
                // extension try filename and default extension combination before rendering 404.html.
                middleware({
                  url: `${parsed.pathname}.${defaultExt}${(parsed.search) ? parsed.search : ''}`,
                  headers: req.headers,
                }, res, next);
              } else {
                // Try to serve default ./404.html
                const rawUrl = (handleError ? `/${path.join(baseDir, `404.${defaultExt}`)}` : req.url);
                const encodedUrl = ensureUriEncoded(rawUrl);
                middleware({
                  url: encodedUrl,
                  headers: req.headers,
                  statusCode: 404,
                }, res, next);
              }
            } else if (err) {
              status[500](res, next, { error: err });
            } else if (stat.isDirectory()) {
              if (!autoIndex && !opts.showDir) {
                status[404](res, next);
                return;
              }
  
  
              // 302 to / if necessary
              if (!pathname.match(/\/$/)) {
                res.statusCode = 302;
                const q = parsed.query ? `?${parsed.query}` : '';
                res.setHeader(
                  'location',
                  ensureUriEncoded(`${parsed.pathname}/${q}`)
                );
                res.end();
                return;
              }
  
              if (autoIndex) {
                middleware({
                  url: urlJoin(
                    encodeURIComponent(pathname),
                    `/index.${defaultExt}`
                  ),
                  headers: req.headers,
                }, res, (autoIndexError) => {
                  if (autoIndexError) {
                    status[500](res, next, { error: autoIndexError });
                    return;
                  }
                  if (opts.showDir) {
                    showDir(opts, stat)(req, res);
                    return;
                  }
  
                  status[403](res, next);
                });
                return;
              }
  
              if (opts.showDir) {
                showDir(opts, stat)(req, res);
              }
            } else {
              serve(stat);
            }
          });
        } catch (err) {
          status[500](res, next, { error: err.message });
        }
      }
  
      function isTextFile(mimeType) {
        return (/^text\/|^application\/(javascript|json)/).test(mimeType);
      }
  
      // serve gzip file if exists and is valid
      function tryServeWithGzip() {
        try {
          fs.stat(gzippedFile, (err, stat) => {
            if (!err && stat.isFile()) {
              hasGzipId12(gzippedFile, (gzipErr, isGzip) => {
                if (!gzipErr && isGzip) {
                  file = gzippedFile;
                  serve(stat);
                } else {
                  statFile();
                }
              });
            } else {
              statFile();
            }
          });
        } catch (err) {
          status[500](res, next, { error: err.message });
        }
      }
  
      // serve brotli file if exists, otherwise try gzip
      function tryServeWithBrotli(shouldTryGzip) {
        try {
          fs.stat(brotliFile, (err, stat) => {
            if (!err && stat.isFile()) {
              file = brotliFile;
              serve(stat);
            } else if (shouldTryGzip) {
              tryServeWithGzip();
            } else {
              statFile();
            }
          });
        } catch (err) {
          status[500](res, next, { error: err.message });
        }
      }
  
      const shouldTryBrotli = opts.brotli && shouldCompressBrotli(req);
      const shouldTryGzip = opts.gzip && shouldCompressGzip(req);
      // always try brotli first, next try gzip, finally serve without compression
      if (shouldTryBrotli) {
        tryServeWithBrotli(shouldTryGzip);
      } else if (shouldTryGzip) {
        tryServeWithGzip();
      } else {
        statFile();
      }
    };
  };
  
  
  httpServerCore = module.exports;
  httpServerCore.version = version;
  httpServerCore.showDir = showDir;
  