Skip to content

Commit

Permalink
Refactor whole in Polka
Browse files Browse the repository at this point in the history
Custom serve-static equivalent

Remove rewrites, colors, update snap
  • Loading branch information
Ram Lmn committed Feb 15, 2018
1 parent 7d90a49 commit 7d0155a
Show file tree
Hide file tree
Showing 17 changed files with 796 additions and 2,045 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
"valid-jsdoc": 0,
"arrow-parens": 0
}
}
}
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
node_modules
test
*.log
*.log
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
16 changes: 8 additions & 8 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
MIT License
MIT Licence

Copyright (c) Ram Lmn <ramlmn@outlook.com>

Expand All @@ -12,10 +12,10 @@ furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
29 changes: 12 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ A simple development server for serving static files.
![Terminal screenshot of serv](./snap/terminal.png)

`serv` starts a simple development server for your static files from the
console. It also has options for directory listing, compression and rewriting
any non-existing paths to root of the server.
console written in NodeJS. It also supports directory listing and compression.

serv can run a `http` or `https`(self-signed) and `http2` server.

`serv` runs on a specified port and can be accessed via `localhost:port`
(similar to `127.0.0.1:port`) or `computer-name:port` if you wish to access it
from another device on the local network.

`serv` also supports `h2 (http/2)` and self-signed certificates (`https`).
> *Note:* serv is a hacky, experimental project
## Installation

Expand All @@ -25,13 +26,15 @@ $ git clone https://github.com/ramlmn/serv.git
$ cd serv
$ npm install && npm link
```

Try `sudo` with `npm link` if it fails.
## Usage

### From terminal
``` bash
$ serv --dir path/to/dir/ --port 8080
```

Use `--help` for help and examples.
#### Flags

Flag | Default | Description
Expand All @@ -40,14 +43,9 @@ Flag | Default | Description
`-p`, `--port` | `8080` | Port to listen on
`-c`, `--compress` | `false` | Enable compression or not
`-l`, `--listing` | `false` | Enable directory listing
`-r`, `--rewrite` | `false` | Rewrite requests to root
`-s`, `--secure` | `false` | Prefer `https` over `http`
`-h2`, `--http2` | `false` | Run a `h2` server

> **Note:**
> * `--listing` and `--rewrite` cannot be used together.
> * `--secure` option is simply ignored if `--http2` is used.
> * `http` requests are not upgraded automatically to `https`
`-f`, `--fast` | `false` | Fast mode(no compression, ETags, logging)

### From Node API

Expand All @@ -60,36 +58,33 @@ const options = {
port: 8080,
compress: true,
listing: false,
rewrite: false,
secure: true,
http2: true,
fast: false,
logger: (request, response) => {
// Log `request` and `response` or whatever
},
}

// Create instance of serv
const server = new serv(options);
const staticServer = new serv(options);

// Start server
try {
const status = await server.start();
const server = await staticServer.start();

if (status.listening) {
console.log('Server Started');
} else {
console.log('Failed to start server', server.status);
console.log('Failed to start server');
}
} catch (err) {
console.log('Status:', server.status);
console.error(err);
}

// For stopping
server.stop();
```

**Note:** `options` are defaulted to as above in flags, check `Serv.DEFAULTS`

## License
[MIT](LICENSE)
102 changes: 51 additions & 51 deletions bin/serv.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,100 +3,100 @@
'use strict';

const yargs = require('yargs');
const chalk = require('chalk');
const Serv = require('../lib/serv.js');
const Serv = require('../lib/index.js');
const logger = require('../lib/helpers/logger.js');

const argv = yargs
const options = yargs
.usage('Usage: $0 [...options]')
.example('$0 --port 8080')
.example('$0 -p 8182 -d dist -c')
.example('$0 --port 8181 --dir ./share --listing')
.example('$0 -p 8182 -d ../../my-other-project --compress')
.help('help')
.describe({
'p': 'Port to listen on',
'd': 'Directory to serve (relative)',
'c': 'Enable compression',
'l': 'Enable dir listing (not with -r)',
'r': 'Rewrite to root (not with -l)',
's': 'Use https',
's': 'Use https - self signed keys',
'h2': 'Enable http2 protocol',
'l': 'Enable directory listing',
'f': 'Enable fast mode(no compression, ETags, logging)',
})
.alias({
'p': 'port',
'd': 'dir',
'c': 'compress',
'l': 'listing',
'r': 'rewrite',
's': 'secure',
'h2': 'http2',
'l': 'listing',
'f': 'fast',
})
.number(['p'])
.boolean(['c', 'l', 'r', 's', 'h2'])
.boolean(['c', 's', 'h2', 'l', 'f'])
.default({
'p': 8080,
'd': './',
'c': false,
'l': false,
'r': false,
'c': true,
's': false,
'h2': false,
'l': false,
'f': false,
})
.argv;

