-
Notifications
You must be signed in to change notification settings - Fork 146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Seanaye/feat/add misc methods #334
base: master
Are you sure you want to change the base?
Conversation
I've been dragging my feet on writing an RFC on these types. I'm a bit concerned with how In order for a method like /// RequestOptions is used to create a [`web_sys::RequestInit`].
struct RequestOptions {
method: http::Method,
headers: http::HeaderMap, // or our current `Headers` type
body: Option<Body>,
cache: web_sys::RequestCache, // These web_sys types are enums
credentials: web_sys::RequestCredentials,
integrity: String,
mode: web_sys::RequestMode,
redirect: web_sys::RequestRedirect,
referrer: String,
referrer_policy: web_sys::ReferrerPolicy,
// signal: Option<&'a web_sys::AbortSignal>,
}
/// A convenient builder for [`Request`].
#[derive(Debug)]
#[must_use = "RequestBuilder does nothing unless you assign a body or call `build`"]
pub struct RequestBuilder {
url: String,
init: RequestOptions,
}
/// A wrapper around [`web_sys::Request`]. Requests are immutable after they are created.
#[derive(Debug)]
pub struct Request {
inner: web_sys::Request, // Only used to `send` and process the `body`
// Cached Rust representations of the request inners.
url: String,
init: RequestOptions,
} I think this creates cleaner code: https://github.com/Alextopher/gloo/blob/http/crates/net/src/http/request.rs |
The problem I see with my suggestion: it's a lot of breaking changes. However, I think it makes the library more mature and idiomatic and opens up more features that you previously had to use |
#335 my current draft |
Could you please provide an example of where a mutation occurs with &T for the current code? I can't find anything other than reading the body which is a bit of a special case imo. I agree the builder needs the missing config fields. I looked at your PR I don't have any concerns other than breaking changes |
I think methods that consume the body should capture ownership over self, reading the body twice (without cloning) is almost always bug. But you're right my mutability concerns are mostly in QueryParams and Headers https://github.com/rustwasm/gloo/blob/master/crates/net/src/http/headers.rs#L61 |
I don't think its required to keep the init options inside of the |
Re: the duplicated reading, I think this is a matter of preference. I agree if the body was its own struct then reading it should take ownership but there is a lot of other data associated with the |
I see that, I think there's a tradeoff: Do we want this signature (which makes the returned value immutable) pub fn integrity(&self) -> &String {
&self.options.integrity
} Or is this good enough? pub fn integrity(&self) -> String {
self.raw.integrity()
} There's also a small time/space tradeoff. Using some extra space in wasm world can asymptomatically reduce the number of calls from wasm to js. What I've been able to do today : but I haven't pushed yet : is remove the the #[derive(Debug)]
pub struct Request {
url: String,
options: RequestOptions,
}
impl From<web_sys::Request> for Request {
fn from(value: web_sys::Request) -> Self {
Self {
url: value.url(),
options: RequestOptions {
method: http::Method::from_bytes(value.method().as_bytes()).unwrap(),
headers: headers_from_js(&value.headers()),
body: value.body().map(Body::from),
cache: value.cache(),
credentials: value.credentials(),
integrity: value.integrity(),
mode: value.mode(),
redirect: value.redirect(),
referrer: value.referrer(),
referrer_policy: value.referrer_policy(),
},
}
}
}
impl TryFrom<Request> for web_sys::Request {
type Error = crate::Error;
fn try_from(value: Request) -> Result<Self, Self::Error> {
let init: web_sys::RequestInit = value.options.into();
web_sys::Request::new_with_str_and_init(value.url.as_str(), &init).map_err(js_to_error)
}
} |
I dislike how it's possible to read the body multiple times. In I can think of changing the signature of send to return a tuple: struct Body(web_sys::ReadableStream);
impl Body {
async fn text(self) -> Result<String>;
// etc
}
impl Request {
fn send(self) -> Result<(Response, Body)>
} |
I think taking ownership is fine actually, just forces users to get what they want out of the response before reading the body. I will add a commit to take the ownership |
Do you think |
I'm not sure - there's probably some value of it with regards to backwards compatibility, Or if someone is making a lot of use of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! There's a couple of comments that need to be addressed
@@ -37,14 +44,14 @@ impl Headers { | |||
|
|||
/// This method appends a new value onto an existing header, or adds the header if it does not | |||
/// already exist. | |||
pub fn append(&self, name: &str, value: &str) { | |||
pub fn append(&mut self, name: &str, value: &str) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of making this &mut
. This makes the method unnecessarily restrictive.
CC: @futursolo, do you have any thoughts on this?
@@ -265,12 +265,12 @@ impl Request { | |||
} | |||
|
|||
/// Gets the body. | |||
pub fn body(&self) -> Option<ReadableStream> { | |||
pub fn body(self) -> Option<ReadableStream> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function can be called multiple times and still be valid.
serde_json::from_str::<T>(&self.text().await?).map_err(Error::from) | ||
} | ||
|
||
/// Reads the reqeust as a String. | ||
pub async fn text(&self) -> Result<String, Error> { | ||
pub async fn text(self) -> Result<String, Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Alextopher While I do think that doing it the |
We could also split bodies like Tower (I think it's Tower I'll double check later). But the idea is splitting the Response into (Body, Parts) where Parts has all of the normal components. I'm not an expert rust user but it feels un-rusty to leave a class of bug the type system could easily solve. |
I don't disagree. But at the same time, I also don't want to take away functionality that would otherwise be possible |
could we add a feature flag that always |
Adds a number of utility methods that I have needed while working on a server integration
ResponseBuilder
ResponseBuilder