Improve docs & Route matching

This commit is contained in:
Adrián Arroyo Calle
2020-12-27 20:14:21 +01:00
parent 5428935ab1
commit 534c74b67c
2 changed files with 83 additions and 6 deletions

View File

@@ -460,6 +460,7 @@ The modules that ship with Scryer Prolog are also called
Probabilistic predicates and random number generators. Probabilistic predicates and random number generators.
* [`http/http_open`](src/lib/http/http_open.pl) Open a stream to * [`http/http_open`](src/lib/http/http_open.pl) Open a stream to
read answers from web servers. HTTPS is also supported. read answers from web servers. HTTPS is also supported.
* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.0 web server.
* [`sgml`](src/lib/sgml.pl) * [`sgml`](src/lib/sgml.pl)
`load_html/3` and `load_xml/3` represent HTML and XML documents `load_html/3` and `load_xml/3` represent HTML and XML documents
as Prolog terms for convenient and efficient reasoning. Use as Prolog terms for convenient and efficient reasoning. Use

View File

@@ -1,4 +1,59 @@
:- module(http_server, [http_listen/2, http_headers/2, http_status_code/2, http_body/2, http_redirect/2, http_query/3]). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written in December 2020 by Adrián Arroyo (adrian.arroyocalle@gmail.com)
Part of Scryer Prolog
This library provides an starting point to build HTTP server based applications.
It currently implements a subset of HTTP/1.0. It is recommended to put a reverse
proxy like nginx in front of this server to have access to more advanced features
(gzip compression, HTTPS, ...)
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 compund 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(Response, file(Filename))
- http_redirect(Response, Url)
- http_query(Request, QueryName, QueryValue)
Some things that are still missing:
- Read forms
- HTTP Basic Auth
- Keep-Alive support
I place this code in the public domain. Use it in any way you want.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(http_server, [
http_listen/2,
http_headers/2,
http_status_code/2,
http_body/2,
http_redirect/2,
http_query/3,
path//1
]).
:- use_module(library(sockets)). :- use_module(library(sockets)).
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
@@ -18,6 +73,8 @@
% - HTML % - HTML
% - Remove ! % - Remove !
% - URL Encode % - URL Encode
% - Forms
% - HTTP Auth
:- dynamic(http_handler/3). :- dynamic(http_handler/3).
@@ -101,10 +158,26 @@ http_redirect(http_response(307, text("Moved Temporarily"), ["Location"-Uri]), U
http_query(http_request(_, _, Queries), Key, Value) :- member(Key-Value, Queries). http_query(http_request(_, _, Queries), Key, Value) :- member(Key-Value, Queries).
path([Part|Pattern]) --> path(Pattern) -->
{
Pattern =.. Parts,
length(Parts, 3),
nth0(1, Parts, Pattern0),
nth0(2, Parts, PartAtom),
(var(PartAtom) -> Part = PartAtom; atom_chars(PartAtom, Part))
},
path(Pattern0),
"/", "/",
string_without("/", Part), string_without("/", Part).
path(Pattern).
path(Pattern) -->
{
Pattern =.. Parts,
Parts = [PartAtom],
(var(PartAtom) -> Part = PartAtom; atom_chars(PartAtom, Part))
},
"/",
string_without("/", Part).
path([]) --> []. path([]) --> [].
@@ -114,8 +187,11 @@ send_response(Stream, http_response(StatusCode0, file(Filename), Headers)) :-
overwrite_header("Connection"-"Close", Headers0, Headers1), overwrite_header("Connection"-"Close", Headers0, Headers1),
write_headers(Stream, Headers1), write_headers(Stream, Headers1),
format(Stream, "\r\n", []), format(Stream, "\r\n", []),
open(Filename, read, FileStream, [type(binary)]), setup_call_cleanup(
pipe_bytes(FileStream, Stream). open(Filename, read, FileStream, [type(binary)]),
pipe_bytes(FileStream, Stream),
close(FileStream)
).
send_response(Stream, http_response(StatusCode0, text(TextResponse), Headers)) :- send_response(Stream, http_response(StatusCode0, text(TextResponse), Headers)) :-
default(StatusCode0, 200, StatusCode), default(StatusCode0, 200, StatusCode),