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) 2008-2022, University of Amsterdam 7 VU University Amsterdam 8 SWI-Prolog Solutions b.v. 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(http_path, 38 [ http_absolute_location/3, % +Spec, -Path, +Options 39 http_clean_location_cache/0 40 ]). 41:- if(exists_source(library(http/http_host))). 42:- autoload(library(http/http_host),[http_public_host/4]). 43:- export(http_absolute_uri/2). % +Spec, -URI 44:- endif. 45:- autoload(library(apply),[exclude/3]). 46:- autoload(library(broadcast),[listen/2]). 47:- autoload(library(debug),[debug/3]). 48:- autoload(library(error), 49 [must_be/2,existence_error/2,instantiation_error/1]). 50:- autoload(library(lists),[reverse/2,append/3]). 51:- autoload(library(option),[option/3]). 52:- autoload(library(pairs),[pairs_values/2]). 53:- autoload(library(uri), 54 [ uri_authority_data/3, uri_authority_components/2, 55 uri_data/3, uri_components/2, uri_normalized/3 56 ]). 57:- use_module(library(settings),[setting/4,setting/2]). 58 59:- predicate_options(http_absolute_location/3, 3, [relative_to(atom)]).
104:- setting(http:prefix, atom, '',
105 'Prefix for all locations of this server').
/
. Options currently only supports the
priority of the path. If http:location/3 returns multiple
solutions the one with the highest priority is selected. The
default priority is 0.
This library provides a default for the abstract location
root
. This defaults to the setting http:prefix or, when not
available to the path /
. It is adviced to define all
locations (ultimately) relative to root
. For example, use
root('home.html')
rather than '/home.html'
.
125:- multifile 126 http:location/3. % Alias, Expansion, Options 127:- dynamic 128 http:location/3. % Alias, Expansion, Options 129 130httplocation(root, Root, [priority(-100)]) :- 131 ( setting(http:prefix, Prefix), 132 Prefix \== '' 133 -> Root = Prefix 134 ; Root = (/) 135 ). 136 137:- if(current_predicate(http_public_host/4)).
http://
) URI for
the abstract specification Spec. Use http_absolute_location/3 to
create references to locations on the same server.144http_absolute_uri(Spec, URI) :- 145 http_public_host(_Request, Host, Port, 146 [ global(true) 147 ]), 148 ( setting(http:public_scheme, Scheme) 149 -> true 150 ; default_port(Scheme, Port) 151 -> true 152 ; Scheme = http 153 ), 154 http_absolute_location(Spec, Path, []), 155 uri_authority_data(host, AuthC, Host), 156 ( default_port(Scheme, Port) 157 -> true 158 ; uri_authority_data(port, AuthC, Port) 159 ), 160 uri_authority_components(Authority, AuthC), 161 uri_data(path, Components, Path), 162 uri_data(scheme, Components, Scheme), 163 uri_data(authority, Components, Authority), 164 uri_components(URI, Components). 165 166default_port(http, 80). 167default_port(https, 443). 168 169:- endif.
184:- dynamic 185 location_cache/3. 186 187http_absolute_location(Spec, Path, Options) :- 188 must_be(ground, Spec), 189 option(relative_to(Base), Options, /), 190 absolute_location(Spec, Base, Path, Options), 191 debug(http_path, '~q (~q) --> ~q', [Spec, Base, Path]). 192 193absolute_location(Spec, Base, Path, _Options) :- 194 location_cache(Spec, Base, Cache), 195 !, 196 Path = Cache. 197absolute_location(Spec, Base, Path, Options) :- 198 expand_location(Spec, Base, L, Options), 199 assert(location_cache(Spec, Base, L)), 200 Path = L. 201 202expand_location(Spec, Base, Path, _Options) :- 203 atomic(Spec), 204 !, 205 ( uri_components(Spec, Components), 206 uri_data(scheme, Components, Scheme), 207 atom(Scheme) 208 -> Path = Spec 209 ; relative_to(Base, Spec, Path) 210 ). 211expand_location(Spec, _Base, Path, Options) :- 212 Spec =.. [Alias, Sub], 213 http_location_path(Alias, Parent), 214 absolute_location(Parent, /, ParentLocation, Options), 215 phrase(path_list(Sub), List), 216 atomic_list_concat(List, /, SubAtom), 217 ( ParentLocation == '' 218 -> Path = SubAtom 219 ; sub_atom(ParentLocation, _, _, 0, /) 220 -> atom_concat(ParentLocation, SubAtom, Path) 221 ; atomic_list_concat([ParentLocation, SubAtom], /, Path) 222 ).
234http_location_path(Alias, Path) :-
235 findall(P-L, http_location_path(Alias, L, P), Pairs),
236 sort(Pairs, Sorted0),
237 reverse(Sorted0, Result),
238 ( Result = [_-One]
239 -> Path = One
240 ; Result == []
241 -> existence_error(http_alias, Alias)
242 ; Result = [P-Best,P2-_|_],
243 P \== P2
244 -> Path = Best
245 ; Result = [_-First|_],
246 pairs_values(Result, Paths),
247 print_message(warning, http(ambiguous_location(Alias, Paths))),
248 Path = First
249 ).
256http_location_path(Alias, Path, Priority) :- 257 http:location(Alias, Path, Options), 258 option(priority(Priority), Options, 0). 259http_location_path(prefix, Path, 0) :- 260 ( catch(setting(http:prefix, Prefix), _, fail), 261 Prefix \== '' 262 -> ( sub_atom(Prefix, 0, _, _, /) 263 -> Path = Prefix 264 ; atom_concat(/, Prefix, Path) 265 ) 266 ; Path = / 267 ).
275relative_to(/, Path, Path) :- !. 276relative_to(_Base, Path, Path) :- 277 sub_atom(Path, 0, _, _, /), 278 !. 279relative_to(Base, Local, Path) :- 280 sub_atom(Base, 0, _, _, /), % file version 281 !, 282 path_segments(Base, BaseSegments), 283 append(BaseDir, [_], BaseSegments) -> 284 path_segments(Local, LocalSegments), 285 append(BaseDir, LocalSegments, Segments0), 286 clean_segments(Segments0, Segments), 287 path_segments(Path, Segments). 288relative_to(Base, Local, Global) :- 289 uri_normalized(Local, Base, Global). 290 291path_segments(Path, Segments) :- 292 atomic_list_concat(Segments, /, Path).
299clean_segments([''|T0], [''|T]) :- 300 !, 301 exclude(empty_segment, T0, T1), 302 clean_parent_segments(T1, T). 303clean_segments(T0, T) :- 304 exclude(empty_segment, T0, T1), 305 clean_parent_segments(T1, T). 306 307clean_parent_segments([], []). 308clean_parent_segments([..|T0], T) :- 309 !, 310 clean_parent_segments(T0, T). 311clean_parent_segments([_,..|T0], T) :- 312 !, 313 clean_parent_segments(T0, T). 314clean_parent_segments([H|T0], [H|T]) :- 315 clean_parent_segments(T0, T). 316 317empty_segment(''). 318empty_segment('.').
328path_list(Var) --> 329 { var(Var), 330 !, 331 instantiation_error(Var) 332 }. 333path_list(A/B) --> 334 !, 335 path_list(A), 336 path_list(B). 337path_list(.) --> 338 !, 339 []. 340path_list(A) --> 341 { must_be(atomic, A) }, 342 [A]. 343 344 345 /******************************* 346 * MESSAGES * 347 *******************************/ 348 349:- multifile 350 prolog:message/3. 351 352prologmessage(http(ambiguous_location(Spec, Paths))) --> 353 [ 'http_absolute_location/2: ambiguous specification: ~q: ~p'- 354 [Spec, Paths] 355 ]. 356 357 358 /******************************* 359 * CACHE CLEANUP * 360 *******************************/
369http_clean_location_cache :- 370 retractall(location_cache(_,_,_)). 371 372:- listen(settings(changed(http:prefix, _, _)), 373 http_clean_location_cache). 374 375:- multifile 376 user:message_hook/3. 377:- dynamic 378 user:message_hook/3. 379 380user:message_hook(make(done(Reload)), _Level, _Lines) :- 381 Reload \== [], 382 http_clean_location_cache, 383 fail
Abstract specification of HTTP server locations
This module provides an abstract specification of HTTP server locations that is inspired on absolute_file_name/3. The specification is done by adding rules to the dynamic multifile predicate http:location/3. The speficiation is very similar to file_search_path/2, but takes an additional argument with options. Currently only one option is defined:
The default priority is 0. Note however that notably libraries may decide to provide a fall-back using a negative priority. We suggest -100 for such cases.
This library predefines a single location at priority -100:
http:prefix
To serve additional resource files such as CSS, JavaScript and icons, see
library(http/http_server_files)
.Here is an example that binds
/login
to login/1. The user can reuse this application while moving all locations using a new rule for the admin location with the option[priority(10)]
.*/