Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ with the provided Dockerfile.
|`-U` or `--utc` |Use UTC time format in log messages.| |
|`--log-ip` |Enable logging of the client's IP address |`false` |
|`-P` or `--proxy` |Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com | |
|`--proxy-options` |Pass proxy [options](https://github.com/http-party/node-http-proxy#options) using nested dotted objects. e.g.: --proxy-options.secure false | |
|`--proxy-config` |Pass in `.json` configuration file or stringified JSON. e.g.: `./path/to/config.json` | |
|`--proxy-all` |Forward every request to the proxy target instead of serving local files|`false`|
|`--proxy-options` |Pass proxy [options](https://github.com/http-party/node-http-proxy#options) using nested dotted objects. e.g.: --proxy-options.secure false |
|`--username` |Username for basic authentication | |
|`--password` |Password for basic authentication | |
|`-S`, `--tls` or `--ssl` |Enable secure request serving with TLS/SSL (HTTPS)|`false`|
|`-C` or `--cert` |Path to ssl cert file |`cert.pem` |
|`-C` or `--cert` |Path to ssl cert file |`cert.pem` |
|`-K` or `--key` |Path to ssl key file |`key.pem` |
|`-r` or `--robots` | Automatically provide a /robots.txt (The content of which defaults to `User-agent: *\nDisallow: /`) | `false` |
|`--no-dotfiles` |Do not show dotfiles| |
Expand Down
41 changes: 41 additions & 0 deletions bin/http-server
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ if (argv.h || argv.help) {
' -P --proxy Fallback proxy if the request cannot be resolved. e.g.: http://someurl.com',
' --proxy-all Send every request to the proxy target instead of serving local files',
' --proxy-options Pass options to proxy using nested dotted objects. e.g.: --proxy-options.secure false',
' --proxy-config Pass in .json configuration file. e.g.: ./path/to/config.json',
' --websocket Enable websocket proxy',
'',
' --username Username for basic authentication [none]',
Expand All @@ -89,6 +90,7 @@ var port = argv.p || argv.port || parseInt(process.env.PORT, 10),
sslPassphrase = process.env.NODE_HTTP_SERVER_SSL_PASSPHRASE,
proxy = argv.P || argv.proxy,
proxyOptions = argv['proxy-options'],
proxyConfig = argv['proxy-config'],
websocket = argv.websocket,
proxyAll = Boolean(argv['proxy-all']),
utc = argv.U || argv.utc,
Expand Down Expand Up @@ -124,6 +126,12 @@ if (!argv.s && !argv.silent) {
chalk.red(error.status.toString()), chalk.red(error.message)
);
}
else if (req.proxy) {
logger.info(
'[%s] %s "%s" (%s)-> "%s"',
date, ip, chalk.cyan(req.url), chalk.magenta('Proxy'), chalk.cyan(req.proxy.target)
);
}
else {
logger.info(
'[%s] %s "%s %s" "%s"',
Expand Down Expand Up @@ -172,6 +180,7 @@ function listen(port) {
logFn: logger.request,
proxy: proxy,
proxyOptions: proxyOptions,
proxyConfig: proxyConfig,
proxyAll: proxyAll,
showDotfiles: argv.dotfiles,
mimetypes: argv.mimetypes,
Expand Down Expand Up @@ -228,6 +237,38 @@ function listen(port) {
}
}

if (proxyConfig) {
try {
if (fs.existsSync(proxyConfig)) {
proxyConfig = fs.readFileSync(proxyConfig, 'utf8');
}
if (typeof proxyConfig === 'string') {
proxyConfig = JSON.parse(proxyConfig);
}
if (typeof proxyConfig !== 'object') {
throw new Error('Invalid proxy config');
}
}
catch (err) {
logger.info(chalk.red('Error: Invalid proxy config or file'));
process.exit(1);
}
// Proxy file overrides cli config
proxy = undefined;
proxyOptions = undefined;
}

if (proxyAll && proxyConfig) {
logger.info(chalk.red('Error: --proxy-all cannot be used with --proxy-config'));
logger.info(
'%s\n%s\n%s',
chalk.yellow('Hint: Use'),
chalk.cyan('"/**": {\n "target": "your-proxy"\n}'),
chalk.yellow('in the proxy config to achieve the same effect.')
);
process.exit(1);
}

if (proxyAll && !proxy) {
logger.info(chalk.red('Error: --proxy-all requires --proxy to be set'));
process.exit(1);
Expand Down
4 changes: 4 additions & 0 deletions doc/http-server.1
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ Requires \-\-proxy.
.BI \-\-proxy\-options
Pass proxy options using nested dotted objects.

.TP
.BI \-\-proxy\-config
Pass in .json configuration file.

.TP
.BI \-\-username " " \fIUSERNAME\fR
Username for basic authentication.
Expand Down
53 changes: 53 additions & 0 deletions lib/http-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var fs = require('fs'),
httpProxy = require('http-proxy'),
corser = require('corser'),
secureCompare = require('secure-compare');
var { minimatch } = require('minimatch');

//
// Remark: backwards compatibility for previous
Expand Down Expand Up @@ -144,6 +145,58 @@ function HttpServer(options) {
});
}

if (typeof options.proxyConfig === 'object') {
var proxy = httpProxy.createProxyServer();
before.push(function (req, res, next) {
for (var key of Object.keys(options.proxyConfig)) {
if (!minimatch(req.url, key)) continue;
req.proxy ??= {};
var matchConfig = options.proxyConfig[key];

if (matchConfig.pathRewrite) {
Object.entries(matchConfig.pathRewrite).forEach(rewrite => {
req.url = req.url.replace(new RegExp(rewrite[0]), rewrite[1]);
});
}

var configEntries = Object.entries(matchConfig).filter(entry => entry[0] !== "pathRewrite");
configEntries.forEach(entry => req.proxy[entry[0]] = entry[1]);
break;
}

if (req.proxy) {
if (options.logFn) {
options.logFn(req, res);
}
proxy.web(req, res, req.proxy, function (err, req, res) {
if (options.logFn) {
options.logFn(req, res, {
message: err.message,
status: res.statusCode });
}
res.emit('next');
});
} else {
next();
}
});
}

before.push(httpServerCore({
root: this.root,
baseDir: options.baseDir,
cache: this.cache,
showDir: this.showDir,
showDotfiles: this.showDotfiles,
autoIndex: this.autoIndex,
defaultExt: this.ext,
gzip: this.gzip,
brotli: this.brotli,
contentType: this.contentType,
mimetypes: options.mimetypes,
handleError: typeof options.proxy !== 'string'
}));

if (!proxyAll) {
before.push(httpServerCore({
root: this.root,
Expand Down
Loading
Loading