#![deny(warnings, rust_2018_idioms)] #![feature(async_await)] use bytes::{BufMut, BytesMut}; use std::env; use std::io::{Error, ErrorKind}; use std::net::ToSocketAddrs; use std::path::Path; use tokio::net::{TcpStream, UnixStream}; use tokio::prelude::*; use tokio_codec::Framed; use tokio_scgi::client::{SCGICodec, SCGIRequest}; fn syntax() -> Error { println!( "Syntax: {} ", env::args().nth(0).unwrap() ); Error::new(ErrorKind::InvalidInput, "Missing required argument") } #[tokio::main] async fn main() -> Result<(), Error> { if env::args().len() <= 1 { return Err(syntax()); } let endpoint = env::args().nth(1).unwrap(); if endpoint.starts_with('-') { // Probably a commandline argument like '-h'/'--help', avoid parsing as a hostname return Err(syntax()); } if endpoint.contains('/') { // Probably a path to a file, assume the argument is a unix socket let addr = Path::new(&endpoint); println!("Connecting to {}", addr.display()); let mut conn = UnixStream::connect(&addr).await?; run_client(&mut conn).await } else { // Probably a TCP endpoint, try to resolve it in case it's a hostname let addr = endpoint .to_socket_addrs() .expect(format!("Invalid TCP endpoint '{}'", endpoint).as_str()) .next() .unwrap(); println!("Connecting to {}", addr); let mut conn = TcpStream::connect(&addr).await?; run_client(&mut conn).await } } /// Runs the client: Sends a request and prints the responses via the provided UDS or TCP connection. async fn run_client(conn: &mut C) -> Result<(), Error> where C: AsyncRead + AsyncWrite + std::marker::Send + std::marker::Unpin + std::fmt::Debug { let (mut tx_scgi, rx_scgi) = Framed::new(conn, SCGICodec::new()).split(); // Send request tx_scgi.send(build_request()).await?; // Consume response // TODO support looping over multiple response calls? match rx_scgi.into_future().await { (None, _) => Err(Error::new(ErrorKind::Other, "No response received")), (Some(Err(e)), _) => Err(Error::new( ErrorKind::Other, format!("Error when waiting for query result: {}", e), )), (Some(Ok(response)), _) => { match String::from_utf8(response.to_vec()) { Ok(s) => println!("Got {} bytes:\n{}", response.len(), s), Err(e) => println!("{} byte response is not UTF8 ({}):\n{:?}", response.len(), e, response) } Ok(()) }, } } fn build_request() -> SCGIRequest { let content_str = b"{\"description\": \"my name is also bort <><><>\"}"; let mut content = BytesMut::with_capacity(content_str.len()); content.put(content_str.to_vec()); let mut headers = Vec::new(); headers.push(("Content-Length".to_string(), content_str.len().to_string())); headers.push(("SCGI".to_string(), "1".to_string())); headers.push(("Content-Type".to_string(), "application/json".to_string())); headers.push(("X-Username".to_string(), "bort".to_string())); SCGIRequest::Request(headers, content) }