GithubHelp home page GithubHelp logo

jfhbrook / node-ecstatic Goto Github PK

View Code? Open in Web Editor NEW
974.0 33.0 194.0 1.15 MB

A static file server middleware that works with core http, express or on the CLI!

Home Page: https://github.com/jfhbrook/node-ecstatic

License: MIT License

JavaScript 99.62% HTML 0.17% Shell 0.21%

node-ecstatic's Introduction

THIS PROJECT IS UNMAINTAINED AND DEPRECATED

Please use something else. See: #259

Ecstatic build status codecov.io

A simple static file server middleware. Use it with a raw http server, express/connect or on the CLI!

Examples:

express 4.x

'use strict';

const express = require('express');
const ecstatic = require('../lib/ecstatic');
const http = require('http');

const app = express();

app.use(ecstatic({
  root: `${__dirname}/public`,
  showdir: true,
}));

http.createServer(app).listen(8080);

console.log('Listening on :8080');

stock http server

'use strict';

const http = require('http');

const ecstatic = require('../lib/ecstatic')({
  root: `${__dirname}/public`,
  showDir: true,
  autoIndex: true,
});

http.createServer(ecstatic).listen(8080);

console.log('Listening on :8080');

fall through

To allow fall through to your custom routes:

ecstatic({ root: __dirname + '/public', handleError: false })

CLI

ecstatic ./public --port 8080

Install:

For using ecstatic as a library, just npm install it into your project:

npm install --save ecstatic

For using ecstatic as a cli tool, either npm install it globally:

npm install ecstatic -g

or install it locally and use npm runscripts to add it to your $PATH, or reference it directly with ./node_modules/.bin/ecstatic.

API:

ecstatic(opts);

$ ecstatic [dir?] {options} --port PORT

In node, pass ecstatic an options hash, and it will return your middleware!

const opts = {
  root: path.join(__dirname, 'public'),
  baseDir: '/',
  autoIndex: true,
  showDir: true,
  showDotfiles: true,
  humanReadable: true,
  hidePermissions: false,
  si: false,
  cache: 'max-age=3600',
  cors: false,
  gzip: true,
  brotli: false,
  defaultExt: 'html',
  handleError: true,
  serverHeader: true,
  contentType: 'application/octet-stream',
  weakEtags: true,
  weakCompare: true,
  handleOptionsMethod: false,
}

If opts is a string, the string is assigned to the root folder and all other options are set to their defaults.

When running in CLI mode, all options work as above, passed in optimist style. port defaults to 8000. If a dir or --root dir argument is not passed, ecsatic will serve the current dir. Ecstatic also respects the PORT environment variable.

opts.root

--root {root}

opts.root is the directory you want to serve up.

opts.host

--host {host}

In CLI mode, opts.host is the host you want ecstatic to listen to. Defaults to 0.0.0.0. This can be overridden with the --host flag or with the HOST environment variable.

opts.port

--port {port}

In CLI mode, opts.port is the port you want ecstatic to listen to. Defaults to 8000. This can be overridden with the --port flag or with the PORT environment variable.

opts.baseDir

--baseDir {dir}

opts.baseDir is / by default, but can be changed to allow your static files to be served off a specific route. For example, if opts.baseDir === "blog" and opts.root = "./public", requests for localhost:8080/blog/index.html will resolve to ./public/index.html.

opts.cache

--cache {value}

Customize cache control with opts.cache , if it is a number then it will set max-age in seconds. Other wise it will pass through directly to cache-control. Time defaults to 3600 s (ie, 1 hour).

If it is a function, it will be executed on every request, and passed the pathname. Whatever it returns, string or number, will be used as the cache control header like above.

opts.showDir

--no-showDir

Turn off directory listings with opts.showDir === false. Defaults to true.

opts.showDotfiles

--no-showDotfiles

Exclude dotfiles from directory listings with opts.showDotfiles === false. Defaults to true.

opts.humanReadable

--no-human-readable

If showDir is enabled, add human-readable file sizes. Defaults to true. Aliases are humanreadable and human-readable.

opts.hidePermissions

--hide-permissions

If hidePermissions is enabled, file permissions will not be displayed. Defaults to false. Aliases are hidepermissions and hide-permissions.

opts.headers

--H {HeaderA: valA} [--H {HeaderB: valB}]

Set headers on every response. opts.headers can be an object mapping string header names to string header values, a colon (:) separated string, or an array of colon separated strings.

