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

Simplify Method model #3440

Merged
merged 5 commits into from Oct 13, 2020
Merged

Simplify Method model #3440

merged 5 commits into from Oct 13, 2020

Conversation

rossabaker
Copy link
Member

@rossabaker rossabaker commented May 18, 2020

  • Drops PermitsBody and NoBody mixins. These were used as marker traits on the constants so we could create a client DSL that does not accept an entity on methods where they're disallowed. It seems like a lot of magic to prevent someone from writing TRACE("oops this is illegal", uri"http://www.example.com/"). I think this supersedes Fix dotty runtime error #3426, but we can merge that while people debate this.

  • Drops Semantics trait. The name appears in no spec, and nobody was using these fields at the type level. All we need are a pair of booleans to reflect the full IANA registry.

  • Method has been a case class since the Spray fork, but it's not a proper product: not all Strings are a valid names, and each name uniquely defines isSafe and isIdempotent.

@rossabaker rossabaker added the breaking Change breaks compatibility (binary or source) label May 18, 2020
Copy link
Member Author

@rossabaker rossabaker left a comment

A third "common property" defined by the specification is cacheability. The definition in RFC7321 is unsatisfying: POST is cacheable in theory, but not in practice. And the field is not defined in the IANA registry, which means going through several WebDAV specifications to try to infer the value. I'm not opposed to tracking it, but I'm not highly motivated to do so.


/**
* An HTTP method.
*
* @param name The name of the method
Copy link
Member Author

@rossabaker rossabaker May 18, 2020

Choose a reason for hiding this comment

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

I could be cajoled into calling this toString.

@@ -75,7 +75,7 @@ class FollowRedirectSpec
) = {
val u = uri("http://localhost") / status.code.toString
val req: Request[IO] = method match {
case _: Method.PermitsBody if body.nonEmpty =>
case (OPTIONS | PATCH | POST | PUT) if body.nonEmpty =>
Copy link
Member Author

@rossabaker rossabaker May 18, 2020

Choose a reason for hiding this comment

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

This could also be preserved as a property of Method, but has the same problem as cacheability: someone has to go through all the WebDAV specs. The answer there is usually "Yes, but a specific type of XML."

@rossabaker
Copy link
Member Author

rossabaker commented May 18, 2020

An alternative that would permit the old restrictions in that DSL would be to make every Method value an object. Then each value would have its own type, and we could add syntax for POST.type, GET.type, etc. This would make the DSL implementation noisier, and prevent the change of Method to a final class.

Another option is to phase out this DSL entirely, which I don't think has worked out well in practice. I know @SystemFw has argued for phasing it out. We can make it gentle if we keep code like we did here, but also deprecate it.

* does not expect, any state change on the origin server as a result of
* applying a safe method to a target resource.
*
* @param isIdempotent A request method is considered "idempotent" if the
Copy link
Contributor

@kevinmeredith kevinmeredith May 19, 2020

Choose a reason for hiding this comment

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

Nitpick suggestion: use MDN's definition:

An HTTP method is idempotent if an identical request can be made once or several times in a row with the same effect while leaving the server in the same state.

https://developer.mozilla.org/en-US/docs/Glossary/Idempotent

@hamnis
Copy link
Contributor

hamnis commented Jun 24, 2020

needs a conflict resolution

@rossabaker
Copy link
Member Author

rossabaker commented Jun 27, 2020

Sneaky breaking change: this removes Method.unapply.

class MethodOps[F[_]](private val method: Method) extends AnyVal {

/** Make a [[org.http4s.Request]] using this [[Method]] */
final def apply(uri: Uri, headers: Header*)(implicit F: Applicative[F]): F[Request[F]] =
Copy link
Contributor

@kevinmeredith kevinmeredith Jun 29, 2020

Choose a reason for hiding this comment

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

Could you please say why the type is F[Request[F]] rather than just Request[F]? Thanks

Copy link
Member Author

@rossabaker rossabaker Jun 29, 2020

Choose a reason for hiding this comment

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

Uh, historical reasons? EntityEncoder used to be effectful, which is why the overload is effectful, and this was effectful so the types didn't change when a body is added. But that hasn't been true for a long time, and we'd like to eliminate the overloads in the client that take an effect of a request, at which case these would break.

I don't like this DSL anyway, but I think there's really no reason for it to continue to be effectful.

@ChristopherDavenport
Copy link
Member

ChristopherDavenport commented Jul 25, 2020

If this removes unapply, how does that effect the dsl?

@rossabaker
Copy link
Member Author

rossabaker commented Aug 6, 2020

I don't think appreciably. Unless people were doing something like:

case Method(name) -> Root / "blah" => 

hamnis
hamnis approved these changes Aug 27, 2020
@rossabaker rossabaker merged commit 399bc97 into http4s:master Oct 13, 2020
@rossabaker rossabaker mentioned this pull request Oct 19, 2020
* @see [http://tools.ietf.org/html/rfc7231#section-4 RFC7321, Section 4]
* @see [http://www.iana.org/assignments/http-methods/http-methods.xhtml IANA HTTP Method Registry]
*/
sealed abstract case class Method private (name: String) extends Renderable with Semantics {
final class Method private (val name: String, val isSafe: Boolean, val isIdempotent: Boolean)
Copy link
Contributor

@kevinmeredith kevinmeredith Nov 5, 2020

Choose a reason for hiding this comment

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

Side question: technically all Safe HTTP Request's Methods are idempotent, no, @rossabaker?

Note that I'm not proposing any change, but asking this question out of curiosity (after digging through my Github notifications).

Copy link
Member Author

@rossabaker rossabaker Nov 8, 2020

Choose a reason for hiding this comment

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

That's correct. No public constructor can be safe without also being idempotent.

An alternative and perhaps better design would be to consolidate these two booleans into the old Semantics trait, but no longer make it a mixin to the Method.

@rossabaker rossabaker deleted the method branch Nov 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Change breaks compatibility (binary or source)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants