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

Models 'Forwarded' header #3609

Merged
merged 6 commits into from Oct 18, 2020
Merged

Conversation

satorg
Copy link
Contributor

@satorg satorg commented Jul 29, 2020

Resolves #3471.

The Forwarded header model tries to be as type safe as possible and thus introduces a hierarchy of model classes for that.

In particular, Element enables the following syntax for building elements:

Element
  .fromFor(Node(...))
  .withBy(Node(...))
  .withHost(Host(...))
  .withProto(Proto(...))

@hamnis
Copy link
Contributor

hamnis commented Jul 29, 2020

Great stuff. =)

This should probably be targeted 0.21 ?

@satorg
Copy link
Contributor Author

satorg commented Jul 29, 2020

I guess it could... Would you suggest to rebase it onto series/0.21 ?

@hamnis
Copy link
Contributor

hamnis commented Jul 29, 2020

@satorg Yes

@satorg satorg changed the base branch from master to series/0.21 July 30, 2020 07:35
@satorg satorg force-pushed the forwarded-header branch 2 times, most recently from 8a14f7e to 070d342 Compare July 30, 2020 08:13
@hamnis
Copy link
Contributor

hamnis commented Jul 30, 2020

We should have some tests for this, I do believe there are some examples in the RFC

@satorg
Copy link
Contributor Author

satorg commented Jul 30, 2020

Yes, for sure. I am working on the tests now, per se.

@satorg satorg force-pushed the forwarded-header branch 2 times, most recently from 8f89055 to d70e3b5 Compare August 10, 2020 08:30
Copy link
Member

@rossabaker rossabaker left a comment

Choose a reason for hiding this comment

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

I was surprised how complicated this is, until I read the spec. This is really nice work.

core/src/main/scala/org/http4s/headers/Forwarded.scala Outdated Show resolved Hide resolved
core/src/main/scala/org/http4s/headers/Forwarded.scala Outdated Show resolved Hide resolved
}
}

final case class Obfuscated(value: String) extends AnyVal with Name with Port
Copy link
Member

Choose a reason for hiding this comment

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

Reading the spec, I expected obfnode to be in Node and obfport to be in Port. The grammar is the same for this type, but it seems weird to me for the type to do double duty.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that it is a controversial approach. I was just thinking – what would be advantages of making them separated?
I mean it would not only require to create two different (although identical) case classes here, but also two parser rules for creating these classes. No doubt we could share some of their implementation, but it would increase overall code complexity anyway (remember, there still should be fromString as well as rendering implemented for both of them). On the other side, I couldn't come up with some real disadvantages besides, yeah, some weirdness.

I'm just trying to come up with some compromise solution that would let us avoid code duplication, preserve type safety and keep code complexity on some maintainable level. WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

This comment is about minutiae, so I don't feel too strongly.

Obfuscated isn't as untyped as String, but it still raises the question, "an obfuscated what?" It's not a particularly meaningful type on its own. Sharing makes the implementation more concise, but isn't semantically meaningful in an HTTP sense.

In the unlikely event that the next version of that RFC allows some other character in an obfuscated host, we've got room to change it. If the two unrelated concepts share a type, we'd have to wait for the next breaking release to implement it.

I don't think either argument matters much in this corner case of a supplemental RFC. I'm thinking more in general design. And you're right that the repetition is annoying, so I'm content either way.

core/src/main/scala/org/http4s/headers/Forwarded.scala Outdated Show resolved Hide resolved
core/src/main/scala/org/http4s/headers/Forwarded.scala Outdated Show resolved Hide resolved
core/src/main/scala/org/http4s/headers/Forwarded.scala Outdated Show resolved Hide resolved
@satorg
Copy link
Contributor Author

satorg commented Aug 10, 2020

I was surprised how complicated this is, until I read the spec. This is really nice work.

Yeah, I also didn't realize that until I dove into this task. Unfortunately, due to my personal circumstances, I just didn't have enough time to squeeze it into 0.21.7, I am sorry for that.

@rossabaker
Copy link
Member

No apologies required at all. Glad for the help, and we won't run out of patch numbers any time soon.

@rossabaker
Copy link
Member

Are we waiting on anything before this leaves draft status?

@satorg
Copy link
Contributor Author

satorg commented Aug 22, 2020

Yeah... Two things actually:

  1. Some more tests (not all cases are covered yet).
  2. The render method.

Sorry, I totally realize that I'm holding this PR, but I haven't given up on it – just cannot set aside too much time for this job now.

@rossabaker
Copy link
Member

No worries. We should be getting ready for an 0.21.8 imminently, but this can always be in the next patch.