options.logger = logger;

/**
* Logs requests
*
* @param {Object} request http request object
* @param {Object} response http response object
*/
function logger(request, response) {
console.log(chalk.yellow('[LOG]'), chalk.magenta((new Date()).toLocaleTimeString()),
chalk.cyan(response.statusCode), '->', chalk.magenta(request.method), request.url);
};

// Options for server
const options = Object.assign({logger}, argv);

// Create new instance of serv
const staticServer = new Serv(options);

// Start the server
(async _ => {
try {
await staticServer.start();
const server = await staticServer.start();
const options = staticServer.options;

const status = staticServer.status;
if (status.options.compress) {
console.log(chalk.yellow('[FLG]'), 'Compression enabled');
if (options.compress) {
console.info('[FLAG]', 'Compression enabled');
}

if (status.options.http2) {
console.log(chalk.yellow('[FLG]'), 'HTTP/2 enabled');
} else if (status.options.secure) {
console.log(chalk.yellow('[FLG]'), 'HTTPS enabled');
if (options.http2) {
console.info('[FLAG]', 'HTTP/2 enabled');
}

if (status.options.listing) {
console.log(chalk.yellow('[FLG]'), 'Listing enabled');
} else if (status.options.rewrite) {
console.log(chalk.yellow('[FLG]'), 'Rewrite enabled');
if (options.secure) {
console.info('[FLAG]', 'HTTPS enabled');
}

console.log(chalk.green('[INF]'), 'Serving directory', chalk.cyan(status.options.dir));
console.log(chalk.green('[INF]'), 'Listening on port', chalk.cyan(status.options.port));
console.info('[INFO]', 'Serving directory', options.dir);
console.info('[INFO]', 'Listening on port', options.port);

if (status.listening) {
console.log(chalk.green('[INF]'), 'Server started at',
chalk.cyan(new Date().toLocaleString()));
if (server.listening) {
console.info('[INFO]', 'Server started at', (new Date()).toISOString());
} else {
console.log(chalk.red('[ERR]'), 'Failed to start server',
chalk.cyan(new Date().toLocaleString()));
process.exit(1);
console.error('[FATAL]', 'Failed to start server', (new Date()).toISOString());
process.exit();
}
} catch (err) {
console.error(chalk.red('[ERR]'), err);
process.exit(1);
console.error('[FATAL]', err);
process.exit();
}
})();


if (process.platform === 'win32') {
require('readline').createInterface({
input: process.stdin,
output: process.stdout,
}).on('SIGINT', _ => {
process.emit('SIGINT');
});
}

process.on('SIGINT', async _ => {
console.log('\n[INFO]', 'Server stopped at', (new Date()).toISOString());
await staticServer.stop();
process.exit();
});

process.on('SIGTERM', async _ => {
console.log('\n[INFO]', 'Server stopped at', (new Date()).toISOString());
await staticServer.stop();
process.exit();
});
24 changes: 24 additions & 0 deletions lib/helpers/add-etag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';

const crypto = require('crypto');

const addEtag = (req, res, next) => {
const data = [];

req.on('data', chunk => {
data.push(chunk);
});

req.on('end', _ => {
const body = Buffer.concat(data);
const etag = crypto.createHash('sha1')
.update(body)
.digest('hex');

res.setHeader('ETag', etag);

next();
});
};

module.exports = addEtag;
26 changes: 26 additions & 0 deletions lib/helpers/https-redirect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';


const HTTPSRedirect = (req, res, next) => {
if (req.hostname === 'localhost') {
return next();
}

// For non-localhost hosts, check the header for the protocol used.
if (!req.headers['x-forwarded-proto']) {
return next();
}

if ((req.headers['x-forwarded-proto'] || '').toLowerCase() === 'http') {
// reason for the redirect
res.setHeader('Non-Authoritative-Reason', 'HSTS');

// internal (temporary) redirect
return res.redirect(307, `https://${req.hostname}${req.url}`);
}

return next();
};


module.exports = HTTPSRedirect;
Loading

0 comments on commit 7d0155a

Please sign in to comment.