opts.H and opts.header are aliased to opts.headers so that you can use -H and --header options to set headers on the command-line like curl:

$ ecstatic ./public -p 5000 -H 'Access-Control-Allow-Origin: *'

opts.si

--si

If showDir and humanReadable are enabled, print file sizes with base 1000 instead of base 1024. Name is inferred from cli options for ls. Aliased to index, the equivalent option in Apache.

opts.autoIndex

--no-autoindex

Serve /path/index.html when /path/ is requested. Turn off autoIndexing with opts.autoIndex === false. Defaults to true.

opts.defaultExt

--defaultExt {ext}

Turn on default file extensions with opts.defaultExt. If opts.defaultExt is true, it will default to html. For example if you want a request to /a-file to resolve to ./public/a-file.html, set this to true. If you want /a-file to resolve to ./public/a-file.json instead, set opts.defaultExt to json.

opts.gzip

--no-gzip

By default, ecstatic will serve ./public/some-file.js.gz in place of ./public/some-file.js when the gzipped version exists and ecstatic determines that the behavior is appropriate. If ./public/some-file.js.gz is not valid gzip, this will fall back to ./public/some-file.js. You can turn this off with opts.gzip === false.

opts.brotli

--brotli

Serve ./public/some-file.js.br in place of ./public/some-file.js when the brotli encoded version exists and ecstatic determines that the behavior is appropriate. If the request does not contain br in the HTTP accept-encoding header, ecstatic will instead attempt to serve a gzipped version (if opts.gzip is true), or fall back to ./public.some-file.js. Defaults to false.

opts.serverHeader

--no-server-header

Set opts.serverHeader to false in order to turn off setting the Server header on all responses served by ecstatic.

opts.contentType

--content-type {type}

Set opts.contentType in order to change default Content-Type header value. Defaults to application/octet-stream.

opts.mimeTypes

--mime-types {filename}

Add new or override one or more mime-types. This affects the HTTP Content-Type header. May be either an object hash of type(s), or a function (file, defaultValue) => 'some/mimetype'. Naturally, only the object hash works on the command line.

ecstatic({ mimeTypes: { 'some/mimetype': ['file_extension', 'file_extension'] } })

It's important to note that any changes to mime handling are global, since the mime module appears to be poorly behaved outside of a global singleton context. For clarity you may prefer to call require('ecstatic').mime.define or require('ecstatic').setCustomGetType directly and skip using this option, particularly in cases where you're using multiple instances of ecstatic's middleware. You've been warned!

opts.handleError

Turn off handleErrors to allow fall-through with opts.handleError === false, Defaults to true.

opts.weakEtags

--no-weak-etags

Set opts.weakEtags to false in order to generate strong etags instead of weak etags. Defaults to true. See opts.weakCompare as well.

opts.weakCompare

--no-weak-compare

Turn off weakCompare to disable the weak comparison function for etag validation. Defaults to true. See https://www.ietf.org/rfc/rfc2616.txt Section 13.3.3 for more details.

opts.handleOptionsMethod

--handle-options-method

Set handleOptionsMethod to true in order to respond to 'OPTIONS' calls with any standard/set headers. Defaults to false. Useful for hacking up CORS support.

opts.cors

--cors

This is a convenience setting which turns on handleOptionsMethod and sets the headers Access-Control-Allow-Origin: * and Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since. This should be enough to quickly make cross-origin resource sharing work between development APIs. More advanced usage can come either from overriding these headers with the headers argument, or by using the handleOptionsMethod flag and then setting headers "manually." Alternately, just do it in your app using separate middlewares/abstractions.

Defaults to false.

middleware(req, res, next);

This works more or less as you'd expect.

ecstatic.showDir(folder);

This returns another middleware which will attempt to show a directory view. Turning on auto-indexing is roughly equivalent to adding this middleware after an ecstatic middleware with autoindexing disabled.

ecstatic.mime.define(mappings);

This defines new mappings for the mime singleton, as specified in the main docs for the ecstatic middleware. Calling this directly should make global mutation more clear than setting the options when instantiating the middleware, and is recommended if you're using more than one middlware instance.

ecstatic.mime.setCustomGetType(fn);

This sets a global custom function for getting the mime type for a filename. If this function returns a falsey value, getType will fall back to the mime module's handling. Calling this directly should make global mutation more clear than setting the options when instantiating the middleware, and is recommended if you're using more than one middleware instance.

