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-2020, 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_path, 37 [ http_absolute_location/3, % +Spec, -Path, +Options 38 http_clean_location_cache/0 39 ]). 40:- if(exists_source(library(http/http_host))). 41:- autoload(library(http/http_host),[http_current_host/4]). 42:- export(http_absolute_uri/2). % +Spec, -URI 43:- endif. 44:- autoload(library(apply),[exclude/3]). 45:- autoload(library(broadcast),[listen/2]). 46:- autoload(library(debug),[debug/3]). 47:- autoload(library(error), 48 [must_be/2,existence_error/2,instantiation_error/1]). 49:- autoload(library(lists),[reverse/2,append/3]). 50:- autoload(library(option),[option/3]). 51:- autoload(library(pairs),[pairs_values/2]). 52:- autoload(library(uri), 53 [ uri_authority_data/3, uri_authority_components/2, 54 uri_data/3, uri_components/2, uri_normalized/3 55 ]). 56:- use_module(library(settings),[setting/4,setting/2]). 57 58:- predicate_options(http_absolute_location/3, 3, [relative_to(atom)]).
103:- setting(http:prefix, atom, '',
104 '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'
.
124:- multifile 125 http:location/3. % Alias, Expansion, Options 126:- dynamic 127 http:location/3. % Alias, Expansion, Options 128 129httplocation(root, Root, [priority(-100)]) :- 130 ( setting(http:prefix, Prefix), 131 Prefix \== '' 132 -> Root = Prefix 133 ; Root = (/) 134 ). 135 136:- if(current_predicate(http_current_host/4)).
http://
) URI for
the abstract specification Spec. Use http_absolute_location/3 to
create references to locations on the same server.
145http_absolute_uri(Spec, URI) :- 146 http_current_host(_Request, Host, Port, 147 [ global(true) 148 ]), 149 http_absolute_location(Spec, Path, []), 150 uri_authority_data(host, AuthC, Host), 151 ( Port == 80 % HTTP scheme 152 -> true 153 ; uri_authority_data(port, AuthC, Port) 154 ), 155 uri_authority_components(Authority, AuthC), 156 uri_data(path, Components, Path), 157 uri_data(scheme, Components, http), 158 uri_data(authority, Components, Authority), 159 uri_components(URI, Components). 160:- endif.
175:- dynamic 176 location_cache/3. 177 178http_absolute_location(Spec, Path, Options) :- 179 must_be(ground, Spec), 180 option(relative_to(Base), Options, /), 181 absolute_location(Spec, Base, Path, Options), 182 debug(http_path, '~q (~q) --> ~q', [Spec, Base, Path]). 183 184absolute_location(Spec, Base, Path, _Options) :- 185 location_cache(Spec, Base, Cache), 186 !, 187 Path = Cache. 188absolute_location(Spec, Base, Path, Options) :- 189 expand_location(Spec, Base, L, Options), 190 assert(location_cache(Spec, Base, L)), 191 Path = L. 192 193expand_location(Spec, Base, Path, _Options) :- 194 atomic(Spec), 195 !, 196 ( uri_components(Spec, Components), 197 uri_data(scheme, Components, Scheme), 198 atom(Scheme) 199 -> Path = Spec 200 ; relative_to(Base, Spec, Path) 201 ). 202expand_location(Spec, _Base, Path, Options) :- 203 Spec =.. [Alias, Sub], 204 http_location_path(Alias, Parent), 205 absolute_location(Parent, /, ParentLocation, Options), 206 phrase(path_list(Sub), List), 207 atomic_list_concat(List, /, SubAtom), 208 ( ParentLocation == '' 209 -> Path = SubAtom 210 ; sub_atom(ParentLocation, _, _, 0, /) 211 -> atom_concat(ParentLocation, SubAtom, Path) 212 ; atomic_list_concat([ParentLocation, SubAtom], /, Path) 213 ).
225http_location_path(Alias, Path) :-
226 findall(P-L, http_location_path(Alias, L, P), Pairs),
227 sort(Pairs, Sorted0),
228 reverse(Sorted0, Result),
229 ( Result = [_-One]
230 -> Path = One
231 ; Result == []
232 -> existence_error(http_alias, Alias)
233 ; Result = [P-Best,P2-_|_],
234 P \== P2
235 -> Path = Best
236 ; Result = [_-First|_],
237 pairs_values(Result, Paths),
238 print_message(warning, http(ambiguous_location(Alias, Paths))),
239 Path = First
240 ).
247http_location_path(Alias, Path, Priority) :- 248 http:location(Alias, Path, Options), 249 option(priority(Priority), Options, 0). 250http_location_path(prefix, Path, 0) :- 251 ( catch(setting(http:prefix, Prefix), _, fail), 252 Prefix \== '' 253 -> ( sub_atom(Prefix, 0, _, _, /) 254 -> Path = Prefix 255 ; atom_concat(/, Prefix, Path) 256 ) 257 ; Path = / 258 ).
266relative_to(/, Path, Path) :- !. 267relative_to(_Base, Path, Path) :- 268 sub_atom(Path, 0, _, _, /), 269 !. 270relative_to(Base, Local, Path) :- 271 sub_atom(Base, 0, _, _, /), % file version 272 !, 273 path_segments(Base, BaseSegments), 274 append(BaseDir, [_], BaseSegments) -> 275 path_segments(Local, LocalSegments), 276 append(BaseDir, LocalSegments, Segments0), 277 clean_segments(Segments0, Segments), 278 path_segments(Path, Segments). 279relative_to(Base, Local, Global) :- 280 uri_normalized(Local, Base, Global). 281 282path_segments(Path, Segments) :- 283 atomic_list_concat(Segments, /, Path).
290clean_segments([''|T0], [''|T]) :- 291 !, 292 exclude(empty_segment, T0, T1), 293 clean_parent_segments(T1, T). 294clean_segments(T0, T) :- 295 exclude(empty_segment, T0, T1), 296 clean_parent_segments(T1, T). 297 298clean_parent_segments([], []). 299clean_parent_segments([..|T0], T) :- 300 !, 301 clean_parent_segments(T0, T). 302clean_parent_segments([_,..|T0], T) :- 303 !, 304 clean_parent_segments(T0, T). 305clean_parent_segments([H|T0], [H|T]) :- 306 clean_parent_segments(T0, T). 307 308empty_segment(''). 309empty_segment('.').
319path_list(Var) --> 320 { var(Var), 321 !, 322 instantiation_error(Var) 323 }. 324path_list(A/B) --> 325 !, 326 path_list(A), 327 path_list(B). 328path_list(.) --> 329 !, 330 []. 331path_list(A) --> 332 { must_be(atomic, A) }, 333 [A]. 334 335 336 /******************************* 337 * MESSAGES * 338 *******************************/ 339 340:- multifile 341 prolog:message/3. 342 343prologmessage(http(ambiguous_location(Spec, Paths))) --> 344 [ 'http_absolute_location/2: ambiguous specification: ~q: ~p'- 345 [Spec, Paths] 346 ]. 347 348 349 /******************************* 350 * CACHE CLEANUP * 351 *******************************/
360http_clean_location_cache :- 361 retractall(location_cache(_,_,_)). 362 363:- listen(settings(changed(http:prefix, _, _)), 364 http_clean_location_cache). 365 366:- multifile 367 user:message_hook/3. 368:- dynamic 369 user:message_hook/3. 370 371user:message_hook(make(done(Reload)), _Level, _Lines) :- 372 Reload \== [], 373 http_clean_location_cache, 374 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)]
.*/