In my hapijs app, given a Request
object, how can I find the original, unparsed, unmodified URL?
function getRequestUrl (request) {
return ...; // What goes here?
}
I've found that I can piece it together somewhat from Request.info.host
, Request.path
, and Request.query
, but it lacks the scheme (ie, http vs https), and is a bit of a kludge. Isn't the plain URL available somewhere?
The full URL isn't stored somewhere you can get it. You need to build it yourself from the parts:
const url = request.connection.info.protocol
+ '://'
+ request.info.host
+ request.url.path
;
Even though it might seem kludgey, it makes sense if you think about it because there is no original, unparsed, unmodified URL. The HTTP request that goes over the wire doesn't contain the URL as typed into the browser address bar for instance:
GET /hello?a=1&b=2 HTTP/1.1 // request.url.path
Host: localhost:4000 // request.info.host
Connection: keep-alive
Accept-Encoding: gzip, deflate, sdch
...
And you only know the protocol based on whether the hapi server connection is in TLS mode or not (request.connection.info.protocol
).
Things to be aware of
If you check either:
request.connection.info.uri
or request.server.info.uri
the reported hostname will be the hostname of the actual machine that the server is running on (the output of hostname
on *nix). If you want the actual host the person typed in the browser (which might be different) you need to check request.info.host
which is parsed from the HTTP request's Host header)
Proxies and X-Forwarded-Proto header
If your request got passed through a proxy(ies)/load balancers/HTTPS terminators, it's possible somewhere along the line HTTPS traffic got terminated and was sent to your server on an HTTP connection, in this case you'll want use the value of the x-forwarded-proto
header if it's there:
const url = (request.headers['x-forwarded-proto'] || request.connection.info.protocol)
+ '://'
+ request.info.host
+ request.url.path
;
With template strings:
const url = `${request.headers['x-forwarded-proto'] || request.connection.info.protocol}://${request.info.host}${request.url.path}`;
hapi-url solves this exact problem. It is prepared to work with X-Forwarded headers to run behind a proxy. There is also an option to override the automatic resolution if the library is not able to resolve the URL correctly.
I use the following syntax now (using coffee script):
server.on 'response', (data) ->
raw = data.raw.req
url = "#{data.connection.info.protocol}://#{raw.headers.host}#{raw.url}"
console.log "Access to #{url}"
Or as javascript:
server.on('response', function(data) {
var raw = data.raw.req;
var url = data.connection.info.protocol + "://" +
raw.headers.host + raw.url;
console.log("Access to " + url);
});
That gives you the exact URL like the user requested it.
You can't get the URL. You have to generate it. I'm using this one:
const url = request.headers['x-forwarded-proto'] + '://' +
request.headers.host +
request.url.path;