Skip to content
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

fix: header in const request bug and improve code architecture #333

Merged
merged 1 commit into from
Dec 12, 2022

Conversation

AntoineRR
Copy link
Collaborator

Description

This PR fixes #323

This is my suggestion for fixing the issue as well as improving the request handling

  • Add a Response struct created from the hashmap returned by the python functions in execute_http_request. I would have liked to return a Response directly from the py function by mapping the Rust struct to a Python TypedDict but I'm not sure if it is doable? Maybe consider it for a later PR.
  • Remove the request_handler module (which became obsolete with the previous point)
  • Fix the bug by applying global and request headers to the response before routing on the correct function
  • Add a unit test for the headers in const requests

@@ -1,7 +1,7 @@
/// This is the module that has all the executor functions
/// i.e. the functions that have the responsibility of parsing and executing functions.
use crate::io_helpers::read_file;
use crate::types::Request;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think importing use crates::types::{Request, Response} would make more sense. Just a nit

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, far better. Autoimport does some weird things sometimes

Comment on lines +12 to +30
pub fn apply_hashmap_headers(
response: &mut HttpResponseBuilder,
headers: &HashMap<String, String>,
) {
for (key, val) in headers.iter() {
response.insert_header((key.clone(), val.clone()));
}
}

#[inline]
pub fn apply_dashmap_headers(
response: &mut HttpResponseBuilder,
headers: &DashMap<String, String>,
) {
for h in headers.iter() {
response.insert_header((h.key().clone(), h.value().clone()));
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very smart! Good job! 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks ;) This avoids merging the headers and then applying them, which creates an unnecessary copy of both headers list. I wanted to make only one function that works with either a hashmap or a dashmap but it doesn't seem to be easy

Comment on lines +309 to +313
async fn apply_middleware(
request: &mut Request,
middleware_type: MiddlewareRoute,
middleware_router: &MiddlewareRouter,
route_uri: &str,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AntoineRR , can you please explain this function a bit?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!
We have two middlewares to apply, one before routing and another after it.
We first route on the middleware function and execute it, which gives us some modifications to apply to the request object. We only care about the headers for now => they become the request headers.
Therefore the middleware could modify the request (at least the "before" middleware for Robyn currently), which is why request is &mut.
For the "after" middleware, I think it cannot currently modify the request or the response, so the execute_midlleware_function should return an empty hashmap and we will just have executed the python function.
The idea here is just to factorize the code to execute both middlewares so it is easier to understand and maintain. Although if you think it makes sense to keep the execution of both middleware separate it is also possible

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect. @AntoineRR do you think we can modify request body in before_request and response body in after_request?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make sense yes. Maybe introducing a common trait for Request and Response could help us with this. The apply_middleware method would become generic and take either a Request or a Response, and the trait will then provide a method for parsing the hashmap returned by execute_middleware_function and apply it to the object.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could try to do this in an other PR, as it will add a new feature for Robyn, i.e. response modification in "after" middleware. What do you think of it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could try to do this in an other PR, as it will add a new feature for Robyn, i.e. response modification in "after" middleware. What do you think of it?

Sounds great. We implemented some of it here too: #297

Just in case you need any reference.

Maybe introducing a common trait for Request and Response could help us with this

What kind of trait will it be?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the reference :)
This could be something like this I guess:

trait Data {
     fn modify_from_hashmap(&mut self, modifications: Hashmap<String, String>) -> &mut self;
}

and then apply_middleware signature would become:

async fn apply_middleware<T: Data>(
    data: &mut T,
    middleware_type: MiddlewareRoute,
    middleware_router: &MiddlewareRouter,
    route_uri: &str,
)

After writing impl of Data for Request and Response, they could be used as the data parameter for the function. Then we call data.modify_from_hashmap with the hashmap from execute_middleware_function and we're good! Using generics in Rust is a zero cost abstraction due to monomorphization so there will be no performance impact from this. Using this solution makes modifying requests and response from a hashmap easier IMO, and everything is contained inside the Request and Response struct.
I don't like the name Data for the trait but it is just an example here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, that makes sense. Perfect. 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using generics in Rust is a zero cost abstraction due to monomorphization so there will be no performance impact from this.

I wasn't aware of this. Thank you! 😄

Copy link
Member

@sansyrox sansyrox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! ❇️ 🔥

@sansyrox sansyrox merged commit c30b22b into sparckles:fix/headers Dec 12, 2022
sansyrox added a commit that referenced this pull request Dec 14, 2022
* fix: allow response headers

* fix: rename get_headers to get_request_headers

* fix: header in const request bug and improve code architecture (#333)

* test: add integration tests to verify headers in non const requests

* docs: add documentation of per route headers

Co-authored-by: Antoine Romero-Romero <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants