From e4d1e4b7a05c46a9eb4d7beb890782ce82d8583c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sat, 12 Dec 2020 21:28:33 +0100 Subject: [PATCH] Request and Response headers --- src/lib/http/http_server.pl | 58 ++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/lib/http/http_server.pl b/src/lib/http/http_server.pl index c1889cff..486483c7 100644 --- a/src/lib/http/http_server.pl +++ b/src/lib/http/http_server.pl @@ -1,4 +1,4 @@ -:- module(http_server, [http_listen/2, sample_handler/1]). +:- module(http_server, [http_listen/2, sample_handler/2, sample_body_handler/2]). :- use_module(library(sockets)). :- use_module(library(dcgs)). @@ -6,39 +6,51 @@ :- use_module(library(error)). :- use_module(library(charsio)). :- use_module(library(lists)). +:- use_module(library(iso_ext)). % TODO -% - Parse request Headers -% - Output response Headers +% - Parse body % - Improve route matching % - Long-running socket_server_accept % - Cookies? % - HTTP Error Codes % - Redirections % - Improve code quality +% - Binary +% - Comments 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)]), + socket_server_accept(Socket, Client, Stream, [type(binary)]), read_header_lines(Stream, Lines), [Request|Headers] = Lines, + % get_bytes(Stream, RequestBody), % Doesn't work ( - 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]) + (phrase(parse_request(Version, Method, Path), Request), maplist(map_parse_header, Headers, HeadersKV)) -> ( + call_handler(Path, HeadersKV, RequestBody, Handlers, Response), + http_response(StatusCode, ResponseBody, RequestHeaders) = Response, + format(Stream, "HTTP/1.0 ~d\r\n", [StatusCode]), + forall(member(RequestHeaderKey-RequestHeaderValue, RequestHeaders), format(Stream, "~s: ~s\r\n", [RequestHeaderKey, RequestHeaderValue])), + format(Stream, "\r\n~s", [ResponseBody]) );( 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), +call_handler(Path, Headers, _, Handlers, Response) :- + member(get(Dcg, Handler), Handlers), phrase(Dcg, Path),!, - call(Handler, Response). + Request = http_request(Headers, _), + call(Handler, Request, Response). + +call_handler(Path, Headers, Body, Handlers, Response) :- + member(post(Dcg, Handler), Handlers), + phrase(Dcg, Path),!, + Request = http_request(Headers, Body), + call(Handler, Request, Response). call_handler(_, _, _, http_response(404, "Not found")). @@ -53,6 +65,15 @@ parse_request(http_version(Major, Minor), Method, Path) --> natural(Minor), "\r\n". +map_parse_header(Header, HeaderKV) :- + phrase(parse_header(HeaderKV), Header). + +parse_header(Key-Value) --> + string_without(":", Key), + ": ", + string_without("\r", Value), + "\r\n". + method(options) --> "OPTIONS". method(get) --> "GET". method(head) --> "HEAD". @@ -94,5 +115,16 @@ read_header_lines(Stream, Hs) :- read_header_lines(Stream, Rest) ). -sample_handler(Response) :- - Response = http_response(200, "Hello Prolog friends!"). \ No newline at end of file + +get_bytes(Stream, Res) :- get_bytes(Stream, [], Res). +get_bytes(Stream, Acc, Res) :- + get_byte(Stream, B), + (B =:= -1 -> + reverse(Acc, Res) + ; get_bytes(Stream, [B|Acc], Res)). + +sample_handler(http_request(Headers, _), Response) :- + member("User-Agent"-UserAgent, Headers), + Response = http_response(200, UserAgent, ["Content-Type"-"text/plain"]). + +sample_body_handler(http_request(Headers, Body), http_response(200, Body, ["Content-Type"-"application/json"])). \ No newline at end of file -- 2.54.0