From 33a0df20c37ab68c2f308f092cbcb816603b39bb Mon Sep 17 00:00:00 2001 From: =?utf8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Mon, 14 Dec 2020 22:57:53 +0100 Subject: [PATCH] Read body --- src/lib/http/http_server.pl | 68 +++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/src/lib/http/http_server.pl b/src/lib/http/http_server.pl index 486483c7..5eeb4847 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/2, sample_body_handler/2]). +:- module(http_server, [http_listen/2, sample_handler/2, sample_body_handler/2, parse_request/5]). :- use_module(library(sockets)). :- use_module(library(dcgs)). @@ -18,27 +18,42 @@ % - Improve code quality % - Binary % - Comments +% - Test Suite +% - Keep-Alive +% - Case insensitive headers 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(binary)]), - read_header_lines(Stream, Lines), - [Request|Headers] = Lines, - % get_bytes(Stream, RequestBody), % Doesn't work - ( - (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). + format("Listening at port ~d\n", [Port]), + accept_loop(Socket, Handlers). + +accept_loop(Socket, Handlers) :- + setup_call_cleanup(socket_server_accept(Socket, Client, Stream, [type(binary)]), + ( + read_header_lines(Stream, Lines), + [Request|Headers] = Lines, + ( + (phrase(parse_request(Version, Method, Path), Request), maplist(map_parse_header, Headers, HeadersKV)) -> ( + ( + member("Content-Length"-ContentLength, HeadersKV) -> + (number_chars(ContentLengthN, ContentLength), get_bytes(Stream, ContentLengthN, RequestBody)) + ;true + ), + format("~w ~s\n", [Method, Path]), + 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) + ), close(Stream)), + accept_loop(Socket, Handlers). call_handler(Path, Headers, _, Handlers, Response) :- member(get(Dcg, Handler), Handlers), @@ -47,9 +62,10 @@ call_handler(Path, Headers, _, Handlers, Response) :- call(Handler, Request, Response). call_handler(Path, Headers, Body, Handlers, Response) :- + chars_utf8bytes(CharBody, Body), member(post(Dcg, Handler), Handlers), phrase(Dcg, Path),!, - Request = http_request(Headers, Body), + Request = http_request(Headers, CharBody), call(Handler, Request, Response). call_handler(_, _, _, http_response(404, "Not found")). @@ -115,16 +131,16 @@ read_header_lines(Stream, Hs) :- read_header_lines(Stream, Rest) ). - -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)). +get_bytes(Stream, Length, Res) :- get_bytes(Stream, Length, [], Res). +get_bytes(Stream, Length, Acc, Res) :- + (Length > 0 -> ( + get_byte(Stream, B), + B =\= -1, + get_bytes(Stream, Length - 1, [B|Acc], Res) + ); reverse(Acc, Res)). sample_handler(http_request(Headers, _), Response) :- member("User-Agent"-UserAgent, Headers), - Response = http_response(200, UserAgent, ["Content-Type"-"text/plain"]). + Response = http_response(200, UserAgent, ["Content-Type"-"text/plain", "Connection"-"Close"]). sample_body_handler(http_request(Headers, Body), http_response(200, Body, ["Content-Type"-"application/json"])). \ No newline at end of file -- 2.54.0