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