WIP http_server
This commit is contained in:
98
src/lib/http/http_server.pl
Normal file
98
src/lib/http/http_server.pl
Normal file
@@ -0,0 +1,98 @@
|
||||
:- module(http_server, [http_listen/2, sample_handler/1]).
|
||||
|
||||
:- use_module(library(sockets)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
% TODO
|
||||
% - Parse request Headers
|
||||
% - Output response Headers
|
||||
% - Improve route matching
|
||||
% - Long-running socket_server_accept
|
||||
% - Cookies?
|
||||
% - HTTP Error Codes
|
||||
% - Redirections
|
||||
% - Improve code quality
|
||||
|
||||
http_listen(Port, Handlers) :-
|
||||
must_be(integer, Port),
|
||||
must_be(list, Handlers),
|
||||
once(socket_server_open(Port, Socket)),
|
||||
socket_server_accept(Socket, Client, Stream, [type(text)]),
|
||||
read_header_lines(Stream, Lines),
|
||||
[Request|Headers] = Lines,
|
||||
(
|
||||
phrase(parse_request(Version, Method, Path), Request) -> (
|
||||
call_handler(Path, Headers, Handlers, Response),
|
||||
http_response(StatusCode, Body) = Response,
|
||||
format(Stream, "HTTP/1.0 ~d\r\n\r\n~s", [StatusCode, Body])
|
||||
);(
|
||||
format(Stream, "HTTP/1.0 400 Bad Request\r\n\r\n", []) % bad format
|
||||
)
|
||||
),
|
||||
close(Stream).
|
||||
|
||||
call_handler(Path, Headers, Handlers, Response) :-
|
||||
member(http_route(Dcg, Handler), Handlers),
|
||||
phrase(Dcg, Path),!,
|
||||
call(Handler, Response).
|
||||
|
||||
call_handler(_, _, _, http_response(404, "Not found")).
|
||||
|
||||
parse_request(http_version(Major, Minor), Method, Path) -->
|
||||
method(Method),
|
||||
" ",
|
||||
string_without(" ", Path),
|
||||
" ",
|
||||
"HTTP/",
|
||||
natural(Major),
|
||||
".",
|
||||
natural(Minor),
|
||||
"\r\n".
|
||||
|
||||
method(options) --> "OPTIONS".
|
||||
method(get) --> "GET".
|
||||
method(head) --> "HEAD".
|
||||
method(post) --> "POST".
|
||||
method(put) --> "PUT".
|
||||
method(delete) --> "DELETE".
|
||||
|
||||
string_without(Not, [Char|String]) -->
|
||||
[Char],
|
||||
{
|
||||
\+ member(Char, Not)
|
||||
},
|
||||
string_without(Not, String).
|
||||
|
||||
string_without(_, []) -->
|
||||
[].
|
||||
|
||||
natural(Nat) -->
|
||||
natural_(NatChars),
|
||||
{
|
||||
number_chars(Nat, NatChars)
|
||||
}.
|
||||
|
||||
natural_([Nat|Nats]) -->
|
||||
[Nat],
|
||||
{
|
||||
char_type(Nat, decimal_digit)
|
||||
},
|
||||
natural_(Nats).
|
||||
|
||||
natural_([]) -->
|
||||
[].
|
||||
|
||||
read_header_lines(Stream, Hs) :-
|
||||
read_line_to_chars(Stream, Cs, []),
|
||||
( Cs == "" -> Hs = []
|
||||
; Cs == "\r\n" -> Hs = []
|
||||
; Hs = [Cs|Rest],
|
||||
read_header_lines(Stream, Rest)
|
||||
).
|
||||
|
||||
sample_handler(Response) :-
|
||||
Response = http_response(200, "Hello Prolog friends!").
|
||||
Reference in New Issue
Block a user