http_open/3 with headers and data
This commit is contained in:
@@ -540,7 +540,7 @@ enum SystemClauseType {
|
|||||||
CpuNow,
|
CpuNow,
|
||||||
#[strum_discriminants(strum(props(Arity = "2", Name = "$det_length_rundown")))]
|
#[strum_discriminants(strum(props(Arity = "2", Name = "$det_length_rundown")))]
|
||||||
DeterministicLengthRundown,
|
DeterministicLengthRundown,
|
||||||
#[strum_discriminants(strum(props(Arity = "3", Name = "$http_open")))]
|
#[strum_discriminants(strum(props(Arity = "7", Name = "$http_open")))]
|
||||||
HttpOpen,
|
HttpOpen,
|
||||||
REPL(REPLCodePtr),
|
REPL(REPLCodePtr),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,15 @@
|
|||||||
Address is a list of characters, and includes the method. Both HTTP
|
Address is a list of characters, and includes the method. Both HTTP
|
||||||
and HTTPS are supported.
|
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:
|
Example:
|
||||||
|
|
||||||
?- http_open("https://github.com/mthom/scryer-prolog", S, []).
|
?- http_open("https://github.com/mthom/scryer-prolog", S, []).
|
||||||
@@ -23,7 +32,12 @@
|
|||||||
http_open(Address, Response, Options) :-
|
http_open(Address, Response, Options) :-
|
||||||
parse_http_options(Options, OptionValues),
|
parse_http_options(Options, OptionValues),
|
||||||
( member(method(Method), OptionValues) -> true; Method = get),
|
( 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) :-
|
parse_http_options(Options, OptionValues) :-
|
||||||
maplist(parse_http_options_, Options, OptionValues).
|
maplist(parse_http_options_, Options, OptionValues).
|
||||||
@@ -32,7 +46,23 @@ parse_http_options_(method(Method), method(Method)) :-
|
|||||||
( var(Method) ->
|
( var(Method) ->
|
||||||
throw(error(instantiation_error, http_open/3))
|
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)), _))
|
throw(error(domain_error(http_option, method(Method)), _))
|
||||||
).
|
).
|
||||||
|
|
||||||
|
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)).
|
||||||
@@ -43,6 +43,7 @@ use std::mem;
|
|||||||
use std::net::{TcpListener, TcpStream};
|
use std::net::{TcpListener, TcpStream};
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::ops::Sub;
|
use std::ops::Sub;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use chrono::{offset::Local, DateTime};
|
use chrono::{offset::Local, DateTime};
|
||||||
@@ -73,7 +74,8 @@ use base64;
|
|||||||
use roxmltree;
|
use roxmltree;
|
||||||
use select;
|
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::body::Buf;
|
||||||
use hyper_tls::HttpsConnector;
|
use hyper_tls::HttpsConnector;
|
||||||
|
|
||||||
@@ -3287,6 +3289,33 @@ impl Machine {
|
|||||||
unreachable!()
|
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) {
|
if let Some(address_sink) = self.machine_st.value_to_str_like(address_sink) {
|
||||||
let address_string = match address_sink {
|
let address_string = match address_sink {
|
||||||
AtomOrString::Atom(atom) => {
|
AtomOrString::Atom(atom) => {
|
||||||
@@ -3302,14 +3331,38 @@ impl Machine {
|
|||||||
let https = HttpsConnector::new();
|
let https = HttpsConnector::new();
|
||||||
let client = Client::builder()
|
let client = Client::builder()
|
||||||
.build::<_, hyper::Body>(https);
|
.build::<_, hyper::Body>(https);
|
||||||
let req = Request::builder()
|
|
||||||
|
// request
|
||||||
|
let mut req = Request::builder()
|
||||||
.method(method)
|
.method(method)
|
||||||
.uri(address)
|
.uri(address)
|
||||||
.body(Body::empty())
|
.body(Body::from(bytes))
|
||||||
.unwrap();
|
.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 buf = hyper::body::aggregate(resp).await.unwrap();
|
||||||
let mut reader = buf.reader();
|
let reader = buf.reader();
|
||||||
|
|
||||||
let mut stream = Stream::from_http_stream(
|
let mut stream = Stream::from_http_stream(
|
||||||
self.machine_st.atom_tbl.build_with(&address_string),
|
self.machine_st.atom_tbl.build_with(&address_string),
|
||||||
|
|||||||
Reference in New Issue
Block a user