1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use log::warn;
use mruby::value::{Value, ValueLike};
use mruby::Mrb;
use mruby::MrbError;
use std::collections::HashMap;
use std::convert::{self, TryFrom};
use crate::nemesis;
use crate::Error;
#[derive(Debug)]
pub struct Response {
pub status: u16,
pub headers: HashMap<String, String>,
pub body: Vec<u8>,
}
impl Response {
const RACK_RESPONSE_TUPLE_LEN: usize = 3;
pub fn from_rack_tuple(interp: &Mrb, response: &[Value]) -> Result<Self, Error> {
if response.len() != Self::RACK_RESPONSE_TUPLE_LEN {
warn!("malformed rack response: {:?}", response);
return Err(Error::RackResponse);
}
let spec = interp.borrow().class_spec::<nemesis::Response>();
let response = spec
.and_then(|spec| spec.borrow().new_instance(interp, response))
.ok_or_else(|| Error::Mrb(MrbError::NotDefined("Nemesis::Response".to_owned())))?;
Ok(Self {
status: Self::status(&response)?,
headers: Self::headers(&response)?,
body: Self::body(&response)?,
})
}
fn status(response: &Value) -> Result<u16, Error> {
let status = response.funcall::<i64, _, _>("status", &[])?;
u16::try_from(status).map_err(|_| Error::Status)
}
fn headers(response: &Value) -> Result<HashMap<String, String>, Error> {
let headers = response
.funcall::<Value, _, _>("header", &[])?
.funcall::<Value, _, _>("each", &[])?
.funcall::<HashMap<String, String>, _, _>("to_h", &[])?;
let headers = headers
.into_iter()
.filter(|(k, _v)| !k.starts_with("rack."))
.collect::<HashMap<_, _>>();
Ok(headers)
}
fn body(response: &Value) -> Result<Vec<u8>, Error> {
let body = response.funcall::<Value, _, _>("body", &[])?;
let parts = body
.funcall::<Value, _, _>("each", &[])?
.funcall::<Vec<Vec<u8>>, _, _>("to_a", &[])?;
let bytes = parts
.into_iter()
.flat_map(convert::identity)
.collect::<Vec<_>>();
if body.respond_to("close")? {
body.funcall::<(), _, _>("close", &[])?;
}
Ok(bytes)
}
}