@@ -160,6 +160,127 @@ describe('vhost(hostname, server)', function () {
160160 . expect ( 200 , 'undefined' , done )
161161 } )
162162
163+ describe ( 'with req.hostname (express 5 / reverse proxy)' , function ( ) {
164+ it ( 'should route by req.hostname over the Host header' , function ( _ , done ) {
165+ var vhosts = [ ]
166+
167+ vhosts . push ( vhost ( 'proxied.com' , proxied ) )
168+ vhosts . push ( vhost ( 'direct.com' , direct ) )
169+
170+ var app = createServer ( vhosts , null , function ( req ) {
171+ // what express 5 provides with `trust proxy` from X-Forwarded-Host
172+ req . hostname = 'proxied.com'
173+ } )
174+
175+ function proxied ( req , res ) { res . end ( 'proxied' ) }
176+ function direct ( req , res ) { res . end ( 'direct' ) }
177+
178+ request ( app )
179+ . get ( '/' )
180+ . set ( 'Host' , 'direct.com' )
181+ . expect ( 200 , 'proxied' , done )
182+ } )
183+
184+ it ( 'should reflect the routed value on req.vhost.host and hostname' , function ( _ , done ) {
185+ var app = createServer ( 'proxied.com' , function ( req , res ) {
186+ res . end ( JSON . stringify ( { host : req . vhost . host , hostname : req . vhost . hostname } ) )
187+ } , function ( req ) {
188+ req . hostname = 'proxied.com'
189+ } )
190+
191+ request ( app )
192+ . get ( '/' )
193+ . set ( 'Host' , 'direct.com:8080' )
194+ . expect ( 200 , '{"host":"proxied.com","hostname":"proxied.com"}' , done )
195+ } )
196+
197+ it ( 'should match a wildcard against req.hostname and capture from it' , function ( _ , done ) {
198+ var app = createServer ( '*.proxied.com' , function ( req , res ) {
199+ res . end ( JSON . stringify ( [ req . vhost . length , req . vhost [ 0 ] , req . vhost . hostname ] ) )
200+ } , function ( req ) {
201+ req . hostname = 'foo.proxied.com'
202+ } )
203+
204+ request ( app )
205+ . get ( '/' )
206+ . set ( 'Host' , 'direct.com' )
207+ . expect ( 200 , '[1,"foo","foo.proxied.com"]' , done )
208+ } )
209+
210+ it ( 'should match a RegExp against req.hostname' , function ( _ , done ) {
211+ var app = createServer ( / u s e r - ( b o b | j o e ) \. p r o x i e d \. c o m / , function ( req , res ) {
212+ res . end ( JSON . stringify ( [ req . vhost . length , req . vhost [ 0 ] ] ) )
213+ } , function ( req ) {
214+ req . hostname = 'user-bob.proxied.com'
215+ } )
216+
217+ request ( app )
218+ . get ( '/' )
219+ . set ( 'Host' , 'direct.com' )
220+ . expect ( 200 , '[1,"bob"]' , done )
221+ } )
222+
223+ it ( 'should 404 when req.hostname does not match' , function ( _ , done ) {
224+ var app = createServer ( 'proxied.com' , function ( req , res ) {
225+ res . end ( 'proxied' )
226+ } , function ( req ) {
227+ // Host header would match, but the resolved hostname wins
228+ req . hostname = 'other.com'
229+ } )
230+
231+ request ( app )
232+ . get ( '/' )
233+ . set ( 'Host' , 'proxied.com' )
234+ . expect ( 404 , done )
235+ } )
236+
237+ it ( 'should fall back to the Host header when req.hostname is empty' , function ( _ , done ) {
238+ var app = createServer ( 'direct.com' , function ( req , res ) {
239+ res . end ( 'direct' )
240+ } , function ( req ) {
241+ req . hostname = ''
242+ } )
243+
244+ request ( app )
245+ . get ( '/' )
246+ . set ( 'Host' , 'direct.com' )
247+ . expect ( 200 , 'direct' , done )
248+ } )
249+
250+ it ( 'should not re-strip a portless IPv6 req.hostname' , function ( _ , done ) {
251+ // req.hostname is already port-free; re-parsing the bracketed literal
252+ // must be a no-op, not mangle it to an empty string
253+ var app = createServer ( '[::1]' , function ( req , res ) {
254+ res . end ( JSON . stringify ( { host : req . vhost . host , hostname : req . vhost . hostname } ) )
255+ } , function ( req ) {
256+ req . hostname = '[::1]'
257+ } )
258+
259+ request ( app )
260+ . get ( '/' )
261+ . set ( 'Host' , 'direct.com:8080' )
262+ . expect ( 200 , '{"host":"[::1]","hostname":"[::1]"}' , done )
263+ } )
264+
265+ it ( 'should call next() when neither req.hostname nor Host is present' , function ( _ , done ) {
266+ var app = http . createServer ( function onRequest ( req , res ) {
267+ req . headers . host = undefined
268+
269+ var mw = vhost ( 'proxied.com' , function ( req , res ) {
270+ res . end ( 'handled' )
271+ } )
272+
273+ mw ( req , res , function ( ) {
274+ res . end ( 'next:' + String ( req . vhost ) )
275+ } )
276+ } )
277+
278+ request ( app )
279+ . get ( '/' )
280+ . expect ( 200 , 'next:undefined' , done )
281+ } )
282+ } )
283+
163284 describe ( 'arguments' , function ( ) {
164285 describe ( 'hostname' , function ( ) {
165286 it ( 'should be required' , function ( ) {
@@ -699,12 +820,16 @@ describe('vhost(hostname, server)', function () {
699820 } )
700821} )
701822
702- function createServer ( hostname , server ) {
823+ function createServer ( hostname , server , pretest ) {
703824 var vhosts = ! Array . isArray ( hostname )
704825 ? [ vhost ( hostname , server ) ]
705826 : hostname
706827
707828 return http . createServer ( function onRequest ( req , res ) {
829+ // allows changes to the request/response objects before the middleware,
830+ // e.g. simulating the `req.hostname` an Express 5 app would provide
831+ if ( pretest ) pretest ( req , res )
832+
708833 var index = 0
709834
710835 function next ( err ) {
0 commit comments