HTTP Status: 100 Continue

Tue, 22 Apr 2008 20:26 UTC

UPDATE: This post contains some false information. Particularly, you should not send back a 417 Expectation Failed response as a way of telling the client that the server could not fulfill the response because of data presented in any of the request headers other than the Expect header. I have corrected my statements here in a newer post, and I apologize if I caused any confusion.

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. :-)


13 Responses to “HTTP Status: 100 Continue”

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.

Comment by Christian Flickinger
Wed, 23 Apr 2008 at 2:06 UTC | Permalink

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)

Comment by julien-pauli
Wed, 23 Apr 2008 at 10:01 UTC | Permalink

Amazon’s S3 service uses this (if you want to play around with something in the wild).

Comment by Richard Harrison
Wed, 23 Apr 2008 at 23:59 UTC | Permalink

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?

Comment by Ralf Jones
Mon, 28 Apr 2008 at 11:33 UTC | Permalink

Ralf, I recommend you read the section in the PHP manual on the use of the header() function. In short, if you need to send the 100 Continue code, 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 Continue and other headers in a response received from the server, you can you curl, fsockopen, or something like Zend_Http.

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-continue header, 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.

Comment by Ben Ramsey
Mon, 28 Apr 2008 at 14:30 UTC | Permalink

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!

Comment by Akki
Wed, 30 Apr 2008 at 14:02 UTC | Permalink

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.

Comment by Dan Kubb
Mon, 18 Aug 2008 at 7:42 UTC | Permalink

> 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.

Comment by Glenn Maynard
Sun, 1 Feb 2009 at 0:46 UTC | Permalink

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.

Comment by Ben Ramsey
Sun, 1 Feb 2009 at 3:49 UTC | Permalink

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?

Comment by Glenn Maynard
Sun, 1 Feb 2009 at 7:28 UTC | Permalink

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:

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";
}

From telnet, I posted this data:

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.

What I got back in response was:

Content-Length: 3
Actual length: 3
Entity body: Foo

If I set Content-Length to 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!

Comment by Ben Ramsey
Sun, 1 Feb 2009 at 15:40 UTC | Permalink

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.

Comment by Glenn Maynard
Sun, 1 Feb 2009 at 20:33 UTC | Permalink

Ajax-Applikationen mit dem Zend Framework…

Ajax wird heute in fast jeder modernen Webanwendung genutzt, bietet doch jedes gute Javascript-Framework mittlerweile einfache Möglichkeiten XHR-Anfragen abzusetzen. In diesem Artikel soll es aber nicht um die Client-Seite gehen, sondern darum, wie man…

Comment by sorgalla.com
Tue, 12 Jan 2010 at 14:40 UTC | Permalink

Leave a Comment

XHTML: You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
APPROVAL: Comments may require approval. Please be patient.