style(rust): Format all Rust code with rustfmt

Change-Id: Iab7e00cc26a4f9727d3ab98691ef379921a33052
Reviewed-on: https://cl.tvl.fyi/c/depot/+/5240
Tested-by: BuildkiteCI
Reviewed-by: kanepyork <rikingcoding@gmail.com>
Reviewed-by: Profpatsch <mail@profpatsch.de>
Reviewed-by: grfn <grfn@gws.fyi>
Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
Vincent Ambo 2022-02-07 18:49:59 +03:00 committed by tazjin
parent 3318982f81
commit 3d8ee62087
42 changed files with 1253 additions and 876 deletions

View file

@ -4,17 +4,17 @@ use std::rc::Rc;
use std::sync::RwLock;
struct Defer<F: Fn()> {
f: F
f: F,
}
impl <F: Fn()> Drop for Defer<F> {
impl<F: Fn()> Drop for Defer<F> {
fn drop(&mut self) {
(self.f)()
}
}
// Only added this for Go-syntax familiarity ;-)
fn defer<F: Fn()>(f: F) -> Defer<F> {
fn defer<F: Fn()>(f: F) -> Defer<F> {
Defer { f }
}
@ -29,7 +29,9 @@ type ErrorHandle<T> = Rc<RwLock<Option<T>>>;
///////////////////
#[derive(Debug)] // Debug trait for some default way to print the type.
enum Error { DropError }
enum Error {
DropError,
}
fn main() {
// Create a place to store the error.
@ -60,7 +62,7 @@ fn main() {
match *drop_err.read().unwrap() {
Some(ref err) => println!("Oh no, an error occured: {:?}!", err),
None => println!("Phew, everything went well.")
None => println!("Phew, everything went well."),
};
}

View file

@ -1,17 +1,17 @@
// Go's defer in Rust!
struct Defer<F: Fn()> {
f: F
f: F,
}
impl <F: Fn()> Drop for Defer<F> {
impl<F: Fn()> Drop for Defer<F> {
fn drop(&mut self) {
(self.f)()
}
}
// Only added this for Go-syntax familiarity ;-)
fn defer<F: Fn()>(f: F) -> Defer<F> {
fn defer<F: Fn()>(f: F) -> Defer<F> {
Defer { f }
}

View file

@ -1,10 +1,10 @@
// Go's defer in Rust, with a little twist!
struct Defer<F: Fn()> {
f: F
f: F,
}
impl <F: Fn()> Drop for Defer<F> {
impl<F: Fn()> Drop for Defer<F> {
fn drop(&mut self) {
(self.f)()
}

View file

@ -1,8 +1,7 @@
use crate::models::{Entry, Keyword, NewEntry, NewKeyword};
use diesel::pg::PgConnection;
use diesel::prelude::*;
use failure::format_err;
use failure::Error;
use failure::{format_err, Error};
use std::borrow::Cow;
/// Maximum number of times we'll follow a `see: ` pointer.

View file

@ -7,8 +7,7 @@ use crate::cfg::Config;
use crate::keyword::KeywordDetails;
use diesel::pg::PgConnection;
use diesel::r2d2::{ConnectionManager, Pool};
use failure::format_err;
use failure::Error;
use failure::{format_err, Error};
use irc::client::prelude::*;
use lazy_static::lazy_static;
use log::{debug, info, warn};
@ -153,8 +152,9 @@ impl App {
// Use `nick` here, so things like "grfn: see glittershark" work.
let val = if let Some(last) = chan_lastmsgs.get(nick_to_grab) {
if last.starts_with("\x01ACTION ") {
// Yes, this is inefficient, but it's better than writing some hacky CTCP parsing code
// I guess (also, characters are hard, so just blindly slicing seems like a bad idea)
// Yes, this is inefficient, but it's better than writing some hacky CTCP parsing
// code I guess (also, characters are hard, so just blindly slicing
// seems like a bad idea)
format!(
"* {} {}",
nick_to_grab,

View file

@ -67,23 +67,24 @@
//!
//! [JWKS]: https://tools.ietf.org/html/rfc7517
#[macro_use] extern crate serde_derive;
#[macro_use]
extern crate serde_derive;
extern crate base64;
extern crate openssl;
extern crate serde;
extern crate serde_json;
use base64::{URL_SAFE_NO_PAD, Config, DecodeError};
use base64::{Config, DecodeError, URL_SAFE_NO_PAD};
use openssl::bn::BigNum;
use openssl::error::ErrorStack;
use openssl::hash::MessageDigest;
use openssl::pkey::{Public, PKey};
use openssl::pkey::{PKey, Public};
use openssl::rsa::Rsa;
use openssl::sign::Verifier;
use serde::de::DeserializeOwned;
use serde_json::Value;
use std::time::{UNIX_EPOCH, Duration, SystemTime};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[cfg(test)]
mod tests;
@ -101,12 +102,16 @@ fn jwt_forgiving() -> Config {
/// JWT algorithm used. The only supported algorithm is currently
/// RS256.
#[derive(Clone, Deserialize, Debug)]
enum KeyAlgorithm { RS256 }
enum KeyAlgorithm {
RS256,
}
/// Type of key contained in a JWT. The only supported key type is
/// currently RSA.
#[derive(Clone, Deserialize, Debug)]
enum KeyType { RSA }
enum KeyType {
RSA,
}
/// Representation of a single JSON Web Key. See [RFC
/// 7517](https://tools.ietf.org/html/rfc7517#section-4).
@ -146,7 +151,7 @@ impl JWKS {
/// Representation of an undecoded JSON Web Token. See [RFC
/// 7519](https://tools.ietf.org/html/rfc7519).
struct JWT<'a> (&'a str);
struct JWT<'a>(&'a str);
/// Representation of a decoded and validated JSON Web Token.
///
@ -217,15 +222,21 @@ pub enum ValidationError {
type JWTResult<T> = Result<T, ValidationError>;
impl From<ErrorStack> for ValidationError {
fn from(err: ErrorStack) -> Self { ValidationError::OpenSSL(err) }
fn from(err: ErrorStack) -> Self {
ValidationError::OpenSSL(err)
}
}
impl From<serde_json::Error> for ValidationError {
fn from(err: serde_json::Error) -> Self { ValidationError::JSON(err) }
fn from(err: serde_json::Error) -> Self {
ValidationError::JSON(err)
}
}
impl From<DecodeError> for ValidationError {
fn from(err: DecodeError) -> Self { ValidationError::InvalidBase64(err) }
fn from(err: DecodeError) -> Self {
ValidationError::InvalidBase64(err)
}
}
/// Attempt to extract the `kid`-claim out of a JWT's header claims.
@ -266,9 +277,7 @@ pub fn token_kid(token: &str) -> JWTResult<Option<String>> {
///
/// It is the user's task to ensure that the correct JWK is passed in
/// for validation.
pub fn validate(token: &str,
jwk: &JWK,
validations: Vec<Validation>) -> JWTResult<ValidJWT> {
pub fn validate(token: &str, jwk: &JWK, validations: Vec<Validation>) -> JWTResult<ValidJWT> {
let jwt = JWT(token);
let public_key = public_key_from_jwk(&jwk)?;
validate_jwt_signature(&jwt, public_key)?;
@ -279,7 +288,7 @@ pub fn validate(token: &str,
if parts.len() != 3 {
// This is unlikely considering that validation has already
// been performed at this point, but better safe than sorry.
return Err(ValidationError::InvalidComponents)
return Err(ValidationError::InvalidComponents);
}
// Perform claim validations before constructing the valid token:
@ -351,7 +360,7 @@ fn validate_jwt_signature(jwt: &JWT, key: Rsa<Public>) -> JWTResult<()> {
verifier.update(data.as_bytes())?;
match verifier.verify(&sig)? {
true => Ok(()),
true => Ok(()),
false => Err(ValidationError::InvalidSignature),
}
}
@ -362,7 +371,7 @@ fn validate_jwt_signature(jwt: &JWT, key: Rsa<Public>) -> JWTResult<()> {
#[serde(untagged)]
enum Audience {
Single(String),
Multi(Vec<String>)
Multi(Vec<String>),
}
/// Internal helper struct for claims that are relevant for claim
@ -376,15 +385,14 @@ struct PartialClaims {
}
/// Apply a single validation to the claim set of a token.
fn apply_validation(claims: &PartialClaims,
validation: Validation) -> Result<(), &'static str> {
fn apply_validation(claims: &PartialClaims, validation: Validation) -> Result<(), &'static str> {
match validation {
// Validate that an 'iss' claim is present and matches the
// supplied value.
Validation::Issuer(iss) => {
match claims.iss {
None => Err("'iss' claim is missing"),
Some(ref claim) => if *claim == iss {
Validation::Issuer(iss) => match claims.iss {
None => Err("'iss' claim is missing"),
Some(ref claim) => {
if *claim == iss {
Ok(())
} else {
Err("'iss' claim does not match")
@ -394,15 +402,17 @@ fn apply_validation(claims: &PartialClaims,
// Validate that an 'aud' claim is present and matches the
// supplied value.
Validation::Audience(aud) => {
match claims.aud {
None => Err("'aud' claim is missing"),
Some(Audience::Single(ref claim)) => if *claim == aud {
Validation::Audience(aud) => match claims.aud {
None => Err("'aud' claim is missing"),
Some(Audience::Single(ref claim)) => {
if *claim == aud {
Ok(())
} else {
Err("'aud' claim does not match")
},
Some(Audience::Multi(ref claims)) => if claims.contains(&aud) {
}
}
Some(Audience::Multi(ref claims)) => {
if claims.contains(&aud) {
Ok(())
} else {
Err("'aud' claim does not match")
@ -447,12 +457,12 @@ fn apply_validation(claims: &PartialClaims,
}
/// Apply all requested validations to a partial claim set.
fn validate_claims(claims: PartialClaims,
validations: Vec<Validation>) -> JWTResult<()> {
let validation_errors: Vec<_> = validations.into_iter()
fn validate_claims(claims: PartialClaims, validations: Vec<Validation>) -> JWTResult<()> {
let validation_errors: Vec<_> = validations
.into_iter()
.map(|v| apply_validation(&claims, v))
.filter_map(|result| match result {
Ok(_) => None,
Ok(_) => None,
Err(err) => Some(err),
})
.collect();

View file

@ -21,14 +21,19 @@ fn test_fragment_decoding() {
let bignum = decode_fragment(fragment).expect("Failed to decode fragment");
let expected = "19947781743618558124649689124245117083485690334420160711273532766920651190711502679542723943527557680293732686428091794139998732541701457212387600480039297092835433997837314251024513773285252960725418984381935183495143908023024822433135775773958512751261112853383693442999603704969543668619221464654540065497665889289271044207667765128672709218996183649696030570183970367596949687544839066873508106034650634722970893169823917299050098551447676778961773465887890052852528696684907153295689693676910831376066659456592813140662563597179711588277621736656871685099184755908108451080261403193680966083938080206832839445289";
assert_eq!(expected, format!("{}", bignum), "Decoded fragment should match ");
assert_eq!(
expected,
format!("{}", bignum),
"Decoded fragment should match "
);
}
#[test]
fn test_decode_find_jwks() {
let json = "{\"keys\":[{\"kty\":\"RSA\",\"alg\":\"RS256\",\"use\":\"sig\",\"kid\":\"mUjI\\/rIMLLtung35BKZfdbrqtlEAAYJ4JX\\/SKvnLxJc=\",\"n\":\"ngRRjNbXgPW29oNtF0JgsyyfTwPyEL0u_X16s453X2AOc33XGFxVKLEQ7R_TiMenaKcr-tPifYqgps_deyi0XOr4I3SOdOMtAVKDZJCANe--CANOHZb-meIfjKhCHisvT90fm5Apd6qPRVsXsZ7A8pmClZHKM5fwZUkBv8NsPLm2Xy2sGOZIiwP_7z8m3j0abUzniPQsx2b3xcWimB9vRtshFHN1KgPUf1ALQ5xzLfJnlFkCxC7kmOxKC7_NpQ4kJR_DKzKFV_r3HxTqf-jddHcXIrrMcLQXCSyeLQtLaz7whQ4F-EfL42z4XgwPr4ji3sct2gWL13EqlbE5DDxLKQ\",\"e\":\"GK7oLCDbNPAF59LhvyseqcG04hDnPs58qGYolr_HHmaR4lulWJ90ozx6e4Ut363yKG2p9vwvivR5UIC-aLPtqT2qr-OtjhBFzUFVaMGZ6mPCvMKk0AgMYdOHvWTgBSqQtNJTvl1yYLnhcWyoE2fLQhoEbY9qUyCBCEOScXOZRDpnmBtz5I8q5yYMV6a920J24T_IYbxHgkGcEU2SGg-b1cOMD7Rja7vCfV---CQ2pR4leQ0jufzudDoe7z3mziJm-Ihcdrz2Ujy5kPEMdz6R55prJ-ENKrkD_X4u5aSlSRaetwmHS3oAVkjr1JwUNbqnpM-kOqieqHEp8LUmez-Znw\"}]}";
let jwks: JWKS = serde_json::from_str(json).expect("Failed to decode JWKS");
let jwk = jwks.find("mUjI/rIMLLtung35BKZfdbrqtlEAAYJ4JX/SKvnLxJc=")
let jwk = jwks
.find("mUjI/rIMLLtung35BKZfdbrqtlEAAYJ4JX/SKvnLxJc=")
.expect("Failed to find required JWK");
public_key_from_jwk(&jwk).expect("Failed to construct public key from JWK");
@ -39,18 +44,21 @@ fn test_token_kid() {
let jwt = "eyJraWQiOiI4ckRxOFB3MEZaY2FvWFdURVZRbzcrVGYyWXpTTDFmQnhOS1BDZWJhYWk0PSIsImFsZyI6IlJTMjU2IiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdXRoLnRlc3QuYXByaWxhLm5vIiwiaWF0IjoxNTM2MDUwNjkzLCJleHAiOjE1MzYwNTQyOTMsInN1YiI6IjQyIiwiZXh0Ijoic21va2V0ZXN0IiwicHJ2IjoiYXJpc3RpIiwic2NwIjoicHJvY2VzcyJ9.gOLsv98109qLkmRK6Dn7WWRHLW7o8W78WZcWvFZoxPLzVO0qvRXXRLYc9h5chpfvcWreLZ4f1cOdvxv31_qnCRSQQPOeQ7r7hj_sPEDzhKjk-q2aoNHaGGJg1vabI--9EFkFsGQfoS7UbMMssS44dgR68XEnKtjn0Vys-Vzbvz_CBSCH6yQhRLik2SU2jR2L7BoFvh4LGZ6EKoQWzm8Z-CHXLGLUs4Hp5aPhF46dGzgAzwlPFW4t9G4DciX1uB4vv1XnfTc5wqJch6ltjKMde1GZwLR757a8dJSBcmGWze3UNE2YH_VLD7NCwH2kkqr3gh8rn7lWKG4AUIYPxsw9CB";
let kid = token_kid(&jwt).expect("Failed to extract token KID");
assert_eq!(Some("8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=".into()),
kid, "Extracted KID did not match expected KID");
assert_eq!(
Some("8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=".into()),
kid,
"Extracted KID did not match expected KID"
);
}
#[test]
fn test_validate_jwt() {
let jwks_json = "{\"keys\":[{\"kty\":\"RSA\",\"alg\":\"RS256\",\"use\":\"sig\",\"kid\":\"8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=\",\"n\":\"l4UTgk1zr-8C8utt0E57DtBV6qqAPWzVRrIuQS2j0_hp2CviaNl5XzGRDnB8gwk0Hx95YOhJupAe6RNq5ok3fDdxL7DLvppJNRLz3Ag9CsmDLcbXgNEQys33fBJaPw1v3GcaFC4tisU5p-o1f5RfWwvwdBtdBfGiwT1GRvbc5sFx6M4iYjg9uv1lNKW60PqSJW4iDYrfqzZmB0zF1SJ0BL_rnQZ1Wi_UkFmNe9arM8W9tI9T3Ie59HITFuyVSTCt6qQEtSfa1e5PiBaVuV3qoFI2jPBiVZQ6LPGBWEDyz4QtrHLdECPPoTF30NN6TSVwwlRbCuUUrdNdXdjYe2dMFQ\",\"e\":\"DhaD5zC7mzaDvHO192wKT_9sfsVmdy8w8T8C9VG17_b1jG2srd3cmc6Ycw-0blDf53Wrpi9-KGZXKHX6_uIuJK249WhkP7N1SHrTJxO0sUJ8AhK482PLF09Qtu6cUfJqY1X1y1S2vACJZItU4Vjr3YAfiVGQXeA8frAf7Sm4O1CBStCyg6yCcIbGojII0jfh2vSB-GD9ok1F69Nmk-R-bClyqMCV_Oq-5a0gqClVS8pDyGYMgKTww2RHgZaFSUcG13KeLMQsG2UOB2OjSC8FkOXK00NBlAjU3d0Vv-IamaLIszO7FQBY3Oh0uxNOvIE9ofQyCOpB-xIK6V9CTTphxw\"}]}";
let jwks: JWKS = serde_json::from_str(jwks_json)
.expect("Failed to decode JWKS");
let jwks: JWKS = serde_json::from_str(jwks_json).expect("Failed to decode JWKS");
let jwk = jwks.find("8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=")
let jwk = jwks
.find("8rDq8Pw0FZcaoXWTEVQo7+Tf2YzSL1fBxNKPCebaai4=")
.expect("Failed to find required JWK");
let pkey = public_key_from_jwk(&jwk).expect("Failed to construct public key");

View file

@ -33,9 +33,12 @@
//! use crimp::Request;
//!
//! let response = Request::get("http://httpbin.org/get")
//! .user_agent("crimp test suite").unwrap()
//! .send().unwrap()
//! .as_string().unwrap();
//! .user_agent("crimp test suite")
//! .unwrap()
//! .send()
//! .unwrap()
//! .as_string()
//! .unwrap();
//!
//! println!("Status: {}\nBody: {}", response.status, response.body);
//! # assert_eq!(response.status, 200);
@ -54,10 +57,9 @@
//!
//! All optional features are enabled by default.
//!
//! * `json`: Adds `Request::json` and `Response::as_json` methods
//! which can be used for convenient serialisation of
//! request/response bodies using `serde_json`. This feature adds a
//! dependency on the `serde` and `serde_json` crates.
//! * `json`: Adds `Request::json` and `Response::as_json` methods which can be used for convenient
//! serialisation of request/response bodies using `serde_json`. This feature adds a dependency on
//! the `serde` and `serde_json` crates.
//!
//! ## Initialisation
//!
@ -72,32 +74,42 @@
extern crate curl;
#[cfg(feature = "json")] extern crate serde;
#[cfg(feature = "json")] extern crate serde_json;
#[cfg(feature = "json")]
extern crate serde;
#[cfg(feature = "json")]
extern crate serde_json;
pub use curl::init;
use curl::easy::{Auth, Easy, Form, List, Transfer, ReadError, WriteError};
use curl::easy::{Auth, Easy, Form, List, ReadError, Transfer, WriteError};
use std::collections::HashMap;
use std::io::Write;
use std::path::Path;
use std::string::{FromUtf8Error, ToString};
use std::time::Duration;
#[cfg(feature = "json")] use serde::Serialize;
#[cfg(feature = "json")] use serde::de::DeserializeOwned;
#[cfg(feature = "json")]
use serde::de::DeserializeOwned;
#[cfg(feature = "json")]
use serde::Serialize;
#[cfg(test)]
mod tests;
/// HTTP method to use for the request.
enum Method {
Get, Post, Put, Patch, Delete
Get,
Post,
Put,
Patch,
Delete,
}
/// Certificate types for client-certificate key pairs.
pub enum CertType {
P12, PEM, DER
P12,
PEM,
DER,
}
/// Builder structure for an HTTP request.
@ -145,7 +157,7 @@ pub struct Response<T> {
pub body: T,
}
impl <'a> Request<'a> {
impl<'a> Request<'a> {
/// Initiate an HTTP request with the given method and URL.
fn new(method: Method, url: &'a str) -> Self {
Request {
@ -158,19 +170,29 @@ impl <'a> Request<'a> {
}
/// Initiate a GET request with the given URL.
pub fn get(url: &'a str) -> Self { Request::new(Method::Get, url) }
pub fn get(url: &'a str) -> Self {
Request::new(Method::Get, url)
}
/// Initiate a POST request with the given URL.
pub fn post(url: &'a str) -> Self { Request::new(Method::Post, url) }
pub fn post(url: &'a str) -> Self {
Request::new(Method::Post, url)
}
/// Initiate a PUT request with the given URL.
pub fn put(url: &'a str) -> Self { Request::new(Method::Put, url) }
pub fn put(url: &'a str) -> Self {
Request::new(Method::Put, url)
}
/// Initiate a PATCH request with the given URL.
pub fn patch(url: &'a str) -> Self { Request::new(Method::Patch, url) }
pub fn patch(url: &'a str) -> Self {
Request::new(Method::Patch, url)
}
/// Initiate a DELETE request with the given URL.
pub fn delete(url: &'a str) -> Self { Request::new(Method::Delete, url) }
pub fn delete(url: &'a str) -> Self {
Request::new(Method::Delete, url)
}
/// Add an HTTP header to a request.
pub fn header(mut self, k: &str, v: &str) -> Result<Self, curl::Error> {
@ -188,7 +210,8 @@ impl <'a> Request<'a> {
/// Set the `Authorization` header to a `Bearer` value with the
/// supplied token.
pub fn bearer_auth(mut self, token: &str) -> Result<Self, curl::Error> {
self.headers.append(&format!("Authorization: Bearer {}", token))?;
self.headers
.append(&format!("Authorization: Bearer {}", token))?;
Ok(self)
}
@ -212,8 +235,11 @@ impl <'a> Request<'a> {
/// Consult the documentation for the `ssl_cert` and `ssl_key`
/// functions in `curl::easy::Easy2` for details on supported
/// formats and defaults.
pub fn tls_client_cert<P: AsRef<Path>>(mut self, cert_type: CertType, cert: P)
-> Result<Self, curl::Error> {
pub fn tls_client_cert<P: AsRef<Path>>(
mut self,
cert_type: CertType,
cert: P,
) -> Result<Self, curl::Error> {
self.handle.ssl_cert(cert)?;
self.handle.ssl_cert_type(match cert_type {
CertType::P12 => "P12",
@ -262,13 +288,17 @@ impl <'a> Request<'a> {
/// ```
/// # use crimp::Request;
/// let response = Request::get("https://httpbin.org/get")
/// .with_handle(|mut handle| handle.referer("Example-Referer")).unwrap()
/// .send().unwrap();
/// .with_handle(|mut handle| handle.referer("Example-Referer"))
/// .unwrap()
/// .send()
/// .unwrap();
/// #
/// # assert!(response.is_success());
/// ```
pub fn with_handle<F>(mut self, function: F) -> Result<Self, curl::Error>
where F: FnOnce(&mut Easy) -> Result<(), curl::Error> {
where
F: FnOnce(&mut Easy) -> Result<(), curl::Error>,
{
function(&mut self.handle)?;
Ok(self)
}
@ -293,12 +323,15 @@ impl <'a> Request<'a> {
/// let mut form = Form::new();
/// form.part("some-name")
/// .contents("some-data".as_bytes())
/// .add().unwrap();
/// .add()
/// .unwrap();
///
/// let response = Request::post("https://httpbin.org/post")
/// .user_agent("crimp test suite").unwrap()
/// .user_agent("crimp test suite")
/// .unwrap()
/// .form(form)
/// .send().unwrap();
/// .send()
/// .unwrap();
/// #
/// # assert_eq!(200, response.status, "form POST should succeed");
/// # assert_eq!(
@ -330,10 +363,10 @@ impl <'a> Request<'a> {
self.handle.url(self.url)?;
match self.method {
Method::Get => self.handle.get(true)?,
Method::Post => self.handle.post(true)?,
Method::Put => self.handle.put(true)?,
Method::Patch => self.handle.custom_request("PATCH")?,
Method::Get => self.handle.get(true)?,
Method::Post => self.handle.post(true)?,
Method::Put => self.handle.put(true)?,
Method::Patch => self.handle.custom_request("PATCH")?,
Method::Delete => self.handle.custom_request("DELETE")?,
}
@ -351,21 +384,22 @@ impl <'a> Request<'a> {
// Optionally set content type if a body payload is configured
// and configure the expected body size (or form payload).
match self.body {
match self.body {
Body::Bytes { content_type, data } => {
self.handle.post_field_size(data.len() as u64)?;
self.headers.append(&format!("Content-Type: {}", content_type))?;
},
self.headers
.append(&format!("Content-Type: {}", content_type))?;
}
#[cfg(feature = "json")]
Body::Json(ref data) => {
self.handle.post_field_size(data.len() as u64)?;
self.headers.append("Content-Type: application/json")?;
},
}
// Do not set content-type header at all if there is no
// body, or if the form handler was invoked above.
_ => (),
// Do not set content-type header at all if there is no
// body, or if the form handler was invoked above.
_ => (),
};
// Configure headers on the request:
@ -407,9 +441,7 @@ impl <'a> Request<'a> {
return true;
}
headers.insert(
split[0].trim().to_string(), split[1].trim().to_string()
);
headers.insert(split[0].trim().to_string(), split[1].trim().to_string());
true
})?;
@ -427,7 +459,7 @@ impl <'a> Request<'a> {
Ok(Response {
status: self.handle.response_code()?,
headers,
body
body,
})
}
}
@ -438,13 +470,14 @@ impl <'a> Request<'a> {
///
/// As we manually set the expected upload size, cURL will call the
/// read callback repeatedly until it has all the data it needs.
fn chunked_read_function<'easy, 'data>(transfer: &mut Transfer<'easy, 'data>,
data: &'data [u8]) -> Result<(), curl::Error> {
fn chunked_read_function<'easy, 'data>(
transfer: &mut Transfer<'easy, 'data>,
data: &'data [u8],
) -> Result<(), curl::Error> {
let mut data = data;
transfer.read_function(move |mut into| {
let written = into.write(data)
.map_err(|_| ReadError::Abort)?;
let written = into.write(data).map_err(|_| ReadError::Abort)?;
data = &data[written..];
@ -452,7 +485,7 @@ fn chunked_read_function<'easy, 'data>(transfer: &mut Transfer<'easy, 'data>,
})
}
impl <T> Response<T> {
impl<T> Response<T> {
/// Check whether the status code of this HTTP response is a
/// success (i.e. in the 200-299 range).
pub fn is_success(&self) -> bool {
@ -466,9 +499,11 @@ impl <T> Response<T> {
/// This function exists for convenience to avoid having to write
/// repetitive `if !response.is_success() { ... }` blocks.
pub fn error_for_status<F, E>(self, closure: F) -> Result<Self, E>
where F: FnOnce(Self) -> E {
where
F: FnOnce(Self) -> E,
{
if !self.is_success() {
return Err(closure(self))
return Err(closure(self));
}
Ok(self)

View file

@ -6,7 +6,7 @@
// docker run --rm -p 4662:80 kennethreitz/httpbin
use super::*;
use serde_json::{Value, json};
use serde_json::{json, Value};
// These tests check whether the correct HTTP method is used in the
// requests.
@ -14,7 +14,8 @@ use serde_json::{Value, json};
#[test]
fn test_http_get() {
let resp = Request::get("http://127.0.0.1:4662/get")
.send().expect("failed to send request");
.send()
.expect("failed to send request");
assert!(resp.is_success(), "request should have succeeded");
}
@ -22,7 +23,8 @@ fn test_http_get() {
#[test]
fn test_http_delete() {
let resp = Request::delete("http://127.0.0.1:4662/delete")
.send().expect("failed to send request");
.send()
.expect("failed to send request");
assert_eq!(200, resp.status, "response status should be 200 OK");
}
@ -30,7 +32,8 @@ fn test_http_delete() {
#[test]
fn test_http_put() {
let resp = Request::put("http://127.0.0.1:4662/put")
.send().expect("failed to send request");
.send()
.expect("failed to send request");
assert_eq!(200, resp.status, "response status should be 200 OK");
}
@ -38,7 +41,8 @@ fn test_http_put() {
#[test]
fn test_http_patch() {
let resp = Request::patch("http://127.0.0.1:4662/patch")
.send().expect("failed to send request");
.send()
.expect("failed to send request");
assert_eq!(200, resp.status, "response status should be 200 OK");
}
@ -50,18 +54,25 @@ fn test_http_patch() {
fn test_http_post() {
let body = "test body";
let response = Request::post("http://127.0.0.1:4662/post")
.user_agent("crimp test suite").expect("failed to set user-agent")
.timeout(Duration::from_secs(5)).expect("failed to set request timeout")
.user_agent("crimp test suite")
.expect("failed to set user-agent")
.timeout(Duration::from_secs(5))
.expect("failed to set request timeout")
.body("text/plain", &body.as_bytes())
.send().expect("failed to send request")
.as_json::<Value>().expect("failed to deserialize response");
.send()
.expect("failed to send request")
.as_json::<Value>()
.expect("failed to deserialize response");
let data = response.body;
assert_eq!(200, response.status, "response status should be 200 OK");
assert_eq!(data.get("data").unwrap(), &json!("test body"),
"test body should have been POSTed");
assert_eq!(
data.get("data").unwrap(),
&json!("test body"),
"test body should have been POSTed"
);
assert_eq!(
data.get("headers").unwrap().get("Content-Type").unwrap(),
@ -70,26 +81,34 @@ fn test_http_post() {
);
}
#[cfg(feature = "json")] #[test]
#[cfg(feature = "json")]
#[test]
fn test_http_post_json() {
let body = json!({
"purpose": "testing!"
});
let response = Request::post("http://127.0.0.1:4662/post")
.user_agent("crimp test suite").expect("failed to set user-agent")
.timeout(Duration::from_secs(5)).expect("failed to set request timeout")
.json(&body).expect("request serialization failed")
.send().expect("failed to send request")
.as_json::<Value>().expect("failed to deserialize response");
.user_agent("crimp test suite")
.expect("failed to set user-agent")
.timeout(Duration::from_secs(5))
.expect("failed to set request timeout")
.json(&body)
.expect("request serialization failed")
.send()
.expect("failed to send request")
.as_json::<Value>()
.expect("failed to deserialize response");
let data = response.body;
assert_eq!(200, response.status, "response status should be 200 OK");
assert_eq!(data.get("json").unwrap(), &body,
"test body should have been POSTed");
assert_eq!(
data.get("json").unwrap(),
&body,
"test body should have been POSTed"
);
assert_eq!(
data.get("headers").unwrap().get("Content-Type").unwrap(),
@ -104,8 +123,10 @@ fn test_http_post_json() {
#[test]
fn test_bearer_auth() {
let response = Request::get("http://127.0.0.1:4662/bearer")
.bearer_auth("some-token").expect("failed to set auth header")
.send().expect("failed to send request");
.bearer_auth("some-token")
.expect("failed to set auth header")
.send()
.expect("failed to send request");
assert!(response.is_success(), "authorized request should succeed");
}
@ -115,8 +136,10 @@ fn test_basic_auth() {
let request = Request::get("http://127.0.0.1:4662/basic-auth/alan_watts/oneness");
let response = request
.basic_auth("alan_watts", "oneness").expect("failed to set auth header")
.send().expect("failed to send request");
.basic_auth("alan_watts", "oneness")
.expect("failed to set auth header")
.send()
.expect("failed to send request");
assert!(response.is_success(), "authorized request should succeed");
}
@ -129,14 +152,20 @@ fn test_large_body() {
let resp = Request::post("http://127.0.0.1:4662/post")
.body("application/octet-stream", &[0; BODY_SIZE])
.send().expect("sending request")
.as_json::<Value>().expect("JSON deserialisation");
.send()
.expect("sending request")
.as_json::<Value>()
.expect("JSON deserialisation");
// httpbin returns the uploaded data as a string in the `data`
// field.
let data = resp.body.get("data").unwrap().as_str().unwrap();
assert_eq!(BODY_SIZE, data.len(), "uploaded data length should be correct");
assert_eq!(
BODY_SIZE,
data.len(),
"uploaded data length should be correct"
);
}
// Tests for various other features.
@ -144,9 +173,13 @@ fn test_large_body() {
#[test]
fn test_error_for_status() {
let response = Request::get("http://127.0.0.1:4662/patch")
.send().expect("failed to send request")
.send()
.expect("failed to send request")
.error_for_status(|resp| format!("Response error code: {}", resp.status));
assert_eq!(Err("Response error code: 405".into()), response,
"returned error should be converted into Result::Err");
assert_eq!(
Err("Response error code: 405".into()),
response,
"returned error should be converted into Result::Err"
);
}

View file

@ -1,11 +1,11 @@
extern crate serde_json;
use serde_json::Value;
use std::convert::TryFrom;
use std::ffi::OsString;
use std::os::unix::ffi::{OsStringExt, OsStrExt};
use std::io::{Error, ErrorKind, Write, stdout, stderr};
use std::io::{stderr, stdout, Error, ErrorKind, Write};
use std::os::unix::ffi::{OsStrExt, OsStringExt};
use std::process::Command;
use std::convert::{TryFrom};
fn render_nix_string(s: &OsString) -> OsString {
let mut rendered = Vec::new();
@ -16,8 +16,8 @@ fn render_nix_string(s: &OsString) -> OsString {
match char::from(*b) {
'\"' => rendered.extend(b"\\\""),
'\\' => rendered.extend(b"\\\\"),
'$' => rendered.extend(b"\\$"),
_ => rendered.push(*b),
'$' => rendered.extend(b"\\$"),
_ => rendered.push(*b),
}
}
@ -48,17 +48,14 @@ fn render_nix_list(arr: &[OsString]) -> OsString {
macro_rules! handle_set_output {
($map_name:ident, $output_name:ident) => {
match $map_name.get(stringify!($output_name)) {
Some(Value::String(s)) =>
$output_name().write_all(s.as_bytes()),
Some(_) => Err(
Error::new(
ErrorKind::Other,
format!("Attribute {} must be a string!", stringify!($output_name)),
)
),
Some(Value::String(s)) => $output_name().write_all(s.as_bytes()),
Some(_) => Err(Error::new(
ErrorKind::Other,
format!("Attribute {} must be a string!", stringify!($output_name)),
)),
None => Ok(()),
}
}
};
}
fn main() -> std::io::Result<()> {
@ -83,7 +80,7 @@ fn main() -> std::io::Result<()> {
}
if in_args {
match(arg.to_str()) {
match (arg.to_str()) {
Some("--arg") | Some("--argstr") => {
nix_args.push(arg);
nix_args.push(args.next().unwrap());
@ -116,9 +113,7 @@ fn main() -> std::io::Result<()> {
nix_args.push(argv[0].clone());
let run = Command::new("nix-instantiate")
.args(nix_args)
.output()?;
let run = Command::new("nix-instantiate").args(nix_args).output()?;
match serde_json::from_slice(&run.stdout[..]) {
Ok(Value::String(s)) => stdout().write_all(s.as_bytes()),
@ -132,25 +127,23 @@ fn main() -> std::io::Result<()> {
match code {
Some(i) => std::process::exit(i),
None => Err(
Error::new(
ErrorKind::Other,
"Attribute exit is not an i32"
)
),
None => {
Err(Error::new(ErrorKind::Other, "Attribute exit is not an i32"))
}
}
},
Some(_) => Err(
Error::new(ErrorKind::Other, "exit must be a number")
),
}
Some(_) => Err(Error::new(ErrorKind::Other, "exit must be a number")),
None => Ok(()),
}
},
Ok(_) => Err(Error::new(ErrorKind::Other, "output must be a string or an object")),
}
Ok(_) => Err(Error::new(
ErrorKind::Other,
"output must be a string or an object",
)),
_ => {
stderr().write_all(&run.stderr[..]);
Err(Error::new(ErrorKind::Other, "internal nix error"))
},
}
}
}
}

View file

@ -1,6 +1,5 @@
extern crate pkg_config;
fn main() {
pkg_config::probe_library("libsystemd")
.expect("Could not probe libsystemd");
pkg_config::probe_library("libsystemd").expect("Could not probe libsystemd");
}

View file

@ -31,11 +31,16 @@
//! `GOOGLE_APPLICATION_CREDENTIALS`, `GOOGLE_CLOUD_PROJECT` and
//! `LOG_NAME` environment variables.
#[macro_use] extern crate failure;
#[macro_use] extern crate log;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate serde_json;
#[macro_use] extern crate lazy_static;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
extern crate chrono;
extern crate env_logger;
@ -48,13 +53,11 @@ use chrono::offset::LocalResult;
use chrono::prelude::{DateTime, TimeZone, Utc};
use failure::ResultExt;
use serde_json::{from_str, Value};
use std::env;
use std::fs::{self, File, rename};
use std::io::{self, Read, ErrorKind, Write};
use std::mem;
use std::fs::{self, rename, File};
use std::io::{self, ErrorKind, Read, Write};
use std::path::PathBuf;
use std::process;
use std::time::{Duration, Instant};
use std::{env, mem, process};
use systemd::journal::{Journal, JournalFiles, JournalRecord, JournalSeek};
#[cfg(test)]
@ -62,10 +65,12 @@ mod tests;
const LOGGING_SERVICE: &str = "https://logging.googleapis.com/google.logging.v2.LoggingServiceV2";
const ENTRIES_WRITE_URL: &str = "https://logging.googleapis.com/v2/entries:write";
const METADATA_TOKEN_URL: &str = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
const METADATA_TOKEN_URL: &str =
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
const METADATA_ID_URL: &str = "http://metadata.google.internal/computeMetadata/v1/instance/id";
const METADATA_ZONE_URL: &str = "http://metadata.google.internal/computeMetadata/v1/instance/zone";
const METADATA_PROJECT_URL: &str = "http://metadata.google.internal/computeMetadata/v1/project/project-id";
const METADATA_PROJECT_URL: &str =
"http://metadata.google.internal/computeMetadata/v1/project/project-id";
/// Convenience type alias for results using failure's `Error` type.
type Result<T> = std::result::Result<T, failure::Error>;
@ -134,14 +139,17 @@ fn get_metadata(url: &str) -> Result<String> {
if response.ok() {
// Whitespace is trimmed to remove newlines from responses.
let body = response.into_string()
let body = response
.into_string()
.context("Failed to decode metadata response")?
.trim().to_string();
.trim()
.to_string();
Ok(body)
} else {
let status = response.status_line().to_string();
let body = response.into_string()
let body = response
.into_string()
.unwrap_or_else(|e| format!("Metadata body error: {}", e));
bail!("Metadata failure: {} ({})", body, status)
}
@ -186,11 +194,9 @@ fn determine_monitored_resource() -> Value {
}
})
} else {
let instance_id = get_metadata(METADATA_ID_URL)
.expect("Could not determine instance ID");
let instance_id = get_metadata(METADATA_ID_URL).expect("Could not determine instance ID");
let zone = get_metadata(METADATA_ZONE_URL)
.expect("Could not determine instance zone");
let zone = get_metadata(METADATA_ZONE_URL).expect("Could not determine instance zone");
json!({
"type": "gce_instance",
@ -253,7 +259,8 @@ fn sign_service_account_token(credentials: &Credentials) -> Result<Token> {
use medallion::{Algorithm, Header, Payload};
let iat = Utc::now();
let exp = iat.checked_add_signed(chrono::Duration::seconds(3600))
let exp = iat
.checked_add_signed(chrono::Duration::seconds(3600))
.ok_or_else(|| format_err!("Failed to calculate token expiry"))?;
let header = Header {
@ -323,7 +330,9 @@ enum Payload {
/// text format.
fn message_to_payload(message: Option<String>) -> Payload {
match message {
None => Payload::TextPayload { text_payload: "empty log entry".into() },
None => Payload::TextPayload {
text_payload: "empty log entry".into(),
},
Some(text_payload) => {
// Attempt to deserialize the text payload as a generic
// JSON value.
@ -333,7 +342,7 @@ fn message_to_payload(message: Option<String>) -> Payload {
// expect other types of JSON payload) and return it
// in that case.
if json_payload.is_object() {
return Payload::JsonPayload { json_payload }
return Payload::JsonPayload { json_payload };
}
}
@ -450,9 +459,7 @@ impl From<JournalRecord> for LogEntry {
// Journald uses syslogd's concept of priority. No idea if this is
// always present, but it's optional in the Stackdriver API, so we just
// omit it if we can't find or parse it.
let severity = record
.remove("PRIORITY")
.and_then(priority_to_severity);
let severity = record.remove("PRIORITY").and_then(priority_to_severity);
LogEntry {
payload,
@ -468,8 +475,7 @@ impl From<JournalRecord> for LogEntry {
/// Attempt to read from the journal. If no new entry is present,
/// await the next one up to the specified timeout.
fn receive_next_record(timeout: Duration, journal: &mut Journal)
-> Result<Option<JournalRecord>> {
fn receive_next_record(timeout: Duration, journal: &mut Journal) -> Result<Option<JournalRecord>> {
let next_record = journal.next_record()?;
if next_record.is_some() {
return Ok(next_record);
@ -525,11 +531,10 @@ fn persist_cursor(cursor: String) -> Result<()> {
if cursor.is_empty() {
error!("Received empty journald cursor position, refusing to persist!");
error!("Please report this message at https://github.com/tazjin/journaldriver/issues/2");
return Ok(())
return Ok(());
}
let mut file = File::create(&*CURSOR_TMP_FILE)
.context("Failed to create cursor file")?;
let mut file = File::create(&*CURSOR_TMP_FILE).context("Failed to create cursor file")?;
write!(file, "{}", cursor).context("Failed to write cursor file")?;
@ -547,9 +552,7 @@ fn persist_cursor(cursor: String) -> Result<()> {
///
/// If flushing is successful the last cursor position will be
/// persisted to disk.
fn flush(token: &mut Token,
entries: Vec<LogEntry>,
cursor: String) -> Result<()> {
fn flush(token: &mut Token, entries: Vec<LogEntry>, cursor: String) -> Result<()> {
if token.is_expired() {
debug!("Refreshing Google metadata access token");
let new_token = get_token()?;
@ -598,7 +601,8 @@ fn write_entries(token: &Token, request: Value) -> Result<()> {
Ok(())
} else {
let status = response.status_line().to_string();
let body = response.into_string()
let body = response
.into_string()
.unwrap_or_else(|_| "no response body".into());
bail!("Write failure: {} ({})", body, status)
}
@ -624,14 +628,12 @@ fn initial_cursor() -> Result<JournalSeek> {
Err(ref err) if err.kind() == ErrorKind::NotFound => {
info!("No previous cursor position, reading from journal tail");
Ok(JournalSeek::Tail)
},
Err(err) => {
(Err(err).context("Could not read cursor position"))?
}
Err(err) => (Err(err).context("Could not read cursor position"))?,
}
}
fn main () {
fn main() {
env_logger::init();
// The directory in which cursor positions are persisted should
@ -641,17 +643,17 @@ fn main () {
process::exit(1);
}
let cursor_position_dir = CURSOR_FILE.parent()
let cursor_position_dir = CURSOR_FILE
.parent()
.expect("Invalid cursor position file path");
fs::create_dir_all(cursor_position_dir)
.expect("Could not create directory to store cursor position in");
let mut journal = Journal::open(JournalFiles::All, false, true)
.expect("Failed to open systemd journal");
let mut journal =
Journal::open(JournalFiles::All, false, true).expect("Failed to open systemd journal");
let seek_position = initial_cursor()
.expect("Failed to determine initial cursor position");
let seek_position = initial_cursor().expect("Failed to determine initial cursor position");
match journal.seek(seek_position) {
Ok(cursor) => info!("Opened journal at cursor '{}'", cursor),

View file

@ -15,7 +15,10 @@ fn test_text_entry_serialization() {
let expected = "{\"labels\":null,\"textPayload\":\"test entry\"}";
let result = to_string(&entry).expect("serialization failed");
assert_eq!(expected, result, "Plain text payload should serialize correctly")
assert_eq!(
expected, result,
"Plain text payload should serialize correctly"
)
}
#[test]
@ -26,7 +29,7 @@ fn test_json_entry_serialization() {
payload: Payload::JsonPayload {
json_payload: json!({
"message": "JSON test"
})
}),
},
severity: None,
};
@ -45,7 +48,10 @@ fn test_plain_text_payload() {
text_payload: "plain text payload".into(),
};
assert_eq!(expected, payload, "Plain text payload should be detected correctly");
assert_eq!(
expected, payload,
"Plain text payload should be detected correctly"
);
}