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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::fmt::Debug;
use std::io;
use js_sys::{ArrayBuffer, Uint8Array};
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use crate::net::{Headers, HeadersIter};
use crate::prelude::*;
use crate::utils::ResultExt;
/// An HTTP Fetch Response.
#[derive(Debug)]
pub struct Response {
inner: web_sys::Response,
headers: Headers,
}
impl Response {
/// Create a new instance of `Response`.
pub(crate) fn new(res: web_sys::Response) -> Self {
let headers = Headers::new(res.headers());
let inner = res;
Self { inner, headers }
}
/// Get the HTTP return status code.
pub fn status(&self) -> u16 {
self.inner.status()
}
/// Get the response body as bytes
///
/// # Implementation notes
///
/// This consumes `self` to ensure that the body stream will not throw a
/// `lock` error.
///
/// # Errors
///
/// An `io::ErrorKind::ConnectionAborted` error will be returned if a
/// connection error occurred.
pub async fn body_bytes(self) -> io::Result<Vec<u8>> {
let fut = JsFuture::from(self.inner.array_buffer().unwrap_throw());
// Connections can be "locked" or "disturbed". Because we consume `self`
// the only error here can be "disturbed" which is akin to an aborted
// connection.
let res = fut.await.err_kind(io::ErrorKind::ConnectionAborted)?;
debug_assert!(res.is_instance_of::<js_sys::ArrayBuffer>());
let buf: ArrayBuffer = res.dyn_into().unwrap_throw();
let slice = Uint8Array::new(&buf);
let mut buf: Vec<u8> = vec![0; slice.length() as usize];
slice.copy_to(&mut buf);
Ok(buf)
}
/// Get the response body as a String.
///
/// # Implementation notes
///
/// This consumes `self` to ensure that the body stream will not throw a
/// `lock` error.
///
/// # Errors
///
/// An `io::ErrorKind::ConnectionAborted` error will be returned if a
/// connection error occurred.
pub async fn body_string(self) -> io::Result<String> {
let fut = JsFuture::from(self.inner.text().unwrap_throw());
// Connections can be "locked" or "disturbed". Because we consume `self`
// the only error here can be "disturbed" which is akin to an aborted
// connection.
let res = fut.await.err_kind(io::ErrorKind::ConnectionAborted)?;
debug_assert!(res.is_instance_of::<js_sys::JsString>());
let string: js_sys::JsString = res.dyn_into().unwrap_throw();
Ok(string.into())
}
/// Get a header.
pub fn header(&self, name: &str) -> Option<String> {
self.headers.inner.get(name).unwrap_throw()
}
/// Insert a header into the request.
pub fn insert_header(&self, name: &str, val: &str) {
self.headers.inner.append(name, val).unwrap_throw();
}
/// Check whether the request contains a header.
pub fn contains_header(&self, name: &str) -> bool {
self.headers.inner.has(name).unwrap_throw()
}
/// Remove a header from the `Request`.
///
/// # Implementation Note
///
/// Unlike other ecosystem crates this does not
pub fn remove_header(&self, name: &str) {
self.headers.inner.delete(name).unwrap_throw();
}
/// Get an iterator over all headers.
pub fn headers(&self) -> HeadersIter {
self.headers.iter()
}
/// Get the length of the body if it's been set.
pub fn body_len(&self) -> Option<usize> {
self.header("content-length")
.map(|s| s.parse().ok())
.flatten()
}
}