Skip to content

Commit 3e6a713

Browse files
committed
Forked expressjs/vhost to vhost-middleware
no issue Direct copy of current master from https://github.com/expressjs/vhost/ with automatic eslint fixes to match our styleguide. Forked because we need a reverse proxy fix and the original module appears abandoned.
1 parent 2045157 commit 3e6a713

File tree

10 files changed

+700
-0
lines changed

10 files changed

+700
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
plugins: ['ghost'],
3+
extends: [
4+
'plugin:ghost/node',
5+
]
6+
};

ghost/vhost-middleware/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 Ghost Foundation
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

ghost/vhost-middleware/README.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Vhost Middleware
2+
3+
Forked from https://github.com/expressjs/vhost/ which appears abandoned.
4+
5+
## Install
6+
7+
`npm install @tryghost/vhost-middleware --save`
8+
9+
or
10+
11+
`yarn add @tryghost/vhost-middleware`
12+
13+
14+
## API
15+
16+
<!-- eslint-disable no-unused-vars -->
17+
18+
```js
19+
var vhost = require('vhost')
20+
```
21+
22+
### vhost(hostname, handle)
23+
24+
Create a new middleware function to hand off request to `handle` when the incoming
25+
host for the request matches `hostname`. The function is called as
26+
`handle(req, res, next)`, like a standard middleware.
27+
28+
`hostname` can be a string or a RegExp object. When `hostname` is a string it can
29+
contain `*` to match 1 or more characters in that section of the hostname. When
30+
`hostname` is a RegExp, it will be forced to case-insensitive (since hostnames are)
31+
and will be forced to match based on the start and end of the hostname.
32+
33+
When host is matched and the request is sent down to a vhost handler, the `req.vhost`
34+
property will be populated with an object. This object will have numeric properties
35+
corresponding to each wildcard (or capture group if RegExp object provided) and the
36+
`hostname` that was matched.
37+
38+
```js
39+
var connect = require('connect')
40+
var vhost = require('vhost')
41+
var app = connect()
42+
43+
app.use(vhost('*.*.example.com', function handle (req, res, next) {
44+
// for match of "foo.bar.example.com:8080" against "*.*.example.com":
45+
console.dir(req.vhost.host) // => 'foo.bar.example.com:8080'
46+
console.dir(req.vhost.hostname) // => 'foo.bar.example.com'
47+
console.dir(req.vhost.length) // => 2
48+
console.dir(req.vhost[0]) // => 'foo'
49+
console.dir(req.vhost[1]) // => 'bar'
50+
}))
51+
```
52+
53+
## Examples
54+
55+
### using with connect for static serving
56+
57+
```js
58+
var connect = require('connect')
59+
var serveStatic = require('serve-static')
60+
var vhost = require('vhost')
61+
62+
var mailapp = connect()
63+
64+
// add middlewares to mailapp for mail.example.com
65+
66+
// create app to serve static files on subdomain
67+
var staticapp = connect()
68+
staticapp.use(serveStatic('public'))
69+
70+
// create main app
71+
var app = connect()
72+
73+
// add vhost routing to main app for mail
74+
app.use(vhost('mail.example.com', mailapp))
75+
76+
// route static assets for "assets-*" subdomain to get
77+
// around max host connections limit on browsers
78+
app.use(vhost('assets-*.example.com', staticapp))
79+
80+
// add middlewares and main usage to app
81+
82+
app.listen(3000)
83+
```
84+
85+
### using with connect for user subdomains
86+
87+
```js
88+
var connect = require('connect')
89+
var serveStatic = require('serve-static')
90+
var vhost = require('vhost')
91+
92+
var mainapp = connect()
93+
94+
// add middlewares to mainapp for the main web site
95+
96+
// create app that will server user content from public/{username}/
97+
var userapp = connect()
98+
99+
userapp.use(function (req, res, next) {
100+
var username = req.vhost[0] // username is the "*"
101+
102+
// pretend request was for /{username}/* for file serving
103+
req.originalUrl = req.url
104+
req.url = '/' + username + req.url
105+
106+
next()
107+
})
108+
userapp.use(serveStatic('public'))
109+
110+
// create main app
111+
var app = connect()
112+
113+
// add vhost routing for main app
114+
app.use(vhost('userpages.local', mainapp))
115+
app.use(vhost('www.userpages.local', mainapp))
116+
117+
// listen on all subdomains for user pages
118+
app.use(vhost('*.userpages.local', userapp))
119+
120+
app.listen(3000)
121+
```
122+
123+
### using with any generic request handler
124+
125+
```js
126+
var connect = require('connect')
127+
var http = require('http')
128+
var vhost = require('vhost')
129+
130+
// create main app
131+
var app = connect()
132+
133+
app.use(vhost('mail.example.com', function (req, res) {
134+
// handle req + res belonging to mail.example.com
135+
res.setHeader('Content-Type', 'text/plain')
136+
res.end('hello from mail!')
137+
}))
138+
139+
// an external api server in any framework
140+
var httpServer = http.createServer(function (req, res) {
141+
res.setHeader('Content-Type', 'text/plain')
142+
res.end('hello from the api!')
143+
})
144+
145+
app.use(vhost('api.example.com', function (req, res) {
146+
// handle req + res belonging to api.example.com
147+
// pass the request to a standard Node.js HTTP server
148+
httpServer.emit('request', req, res)
149+
}))
150+
151+
app.listen(3000)
152+
```
153+
154+
155+
## Develop
156+
157+
This is a mono repository, managed with [lerna](https://lernajs.io/).
158+
159+
Follow the instructions for the top-level repo.
160+
1. `git clone` this repo & `cd` into it as usual
161+
2. Run `yarn` to install top-level dependencies.
162+
163+
164+
## Run
165+
166+
- `yarn dev`
167+
168+
169+
## Test
170+
171+
- `yarn lint` run just eslint
172+
- `yarn test` run lint and tests
173+
174+
175+
176+
177+
# Copyright & License
178+
179+
Copyright (c) 2019 Ghost Foundation - Released under the [MIT license](LICENSE).

ghost/vhost-middleware/index.js

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*!
2+
* vhost
3+
* Copyright(c) 2014 Jonathan Ong
4+
* Copyright(c) 2014-2015 Douglas Christopher Wilson
5+
* MIT Licensed
6+
*/
7+
8+
'use strict';
9+
10+
/**
11+
* Module exports.
12+
* @public
13+
*/
14+
15+
module.exports = vhost;
16+
17+
/**
18+
* Module variables.
19+
* @private
20+
*/
21+
22+
var ASTERISK_REGEXP = /\*/g;
23+
var ASTERISK_REPLACE = '([^.]+)';
24+
var END_ANCHORED_REGEXP = /(?:^|[^\\])(?:\\\\)*\$$/;
25+
var ESCAPE_REGEXP = /([.+?^=!:${}()|[\]/\\])/g;
26+
var ESCAPE_REPLACE = '\\$1';
27+
28+
/**
29+
* Create a vhost middleware.
30+
*
31+
* @param {string|RegExp} hostname
32+
* @param {function} handle
33+
* @return {Function}
34+
* @public
35+
*/
36+
37+
function vhost(hostname, handle) {
38+
if (!hostname) {
39+
throw new TypeError('argument hostname is required');
40+
}
41+
42+
if (!handle) {
43+
throw new TypeError('argument handle is required');
44+
}
45+
46+
if (typeof handle !== 'function') {
47+
throw new TypeError('argument handle must be a function');
48+
}
49+
50+
// create regular expression for hostname
51+
var regexp = hostregexp(hostname);
52+
53+
return function vhost(req, res, next) {
54+
var vhostdata = vhostof(req, regexp);
55+
56+
if (!vhostdata) {
57+
return next();
58+
}
59+
60+
// populate
61+
req.vhost = vhostdata;
62+
63+
// handle
64+
handle(req, res, next);
65+
};
66+
}
67+
68+
/**
69+
* Get hostname of request.
70+
*
71+
* @param (object} req
72+
* @return {string}
73+
* @private
74+
*/
75+
76+
function hostnameof(req) {
77+
var host = req.headers.host;
78+
79+
if (!host) {
80+
return;
81+
}
82+
83+
var offset = host[0] === '['
84+
? host.indexOf(']') + 1
85+
: 0;
86+
var index = host.indexOf(':', offset);
87+
88+
return index !== -1
89+
? host.substring(0, index)
90+
: host;
91+
}
92+
93+
/**
94+
* Determine if object is RegExp.
95+
*
96+
* @param (object} val
97+
* @return {boolean}
98+
* @private
99+
*/
100+
101+
function isregexp(val) {
102+
return Object.prototype.toString.call(val) === '[object RegExp]';
103+
}
104+
105+
/**
106+
* Generate RegExp for given hostname value.
107+
*
108+
* @param (string|RegExp} val
109+
* @private
110+
*/
111+
112+
function hostregexp(val) {
113+
var source = !isregexp(val)
114+
? String(val).replace(ESCAPE_REGEXP, ESCAPE_REPLACE).replace(ASTERISK_REGEXP, ASTERISK_REPLACE)
115+
: val.source;
116+
117+
// force leading anchor matching
118+
if (source[0] !== '^') {
119+
source = '^' + source;
120+
}
121+
122+
// force trailing anchor matching
123+
if (!END_ANCHORED_REGEXP.test(source)) {
124+
source += '$';
125+
}
126+
127+
return new RegExp(source, 'i');
128+
}
129+
130+
/**
131+
* Get the vhost data of the request for RegExp
132+
*
133+
* @param (object} req
134+
* @param (RegExp} regexp
135+
* @return {object}
136+
* @private
137+
*/
138+
139+
function vhostof(req, regexp) {
140+
var host = req.headers.host;
141+
var hostname = hostnameof(req);
142+
143+
if (!hostname) {
144+
return;
145+
}
146+
147+
var match = regexp.exec(hostname);
148+
149+
if (!match) {
150+
return;
151+
}
152+
153+
var obj = Object.create(null);
154+
155+
obj.host = host;
156+
obj.hostname = hostname;
157+
obj.length = match.length - 1;
158+
159+
for (var i = 1; i < match.length; i++) {
160+
obj[i - 1] = match[i];
161+
}
162+
163+
return obj;
164+
}

0 commit comments

Comments
 (0)