rwf2 / Rocket

A web framework for Rust.
https://rocket.rs
Other
24.43k stars 1.56k forks source link

HTTP 400 is returned when it is the incorrect response code. #46

Closed sgrif closed 7 years ago

sgrif commented 7 years ago

If deserializing the response data into a struct using FromForm fails, Rocket by default will return 400 BAD REQUEST, regardless of the reason that deserialization failed. This is the incorrect response code, unless deserialization failed due to malformed JSON. It is clearly defined in RFC 2616 as "The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.". For failures due to missing fields, unrecognized fields, fields of the wrong type, or pretty much any failure that isn't the result of the body being syntactically incorrect for the declared content-type, 422 UNPROCESSABLE ENTITY should be used instead.

SergioBenitez commented 7 years ago

That seems like the right thing to do. Slating for 0.2.

TheNeikos commented 7 years ago

As a side note, the docs also state that a 400 gets emitted if it fails to parse the form. https://rocket.rs/guide/requests/#forms

This would need to be updated if #103 is merged

shssoichiro commented 7 years ago

Actually, 400 (Bad Request) is the correct code to emit if the request cannot be parsed. 422 (Unprocessable Entity) on the other hand means, "The request data you gave is syntactically valid, but it doesn't fit the constraints for this route."

mehcode commented 7 years ago

To clarify further because why not. 400 should be returned if the request body isn't JSON, XML, etc. Any other considerations (eg. an int vs a string or a parametric validation) should be 422.

SergioBenitez commented 7 years ago

Alright! This is one of two remaining issues for 0.2!

So, looking at the RFC, section 8.2.1, we should return a 400 when any of the following occurs:

  1. A form string has no = at all or has no = after an &.
  2. The form string contains spaces.
  3. The form string contains unencoded URL characters.

The first one is pretty easy to detect but requires a change to the FormItems iterator. The second one means we have to check every string for spaces, which we can do efficiently if we change the FormItems decorator again. The last one...seems expensive, and I don't really know what the point of it is besides being able to send it in the URL.

Would be good to know what other frameworks do here. My intuition is that we should just check for 1 and ignore 2 and 3. This can be done by simply adding a method to the FormItems structure to see if there is any remaining, unparsed text. But even then: should Rocket just let this fly?

It's not clear to me what the correct approach is here.

[Copied this from #103]

mehcode commented 7 years ago

I'm partial to just doing [1]. That's a simple parse failure.

I can't find the source, but I remember reading a quote (similar) to the following in a RFC (probably the HTML one).

Accept as much variation as feasible but return exactly 1 result.

I'd argue that while doing [2] and [3] are very cheap performance-wise (just a simple character rejection with a blacklist of only 7 characters) it feels like being overly strict. What happens if there exists a browser that decides it wants to send spaces? It's not ambiguous to accept spaces so I don't see a problem. I could see a warning log though.

YetAnotherMinion commented 7 years ago

What happens if there exists a browser that decides it wants to send spaces? It's not ambiguous to accept spaces so I don't see a problem. I could see a warning log though.

The short answer is that if the browser sends spaces, it is not in compliance with the RFC.

Consistent behavior globally is really valuable. Feature matrix hell is not fun.

Personally I caution against doing things that are technically possible without a substantial and valuable use case. The smaller the set of rules that describe the system, the easier it is for my limited human brain to reason about. Unnecessary complexity saps productivity like one would not believe.