@satorg satorg force-pushed the forwarded-header branch 3 times, most recently from 518d33c to 821e02c Compare August 27, 2020 06:35
@satorg
Copy link
Contributor Author

satorg commented Aug 27, 2020

Ok, it seems I'm getting close. I have implemented the model renderers and mostly need to add some tests for them.

@satorg
Copy link
Contributor Author

satorg commented Aug 27, 2020

Looks like the last test failures are due to #3664...

@jyoo980
Copy link
Contributor

jyoo980 commented Aug 30, 2020

@satorg sorry for the hold up, I've written up my investigations into the flaky failures in #3664

@satorg
Copy link
Contributor Author

satorg commented Aug 30, 2020

@jyoo980 no worries, I'm still working on my tests here anyway.

@satorg satorg force-pushed the forwarded-header branch 3 times, most recently from cdc98c1 to 3e1a634 Compare September 7, 2020 08:41
@satorg satorg marked this pull request as ready for review September 7, 2020 08:47
@satorg
Copy link
Contributor Author

satorg commented Sep 7, 2020

I believe I'm pretty much done with this PR.
Hopefully, it reproduces the corresponding RFC as close as possible (with an exception of the controversial approach of sharing the Obfuscated type between Node.Name and Node.Port).
Anyway I'm open for any suggestions, feel free to point them out.

cc @rossabaker, @hamnis

import org.http4s.{ParseResult, Uri}
import org.scalacheck.{Arbitrary, Gen}

private[http4s] trait ForwardedArbitraryInstances
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wonder, should this trait be moved to org.http4s.laws.discipline (where ArbitraryInstances sits)?

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 yes, but that doesn't need to block this.

hamnis
hamnis approved these changes Sep 7, 2020
@hamnis
Copy link
Contributor

hamnis commented Sep 11, 2020

A colleage of mine just hit this in production scala/bug#8831
Could this be a problem with the backticks for the parameters ?

@satorg
Copy link
Contributor Author

satorg commented Sep 11, 2020

@hamnis, interesting issue, never seen it before. Thanks for pointing this out to me!

I'd guess it's unlikely to affect this code since the bug usually appears when there are some common prefixes among parameters.

But to be on the safer side, I agree, we can consider getting rid of backticks. As I wrote some time before, the backticks are only necessary for the for parameter, while the other parameters get backticked just to look consistently with for.

But it is not the only option – we could rename all the parameters somehow else. For example, since all the parameters are actually optional, they could be renamed to

sealed trait Element extends Renderable { _: Product =>
    def maybeBy: Option[Node]
    def maybeFor: Option[Node]
    def maybeHost: Option[Host]
    def maybeProto: Option[Proto]
...
}

..or something like that. WDYT?

@hamnis
Copy link
Contributor

hamnis commented Sep 14, 2020

I've never been a fan of the backticks of for instance header classes just to get the "correct" name, it makes them less discoverable by for instance IDEA. So I prefer the latter version here. Other maintainers may prefer differently.

@rossabaker any guidelines here?

@satorg
Copy link
Contributor Author

satorg commented Oct 13, 2020

Looks like the most recent scalafmt has broken this PR. I'll take care of it.

@rossabaker, btw, I'd appreciate your thoughts regarding the questions above. Feel free to make any suggestions please – I'm open to polish the PR as much as possible.

Copy link
Member

@rossabaker rossabaker 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 your patience while I dig out of my hole. This is great work, and everything I mentioned is pretty insignificant.

import org.http4s.{ParseResult, Uri}
import org.scalacheck.{Arbitrary, Gen}

private[http4s] trait ForwardedArbitraryInstances
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 yes, but that doesn't need to block this.

core/src/main/scala/org/http4s/headers/Forwarded.scala Outdated Show resolved Hide resolved
core/src/main/scala/org/http4s/headers/Forwarded.scala Outdated Show resolved Hide resolved
core/src/main/scala/org/http4s/headers/Forwarded.scala Outdated Show resolved Hide resolved
@satorg
Copy link
Contributor Author

satorg commented Oct 16, 2020

@rossabaker @hamnis I have worked through most of your comments, feel free to review the changes once again :)

Moreover, I voluntarily decided to get rid of backticks in Element's field names – I've renamed them to maybe* pattern since they're options actually. I've put it into the separate last commit so if you don't like it, we can drop it.

Also please feel free to make any further suggestions, if any. There's no rush on my side, I don't mind to continue polishing if necessary.

hamnis
hamnis approved these changes Oct 16, 2020
@rossabaker rossabaker merged commit d8fab09 into http4s:series/0.21 Oct 18, 2020
@satorg satorg deleted the forwarded-header branch October 18, 2020 20:51
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.

Support parsing the 'Forwarded' header: RFC 7239
4 participants