1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2010-2016, University of Amsterdam, 7 VU University Amsterdam 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(http_host, 37 [ http_public_url/2, % +Request, -PublicURL 38 http_public_host_url/2, % +Request, -HostURL 39 http_public_host/4, % +Request, -Host, -Port, +Options 40 % deprecated 41 http_current_host/4 % +Request, -Host, -Port, +Options 42 ]). 43:- use_module(library(http/thread_httpd)). 44:- use_module(library(http/http_wrapper)). 45:- use_module(library(socket)). 46:- use_module(library(option)). 47:- use_module(library(settings)). 48 49:- setting(http:public_host, atom, '', 50 'Name the outside world can use to contact me'). 51:- setting(http:public_port, integer, 80, 52 'Port on the public server'). 53:- setting(http:public_scheme, oneof([http,https]), http, 54 'Default URL scheme to use'). 55 56:- predicate_options(http_public_host/4, 4, [global(boolean)]). 57:- predicate_options(http_current_host/4, 4, 58 [pass_to(http_public_host/4, 4)]). 59 60/** <module> Obtain public server location 61 62This library finds the public address of the running server. This can be 63used to construct URLs that are visible from anywhere on the internet. 64This module was introduced to deal with OpenID, where a request is 65redirected to the OpenID server, which in turn redirects to our server 66(see http_openid.pl). 67 68The address is established from the settings `http:public_host` and 69`http:public_port` if provided. Otherwise it is deduced from the request. 70*/ 71 72%! http_public_url(+Request, -URL) is det. 73% 74% True when URL is an absolute URL for the current request. 75% Typically, the login page should redirect to this URL to avoid 76% losing the session. 77 78http_public_url(Request, URL) :- 79 http_public_host_url(Request, HostURL), 80 option(request_uri(RequestURI), Request), 81 atomic_list_concat([HostURL, RequestURI], URL). 82 83 84%! http_public_host_url(+Request, -URL) is det. 85% 86% True when URL is the public URL at which this server can be 87% contacted. This value is not easy to obtain. See 88% http_public_host/4 for the hardest part: find the host and 89% port. 90 91http_public_host_url(Request, URL) :- 92 http_public_host(Request, Host, Port, 93 [ global(true) 94 ]), 95 setting(http:public_scheme, Scheme), 96 ( scheme_port(Scheme, Port) 97 -> format(atom(URL), '~w://~w', [Scheme, Host]) 98 ; format(atom(URL), '~w://~w:~w', [Scheme, Host, Port]) 99 ). 100 101scheme_port(http, 80). 102scheme_port(https, 443). 103 104%! http_public_host(?Request, -Hostname, -Port, +Options) is det. 105% 106% Current global host and port of the HTTP server. This is the 107% basis to form absolute address, which we need for redirection 108% based interaction such as the OpenID protocol. Options are: 109% 110% * global(+Bool) 111% If =true= (default =false=), try to replace a local hostname 112% by a world-wide accessible name. 113% 114% This predicate performs the following steps to find the host and 115% port: 116% 117% 1. Use the settings =http:public_host= and =http:public_port= 118% 2. Use =X-Forwarded-Host= header, which applies if this server 119% runs behind a proxy. 120% 3. Use the =Host= header, which applies for HTTP 1.1 if we are 121% contacted directly. 122% 4. Use gethostname/1 to find the host and 123% http_current_server/2 to find the port. 124% 125% @param Request is the current request. If it is left unbound, 126% and the request is needed, it is obtained with 127% http_current_request/1. 128 129http_public_host(_Request, Host, Port, _) :- 130 setting(http:public_host, PublicHost), PublicHost \== '', 131 !, 132 Host = PublicHost, 133 setting(http:public_port, Port). 134http_public_host(Request, Host, Port, Options) :- 135 ( var(Request) 136 -> http_current_request(Request) 137 ; true 138 ), 139 ( memberchk(x_forwarded_host(Forwarded), Request) 140 -> Port = 80, 141 primary_forwarded_host(Forwarded, Host) 142 ; memberchk(host(Host0), Request), 143 ( option(global(true), Options, false) 144 -> global_host(Host0, Host) 145 ; Host = Host0 146 ), 147 option(port(Port), Request, 80) 148 ), 149 !. 150http_public_host(_Request, Host, Port, _Options) :- 151 gethostname(Host), 152 http_current_server(_:_Pred, Port). 153 154%! http_current_host(?Request, -Hostname, -Port, +Options) is det. 155% 156% @deprecated Use http_public_host/4 (same semantics) 157 158http_current_host(Request, Hostname, Port, Options) :- 159 http_public_host(Request, Hostname, Port, Options). 160 161%! primary_forwarded_host(+Spec, -Host) is det. 162% 163% x_forwarded host contains multiple hosts seperated by ', ' if 164% there are multiple proxy servers in between. The first one is 165% the one the user's browser knows about. 166 167primary_forwarded_host(Spec, Host) :- 168 sub_atom(Spec, B, _, _, ','), 169 !, 170 sub_atom(Spec, 0, B, _, Host). 171primary_forwarded_host(Host, Host). 172 173 174%! global_host(+HostIn, -Host) 175% 176% Globalize a hostname. Used if we need to pass our hostname to a 177% client and expect the client to be able to contact us. In this 178% case we cannot use a name such as `localhost' or the plain 179% hostname of the machine. We assume (possibly wrongly) that if 180% the host contains a '.', it is globally accessible. 181% 182% If the heuristics used by this predicate do not suffice, the 183% setting http:public_host can be used to override. 184 185global_host(_, Host) :- 186 setting(http:public_host, PublicHost), PublicHost \== '', 187 !, 188 Host = PublicHost. 189global_host(localhost, Host) :- 190 !, 191 gethostname(Host). 192global_host(Local, Host) :- 193 sub_atom(Local, _, _, _, '.'), 194 !, 195 Host = Local. 196global_host(Local, Host) :- 197 tcp_host_to_address(Local, IP), 198 tcp_host_to_address(Host, IP)