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),
}
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).
( 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
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};
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;
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) => {
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),