From: Adrián Arroyo Calle Date: Sun, 27 Mar 2022 12:18:38 +0000 (+0200) Subject: http_open/3 with headers and data X-Git-Tag: v0.9.1~87^2~2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=6798b22b49ebcbcccc95425491364183c6b0939f;p=scryer-prolog.git http_open/3 with headers and data --- diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 766ece84..b16e7bd8 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -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), } diff --git a/src/lib/http/http_open.pl b/src/lib/http/http_open.pl index 73b15338..1c89b5a5 100644 --- a/src/lib/http/http_open.pl +++ b/src/lib/http/http_open.pl @@ -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, []). @@ -23,7 +32,12 @@ 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 diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index efd70c8a..531f2436 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -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 = 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 = 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),