em_node_agent_client/client/
mod.rs1#![allow(unused_extern_crates)]
7extern crate mime;
8extern crate chrono;
9extern crate url;
10extern crate uuid;
11
12use hyper;
13use hyper::client::{Request, Response};
14use hyper::header::{Header, Headers, HeaderFormat, ContentType};
15use hyper::method::Method;
16use hyper::Url;
17use self::url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
18use futures;
19use futures::{Future, Stream};
20use futures::{future, stream};
21use std::borrow::Cow;
22use std::io::{Read, Error, ErrorKind};
23use std::error;
24use std::fmt;
25use std::path::Path;
26use std::sync::Arc;
27use std::str;
28use std::str::FromStr;
29use std::string::ToString;
30use mimetypes;
31use serde_json;
32
33#[allow(unused_imports)]
34use std::collections::{HashMap, BTreeMap};
35
36use crate::ApiError;
37
38use {
39 CertificateApi,
40 EnclaveApi,
41 SystemApi
42};
43
44use models;
45
46define_encode_set! {
47 pub ID_ENCODE_SET = [PATH_SEGMENT_ENCODE_SET] | {'|'}
52}
53
54fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> {
56 let url = Url::from_str(input)?;
58
59 let scheme = url.scheme();
60
61 if let Some(correct_scheme) = correct_scheme {
63 if scheme != correct_scheme {
64 return Err(ClientInitError::InvalidScheme);
65 }
66 }
67
68 let host = url.host().ok_or_else(|| ClientInitError::MissingHost)?;
69 let port = url.port().map(|x| format!(":{}", x)).unwrap_or_default();
70 Ok(format!("{}://{}{}", scheme, host, port))
71}
72
73pub struct Client {
75 hyper_client: Arc<hyper::client::Client>,
76 base_path: String,
77 headers: Headers,
78}
79
80impl fmt::Debug for Client {
81 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 write!(f, "Client {{ base_path: {} }}", self.base_path)
83 }
84}
85
86impl Clone for Client {
87 fn clone(&self) -> Self {
88 Client {
89 hyper_client: self.hyper_client.clone(),
90 base_path: self.base_path.clone(),
91 headers: self.headers.clone(),
92 }
93 }
94}
95
96impl Client {
97
98 pub fn try_new_http(base_path: &str) -> Result<Client, ClientInitError> {
103 Self::try_new_with_connector(
104 base_path,
105 Some("http"),
106 hyper::net::HttpConnector,
107 )
108 }
109
110 pub fn try_new_with_connector<C, S>(
126 base_path: &str,
127 protocol: Option<&'static str>,
128 connector: C,
129 ) -> Result<Client, ClientInitError>
130 where
131 C: hyper::net::NetworkConnector<Stream = S> + Send + Sync + 'static,
132 S: hyper::net::NetworkStream
133 {
134 let hyper_client = hyper::Client::with_connector(connector);
135
136 Ok(Client {
137 hyper_client: Arc::new(hyper_client),
138 base_path: into_base_path(base_path, protocol)?,
139 headers: Headers::new(),
140 })
141 }
142
143 pub fn try_new_with_hyper_client(
157 hyper_client: Arc<hyper::client::Client>,
158 base_path: &str
159 ) -> Result<Client, ClientInitError>
160 {
161 Ok(Client {
162 hyper_client: hyper_client,
163 base_path: into_base_path(base_path, None)?,
164 headers: Headers::new(),
165 })
166 }
167
168 pub fn headers(&mut self) -> &mut Headers {
169 &mut self.headers
170 }
171}
172
173impl CertificateApi for Client {
174
175 type Error = ApiError;
176
177
178 fn get_issue_certificate_response(&self, param_task_id: uuid::Uuid) -> Result<models::IssueCertificateResponse, ApiError> {
179 let mut url = format!(
180 "{}/v1/certificate/result/{task_id}",
181 self.base_path, task_id=utf8_percent_encode(¶m_task_id.to_string(), ID_ENCODE_SET)
182 );
183
184 let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
185
186 let query_string_str = query_string.finish();
187 if !query_string_str.is_empty() {
188 url += "?";
189 url += &query_string_str;
190 }
191
192 let url = match Url::from_str(&url) {
193 Ok(url) => url,
194 Err(err) => return Err(ApiError(format!("Unable to build URL: {}", err))),
195 };
196
197 let mut request = self.hyper_client.request(Method::Post, url);
198 request = request.headers(self.headers.clone());
199
200 request.send()
201 .map_err(|e| ApiError(format!("No response received: {}", e)))
202 .and_then(|mut response| {
203 match response.status.to_u16() {
204 200 => {
205 let mut body = Vec::new();
206 response.read_to_end(&mut body)
207 .map_err(|e| ApiError(format!("Failed to read response: {}", e)))?;
208
209 str::from_utf8(&body)
210 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
211 .and_then(|body|
212 serde_json::from_str::<models::IssueCertificateResponse>(body)
213 .map_err(|e| e.into())
214
215 )
216 },
217 code => {
218 let headers = response.headers.clone();
219 let mut body = Vec::new();
220 let result = response.read_to_end(&mut body);
221 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
222 code,
223 headers,
224 match result {
225 Ok(_) => match str::from_utf8(&body) {
226 Ok(body) => Cow::from(body),
227 Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
228 },
229 Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
230 })))
231 }
232 }
233 })
234
235 }
236
237 fn issue_certificate(&self, param_body: models::IssueCertificateRequest) -> Result<models::IssueCertificateResponse, ApiError> {
238 let mut url = format!(
239 "{}/v1/certificate/issue",
240 self.base_path
241 );
242
243 let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
244
245 let query_string_str = query_string.finish();
246 if !query_string_str.is_empty() {
247 url += "?";
248 url += &query_string_str;
249 }
250
251 let url = match Url::from_str(&url) {
252 Ok(url) => url,
253 Err(err) => return Err(ApiError(format!("Unable to build URL: {}", err))),
254 };
255
256 let mut request = self.hyper_client.request(Method::Post, url);
257 request = request.headers(self.headers.clone());
258 let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize");
259 request = request.body(body.as_bytes());
260
261 request = request.header(ContentType(mimetypes::requests::ISSUE_CERTIFICATE.clone()));
262
263 request.send()
264 .map_err(|e| ApiError(format!("No response received: {}", e)))
265 .and_then(|mut response| {
266 match response.status.to_u16() {
267 200 => {
268 let mut body = Vec::new();
269 response.read_to_end(&mut body)
270 .map_err(|e| ApiError(format!("Failed to read response: {}", e)))?;
271
272 str::from_utf8(&body)
273 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
274 .and_then(|body|
275 serde_json::from_str::<models::IssueCertificateResponse>(body)
276 .map_err(|e| e.into())
277
278 )
279 },
280 code => {
281 let headers = response.headers.clone();
282 let mut body = Vec::new();
283 let result = response.read_to_end(&mut body);
284 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
285 code,
286 headers,
287 match result {
288 Ok(_) => match str::from_utf8(&body) {
289 Ok(body) => Cow::from(body),
290 Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
291 },
292 Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
293 })))
294 }
295 }
296 })
297
298 }
299
300}
301
302impl EnclaveApi for Client {
303
304 type Error = ApiError;
305
306
307 fn get_fortanix_attestation(&self, param_body: models::GetFortanixAttestationRequest) -> Result<models::GetFortanixAttestationResponse, ApiError> {
308 let mut url = format!(
309 "{}/v1/enclave/attest",
310 self.base_path
311 );
312
313 let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
314
315 let query_string_str = query_string.finish();
316 if !query_string_str.is_empty() {
317 url += "?";
318 url += &query_string_str;
319 }
320
321 let url = match Url::from_str(&url) {
322 Ok(url) => url,
323 Err(err) => return Err(ApiError(format!("Unable to build URL: {}", err))),
324 };
325
326 let mut request = self.hyper_client.request(Method::Post, url);
327 request = request.headers(self.headers.clone());
328 let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize");
330 request = request.body(body.as_bytes());
331
332 request = request.header(ContentType(mimetypes::requests::GET_FORTANIX_ATTESTATION.clone()));
333
334 request.send()
335 .map_err(|e| ApiError(format!("No response received: {}", e)))
336 .and_then(|mut response| {
337 match response.status.to_u16() {
338 200 => {
339 let mut body = Vec::new();
340 response.read_to_end(&mut body)
341 .map_err(|e| ApiError(format!("Failed to read response: {}", e)))?;
342
343 str::from_utf8(&body)
344 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
345 .and_then(|body|
346 serde_json::from_str::<models::GetFortanixAttestationResponse>(body)
347 .map_err(|e| e.into())
348
349 )
350 },
351 code => {
352 let headers = response.headers.clone();
353 let mut body = Vec::new();
354 let result = response.read_to_end(&mut body);
355 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
356 code,
357 headers,
358 match result {
359 Ok(_) => match str::from_utf8(&body) {
360 Ok(body) => Cow::from(body),
361 Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
362 },
363 Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
364 })))
365 }
366 }
367 })
368
369 }
370
371 fn get_target_info(&self) -> Result<models::TargetInfo, ApiError> {
372 let mut url = format!(
373 "{}/v1/enclave/target-info",
374 self.base_path
375 );
376
377 let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
378
379 let query_string_str = query_string.finish();
380 if !query_string_str.is_empty() {
381 url += "?";
382 url += &query_string_str;
383 }
384
385 let url = match Url::from_str(&url) {
386 Ok(url) => url,
387 Err(err) => return Err(ApiError(format!("Unable to build URL: {}", err))),
388 };
389
390 let mut request = self.hyper_client.request(Method::Get, url);
391 request = request.headers(self.headers.clone());
392
393 request.send()
394 .map_err(|e| ApiError(format!("No response received: {}", e)))
395 .and_then(|mut response| {
396 match response.status.to_u16() {
397 200 => {
398 let mut body = Vec::new();
399 response.read_to_end(&mut body)
400 .map_err(|e| ApiError(format!("Failed to read response: {}", e)))?;
401
402 str::from_utf8(&body)
403 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
404 .and_then(|body|
405 serde_json::from_str::<models::TargetInfo>(body)
406 .map_err(|e| e.into())
407
408 )
409 },
410 code => {
411 let headers = response.headers.clone();
412 let mut body = Vec::new();
413 let result = response.read_to_end(&mut body);
414 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
415 code,
416 headers,
417 match result {
418 Ok(_) => match str::from_utf8(&body) {
419 Ok(body) => Cow::from(body),
420 Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
421 },
422 Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
423 })))
424 }
425 }
426 })
427
428 }
429
430}
431
432impl SystemApi for Client {
433
434 type Error = ApiError;
435
436
437 fn get_agent_version(&self) -> Result<models::VersionResponse, ApiError> {
438 let mut url = format!(
439 "{}/v1/sys/version",
440 self.base_path
441 );
442
443 let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
444
445 let query_string_str = query_string.finish();
446 if !query_string_str.is_empty() {
447 url += "?";
448 url += &query_string_str;
449 }
450
451 let url = match Url::from_str(&url) {
452 Ok(url) => url,
453 Err(err) => return Err(ApiError(format!("Unable to build URL: {}", err))),
454 };
455
456 let mut request = self.hyper_client.request(Method::Get, url);
457 request = request.headers(self.headers.clone());
458
459 request.send()
460 .map_err(|e| ApiError(format!("No response received: {}", e)))
461 .and_then(|mut response| {
462 match response.status.to_u16() {
463 200 => {
464 let mut body = Vec::new();
465 response.read_to_end(&mut body)
466 .map_err(|e| ApiError(format!("Failed to read response: {}", e)))?;
467
468 str::from_utf8(&body)
469 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
470 .and_then(|body|
471 serde_json::from_str::<models::VersionResponse>(body)
472 .map_err(|e| e.into())
473
474 )
475 },
476 code => {
477 let headers = response.headers.clone();
478 let mut body = Vec::new();
479 let result = response.read_to_end(&mut body);
480 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
481 code,
482 headers,
483 match result {
484 Ok(_) => match str::from_utf8(&body) {
485 Ok(body) => Cow::from(body),
486 Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
487 },
488 Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
489 })))
490 }
491 }
492 })
493
494 }
495
496}
497
498
499#[derive(Debug)]
500pub enum ClientInitError {
501 InvalidScheme,
502 InvalidUri(hyper::error::ParseError),
503 MissingHost,
504}
505
506impl From<hyper::error::ParseError> for ClientInitError {
507 fn from(err: hyper::error::ParseError) -> ClientInitError {
508 ClientInitError::InvalidUri(err)
509 }
510}
511
512impl fmt::Display for ClientInitError {
513 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
514 <Self as fmt::Debug>::fmt(self, f)
515 }
516}
517
518impl error::Error for ClientInitError {
519 fn description(&self) -> &str {
520 "Failed to produce a hyper client."
521 }
522}