Read body

This commit is contained in:
Adrián Arroyo Calle
2020-12-14 22:57:53 +01:00
parent e4d1e4b7a0
commit 33a0df20c3

View File

@@ -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(sockets)).
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
@@ -18,27 +18,42 @@
% - Improve code quality % - Improve code quality
% - Binary % - Binary
% - Comments % - Comments
% - Test Suite
% - Keep-Alive
% - Case insensitive headers
http_listen(Port, Handlers) :- http_listen(Port, Handlers) :-
must_be(integer, Port), must_be(integer, Port),
must_be(list, Handlers), must_be(list, Handlers),
once(socket_server_open(Port, Socket)), once(socket_server_open(Port, Socket)),
socket_server_accept(Socket, Client, Stream, [type(binary)]), format("Listening at port ~d\n", [Port]),
read_header_lines(Stream, Lines), accept_loop(Socket, Handlers).
[Request|Headers] = Lines,
% get_bytes(Stream, RequestBody), % Doesn't work accept_loop(Socket, Handlers) :-
( setup_call_cleanup(socket_server_accept(Socket, Client, Stream, [type(binary)]),
(phrase(parse_request(Version, Method, Path), Request), maplist(map_parse_header, Headers, HeadersKV)) -> ( (
call_handler(Path, HeadersKV, RequestBody, Handlers, Response), read_header_lines(Stream, Lines),
http_response(StatusCode, ResponseBody, RequestHeaders) = Response, [Request|Headers] = Lines,
format(Stream, "HTTP/1.0 ~d\r\n", [StatusCode]), (
forall(member(RequestHeaderKey-RequestHeaderValue, RequestHeaders), format(Stream, "~s: ~s\r\n", [RequestHeaderKey, RequestHeaderValue])), (phrase(parse_request(Version, Method, Path), Request), maplist(map_parse_header, Headers, HeadersKV)) -> (
format(Stream, "\r\n~s", [ResponseBody]) (
);( member("Content-Length"-ContentLength, HeadersKV) ->
format(Stream, "HTTP/1.0 400 Bad Request\r\n\r\n", []) % bad format (number_chars(ContentLengthN, ContentLength), get_bytes(Stream, ContentLengthN, RequestBody))
) ;true
), ),
close(Stream). 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) :- call_handler(Path, Headers, _, Handlers, Response) :-
member(get(Dcg, Handler), Handlers), member(get(Dcg, Handler), Handlers),
@@ -47,9 +62,10 @@ call_handler(Path, Headers, _, Handlers, Response) :-
call(Handler, Request, Response). call(Handler, Request, Response).
call_handler(Path, Headers, Body, Handlers, Response) :- call_handler(Path, Headers, Body, Handlers, Response) :-
chars_utf8bytes(CharBody, Body),
member(post(Dcg, Handler), Handlers), member(post(Dcg, Handler), Handlers),
phrase(Dcg, Path),!, phrase(Dcg, Path),!,
Request = http_request(Headers, Body), Request = http_request(Headers, CharBody),
call(Handler, Request, Response). call(Handler, Request, Response).
call_handler(_, _, _, http_response(404, "Not found")). call_handler(_, _, _, http_response(404, "Not found")).
@@ -115,16 +131,16 @@ read_header_lines(Stream, Hs) :-
read_header_lines(Stream, Rest) read_header_lines(Stream, Rest)
). ).
get_bytes(Stream, Length, Res) :- get_bytes(Stream, Length, [], Res).
get_bytes(Stream, Res) :- get_bytes(Stream, [], Res). get_bytes(Stream, Length, Acc, Res) :-
get_bytes(Stream, Acc, Res) :- (Length > 0 -> (
get_byte(Stream, B), get_byte(Stream, B),
(B =:= -1 -> B =\= -1,
reverse(Acc, Res) get_bytes(Stream, Length - 1, [B|Acc], Res)
; get_bytes(Stream, [B|Acc], Res)). ); reverse(Acc, Res)).
sample_handler(http_request(Headers, _), Response) :- sample_handler(http_request(Headers, _), Response) :-
member("User-Agent"-UserAgent, Headers), 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"])). sample_body_handler(http_request(Headers, Body), http_response(200, Body, ["Content-Type"-"application/json"])).