ecstatic.mime.getType(filename, defaultValue);

This will return the mimetype for a filename, first using any function supplied with ecstatic.mime.setCustomGetType, then trying require('mime').getType, then falling back to defaultValue. Generally you don't want to use this directly.

ecstatic.mime.setCustomLookupCharset(fn);

This sets a global custom function for getting the charset for a mime type. If this function returns a falsey value, lookupCharset will fall back on the charset module's handling. Calling this directly should make global mutation more clear than setting the options when instantiating the middleware, and is recommended if you're using more than one middleware instance.

ecstatic.mime.lookupCharset(mimeType);

This will look up the charset for the supplied mime type, first using any function supplied with ecstatic.mime.setCustomLookupCharset, then trying require('charset')(mimeType). Generally you don't want to use this directly.

Tests:

Ecstatic has a fairly extensive test suite. You can run it with:

$ npm test

Contribute:

Without outside contributions, ecstatic would wither and die! Before contributing, take a quick look at the contributing guidelines in ./CONTRIBUTING.md . They're relatively painless, I promise.

License:

MIT. See LICENSE.txt. For contributors, see CONTRIBUTORS.md

node-ecstatic's People

Contributors

camwiegert avatar choonkeat avatar clee avatar colinf avatar curimit avatar dominictarr avatar dotnetcarpenter avatar drsdavidsoft avatar indexzero avatar isaacs avatar jakeburden avatar jessetane avatar jfhbrook avatar jonahoffline avatar joshgillies avatar kuttkatrea avatar leesei avatar mathiasbynens avatar mmalecki avatar mmmm1998 avatar ronag avatar s25g5d4 avatar shinnn avatar siranthony avatar tehshrike avatar thornjad avatar tomsteele avatar villesalonen avatar webpro avatar yfr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-ecstatic's Issues

xss issues

The new directory listing code from nodejitsu's static server has some xss flaws since req.url comes directly from the user and hasn't been filtered at all. It should go through a combination of encodeURI() and ent.encode() before being rendered to the page.

Status code not set to 304

I have been having a problem in that the http response status code is 200 rather than 304 for files which have not changed. It works for me if I make the following change in status-handler.js

exports['304'] = function (res, next) {
  res.writeHead(304, res.headers);
  res.end();
};

I don't really understand why and it could potentially need to be similarly changed in several other places in that file, so would rather see if anyone understands it before I submit a pull request.

Cheers,
Colin

ecstatic not working on windows

in file ecstatic.js, you initialize root var with a '/' at the end.

  var root = path.resolve(dir) + '/',

