Scryer Prolog

Module http_server

:- use_module(library(http/http_server)).

This library provides an starting point to build HTTP server based applications. It is based on Warp, which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However, some advanced features that Warp provides are still not accesible.

Usage

The main predicate of the library is http_listen/2, which needs a port number (usually 80) and a list of handlers. A handler is a compound term with the functor as one HTTP method (in lowercase) and followed by a Route Match and a predicate which will handle the call.


text_handler(Request, Response) :-
  http_status_code(Response, 200),
  http_body(Response, text("Welcome to Scryer Prolog!")).

parameter_handler(User, Request, Response) :-
  http_body(Response, text(User)).

http_listen(7890, [
  get(echo, text_handler),                 % GET /echo
  post(user/User, parameter_handler(User)) % POST /user/<User>
]).

Every handler predicate will have at least 2-arity, with Request and Response. Although you can work directly with http_request and http_response terms, it is recommeded to use the helper predicates, which are easier to understand and cleaner:

  • http_headers(Response/Request, Headers)

  • http_status_code(Responde, StatusCode)

  • http_body(Response/Request, text(Body))

  • http_body(Response/Request, binary(Body))

  • http_body(Request, form(Form))

  • http_body(Response, file(Filename))

  • http_redirect(Response, Url)

  • http_query(Request, QueryName, QueryValue)

Some things that are still missing:

  • Read forms in multipart format
  • Session handling via cookies
  • HTML Templating (but you can use Teruel, Marquete or Djota for that)

http_listen(+Port, +Handlers).

Equivalent to http_listen(Port, Handlers, []).

http_listen(+Port, +Handlers, +Options).

Listens for HTTP connections on port Port. Each handler on the list Handlers should be of the form: HttpVerb(PathUnification, Predicate). For example: get(user/User, get_info(User)) will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable) and it will call get_info with three arguments: an http_request term, an http_response term and User.

The following options are supported:

  • tls_key(+Key) - a TLS key for HTTPS (string)

  • tls_cert(+Cert) - a TLS cert for HTTPS (string)

  • content_length_limit(+Limit) - maximum length (in bytes) for the incoming bodies. By default, 32KB.

In order to have a HTTPS server (instead of plain HTTP), both tls_key and tls_cert options must be provided.

http_headers(?Request_Response, ?Headers).

True iff Request_Response is a request or response with headers Headers. Can be used both to get headers (usually in from a request) and to add headers (usually in a response).

http_body(?Request_Response, ?Body).

True iff Body is the body of the request or response. A body can be of the following types:

  • bytes(Bytes) for both requests and responses, interprets the body as bytes
  • text(Bytes) for both requests and responses, interprets the body as text
  • form(Form) only for requests, interprets the body as an application/x-www-form-urlencoded form.
  • file(File) only for responses, interprets the body as the content of a file (useful to send static files).

http_status_code(?Response, ?StatusCode).

True iff the status code of the response Response unifies with StatusCode.

http_redirect(-Response, +Uri).

True iff Response is a response that redirects the user to the uri Uri.

http_query(+Request, ?Key, ?Value).

True iff there's a query in request Request with key Key and value Value.

http_basic_auth(+LoginPredicate, +Handler, +Request, -Response)

Metapredicate that wraps an existing Handler with an HTTP Basic Auth flow. Checks if a given user + password is authorized to execute that handler, returning 401 if it's not satisfied.

LoginPredicate must be a predicate of arity 2 that takes a User and a Password. Handler will have, in addition to the Request and Response arguments, a User argument containing the User given in the authentication.

Example:


main :-
   http_listen(8800,[get('/', http_basic_auth(login, inside_handler("data")))]).

login(User, Pass) :-
   User = "aarroyoc",
   Pass = "123456".

inside_handler(Data, User, Request, Response) :-
   http_body(Response, text(User)).