HTTP Status: 100 Continue
As a self-described HTTP status code pedant and an architect of RESTful Web services, I’ve spent a great deal of time researching and thinking about the proper use of HTTP status codes in Web applications. It occurs to me that many people use status codes in their applications in ways that I find improper, so I’ve decided to start up a series of blog posts to talk about select status codes and how they might appropriately fit within your Web applications, specifically in a RESTful manner. Consider these “Ben’s HTTP Status Codes Best Practices.”
I won’t be covering these status codes in any particular order. However, the first code up is 100 Continue merely because I’ve been thinking about it lately.
The 100 Continue status code is an informational response that tells the client application it can continue sending the request. In short, the client can make an abbreviated request of the service to check whether or not it should continue making the full request. The service can then respond either “yes” (100 Continue) or “no” (417 Expectation Failed) with an explanation of why not. In RESTful Web Services, Richardson and Ruby refer to this as a “look-before-you-leap” (LBYL) request.
This is helpful in situations where the client might want to post or put a very large file to the service but wants to ensure that the service will accept it before actually sending the file. Consider the following request:
POST /content/videos HTTP/1.1
Host: media.example.org
Content-Type: video/mp4
Content-Length: 105910000
Authorization: Basic bWFkZTp5b3VfbG9vaw==
Expect: 100-continue
In this case, the client is attempting to post an MPEG-4 video that is 101MB. The client sends this request to your service without the video in the body, and it includes the Expect: 100-continue header. 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.
By now, you can probably see how this can be extended to apply to any of the other headers. For example, a client can try a POST or a PUT request first just to see if its authorization succeeds. If it does, your service would return 100, letting the client know it may proceed with the request. Likewise, the client might want to determine whether the service will accept a video/mp4 file before sending it. The list goes on.
It should be noted that the service shouldn’t require the client to use a LBYL request, but it can be helpful to a client so that it doesn’t attempt to send something that would fail at the service anyway.
I’m considering implementing these kinds of responses in a service I’m building, so I’ll let you know how it goes. :-)
12 Comments
Great post! You always enlighten me on things I take for granted... like http status codes!
They seem so hidden and untouchable, and you just want to use the standard 300/404/500...
The uses for some of them are very intriguing, and at least this one, 100, can significantly reduce overhead... instead of sending that 100mb file each time, then verifying details.. you can now verify before sending! GENIUS.
Keep going Ben, HTTP is a wonderfull and powerfull protocol, show us what it can do in real world ! ;-)
(Chris Shifflet's HTTP developer's Handbook is very nice)
Amazon's S3 service uses this (if you want to play around with something in the wild).
Can use 100 in PHP ?
As I know, PHP will save the file uploaded to /tmp first whatever u do
How to check "Content-Length" before the file saved?
Ralf, I recommend you read the section in the PHP manual on the use of the
"header()":http://php.net/headerfunction. In short, if you need to send the100 Continuecode, you may do so like this:header('HTTP/1.1 100 Continue');If you are building a client application and need to check for the
100 Continueand other headers in a response received from the server, you can you "curl":http://php.net/curl, "fsockopen":http://us3.php.net/fsockopen, or something like "Zend_Http":http://framework.zend.com/m....Be aware that the Content-Length header cannot be trusted. The value it contains should only be considered for informational purposes and may not be the actual size of the uploaded file. However, with a request containing an
Expect: 100-continueheader, there won't be a file uploaded, so you have only the Content-Length header to go by for determining whether or not to tell the client to continue sending the full request.Hi,
I was wondering if there was a response status to allow a large file to be "served" in parts and in just one response?
I need a way to do this and I'd be grateful if the pedant offers succor :)
thanks!
For the specific example of uploading large files, what do you think about returning 413 (Request Entity Too Large)? It seems that this provides the user agent with more information than a 417 (Expectation Failed) status does, and is more specific in describing the problem.
> 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. 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.
> For the specific example of uploading large files, what do you think about returning 413 (Request Entity Too Large)?
This is the correct behavior.
> Be aware that the Content-Length header cannot be trusted.
I'm not sure what this means. Except for chunked connections, Content-Length must be trusted, or you won't be able to tell where a request/response body ends.
Glenn, after writing a lengthy reply in rebuttal to your message, I finally saw what you were getting at and retracted my reply.
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."
So, it seems the problem is that Ruby and Richardson got it wrong in RESTful Web Services when they say: "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).
> Be aware that the Content-Length header cannot be trusted.
I say this because Content-Length header can be spoofed easily. Someone can create a client to post arbitrary data to your service. If, for example, you only want to allow 50 MB uploads, and some malicious user wants to post something that's 100 MB, they can set the Content-Length header to 50 MB and put however much data they wish in the entity body. So, you can't necessarily take the Content-Length at face value; you should also validate that the content of the entity body is not greater than the length you want to accept.
The only way that can happen is with a Transfer-Encoding to compress the data, in which case the Content-Length header is illegal and should be explicitly ignored (4.4 #3).
If it's just a plain, non-chunked request (T-E: identity), you can't give a fake Content-Length, because the header itself defines the amount of data that's going to be read as the request body. If you say Content-Length: 1, and then write a gigabyte of data, the server is only going to read the first byte as the request body, and the rest will be a new request...
Can you put a note in the post about 100-continue, so people who don't read comments aren't led astray?
I stand corrected. I felt certain that I could spoof the Content-Length header, but it appears that the server respects that header and only reads that much data and stops.
I did a quick test. On the server I placed this file:
[code]
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$entity = file_get_contents('php://input');
echo 'Content-Length: ' . $_SERVER['CONTENT_LENGTH'] . "\n";
echo 'Actual length: ' . strlen($entity) . "\n";
echo 'Entity body: ' . $entity . "\n";
}
[/code]
From telnet, I posted this data:
[code]
POST /~ramsey/http/length.php HTTP/1.1
Host: localhost
Content-Length: 3
Foo bar. This is a longer entity than the Content-Lenth says.
[/code]
What I got back in response was:
[code]
Content-Length: 3
Actual length: 3
Entity body: Foo
[/code]
If I set
Content-Lengthto 100, however, the server appears to wait until I've submitted the full 100 bytes, or it times out—whichever comes first.So, yeah, I'll put a note in the body of this post soon, but I'll also make a new blog post to explain my fallacy and I'll link to that post from here.
Thanks!
It's not just that it's respecting the header; that's the only way it can work with HTTP 1.1's keepalive. This may have been different with HTTP 1.0 (which didn't do keep-alive without extensions, so servers might have got away with ignoring Content-Length and reading until the connection closed).
Telnet is line-buffering. The server will actually respond as soon as you finish typing "Foo", but telnet just isn't sending the data until you hit enter. This can be confusing if you don't know about it, since it looks like it's something the server is doing, when it's actually the client.