|
| 1 | +//! Common HTTP and proxy-attestation service-related functionality |
| 2 | +//! |
| 3 | +//! Provides material for posting buffers over HTTP, and for sending messages |
| 4 | +//! to the proxy attestation service over a HTTP interface. |
| 5 | +//! |
| 6 | +//! # Authors |
| 7 | +//! |
| 8 | +//! The Veracruz Development Team. |
| 9 | +//! |
| 10 | +//! # Copyright and licensing |
| 11 | +//! |
| 12 | +//! See the `LICENSE_MIT.markdown` file in the Veracruz root directory for copyright |
| 13 | +//! and licensing information. |
| 14 | +
|
| 15 | +use err_derive::Error; |
| 16 | +use log::{error, info}; |
| 17 | +use reqwest::{blocking, Error as ReqwestError, header, StatusCode}; |
| 18 | +use std::{collections::HashMap, string::String, vec::Vec}; |
| 19 | +use transport_protocol::{ |
| 20 | + ProxyAttestationServerResponse, TransportProtocolError, |
| 21 | +}; |
| 22 | + |
| 23 | +/////////////////////////////////////////////////////////////////////////////// |
| 24 | +// Errors. |
| 25 | +/////////////////////////////////////////////////////////////////////////////// |
| 26 | + |
| 27 | +/// HTTP-related errors. |
| 28 | +#[derive(Debug, Error)] |
| 29 | +pub enum HttpError { |
| 30 | + /// Reqwest generated an error |
| 31 | + #[error(display = "Reqwest generated an error:{}", _0)] |
| 32 | + ReqwestError(ReqwestError), |
| 33 | + /// Invalid Header value |
| 34 | + #[error(display = "Invalid header value:{}", _0)] |
| 35 | + InvalidHeaderValue(header::InvalidHeaderValue), |
| 36 | + /// An unexpected HTTP status code was returned. |
| 37 | + #[error(display = "An unexpected HTTP return code was returned.")] |
| 38 | + UnexpectedHttpCode, |
| 39 | + /// Response did not contain a field that we expected |
| 40 | + #[error(display = "Response did not contain an expected field.")] |
| 41 | + PoorlyFormedResponse, |
| 42 | + #[error( |
| 43 | + display = "A transport protocol message could not be (de)serialized: {}.", |
| 44 | + _0 |
| 45 | + )] |
| 46 | + SerializationError(TransportProtocolError), |
| 47 | + #[error( |
| 48 | + display = "A base64-encoded message could not be (de)serialized: {}.", |
| 49 | + _0 |
| 50 | + )] |
| 51 | + Base64Error(base64::DecodeError), |
| 52 | + #[error(display = "A transport protocol error occurred: {}.", _0)] |
| 53 | + TransportProtocolError(TransportProtocolError), |
| 54 | + #[error(display = "An attestation-related error occurred: {}.", _0)] |
| 55 | + AttestationError(TransportProtocolError), |
| 56 | + #[error(display = "The proxy attestation service issued an unexpected reply.")] |
| 57 | + ProtocolError(ProxyAttestationServerResponse), |
| 58 | + #[error(display = "Unable to convert bytes to UTF8: {}.", _0)] |
| 59 | + Utf8Error(std::str::Utf8Error), |
| 60 | +} |
| 61 | + |
| 62 | +/////////////////////////////////////////////////////////////////////////////// |
| 63 | +// HTTP-related functionality. |
| 64 | +/////////////////////////////////////////////////////////////////////////////// |
| 65 | + |
| 66 | +#[derive(Debug)] |
| 67 | +pub enum HttpResponse { |
| 68 | + Ok(Vec<u8>), // 200: Body |
| 69 | + Created(String, Vec<u8>), //201: Location, Body |
| 70 | + Accepted(Vec<u8>), // 202: Body |
| 71 | + NonAuthoritativeInformation(Vec<u8>), // 203: Body |
| 72 | + NoContent, // 204 |
| 73 | + ResetContent(Vec<u8>), // 205: Body |
| 74 | + PartialContent(Vec<u8>), // 206: Body |
| 75 | +} |
| 76 | + |
| 77 | +fn convert_reqwest_response_to_http_response(res: blocking::Response) -> Result<HttpResponse, HttpError> { |
| 78 | + let response = match res.status() { |
| 79 | + StatusCode::OK => { |
| 80 | + let body = res.bytes().map_err(|_| HttpError::PoorlyFormedResponse)?; |
| 81 | + HttpResponse::Ok(body.to_vec()) |
| 82 | + }, |
| 83 | + StatusCode::CREATED => { |
| 84 | + let location = match res.headers().get(header::LOCATION) { |
| 85 | + None => return Err(HttpError::PoorlyFormedResponse), |
| 86 | + Some(loc) => loc.to_str().map_err(|_| HttpError::PoorlyFormedResponse)?.to_string(), |
| 87 | + }; |
| 88 | + let body = res.bytes().map_err(|_| HttpError::PoorlyFormedResponse)?; |
| 89 | + |
| 90 | + HttpResponse::Created(location, body.to_vec()) |
| 91 | + } |
| 92 | + StatusCode::ACCEPTED => { |
| 93 | + let body = res.bytes().map_err(|_| HttpError::PoorlyFormedResponse)?; |
| 94 | + HttpResponse::Accepted(body.to_vec()) |
| 95 | + } |
| 96 | + StatusCode::NON_AUTHORITATIVE_INFORMATION => { |
| 97 | + let body = res.bytes().map_err(|_| HttpError::PoorlyFormedResponse)?; |
| 98 | + HttpResponse::NonAuthoritativeInformation(body.to_vec()) |
| 99 | + } |
| 100 | + StatusCode::NO_CONTENT => HttpResponse::NoContent, |
| 101 | + StatusCode::RESET_CONTENT => { |
| 102 | + let body = res.bytes().map_err(|_| HttpError::PoorlyFormedResponse)?; |
| 103 | + HttpResponse::ResetContent(body.to_vec()) |
| 104 | + } |
| 105 | + StatusCode::PARTIAL_CONTENT => { |
| 106 | + let body = res.bytes().map_err(|_| HttpError::PoorlyFormedResponse)?; |
| 107 | + HttpResponse::PartialContent(body.to_vec()) |
| 108 | + } |
| 109 | + _ => return Err(HttpError::UnexpectedHttpCode), |
| 110 | + }; |
| 111 | + return Ok(response); |
| 112 | +} |
| 113 | + |
| 114 | +/// Sends an encoded `buffer` via HTTP to a server at `url`. Fails if the |
| 115 | +/// Curl session fails for any reason, or if a non-success HTTP code is |
| 116 | +/// returned. |
| 117 | +pub fn post_buffer<U>(url: U, buffer: &String, content_type_option: Option<&str>) -> Result<HttpResponse, HttpError> |
| 118 | +where |
| 119 | + U: AsRef<str>, |
| 120 | +{ |
| 121 | + let url = url.as_ref(); |
| 122 | + let buffer: String = buffer.to_string(); |
| 123 | + |
| 124 | + info!( |
| 125 | + "Posting buffer {} ({} bytes) to {}.", |
| 126 | + buffer, |
| 127 | + buffer.len(), |
| 128 | + url |
| 129 | + ); |
| 130 | + |
| 131 | + let request_builder = blocking::Client::new() |
| 132 | + .post(url) |
| 133 | + .body(buffer); |
| 134 | + |
| 135 | + let request_builder = match content_type_option { |
| 136 | + Some(content_type) => { |
| 137 | + request_builder.header(header::CONTENT_TYPE, header::HeaderValue::from_str(content_type).map_err(|err| HttpError::InvalidHeaderValue(err))?) |
| 138 | + } |
| 139 | + None => request_builder, // do nothing |
| 140 | + }; |
| 141 | + let ret = request_builder.send() |
| 142 | + .map_err(|err| HttpError::ReqwestError(err))?; |
| 143 | + let response = convert_reqwest_response_to_http_response(ret)?; |
| 144 | + |
| 145 | + return Ok(response); |
| 146 | +} |
| 147 | + |
| 148 | +pub fn post_form<U: AsRef<str>>(url: U, form_data: &HashMap<String, String>) -> Result<HttpResponse, HttpError> { |
| 149 | + let url = url.as_ref(); |
| 150 | + let client_builder = blocking::ClientBuilder::new(); |
| 151 | + |
| 152 | + let client = client_builder.build() |
| 153 | + .map_err(|err| { |
| 154 | + HttpError::ReqwestError(err) |
| 155 | + })?; |
| 156 | + let mut form = blocking::multipart::Form::new(); |
| 157 | + for (key, value) in &*form_data { |
| 158 | + form = form.text(key.clone(), value.clone()); |
| 159 | + } |
| 160 | + |
| 161 | + let response = client.post(url) |
| 162 | + .multipart(form) |
| 163 | + .send() |
| 164 | + .map_err(|err| { |
| 165 | + HttpError::ReqwestError(err) |
| 166 | + })?; |
| 167 | + let response = convert_reqwest_response_to_http_response(response)?; |
| 168 | + return Ok(response); |
| 169 | +} |
0 commit comments