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

allow consumers to add headers to responses #276

Closed
wants to merge 3 commits into from
Closed

Conversation

ahl
Copy link
Collaborator

@ahl ahl commented Feb 15, 2022

This it to unblock @bnaecker

@davepacheco I can see other ways of doing this (as we discussed); this seemed the least invasive, but I'm happy to weave this in more seamlessly if it seems objectionable.

@ahl ahl requested a review from davepacheco February 15, 2022 00:17
#[derive(Default)]
pub struct RequestContextMutableState {
response_headers:
Vec<(http::header::HeaderName, http::header::HeaderValue)>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Might it make more sense for this to be a mapping? Or are multiple headers with the same name allowed?

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 guess I could see it either way. If it was a mapping would your proposal be that we return an error if there's a collision?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe multiple headers with the same name are allowed in HTTP, and it looks like hyper's representation is a multimap. You could even use that here.

I'd suggest we provide some combination of:

  • "set" header -- replaces any previous value (corresponds to HeaderMap::insert)
  • "append" header -- adds a new header (if you use this multiple times, you'll get multiple values for this header in the response -- corresponds to HeaderMap::append)

The former is less error-prone for most cases, but the latter is more general. I don't have much preference about which or both.

dropshot/src/test_util.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@bnaecker bnaecker left a comment

Choose a reason for hiding this comment

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

Thanks for starting this work!

#[derive(Default)]
pub struct RequestContextMutableState {
response_headers:
Vec<(http::header::HeaderName, http::header::HeaderValue)>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe multiple headers with the same name are allowed in HTTP, and it looks like hyper's representation is a multimap. You could even use that here.

I'd suggest we provide some combination of:

  • "set" header -- replaces any previous value (corresponds to HeaderMap::insert)
  • "append" header -- adds a new header (if you use this multiple times, you'll get multiple values for this header in the response -- corresponds to HeaderMap::append)

The former is less error-prone for most cases, but the latter is more general. I don't have much preference about which or both.

*/
pub fn add_response_header(
self: Arc<Self>,
header: http::header::HeaderName,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit, and please feel free to ignore: I see that HeaderMap::insert() takes IntoHeaderName, and I wonder if that's not more general in a useful way. We could even expose a &mut HeaderMap directly to people and avoid dealing with a bunch of these issues ourselves.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That would make it much easier to use. Good idea.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with Dave here. Let HeaderMap handle issues of conversion, duplicate values, etc.

pub fn add_response_header(
self: Arc<Self>,
header: http::header::HeaderName,
value: http::header::HeaderValue,
Copy link
Collaborator

Choose a reason for hiding this comment

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

What do we want to do if somebody tries to set a header that Dropshot really should own (like maybe "date" or "transfer-encoding")? I can imagine:

  • silently ignore it
  • let them clobber it
  • panic
  • make this function return a Result, even though it feels like this is something that could be detectable at compile time, except that I don't see how to do that

I hate all of these. What do you think?

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 we let them clobber it. It's either going to let consumers do something ... tricky/sketchy, or be a foot-gun, but it's a foot-gun that would--I think--require careful aiming. In other words, I think it might be a useful escape hatch and is unlikely to cause mysterious errors.

dropshot/src/handler.rs Outdated Show resolved Hide resolved
dropshot/src/test_util.rs Outdated Show resolved Hide resolved
dropshot/tests/test_demo.rs Outdated Show resolved Hide resolved
let funcparams = Extractor::from_request(Arc::clone(&rqctx)).await?;
let future = self.handler.handle_request(rqctx, funcparams);
future.await

future.await.map(|mut response| {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Discussed offline. Summary: this only sets the header on success. That's probably okay (maybe better) for our expected use case but we should rename these functions for clarity.

A subtle case that came up: suppose we set the ETag header when looking up an object, then did an authz check and decided you couldn't see it, and sent a 404 with the ETag header. We'd leak information. (This isn't a problem if the behavior here is to only set these headers on success.)

@ahl
Copy link
Collaborator Author

ahl commented Feb 24, 2022

closing in favor of #283

@ahl ahl closed this Feb 24, 2022
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.

3 participants