]> Repositorios git - scryer-prolog.git/commitdiff
http_open/3 with headers and data
authorAdrián Arroyo Calle <[email protected]>
Sun, 27 Mar 2022 12:18:38 +0000 (14:18 +0200)
committerAdrián Arroyo Calle <[email protected]>
Mon, 28 Mar 2022 18:38:55 +0000 (20:38 +0200)
build/instructions_template.rs
src/lib/http/http_open.pl
src/machine/system_calls.rs

index 766ece84374137b37ff9eb641cb1f8bfa5b6bbd9..b16e7bd8336c1fdd2260557c9df9aaf74922c323 100644 (file)
@@ -540,7 +540,7 @@ enum SystemClauseType {
     CpuNow,
     #[strum_discriminants(strum(props(Arity = "2", Name = "$det_length_rundown")))]
     DeterministicLengthRundown,
-    #[strum_discriminants(strum(props(Arity = "3", Name = "$http_open")))]
+    #[strum_discriminants(strum(props(Arity = "7", Name = "$http_open")))]
     HttpOpen,
     REPL(REPLCodePtr),
 }
index 73b15338d3b9adfed75b6f765d896b8d57cbe6d9..1c89b5a589aeb08865dab6615c9707e82e7200f7 100644 (file)
@@ -9,6 +9,15 @@
    Address is a list of characters, and includes the method. Both HTTP
    and HTTPS are supported.
 
+   Options supported:
+
+     * method(+Method): Sets the HTTP method of the call. Method can be get (default), head, delete, post, put or patch.
+     * data(+Data): Data to be sent in the request. Useful for POST, PUT and PATCH operations.
+     * size(-Size): Unifies with the value of the Content-Length header
+     * request_headers(+RequestHeaders): Headers to be used in the request
+     * headers(-ListHeaders): Unifies with a list with all headers returned in the response
+     * status_code(-Code): Unifies with the status code of the request (200, 201, 404, ...)
+
    Example:
 
        ?- http_open("https://github.com/mthom/scryer-prolog", S, []).
 http_open(Address, Response, Options) :-
     parse_http_options(Options, OptionValues),
     ( member(method(Method), OptionValues) -> true; Method = get),
-    '$http_open'(Address, Response, Method).
+    ( member(data(Data), OptionValues) -> true; Data = []),
+    ( member(request_headers(RequestHeaders), OptionValues) -> true; RequestHeaders = ['user-agent'("Scryer Prolog")]),
+    ( member(status_code(Code), OptionValues) -> true; true),
+    ( member(headers(Headers), OptionValues) -> true; true),
+    ( member(size(Size), OptionValues) -> member('content-length'(Size), Headers); true),
+    '$http_open'(Address, Response, Method, Code, Data, Headers, RequestHeaders).
 
 parse_http_options(Options, OptionValues) :-
     maplist(parse_http_options_, Options, OptionValues).
@@ -32,7 +46,23 @@ parse_http_options_(method(Method), method(Method)) :-
     (  var(Method) ->
        throw(error(instantiation_error, http_open/3))
     ;
-       lists:member(Method, [get, post, put, delete, patch, head]) -> true
+       member(Method, [get, post, put, delete, patch, head]) -> true
     ;
        throw(error(domain_error(http_option, method(Method)), _))
-    ).
\ No newline at end of file
+    ).
+
+parse_http_options_(data(Data), data(Data)) :-
+    (  var(Data) ->
+       throw(error(instantiation_error, http_open/3))
+    ;  true
+    ).
+
+parse_http_options_(request_headers(Headers), request_headers(Headers)) :-
+    (  var(Headers) ->
+       throw(error(instantiation_error, http_open/3))
+    ;  true
+    ).
+
+parse_http_options_(size(Size), size(Size)).
+parse_http_options_(status_code(Code), status_code(Code)).
+parse_http_options_(headers(Headers), headers(Headers)).
\ No newline at end of file
index efd70c8a8d131f269efca1ef58407d4558e994c7..531f24363beb33206bb4f31a13518fc6002571e1 100644 (file)
@@ -43,6 +43,7 @@ use std::mem;
 use std::net::{TcpListener, TcpStream};
 use std::num::NonZeroU32;
 use std::ops::Sub;
+use std::str::FromStr;
 use std::process;
 
 use chrono::{offset::Local, DateTime};
@@ -73,7 +74,8 @@ use base64;
 use roxmltree;
 use select;
 
-use hyper::{Body, Client, Method, Request, Uri};
+use hyper::{Body, Client, HeaderMap, Method, Request, Uri};
+use hyper::header::{HeaderName, HeaderValue};
 use hyper::body::Buf;
 use hyper_tls::HttpsConnector;
 
@@ -3287,6 +3289,33 @@ impl Machine {
                 unreachable!()
             }
         );
+        let address_status = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4]));
+        let address_data = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[5]));
+        let mut bytes: Vec<u8> = Vec::new();
+        if let Some(string) = self.machine_st.value_to_str_like(address_data) {
+            bytes = string.as_str().bytes().collect();
+        }
+        let stub_gen = || functor_stub(atom!("http_open"), 3);
+
+        let headers = match self.machine_st.try_from_list(self.machine_st.registers[7], stub_gen) {
+            Ok(addrs) => {
+                let mut header_map = HeaderMap::new();
+                for heap_cell in addrs{
+                   read_heap_cell!(heap_cell,
+                       (HeapCellValueTag::Str, s) => {
+                           let name = cell_as_atom_cell!(self.machine_st.heap[s]).get_name();
+                           let value = self.machine_st.value_to_str_like(self.machine_st.heap[s + 1]).unwrap();
+                           header_map.insert(HeaderName::from_str(name.as_str()).unwrap(), HeaderValue::from_str(value.as_str()).unwrap());
+                       }
+                       _ => {
+                           unreachable!()
+                       }
+                   )
+                }
+                header_map
+            },
+            Err(e) => return Err(e)
+        };
         if let Some(address_sink) = self.machine_st.value_to_str_like(address_sink) {
             let address_string = match address_sink {
                 AtomOrString::Atom(atom) => {
@@ -3302,14 +3331,38 @@ impl Machine {
                 let https = HttpsConnector::new();
                 let client = Client::builder()
                     .build::<_, hyper::Body>(https);
-                let req = Request::builder()
+
+                // request
+                let mut req = Request::builder()
                     .method(method)
                     .uri(address)
-                    .body(Body::empty())
+                    .body(Body::from(bytes))
                     .unwrap();
-                let mut resp = client.request(req).await.unwrap();
+                // request headers
+                *req.headers_mut() = headers;
+                // do it!
+                let resp = client.request(req).await.unwrap();
+                // status code
+                let status = resp.status().as_u16();
+                self.machine_st.unify_fixnum(Fixnum::build_with(status as i64), address_status);
+                // headers
+                let headers: Vec<HeapCellValue> = resp.headers().iter().map(|(header_name, header_value)| {
+                    let h = self.machine_st.heap.len();
+
+                    let header_term = functor!(
+                        self.machine_st.atom_tbl.build_with(header_name.as_str()),
+                        [cell(string_as_cstr_cell!(self.machine_st.atom_tbl.build_with(header_value.to_str().unwrap())))]
+                    );
+
+                    self.machine_st.heap.extend(header_term.into_iter());
+                    str_loc_as_cell!(h)
+                }).collect();
+
+                let headers_list = iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter());
+                unify!(self.machine_st, heap_loc_as_cell!(headers_list), self.machine_st.registers[6]);
+                // body
                 let buf = hyper::body::aggregate(resp).await.unwrap();
-                let mut reader = buf.reader();
+                let reader = buf.reader();
 
                 let mut stream = Stream::from_http_stream(
                     self.machine_st.atom_tbl.build_with(&address_string),