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%   @param  Dir is either a directory or an path-specification as
   91%           used by absolute_file_name/3.  This option provides
   92%           great flexibility in (re-)locating the physical files
   93%           and allows merging the files of multiple physical
   94%           locations into one web-hierarchy by using multiple
   95%           user:file_search_path/2 clauses that define the same
   96%           alias.
   97%   @see    The hookable predicate file_mime_type/2 is used to
   98%           determine the ``Content-type`` from the file name.
   99
  100http_reply_from_files(Dir, Options, Request) :-
  101    (   memberchk(path_info(PathInfo), Request)
  102    ->  true
  103    ;   PathInfo = ''
  104    ),
  105    http_safe_file(PathInfo, []),
  106    locate_file(Dir, PathInfo, Result, ResultType, Options),
  107    !,
  108    reply(ResultType, Result, Options, Request).
  109
  110reply(file, Path, Options, Request) :-
  111    http_reply_file(Path, [unsafe(true)|Options], Request).
  112reply(index, Path, Options, Request) :-
  113    http_reply_dirindex(Path, [unsafe(true)|Options], Request).
  114reply(redirect, _, _, Request) :-
  115    memberchk(path(Path), Request),
  116    atom_concat(Path, /, NewLocation),
  117    http_redirect(moved_temporary, NewLocation, Request).
  118
  119locate_file(Dir, PathInfo, Result, ResultType, Options) :-
  120    absolute_file_name(Dir, DirPath,
  121                       [ file_type(directory),
  122                         access(read),
  123                         solutions(all)
  124                       ]),
  125    directory_file_path(DirPath, PathInfo, Path),
  126    (   exists_file(Path)
  127    ->  ResultType = file,
  128        Result = Path
  129    ;   exists_directory(Path),
  130        (   sub_atom(Path, _, _, 0, /)
  131        ->  (   option(indexes(Indexes), Options, ['index.html']),
  132                member(Index, Indexes),
  133                directory_file_path(Path, Index, IndexFile),
  134                exists_file(IndexFile)
  135            ->  Result = IndexFile,
  136                ResultType = file
  137            ;   Result = Path,
  138                ResultType = index
  139            )
  140        ;   ResultType = redirect
  141        )
  142    )