In windows it makes following test always wrong:

 if (file.slice(0, root.length) !== root) {

eg: file is c:\test\project\public\index.html and root is c:\test\project\public/
=> file.slice(0, root.length) = c:\test\project\public\ and root = c:\test\project\public/

Is trailing slash on root really necessary?

When I remove it, it works.

example directory listings are broken

The union example now gives:

Not Found.

and the express example says

Cannot GET /

the core.js example works.

This bug seems to relate to the new logic at lib/ecstatic.js line 40 so that showDir only ever fires when next isn't a function.

Not working on Windows

I am having to work on a Windows XP machine today, and node-ecstatic does not appear to work on WIndows.

Ensure 304's actually work

At one point in time, 304's weren't being set. Our fix at the time was to use res.writeHead instead of setting res.statusCode. This seems wrong, though.

One user commented on the issue after its closure:

#25 (comment)

I have a feeling this mostly works in master, though I need to make sure there are tests for it.

status 405 not check next

why status-handlers not check next? so in flatiron, post request is returned 405 in ecstatic, not to router

showDir serves up broken links for files with special characters

I'm displaying a directory tree within my Express app with the sample code listed on the project's main page:
app.use(ecstatic(__dirname + '/share'));
This works fine for most of the files under the share directory tree.
However, I have some directories whose names contain spaces (not my typical naming style in Linux, but since these names will be visible through my site, I want them to be more readable), and while these directories display correctly (the spaces are converted to "%20"s in the URL, I can't access files within these directories - I get the default Express error "Cannot GET /[path]/[file]" error.

I've tested several cases with spaces in file and/or directory names:

  • File names with spaces in them - open properly
  • Directory names with spaces in them - display properly
  • File names without spaces in them, inside directories whose names have spaces in them - "Cannot GET" error
  • File names with spaces in them, inside directories whose names have spaces in them - "Cannot GET" error

Use HTML5 doctype?

Should we use HTML5 doctype?
I'm thinking of modifying showdir.js:render() to something like this:

- var html = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\
-   "http://www.w3.org/TR/html4/loose.dtd">\
+ var html = '<!doctype html>\
+   <meta charset="utf-8">\

"handleError" does not work with a 404.html

The handleError option does not appear to be working when you have a 404.html file in your root dir. https://github.com/jesusabdullah/node-ecstatic#fall-through

Given the following simple app and a 404.html file in the public, I cannot get to the /version route.

var ecstatic = require('ecstatic');
var flatiron = require('flatiron');
var app = flatiron.app;
var port = 3000;

app.use(flatiron.plugins.http, {
  before: [
    ecstatic({
      root: __dirname + '/public',
      handleError: false
    })
  ],
  after: []
});

app.router.get('/version', function () {
  this.res.writeHead(200, { 'Content-Type': 'text/plain' });
  this.res.end('flatiron ' + flatiron.version);
});

app.start(port);
console.log('Flatiron running on ' + port);

I would expect that even though a 404.html file exists it would fall through to the router.

In fact it looks like handleError is a total no-op and doesn't appear anywhere in the codebase. https://github.com/jesusabdullah/node-ecstatic/search?q=handleError&ref=cmdform

Problems when requesting files whose folder names have spaces

When requesting files whose folder names have spaces it will throw an error.
URL: http://localhost:8080/folder_with%20space
file: notes.txt

Suppose I request notes.txt from the index listing. When clicked it looks for:
http://localhost:8080/folder_with%2520space/notes.txt
and throws this error:
"Cannot GET /folder_with%2520space/notes.txt"

It looks like the server finds the % in the url request and tries to re-encode it with 25.

I noticed this from using http-server.

querystring lost on default autoIndex returns

The issue: requesting /foo?params=bar the url becomes /foo

I notice that requesting /foo/index.html?params=bar the querystring stays.

The issue is that i would like for the querystring to persist in through the request, so that you could say, bookmark/share/etc...Think the issue may be in path.normalize.

next() vs. res.emit("next")

There is some behavior that checks for the existence of "next". I don't think this will cause problems with union's res.emit("next") since the function next is shimmed out.

Just something to watch out for.

v0.2.0 roadmap

COMPLETE:

  • Refactor of overall code structure (cps, less middlewares, generalized custom error page behavior)
  • Includes fixes from v0.1.x branch (writeHead, path.join, etc.)
  • Fix mime typer

TODO:

  • Add test coverage for union
  • Add test coverage for flatiron/director
  • Add test coverage for flatiron/broadway
  • Add test coverage for 304 caching
  • Add test coverage for showing empty directory
  • Add 'equip' wrapper.
  • Write documentation for all new parts
  • Make the tests pass.
  • Close issues.

FUTURE:

  • Rewrite tests to use vows
  • Add test coverage for mime typer
  • Investigate the null byte issue

Status of responses is not respected in union

Because of ecstatic status handlers using:

if (typeof next === "function") {
    next();
}

and union using it's default error handler as

  (this.res || res).writeHead(404, {"Content-Type": "text/plain"});
  (this.res || res).end("Not Found\n");

almost every request that passes through ecstatic and end in any status other than 200 or 500, ends up in union's default error handler, ending in an error 404.

It would be useful to have a option to define a html file to display in case of specific error codes (as the 404.html file for 404 errors) to be able to remove the next() statements from the status handlers and so be able to keep the intended error codes (for example, a 403 when trying to access a directory with showDir = false)

Readme Issue

This text:

Serve /path/index.html when /path/ is requested. Turn off autoIndexing with opts.autoIndex === true. Defaults to true.

I believe should read:

Serve /path/index.html when /path/ is requested. Turn on autoIndexing with opts.autoIndex === true. Defaults to false.

Feature request: gzip support

I want to use ecstatic for a project which helps build client side apps. Part of this project gzip's assets, however, ecstatic doesn't currently check the Accept-Encoding header to see if the client accepts gzip/compressed resources.

ecstatic shouldn't attempt to gzip anything on the fly, but rather if the client accepts compressed resources and a file.ext.gz exists then serve that, if not serve the file.ext if it exists.

Id be happy to work on a pull request if you agree with having this feature.

req.showdir

In order to ensure that an auto-generated directory view is shown instead of a custom 404.html page (this only happens if 404.html exists), a property now gets a boolean property called 'showdir' attached to it.

This is almost definitely a bad idea, but hard to fix without a code refactor.

Breaks in Union

This is because Union's stream piping is currently broken. :'(

Arguments to path.resolve must be strings

Hey Josh,

I recently updated my local version of node to v0.10.10 and ran into a strange path.resolve error when trying to run ecstatic based on the documentation provided.

Below is the full error stack.

$ node -v
v0.10.10

$ node app.js 

path.js:313
        throw new TypeError('Arguments to path.resolve must be strings');
              ^
TypeError: Arguments to path.resolve must be strings
    at Object.exports.resolve (path.js:313:15)
    at module.exports (/Users/cole/Public/github/rgb/node_modules/ecstatic/lib/ecstatic.js:14:29)
    at Object.<anonymous> (/Users/cole/Public/github/rgb/app.js:16:5)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:901:3

The path.resolve api does not look like it has changed so I am not sure why updating node would have made a difference here.

I was able to fix this issue by simply passing in a string instead of an object for the directory in the ecstatic options. I could totally be missing something obvious here but I thought it would be best to make you aware of what I encountered. Let me know if you have any questions or if I can help in any way.

Broken example:

app.use(flatiron.plugins.http, {
  before: [
    ecstatic({ root: __dirname + '/public/' })
  ]
});

Working example:

app.use(flatiron.plugins.http, {
  before: [
    ecstatic(__dirname + '/public/' )
  ]
});

Organization

estatic has a lot of branching in it. It'd be nice to manage this "better".

option to set response headers, specially for CORS

I'm developing locally and need multiple servers running on different ports, so XHR doesn't work (different port is also blocked by same-origin policy).

I want to simply add Access-Control-Allow-Origin: * to the response header but other users might have different needs so I'm thinking on a new option to set the custom headers:

var opts = {
  root: __dirname + '/public',
  header: {
    'Access-Control-Allow-Origin': '*',
    'X-Foo': 'bar'
  }
};

If you agree I can implement this feature and ask for a pull request next week.

gzip issues

gzip is not in the opt parser. When I add it and try to use that feature it is buggy. What is the status of gzip support?

Add API entry points for custom and async mime lookup

Hi, I'm trying to serve text files with unusual filename extensions, which wind up as octet-stream due to ecstatic's reliance on the "mime" module:

sven:/gr2$ nodejs -e 'console.log(require("mime").lookup("common.flp"));'
application/octet-stream

I'd prefer them to be resolved according to my local mime types DB:

sven:/gr2$ file --mime common.flp
common.flp: text/plain; charset=us-ascii

This could be achieved by using the "mime-magic" module:

sven:/gr2$ nodejs -e 'var mime = require("mime-magic"); mime("common.flp", console.log);'
null 'text/plain'

It seems that in the "mime" module, there's currently no way to distinguish known octet-streams from fallback octet-streams (mime bug #55).

I'd be glad if you could implement the mime-magic lookup into mainstream ecstatic.

(Edit 2015-12-08: The filename-based lookup is fine for default. If mime-magic lookup ever gets into mainstream ecstatic, only use it as a fallback for when filename-based mime has no idea.)

relative links are broken

If you visit a folder with an index file without putting a trailing '/', relative links don't work correctly. eg, if you visit the index page at "www.yoursite.com/folder" and click on a relative link to "otherFolder/random.html", the path will be resolved as "www.yoursite.com/otherFolder/random.html", instead of the appropriate "www.yoursite.com/folder/otherFolder/random.html". I suppose the fix to this is to redirect the browser to the same folder with a trailing '/'.

href is invalid when there is query string in url

Start ecstatic with --showDir, open this url:

http://localhost:8080/?key=value

The href's got ?key=value within, which is invalid.

http://localhost:8080/?key=value/folder%20with%20space/

Changing this in showdir.js:writeRow() will ignore the query string:

-                req.url.replace(/\/$/, '')
+                parsed.pathname.replace(/\/$/, '')

But the test 'malformed showdir uri' will then fail, I think this test is incorrect in putting the malformed string in the query.
The test failed incidentally in the first place because of this issue.

We already protect against invalid pathname in ecstatic.js:middleware(). We could check the query part by passing req.url there.

I would suggest checking req.url in ecstatic.js:middleware(), and test malformed string in both query and pathname in malformed-dir.js.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.