HTTP Status: 100 Continue Corrections

Last year, I wrote about the 100 Continue HTTP status code and the usage of the Expect header with the 100-continue expect value. However, I made a few erroneous statements, and a reader recently corrected me on them. So, I’m writing now to correct those statements so that I’m not misleading anymore readers.

The incorrect statements I made:

The service can then respond either “yes” (100 Continue) or “no” (417 Expectation Failed) with an explanation of why not.

Following that logic, I continued with:

Let’s say your service limits uploads to 100MB. In that case, you would return a 417 Expectation Failed including a reason for the failure in the body of the response.

This is incorrect behavior, and Glenn Maynard corrected it in his comments with:

This is incorrect. 417 means that an Expect you sent is unsupported; for example, if you send “Expect: bacon”, and the server doesn’t support that, then it responds with 417. Responding with 417 because the request was going to be too large badly violates the spec.

The purpose of 100-continue is to give the server a chance to give an error to the client before it sends the request body, rather than after. If doing that required that all of those detailed 4xx/5xx errors were turned into an opaque 417 and useless, plain language errors in the body, it would be useless.

Glenn is correct. What I missed in Section 14.20 of RFC 2616 was this: “The server MUST respond with a 417 (Expectation Failed) status if any of the expectations cannot be met or, if there are other problems with the request, some other 4xx status.” Furthermore, Section 8.2.3 states: “Upon receiving a request which includes an Expect request-header field with the ‘100-continue’ expectation, an origin server MUST either respond with 100 (Continue) status and continue to read from the input stream, or respond with a final status code.” (Emphasis added.)

My faulty logic occurred because I was using the “look-before-you-leap” (LBYL) concept presented in RESTful Web Services, in which Ruby and Richardson propose that a request including the Expect header can be made to determine whether the server will fulfill the request, given the headers present. They state, “if the answer is no, the server sends a status code of 417 (‘Expectation Failed’). The answer might be no because […] 500 MB is just too big” (pg. 250). According to RFC 2616, this is wrong. If the server does not support the 100-continue expectation, then, and only then, should it send back the 417 Expectation Failed response. Otherwise, if, for example, the Content-Length is too big for the server to fulfill the response, then it would respond with a “final status code,” such as 413 Request Entity Too Large.

You may still use this approach to perform LBYL requests, but be sure to send back a proper 4xx status code, rather than a 417.

Furthermore, for another fallacy of mine, if you read the comments on the 100 Continue post, I also stated “be aware that the Content-Length header cannot be trusted.” Glenn also corrected me on this. I had assumed that I could spoof the Content-Length header, and that if the server only checked the Content-Length header to determine the size of the request entity, then it could be fooled, but this is not the case. Web servers use the Content-Length header to determine how much data to read from the entity body. If your Content-Length header is 50 MB and the entity body contains 100 MB, the server will read only the first 50 MB of the body and stop.