Skip to content

Instantly share code, notes, and snippets.

@DoumanAsh
Created July 30, 2018 17:14
Show Gist options
  • Select an option

  • Save DoumanAsh/3b7e8d1f878653402ba75cd31cf409a2 to your computer and use it in GitHub Desktop.

Select an option

Save DoumanAsh/3b7e8d1f878653402ba75cd31cf409a2 to your computer and use it in GitHub Desktop.
pub struct Builder {
parts: http::request::Parts,
}
impl Builder {
///Starts process of creating request.
pub fn new(uri: hyper::Uri, method: hyper::Method) -> Self {
let parts = http::request::Parts {
method,
uri,
version: http::Version::default(),
headers: http::HeaderMap::default(),
extensions: http::Extensions::default()
};
Self {
parts
}
}
///Gets reference to headers.
pub fn headers(&mut self) -> &mut http::HeaderMap {
&mut self.parts.headers
}
///Sets new header to request.
///
///If header exists, it replaces it.
///
///# Panics
///
///- On attempt to set invalid header value.
pub fn set_header<K: header::IntoHeaderName, V>(mut self, key: K, value: V) -> Self where HeaderValue: HttpTryFrom<V> {
let value = match HeaderValue::try_from(value) {
Ok(value) => value,
Err(_) => panic!("Attempt to set invalid header"),
};
let _ = self.headers().insert(key, value);
self
}
#[inline]
///Sets new header to request, only if it wasn't set previously.
///
///# Panics
///
///- On attempt to set invalid header value.
pub fn set_header_if_none<K: header::AsHeaderName, V>(mut self, key: K, value: V) -> Self where HeaderValue: HttpTryFrom<V> {
match self.headers().entry(key).expect("Valid header name") {
http::header::Entry::Vacant(entry) => match HeaderValue::try_from(value) {
Ok(value) => {
entry.insert(value);
},
Err(_) => panic!("Attempt to set invalid header value")
},
_ => (),
}
self
}
#[inline]
///Sets `Content-Length` header.
///
///It replaces previous one, if there was any.
pub fn content_len(self, len: u64) -> Self {
self.set_header(http::header::CONTENT_LENGTH, len)
}
///Adds authentication header.
pub fn basic_auth<U: fmt::Display, P: fmt::Display>(mut self, username: U, password: Option<P>) -> Self {
const BASIC: &'static str = "basic ";
let auth = match password {
Some(password) => format!("{}:{}", username, password),
None => format!("{}:", username)
};
let encode_len = BASE64.encode_len(auth.as_bytes().len());
let header_value = unsafe {
let mut header_value = bytes::BytesMut::with_capacity(encode_len + BASIC.as_bytes().len());
header_value.put_slice(BASIC.as_bytes());
BASE64.encode_mut(auth.as_bytes(), &mut header_value.bytes_mut()[..encode_len]);
header_value.advance_mut(encode_len);
http::header::HeaderValue::from_shared_unchecked(header_value.freeze())
};
let _ = self.headers().insert(http::header::AUTHORIZATION, header_value);
self
}
///Sets request's query by overwriting existing one, if any.
///
///# Panics
///
///- If unable to encode data.
///- If URI creation fails
pub fn query<Q: Serialize>(mut self, query: Q) -> Self {
let mut uri_parts = self.parts.uri.into_parts();
let path = uri_parts.path_and_query;
let mut buffer = bytes::BytesMut::new().writer();
let query = serde_urlencoded::to_string(&query).expect("To url-encode");
let _ = match path {
Some(path) => write!(buffer, "{}?{}", path.path(), query),
None => write!(buffer, "/?{}", query),
};
uri_parts.path_and_query = Some(http::uri::PathAndQuery::from_shared(buffer.into_inner().freeze()).expect("To create path and query"));
self.parts.uri = match http::Uri::from_parts(uri_parts) {
Ok(uri) => uri,
Err(error) => panic!("Unable to set query for URI: {}", error)
};
self
}
///Creates request with specified body.
pub fn body<B: Into<hyper::Body>>(self, body: B) -> Request {
let inner = HyperRequest::from_parts(self.parts, body.into());
Request {
inner
}
}
///Creates request with Form payload.
pub fn form<F: Serialize>(self, body: F) -> Result<Request, serde_urlencoded::ser::Error> {
let body = serde_urlencoded::to_string(&body)?;
Ok(self.set_header_if_none(header::CONTENT_TYPE, "application/x-www-form-urlencoded").body(body))
}
///Creates request with JSON payload.
pub fn json<J: Serialize>(self, body: J) -> serde_json::Result<Request> {
let mut buffer = bytes::BytesMut::new().writer();
let _ = serde_json::to_writer(&mut buffer, &body)?;
let body = buffer.into_inner().freeze();
Ok(self.set_header_if_none(header::CONTENT_TYPE, "application/json").body(body))
}
///Creates request with no body.
///
///Explicitly sets `Content-Length` to 0
pub fn empty(self) -> Request {
self.content_len(0).body(hyper::Body::empty())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment