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