]> Repositorios git - scryer-prolog.git/commitdiff
ADDED: library(http/http_open), opening HTTP and HTTPS streams for reading
authorMarkus Triska <[email protected]>
Sat, 13 Jun 2020 07:32:08 +0000 (09:32 +0200)
committerMarkus Triska <[email protected]>
Wed, 17 Jun 2020 18:10:22 +0000 (20:10 +0200)
Example:

   ?- http_open("https://github.com/mthom/scryer-prolog", Stream, []),
      length(Ls, 10),
      maplist(get_char(Stream), Ls).

Yielding:

   %@    Stream = '$stream'(0x7fba4c59ef10), Ls = "\n\n\n\n\n<!DOC"
   %@ ;  false.

README.md
src/lib/http/http_open.pl [new file with mode: 0644]

index 4cc05b7bc0f38c0373b0d4b3e0696cfa5992bcbd..c7e6281f3e2edbe4866e13598fa90c5dec12f757 100644 (file)
--- a/README.md
+++ b/README.md
@@ -420,6 +420,8 @@ The modules that ship with Scryer&nbsp;Prolog are also called
   Provides *delimited continuations* via `reset/3` and `shift/1`.
 * [`random`](src/lib/random.pl)
   Probabilistic predicates and random number generators.
+* [`http/http_open`](src/lib/http/http_open.pl) Open a stream to
+  read answers from web&nbsp;servers. HTTPS is also supported.
 * [`sockets`](src/lib/sockets.pl)
   Predicates for opening and accepting TCP connections as streams.
   TLS negotiation is performed via the option `tls(true)` in
diff --git a/src/lib/http/http_open.pl b/src/lib/http/http_open.pl
new file mode 100644 (file)
index 0000000..bc276e9
--- /dev/null
@@ -0,0 +1,83 @@
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+   Written June 2020 by Markus Triska ([email protected])
+   Part of Scryer Prolog.
+
+   http_open(+Address, -Stream, +Options)
+   ======================================
+
+   Yields Stream to read the body of an HTTP reply from Address.
+   Address is a list of characters, and includes the method. Both HTTP
+   and HTTPS are supported. Redirects are followed.
+
+   Currently, Options must be the empty list. Options may be
+   added in the future to give more control over the connection.
+
+   We use HTTP/1.0 until we can read chunked transfer-encoding.
+
+   Example:
+
+       ?- http_open("https://github.com/mthom/scryer-prolog", S, []).
+       %@    S = '$stream'(0x7f86f94a6cd0)
+       %@ ;  false.
+
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+:- module(http_open, [http_open/3]).
+
+:- use_module(library(sockets)).
+:- use_module(library(error)).
+:- use_module(library(format)).
+:- use_module(library(charsio)).
+:- use_module(library(dcgs)).
+
+http_open(Address, Stream, Options) :-
+        must_be(list, Options),
+        must_be(list, Address),
+        once(phrase((list(SchemeCs), "://", list(Rest)), Address)),
+        atom_chars(Scheme, SchemeCs),
+        chars_host_url(Rest, Host, URL),
+        connect(Scheme, Host, Stream0),
+        format(Stream0, "\
+GET ~s HTTP/1.0\r\n\
+Host: ~w\r\n\
+User-Agent: Scryer Prolog\r\n\
+Connection: close\r\n\r\n\
+", [URL,Host]),
+        read_line_to_chars(Stream0, StatusLine, []),
+        once(phrase(("HTTP/1.",(['0']|['1'])," ",[D1]), StatusLine, _)),
+        read_header_lines(Stream0, HeaderLines),
+        handle_response(D1, HeaderLines, Stream0, Stream).
+
+list([]) --> [].
+list([L|Ls]) --> [L], list(Ls).
+
+handle_response('2', _, Stream, Stream).              % ok
+handle_response('3', HeaderLines, Stream0, Stream) :- % redirect
+        close(Stream0),
+        once((member(Line, HeaderLines),
+              phrase(("Location: ",list(Location),"\r\n"), Line))),
+        http_open(Location, Stream, []).
+
+% Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+
+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)
+        ).
+
+chars_host_url(Cs, Host, [/|Us]) :-
+        (   phrase((list(Hs),"/",list(Us)), Cs) ->
+            true
+        ;   Hs = Cs,
+            Us = []
+        ),
+        atom_chars(Host, Hs).
+
+connect(https, Host, Stream) :-
+        socket_client_open(Host:443, Stream, [tls(true)]).
+connect(http, Host, Stream) :-
+        socket_client_open(Host:80, Stream, []).
+