View source with formatted comments or as raw
    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)  2011-2020, VU University Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(http_files,
   36          [ http_reply_from_files/3     % +Dir, +Options, +Request
   37          ]).   38:- use_module(library(http/http_dispatch)).   39:- use_module(library(http/http_dirindex)).   40:- use_module(library(filesex)).   41:- use_module(library(lists)).   42:- use_module(library(option)).   43
   44:- predicate_options(http_reply_from_files/3, 2, [indexes(list(atom))]).   45
   46/** <module> Serve plain files from a hierarchy
   47
   48Although the SWI-Prolog Web Server is   intended to serve documents that
   49are computed dynamically, serving plain   files  is sometimes necessary.
   50This small module combines the   functionality  of http_reply_file/3 and
   51http_reply_dirindex/3 to act as a simple   web-server. Such a server can
   52be created using the following code  sample,   which  starts a server at
   53port 8080 that serves files from the  current directory ('.'). Note that
   54the handler needs a `prefix` option to   specify that it must handle all
   55paths that begin with the registed location of the handler.
   56
   57```
   58:- use_module(library(http/http_server)).
   59:- use_module(library(http/http_files)).
   60
   61:- http_handler(root(.), http_reply_from_files('.', []), [prefix]).
   62
   63:- initialization(http_server([port(8080)]), main).
   64```
   65
   66@see    pwp_handler/2 provides similar facilities, where .pwp files
   67        can be used to add dynamic behaviour.
   68*/
   69
   70
   71%!  http_reply_from_files(+Dir, +Options, +Request)
   72%
   73%   HTTP handler that serves files  from   the  directory  Dir. This
   74%   handler uses http_reply_file/3 to  reply   plain  files.  If the
   75%   request resolves to a directory, it uses the option =indexes= to
   76%   locate an index file (see   below) or uses http_reply_dirindex/3
   77%   to create a listing of the directory.
   78%
   79%   Options:
   80%
   81%     * indexes(+List)
   82%     List of files tried to find an index for a directory.  The
   83%     default is `['index.html']`.
   84%
   85%   Note that this handler must be tagged as a =prefix= handler (see
   86%   http_handler/3 and module introduction). This  also implies that
   87%   it is possible to  override  more   specific  locations  in  the
   88%   hierarchy using http_handler/3 with a longer path-specifier.
   89%
   90%   When  using  http_handler/3  to  bind  this  predicate  to  an  HTTP
   91%   location, make sure it is bound to a   location  that ends in a `/`.
   92%   When  using  http:location/3  to  define   symbolic  names  to  HTTP
   93%   locations this is written as
   94%
   95%      :- http_handler(aliasname(.),
   96%                      http_reply_from_files(srcdir, []),
   97%                      [prefix]).
   98%
   99%   @param  Dir is either a directory or an path-specification as
  100%           used by absolute_file_name/3.  This option provides
  101%           great flexibility in (re-)locating the physical files
  102%           and allows merging the files of multiple physical
  103%           locations into one web-hierarchy by using multiple
  104%           user:file_search_path/2 clauses that define the same
  105%           alias.
  106%   @see    The hookable predicate file_mime_type/2 is used to
  107%           determine the ``Content-type`` from the file name.
  108
  109http_reply_from_files(Dir, Options, Request) :-
  110    (   memberchk(path_info(PathInfo), Request)
  111    ->  true
  112    ;   PathInfo = ''
  113    ),
  114    http_safe_file(PathInfo, []),
  115    locate_file(Dir, PathInfo, Result, ResultType, Options),
  116    !,
  117    reply(ResultType, Result, Options, Request).
  118
  119reply(file, Path, Options, Request) :-
  120    http_reply_file(Path, [unsafe(true)|Options], Request).
  121reply(index, Path, Options, Request) :-
  122    http_reply_dirindex(Path, [unsafe(true)|Options], Request).
  123reply(redirect, _, _, Request) :-
  124    memberchk(path(Path), Request),
  125    atom_concat(Path, /, NewLocation),
  126    http_redirect(moved_temporary, NewLocation, Request).
  127
  128locate_file(Dir, PathInfo, Result, ResultType, Options) :-
  129    absolute_file_name(Dir, DirPath,
  130                       [ file_type(directory),
  131                         access(read),
  132                         solutions(all)
  133                       ]),
  134    directory_file_path(DirPath, PathInfo, Path),
  135    (   exists_file(Path)
  136    ->  ResultType = file,
  137        Result = Path
  138    ;   exists_directory(Path),
  139        (   sub_atom(Path, _, _, 0, /)
  140        ->  (   option(indexes(Indexes), Options, ['index.html']),
  141                member(Index, Indexes),
  142                directory_file_path(Path, Index, IndexFile),
  143                exists_file(IndexFile)
  144            ->  Result = IndexFile,
  145                ResultType = file
  146            ;   Result = Path,
  147                ResultType = index
  148            )
  149        ;   ResultType = redirect
  150        )
  151    )