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,
   45                     [ indexes(list(atom)),
   46                       pass_to(http_dispatch:http_reply_file/3, 2),
   47                       pass_to(http_dirindex:http_reply_dirindex/3, 2)
   48                     ]).   49
   50/** <module> Serve plain files from a hierarchy
   51
   52Although the SWI-Prolog Web Server is   intended to serve documents that
   53are computed dynamically, serving plain   files  is sometimes necessary.
   54This small module combines the   functionality  of http_reply_file/3 and
   55http_reply_dirindex/3 to act as a simple   web-server. Such a server can
   56be created using the following code  sample,   which  starts a server at
   57port 8080 that serves files from the  current directory ('.'). Note that
   58the handler needs a `prefix` option to   specify that it must handle all
   59paths that begin with the registed location of the handler.
   60
   61```
   62:- use_module(library(http/http_server)).
   63:- use_module(library(http/http_files)).
   64
   65:- http_handler(root(.), http_reply_from_files('.', []), [prefix]).
   66
   67:- initialization(http_server([port(8080)]), main).
   68```
   69
   70@see    pwp_handler/2 provides similar facilities, where .pwp files
   71        can be used to add dynamic behaviour.
   72*/
   73
   74
   75%!  http_reply_from_files(+Dir, +Options, +Request)
   76%
   77%   HTTP handler that serves files  from   the  directory  Dir. This
   78%   handler uses http_reply_file/3 to  reply   plain  files.  If the
   79%   request resolves to a directory, it uses the option =indexes= to
   80%   locate an index file (see   below) or uses http_reply_dirindex/3
   81%   to create a listing of the directory.
   82%
   83%   Options:
   84%
   85%     * indexes(+List)
   86%     List of files tried to find an index for a directory.  The
   87%     default is `['index.html']`.
   88%
   89%   Note that this handler must be tagged as a =prefix= handler (see
   90%   http_handler/3 and module introduction). This  also implies that
   91%   it is possible to  override  more   specific  locations  in  the
   92%   hierarchy using http_handler/3 with a longer path-specifier.
   93%
   94%   When  using  http_handler/3  to  bind  this  predicate  to  an  HTTP
   95%   location, make sure it is bound to a   location  that ends in a `/`.
   96%   When  using  http:location/3  to  define   symbolic  names  to  HTTP
   97%   locations this is written as
   98%
   99%      :- http_handler(aliasname(.),
  100%                      http_reply_from_files(srcdir, []),
  101%                      [prefix]).
  102%
  103%   @param  Dir is either a directory or an path-specification as
  104%           used by absolute_file_name/3.  This option provides
  105%           great flexibility in (re-)locating the physical files
  106%           and allows merging the files of multiple physical
  107%           locations into one web-hierarchy by using multiple
  108%           user:file_search_path/2 clauses that define the same
  109%           alias.
  110%   @see    The hookable predicate file_mime_type/2 is used to
  111%           determine the ``Content-type`` from the file name.
  112
  113http_reply_from_files(Dir, Options, Request) :-
  114    (   memberchk(path_info(PathInfo), Request)
  115    ->  true
  116    ;   PathInfo = ''
  117    ),
  118    http_safe_file(PathInfo, []),
  119    locate_file(Dir, PathInfo, Result, ResultType, Options),
  120    !,
  121    reply(ResultType, Result, Options, Request).
  122
  123reply(file, Path, Options, Request) :-
  124    http_reply_file(Path, [unsafe(true)|Options], Request).
  125reply(index, Path, Options, Request) :-
  126    http_reply_dirindex(Path, [unsafe(true)|Options], Request).
  127reply(redirect, _, _, Request) :-
  128    memberchk(path(Path), Request),
  129    atom_concat(Path, /, NewLocation),
  130    http_redirect(moved_temporary, NewLocation, Request).
  131
  132locate_file(Dir, PathInfo, Result, ResultType, Options) :-
  133    absolute_file_name(Dir, DirPath,
  134                       [ file_type(directory),
  135                         access(read),
  136                         solutions(all)
  137                       ]),
  138    directory_file_path(DirPath, PathInfo, Path),
  139    (   exists_file(Path)
  140    ->  ResultType = file,
  141        Result = Path
  142    ;   exists_directory(Path),
  143        (   sub_atom(Path, _, _, 0, /)
  144        ->  (   option(indexes(Indexes), Options, ['index.html']),
  145                member(Index, Indexes),
  146                directory_file_path(Path, Index, IndexFile),
  147                exists_file(IndexFile)
  148            ->  Result = IndexFile,
  149                ResultType = file
  150            ;   Result = Path,
  151                ResultType = index
  152            )
  153        ;   ResultType = redirect
  154        )
  155    )