________________________________________________________________________

This file is part of Logtalk <https://logtalk.org/>  
SPDX-FileCopyrightText: 1998-2026 Paulo Moura <pmoura@logtalk.org> and
Jacinto Dávila <jdavila@optimusprime.ai>  
SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
________________________________________________________________________


`http_server`
=============

The `http_server` library provides the user-facing facade for starting and
stopping HTTP and HTTPS servers. It selects a default transport from the
`scheme/1` option while still allowing explicit transport selection when an
application needs lower-level control.

By default, `scheme(http)` uses `http_socket_transport` and `scheme(https)`
uses `http_process_transport` (selecting a TLS listener). Incompatible scheme,
transport, and listener transport combinations are rejected with a
`consistency_error/3` exception.


API documentation
-----------------

Open the [../../apis/library_index.html#http_server](../../apis/library_index.html#http_server)
link in a web browser.


Loading
-------

To load all entities in this library, load the `loader.lgt` file:

	| ?- logtalk_load(http_server(loader)).


Testing
-------

To test this library predicates, load the `tester.lgt` file:

	| ?- logtalk_load(http_server(tester)).


Bounded serving
---------------

To serve a given number of connections, use the `serve/4` predicate. For
example:

    | ?- http_server::serve(8080, my_handler, 10, [scheme(http)]).

To inspect or reuse a listener, open it explicitly and close it after serving:

    | ?- http_server::open('127.0.0.1', Port, Server, []),
         http_server::serve(Server, my_handler, 1, ClientInfos),
         http_server::close(Server).


Open-ended serving
------------------

To serve any number of connections until shutdown, use the `serve_until_shutdown/5`
predicate. For example:

    | ?- http_server::serve_until_shutdown('127.0.0.1', 8080, my_handler, control, []).
    ...

    | ?- http_server::request_shutdown(control).
    ...

When the Prolog backend supports threads, you can also use the
`start/4-5` and `stop/1` predicates. For example:

    | ?- http_server::start(8080, my_handler, Server, []),
         http_server::stop(Server).


HTTPS
-----

For HTTPS, the server must have TLS credentials. The facade selects
`http_process_transport` and a TLS listener when `scheme(https)` is used, but
the transport still needs either explicit certificate and key files or a
request to create temporary credentials.

Use `tls_certificate_file/1` and `tls_key_file/1` for real deployments:

    | ?- http_server::serve(
            '127.0.0.1', 8443, my_handler, 10, _ClientInfos,
            [
                scheme(https),
                tls_certificate_file('/path/to/cert.pem'),
                tls_key_file('/path/to/key.pem')
            ]
         ).

Use `temporary_tls_credentials/1` for local examples and tests. Its argument is
a file name prefix used for temporary certificate and key files. The temporary
files are created by the process transport and removed when the listener is
closed:

    | ?- http_server::serve(
            '127.0.0.1', 8443, my_handler, 10, _ClientInfos,
            [scheme(https), temporary_tls_credentials('logtalk_http_server_')]
         ).

If `scheme(https)` is used without either `temporary_tls_credentials/1` or both
`tls_certificate_file/1` and `tls_key_file/1`, the process transport rejects the
listener options.


Handling server requests
------------------------

The handler argument in the examples above, `my_handler`, is the object that
implements `http_handler_protocol` and receives each accepted HTTP request,
building the corresponding HTTP response.

For ordinary HTTP requests, the handler is expected to provide a `handle/2`
predicate compatible with the `http_server_core` request/response pipeline.
For example, a minimal handler can pattern-match the request and return a
plain text response:

    :- object(my_handler,
        implements(http_handler_protocol)).

        :- uses(http_core, [
            response/6
        ]).

        handle(Request, Response) :-
            Request = request(_Method, _Target, Version, _Headers, _Body, _Properties),
            response(
                Version, status(200, 'OK'), [],
                content('text/plain', text('Hello!')), [], Response
            ).

    :- end_object.

When a server accepts a connection, the selected transport reads the request,
calls `my_handler::handle(Request, Response)`, writes the response back to the
client, and then follows the selected serving policy. Parametric objects can be
used when the handler needs configuration, e.g. `static_site_handler(Root)`.


Options
-------

The predicates that open or start a listener accept the following facade
options:

- `scheme(Scheme)` controls the URL scheme served by the listener. Valid values:
    `http` or `https`. Default: `http`.
- `transport(Transport)` selects the transport object. Valid values: `default` or an
    object implementing `http_transport_protocol`. Default: `default`, which
    resolves to `http_socket_transport` for `scheme(http)` and to
    `http_process_transport` for `scheme(https)`.
- `listener_options(Options)` passes additional listener options directly to
    the selected transport. Default: `[]`.
- `serve_options(Options)` passes additional serving options directly to the
    selected transport. Default: `[]`.
- `control(Control)` names the shutdown control used by `start/4-5` and
    open-ended serving. Valid values: any non-variable term, or `default` to create a
    fresh internal control term. Default: `default`.

For convenience, selected listener and serving options can also be written
directly in the facade option list instead of wrapping them in
`listener_options/1` or `serve_options/1`:

- Listener options: `backlog/1`, `type/1`, `listener_transport/1`,
    `listener_helper_executable/1`, `temporary_tls_credentials/1`,
    `tls_certificate_file/1`, and `tls_key_file/1`.
- Serving options: `workers/1` and `shutdown/1`.

The direct listener options accepted by the facade have the following values:
`backlog/1` takes a positive integer, `type/1` takes `binary` or `text`,
`listener_transport/1` takes `tcp` or `tls`, `listener_helper_executable/1`
takes `ncat` or `socat`, and the TLS credential options take file names or a
temporary file name prefix as atoms.

The direct serving options accepted by the facade are `workers(serial)`,
`workers(per_connection)`, `workers(pool(Size))`,
`workers(pool(Size, rolling))`, `shutdown(keep_open)`, and `shutdown(close)`.

These direct options are pass-through options. They are checked by the facade
for basic shape, but the selected transport still decides whether a particular
option is supported and what its transport-specific default is. For example,
`listener_transport(tls)`, `listener_helper_executable/1`, and the TLS
credential options are options for the process transport. With `scheme(https)`,
the facade adds `listener_transport(tls)` automatically unless another listener
transport is explicitly specified.

When the process transport is selected, these listener defaults apply:
`backlog(5)`, `type(binary)`, `listener_transport(tcp)`, and
`listener_helper_executable(ncat)`. The facade changes the effective listener
transport to `tls` for `scheme(https)`. The TLS credential options have no
default usable certificate: `temporary_tls_credentials/1` must be requested
explicitly, or both `tls_certificate_file/1` and `tls_key_file/1` must be
provided when opening a TLS listener.

Common serving defaults are inherited from the transports: `workers(serial)`
and `shutdown(keep_open)`. The convenience predicates that open and close a
listener around a bounded request loop add `shutdown(close)` while serving.
