URL Decode

This commit is contained in:
Adrián Arroyo Calle
2020-12-28 14:42:20 +01:00
parent 155004bdbb
commit 3f8d3afe5c

View File

@@ -53,7 +53,8 @@
http_status_code/2, http_status_code/2,
http_body/2, http_body/2,
http_redirect/2, http_redirect/2,
http_query/3 http_query/3,
urldecode//1
]). ]).
:- use_module(library(sockets)). :- use_module(library(sockets)).
@@ -64,6 +65,7 @@
:- use_module(library(lists)). :- use_module(library(lists)).
:- use_module(library(iso_ext)). :- use_module(library(iso_ext)).
:- use_module(library(time)). :- use_module(library(time)).
:- use_module(library(crypto)).
% TODO % TODO
% - HTTP Error Codes % - HTTP Error Codes
@@ -72,28 +74,16 @@
% - Remove ! % - Remove !
% - URL Encode % - URL Encode
% The route matching system needs a dynamic predicate to re-use the vars
% defined in the patterns
:- dynamic(http_handler/3).
% Server initialization % Server initialization
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)),
register_handlers(Handlers),
format("Listening at port ~d\n", [Port]), format("Listening at port ~d\n", [Port]),
accept_loop(Socket). accept_loop(Socket, Handlers).
% Register handlers
register_handlers([]).
register_handlers([Handler|Handlers]) :-
Handler =.. [Method, Path, Closure],
asserta(http_handler(Method, Path, Closure)),
register_handlers(Handlers).
% Server loop % Server loop
accept_loop(Socket) :- accept_loop(Socket, Handlers) :-
setup_call_cleanup(socket_server_accept(Socket, Client, Stream, [type(binary)]), setup_call_cleanup(socket_server_accept(Socket, Client, Stream, [type(binary)]),
( (
read_header_lines(Stream, Lines), read_header_lines(Stream, Lines),
@@ -109,7 +99,7 @@ accept_loop(Socket) :-
phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString), phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString),
format("~s ~w ~s\n", [TimeString, Method, Path]), format("~s ~w ~s\n", [TimeString, Method, Path]),
( (
(http_handler(Method, Pattern, Handler), phrase(path(Pattern), Path)) -> match_handler(Handlers, Method, Path, Handler) ->
( (
HttpRequest = http_request(HeadersKV, binary(Body), Queries), HttpRequest = http_request(HeadersKV, binary(Body), Queries),
HttpResponse = http_response(_, _, _), HttpResponse = http_response(_, _, _),
@@ -126,7 +116,13 @@ accept_loop(Socket) :-
), ),
! % Remove ! % Remove
), close(Stream)), ), close(Stream)),
accept_loop(Socket). accept_loop(Socket, Handlers).
match_handler(Handlers, Method, Path, Handler) :-
member(H, Handlers),
copy_term(H, H1),
H1 =.. [Method, Pattern, Handler],
phrase(path(Pattern), Path).
% Helper and recommended predicates % Helper and recommended predicates
@@ -239,16 +235,28 @@ parse_path(Path, []) -->
string_without(" ", Path). string_without(" ", Path).
parse_queries([Key-Value|Queries]) --> parse_queries([Key-Value|Queries]) -->
string_without("=", Key), string_without("=", Key0),
{
phrase(urldecode(Key), Key0)
},
"=", "=",
string_without("&", Value), string_without("&", Value0),
{
phrase(urldecode(Value), Value0)
},
"&", "&",
parse_queries(Queries). parse_queries(Queries).
parse_queries([Key-Value]) --> parse_queries([Key-Value]) -->
string_without("=", Key), string_without("=", Key0),
{
phrase(urldecode(Key), Key0)
},
"=", "=",
string_without(" ", Value). string_without(" ", Value0),
{
phrase(urldecode(Value), Value0)
}.
map_parse_header(Header, HeaderKV) :- map_parse_header(Header, HeaderKV) :-
phrase(parse_header(HeaderKV), Header). phrase(parse_header(HeaderKV), Header).
@@ -337,3 +345,68 @@ char_lower(Char, Lower) :-
LowerCode is Code + 32, LowerCode is Code + 32,
char_code(Lower, LowerCode) char_code(Lower, LowerCode)
; Char = Lower). ; Char = Lower).
% Decodes a UTF-8 URL Encoded string: RFC-1738
urldecode([Char|Chars]) -->
[Char],
{
Char \= '%'
},
urldecode(Chars).
urldecode([Char|Chars]) -->
"%",
[A],
[B],
{
hex_bytes([A,B], Bytes),
Bytes = [FirstByte|_],
FirstByte < 128,
chars_utf8bytes(Chars0, Bytes),
Chars0 = [Char]
},
urldecode(Chars).
urldecode([Char|Chars]) -->
"%",
[A, B],
"%",
[C, D],
{
hex_bytes([A,B,C,D], Bytes),
Bytes = [FirstByte|_],
FirstByte < 224,
chars_utf8bytes(Chars0, Bytes),
Chars0 = [Char]
},
urldecode(Chars).
urldecode([Char|Chars]) -->
"%",
[A, B],
"%",
[C, D],
"%",
[E, F],
{
hex_bytes([A,B,C,D,E,F], Bytes),
Bytes = [FirstByte|_],
FirstByte < 240,
chars_utf8bytes(Chars0, Bytes),
Chars0 = [Char]
},
urldecode(Chars).
urldecode([Char|Chars]) -->
"%",
[A, B],
"%",
[C, D],
"%",
[E, F],
"%",
[H, I],
{
hex_bytes([A,B,C,D,E,F,H,I], Bytes),
chars_utf8bytes(Chars0, Bytes),
Chars0 = [Char]
},
urldecode(Chars).
urldecode([]) --> [].