use http::Request;
use http_by_chunks::{ConverterTo, HttpByChunks};
use serde_json::Value;
use std::{
io::{Read, Write},
net::{SocketAddr, TcpStream},
};
#[derive(Debug)]
pub enum ClientError {
StatusCode(u16),
Io(std::io::Error),
Http(http::Error),
Serde(serde_json::Error),
}
impl From<std::io::Error> for ClientError {
fn from(value: std::io::Error) -> Self {
Self::Io(value)
}
}
impl From<http::Error> for ClientError {
fn from(value: http::Error) -> Self {
Self::Http(value)
}
}
impl From<serde_json::Error> for ClientError {
fn from(value: serde_json::Error) -> Self {
Self::Serde(value)
}
}
pub type Result<T> = std::result::Result<T, ClientError>;
pub struct Client {
_addr: SocketAddr,
socket: TcpStream,
static_read_buf: [u8; 4096],
read_buf: Vec<u8>,
}
struct JsonConverter;
impl ConverterTo<Value> for JsonConverter {
fn convert(&self, buf: &[u8]) -> Value {
serde_json::from_slice(buf).unwrap_or(Value::Null)
}
}
static JSON_CONVERTER: JsonConverter = JsonConverter;
impl Client {
pub fn connect(addr: SocketAddr) -> Result<Self> {
let socket = TcpStream::connect(addr)?;
Ok(Self {
_addr: addr,
socket,
static_read_buf: [0; 4096],
read_buf: Vec::new(),
})
}
pub fn get(&mut self, uri: &str) -> Result<Value> {
self.do_no_body("GET", uri)
}
pub fn gets<const X: usize>(&mut self, uris: &[&str; X]) -> Result<[Option<Result<Value>>; X]> {
self.do_no_bodys("GET", uris)
}
pub fn delete(&mut self, uri: &str) -> Result<Value> {
self.do_no_body("DELETE", uri)
}
pub fn deletes<const X: usize>(
&mut self,
uris: &[&str; X],
) -> Result<[Option<Result<Value>>; X]> {
self.do_no_bodys("DELETE", uris)
}
pub fn post(&mut self, uri: &str, value: Value) -> Result<Value> {
self.do_body("POST", uri, value)
}
pub fn posts<const X: usize>(
&mut self,
uris: &[&str; X],
values: &[Value; X],
) -> Result<[Option<Result<Value>>; X]> {
self.do_bodys("POST", uris, values)
}
pub fn put(&mut self, uri: &str, value: Value) -> Result<Value> {
self.do_body("PUT", uri, value)
}
pub fn puts<const X: usize>(
&mut self,
uris: &[&str; X],
values: &[Value; X],
) -> Result<[Option<Result<Value>>; X]> {
self.do_bodys("PUT", uris, values)
}
pub fn patch(&mut self, uri: &str, value: Value) -> Result<Value> {
self.do_body("PATCH", uri, value)
}
pub fn patches<const X: usize>(
&mut self,
uris: &[&str; X],
values: &[Value; X],
) -> Result<[Option<Result<Value>>; X]> {
self.do_bodys("PATCH", uris, values)
}
fn do_no_body(&mut self, method: &str, uri: &str) -> Result<Value> {
let req = Request::builder()
.method(method)
.uri(uri)
.body(Value::Null)?;
self.send_request(&req)?;
self.read_response()
}
fn do_no_bodys<const X: usize>(
&mut self,
method: &str,
uris: &[&str; X],
) -> Result<[Option<Result<Value>>; X]> {
let mut res = [const { None }; X];
for uri in uris {
let req = Request::builder()
.method(method)
.uri(*uri)
.body(Value::Null)?;
self.send_request(&req)?;
}
(0..uris.len()).for_each(|x| {
res[x] = Some(self.read_response());
});
Ok(res)
}
fn do_body(&mut self, method: &str, uri: &str, value: Value) -> Result<Value> {
let req = Request::builder().method(method).uri(uri).body(value)?;
self.send_request(&req)?;
self.read_response()
}
fn do_bodys<const X: usize>(
&mut self,
method: &str,
uris: &[&str; X],
values: &[Value; X],
) -> Result<[Option<Result<Value>>; X]> {
let mut res = [const { None }; X];
for i in 0..uris.len() {
let req = Request::builder()
.method(method)
.uri(uris[i])
.body(values[i].clone())?;
self.send_request(&req)?;
}
(0..uris.len()).for_each(|x| {
res[x] = Some(self.read_response());
});
Ok(res)
}
fn send_request(&mut self, req: &Request<Value>) -> Result<()> {
self.socket.write_all(&req_to_bytes(req))?;
Ok(())
}
fn read_response(&mut self) -> Result<Value> {
let mut c = HttpByChunks::<Value>::new(&JSON_CONVERTER);
loop {
let n = self.socket.read(&mut self.static_read_buf)?;
self.read_buf.extend_from_slice(&self.static_read_buf[..n]);
self.read_buf = c.append(&self.read_buf).to_vec();
if let Some(resp) = c.build_response() {
let mut resp = resp?;
if !resp.status().is_success() {
return Err(ClientError::StatusCode(resp.status().as_u16()));
}
return Ok(resp.body_mut().take());
}
}
}
}
fn req_to_bytes(req: &Request<Value>) -> Vec<u8> {
let mut res = vec![];
res.extend_from_slice(format!("{} {} HTTP/1.1\r\n", req.method(), req.uri()).as_bytes());
if *req.body() == Value::Null {
res.extend_from_slice("\r\n".to_string().as_bytes());
} else if let Ok(b) = serde_json::to_vec(req.body()) {
res.extend_from_slice(format!("Content-Length: {}\r\n\r\n", b.len()).as_bytes());
res.extend_from_slice(&b);
}
res
}