[openstack-dev] [api] Changing 403 Forbidden to 400 Bad Request for OverQuota was: [nova] Which error code should we return when OverQuota

Chris Dent chdent at redhat.com
Wed May 6 19:15:25 UTC 2015


On Wed, 6 May 2015, Jay Pipes wrote:

> I think Sean makes an excellent point that if you have >1 condition that 
> results in a 403 Forbidden, it actually does not make things more expressive. 
> It actually just means both humans and clients need to now delve deeper into 
> the error context to determine if this is something they actually don't have 
> permission to do, or whether they've exceeded their quota but otherwise have 
> permission to do some action.

As I said to Sean in IRC, I can see where you guys are coming from
and I haven't really got a better counter-proposal than "my experience
writing servers and clients doesn't like this" so it's not like I want
to fight about it. I do think it is worth discussion and there are
obviously costs either way that we should identify and balance.
Interestingly, in the process of writing this response I think I've
managed to come up with a few reasons. On the other hand maybe I'm
just getting out yet more paint for the shed.

Basically it seems to me that the proposal to use 400 just moves the
problem around, one option has conditionals localized under 400,
centralizing the ambiguity. The other option puts the ambiguity in
categories. I guess my brain works better with the latter: a kind of
cascading decision tree.

Note: I think we're all perpetuating the myth or wish that we actually
do do something in code in response to 400 errors. Maybe in some very
special clients it might happen, but in ad-hoc clients (the best kind)
for the most part we report the status and fail and let the human decide
what's next.

In that sort of context I want the response _codes_ to have some
semantics because I want to branch on the codes (if I branch at all)
and nothing else:

* 400: bro, something bogus happened, I'm pretty sure it was your
        fault
* 401: Tell me who you are and you might get to do this
* 402: You might get to do this if you pay
* 403: You didn't get to do this because the _server_ forbids you
* 404: You didn't get to do this because it ain't there
* 405: You didn't get to do this because that action is not available
* 406: I've got the thing you want, but not in the form you want it
* 407: Some man in the middle proxy needs auth
* 408: You spoke too slowly for my awesome brains
* 409: Somebody else got there first
* 410: Seriously, it ain't there and it never will be
* 411: Why u no content-length!?
* 412: You sent conditional headers and I can't meet their
        requirements
* 413: Too big in the body!
* 414: Too big in the URI!
* 415: You sent me a thing and I might have been able to do
        something with it if it were in a different form
[...]

These all mean things as defined by rfcs 7231 and 7235. Those rfcs
were not pulled out of thin air: They are part of the suite of rfcs
that define HTTP. Do we want to do HTTP? Yes, I think so. In that case,
we ought to follow it where possible.

Each of those codes above have different levels of ambiguity. Some
are quite specific. For example 405, 406, 411, 412 and 415. Where we
can be sure they are the correct response we should use them and
most assuredly _not_ 400.

403, as you've both identified, is a lot more squiffy: "the server
understood the request but refuses to authorize it...a request
might be forbidden for reasons unrelated to the credentials".

Which leads us to 400. How I tend to use 400 is when none of 405, 406,
409, 411, 412 or 415 can be used because the representation is
_claiming_ legitimate form (based on the headers) and no conditionals
are being violated and where none of 401, 403 or 404 can be used because
the thing is there, I am authentic and the server is not forbidding .
What that means is that there's some crufty about the otherwise good
representation: You've claimed to be sending JSON and you did, but you
left out a required field.

There is no other 4xx that covers that, thus 400.

Now if we try to meld my rules with this idea about signifying over
quota, I feel we've now discovered some collisions:

     My use of 400 means "there's something wrong with your request".
     This is also what the spec says: "the client seems to have erred".

     Both of these essentially say "that request was pretty okay, but not
     quite right and you can change the _request_ (or perhaps the client
     side environment) and achieve success".

     In the case of quota you need to change the server side environment,
     not this request. In fact if you do change the server (your quota)
     and then do the same request again it will likely work.

     Looking at 403 again: "the server understood the request but refuses
     to authorize it".

4xx means client side error ("The 4xx (Client Error) class of status
code indicates that the client seems to have erred."), so arguably
over quota doesn't really work in _any_ 4xx because the client made no
error, the service just has a quota lower than they need. We don't
want to go down the non 4xx road at this time, so given our choices
403 is the one that most says "the server won't have it". It doesn't
really fit in with 4xx at all, just like over quota doesn't!

-=-=-

Moving away from that specific issue I think it important that we
get our use of HTTP right. Whether we use 403 or 400 for quota is
not going to break the world. Using 400 for nearly everything,
however, may because it will make us seem like yet another system
that couldn't be bothered to do things correctly and, you know,
there are enough of those out there. Let's not be one of those.

If we follow the global specs for HTTP (where possible) and not our
own little derivations then we don't have to be responsible for
creating and maintaining our own special rules, we just use the
rules that exist.

So yeah, my argument basically comes down to: There's a spec, let's
follow it as much as possible.

> p.s. And, yes, Chris, I definitely do see your side of the coin on this. It's 
> nuanced, and a grey area...

  (╯°□°)╯︵ ┻━┻

(not really throwing a table, I just think it is fun to paste that
rather evocative image)

-- 
Chris Dent tw:@anticdent freenode:cdent
https://tank.peermore.com/tanks/cdent


More information about the OpenStack-dev mailing list