Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support HTTP methods #2

Merged
merged 4 commits into from
Feb 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "af"
version = "0.0.1"
version = "0.0.3"
authors = ["Ridwan Hoq <ridhoq@users.noreply.github.com>"]
edition = "2018"
description = "A (http) fetch CLI 馃榾馃憤馃徑"
Expand All @@ -10,6 +10,7 @@ repository = "https://github.com/ridhoq/af"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
http = "0.2.2"
hyper = { version = "0.14", features = ["full"] }
hyper-tls = "0.5.0"
structopt = "0.3.21"
Expand Down
81 changes: 75 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,68 @@
use hyper::{body::HttpBody as _, Client, http::Uri};
use std::error;
use std::fmt;

use http::{Method, Uri};
use hyper::{body::HttpBody as _, Client, Request};
use hyper_tls::HttpsConnector;
use structopt::clap::AppSettings;
use structopt::StructOpt;
use tokio::io::{self, AsyncWriteExt as _};

#[derive(StructOpt)]
#[derive(Debug, Clone)]
struct InvalidHttpMethodError;

impl fmt::Display for InvalidHttpMethodError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid or unknown HTTP Method")
}
}

/// Parse HTTP method from string. Throws error if invalid/unknown HTTP method
fn parse_method(src: &str) -> Result<Method, InvalidHttpMethodError> {
match src.to_uppercase().as_str() {
"GET" => Ok(Method::GET),
"PUT" => Ok(Method::PUT),
"POST" => Ok(Method::POST),
"HEAD" => Ok(Method::HEAD),
"PATCH" => Ok(Method::PATCH),
"TRACE" => Ok(Method::TRACE),
"DELETE" => Ok(Method::DELETE),
"OPTIONS" => Ok(Method::OPTIONS),
"CONNECT" => Ok(Method::CONNECT),
_ => Err(InvalidHttpMethodError),
}
}

#[derive(StructOpt, Debug)]
#[structopt(setting = AppSettings::AllowMissingPositional)]
/// A (http) fetch CLI 馃榾馃憤
struct Cli {
// The URI to fetch
uri: Uri
/// HTTP method. If no HTTP method is provided, GET is used by default
#[structopt(name = "METHOD", index = 1, default_value = "GET", parse(try_from_str = parse_method))]
method: Method,

/// URI to fetch
#[structopt(name = "URI", index = 2, required = true, parse(try_from_str))]
uri: Uri,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>>{
async fn main() -> Result<(), Box<dyn error::Error>> {
let args = Cli::from_args();

// Set up the HTTPS connector with the client
let https = HttpsConnector::new();
let client = Client::builder().build::<_, hyper::Body>(https);

let mut res = client.get(args.uri).await?;
let req = Request::builder()
.method(args.method)
.uri(args.uri)
// TODO: couldn't figure out how to not pass a body with the Request::builder
// TODO: pass in a real body when doing POST/PUT/etc
.body(hyper::Body::from(""))
.unwrap();

let mut res = client.request(req).await?;

println!("Response: {}", res.status());
println!("Headers: {:#?}\n", res.headers());
Expand All @@ -31,3 +76,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>>{

Ok(())
}

#[cfg(test)]
mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*;

#[test]
fn test_valid_http_method() {
let get_lower = "get";
let get_upper = "GET";

assert_eq!(parse_method(get_lower).unwrap(), Method::GET);
assert_eq!(parse_method(get_upper).unwrap(), Method::GET);
}

#[test]
fn test_invalid_http_method() {
let poop = "poop";
let empty = "";

assert!(parse_method(poop).is_err());
assert!(parse_method(empty).is_err());
}
}