Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #9524 (new defect)

Opened 1 year ago

Last modified 7 months ago

[PATCH] Head method sets Content-Type header to "text/plain" by default

Reported by: gbuesing Assigned to: core
Priority: normal Milestone: 2.x
Component: ActionPack Version: edge
Severity: normal Keywords:
Cc:

Description

Currently, the head method doesn't set a default Content-Type, so unless it's explicitly passed in as an argument to head (i.e., head :ok, :content_type => 'whatever'), the Content-Type will be set to the MIME type associated with the response format.

So, for example, from the default scaffold:

# PUT /posts/1
# PUT /posts/1.xml
def update
  ...
  respond_to do |format|
    ...
    format.html { redirect_to(@post) }
    format.xml { head :ok }
    ...
  end
end

... if you successfully PUT to /posts/1.xml, you'll get a 200 response with an empty body (in reality, it's a single space), and a Content-Type of "application/xml".

However, this Content-Type is misleading and incorrect, since no XML document is contained in the response body, given that, according to the spec, a well-formed XML document must contain one or more XML elements.

A blank space contains no XML elements, so therefore, it's malformed XML.

Any client that relies on Content-Type to determine how to handle the response body will be mislead -- in a browser, for example, if this response is returned from an Ajax request, a call to XMLHttpRequest.responseXML will return "<parsererror>".

A blank response body is also not a well-formed XHTML, ATOM, RSS, or PDF document.

The proper default Content-Type setting for a blank response body is "text/plain" -- an empty response body qualifies as plain text, and plain text is recognized by every HTTP client.

Additionally, this lets clients know that they don't need to do any additional parsing or evaluation of the response body.

This patch sets this default for the head method -- it can be overridden, if ever necessary, by passing a an explicit :content_type argument:

head :ok, :content_type => 'text/enriched'

Tests included.

Attachments

head_method_content_type_default.diff (2.0 kB) - added by gbuesing on 09/10/07 20:47:37.
head_method_content_type_default.2.diff (2.1 kB) - added by danger on 09/10/07 21:52:57.
recreated original patch from the rails root directory

Change History

09/10/07 20:47:37 changed by gbuesing

  • attachment head_method_content_type_default.diff added.

09/10/07 21:52:57 changed by danger

  • attachment head_method_content_type_default.2.diff added.

recreated original patch from the rails root directory

09/10/07 21:57:55 changed by danger

+1 I think this makes lots of sense. Tests all pass. Patch recreated from rails trunk directory.

(follow-up: ↓ 3 ) 09/11/07 16:33:54 changed by dkubb

If you're returning a response with no entity body a more appropriate status code to return is 204 No Content. According to RFC 2616 (section 7.2.1) it says a response including an entity body SHOULD include Content-Type, so its reasonable to assume that if there is no entity body you can omit Content-Type (and the other Content-* headers) altogether.

I think returning text/plain when there is no body is a bit of a hack. I would rather see apps demonstrate their intentions more clearly and use 204 No Content in these cases. Rails should be patched to omit the Content-* headers when 204 is returned. It might also be good to update rails' scaffold generator to use head :no_content instead of head :ok too.

(in reply to: ↑ 2 ) 09/11/07 19:23:35 changed by gbuesing

Replying to dkubb:

Using a 204 response type makes sense -- it is the most appropriate HTTP response code for the situation -- but unfortunately, most browsers choke on it when requesting via the XmlHttpRequest object. Here's a description of the problem from the YUI Connection Manager release notes:

"The XHR implementation in IE6 and IE7, Opera, and Safari do not properly handle an HTTP 204 response. IE6/7 will instead return a Win error 1223.... Opera and Safari provide no discernable response with HTTP 204(e.g., response object's properties are undefined). This response will trigger the failure callback with a status of 0 and statusText of "communication failure"." http://www.lib.virginia.edu/scripts/yui-2.3.0/build/connection/

Therefore, I see the lack of browser compatibility as a deal killer for using 204 responses right now (sadly.)

As far as not sending any Content-Type header with a blank response -- here's the relevant passage from RFC 2616:

"If and only if the media type is not given by a Content-Type field, the recipient MAY attempt to guess the media type via inspection of its content and/or the name extension(s) of the URI used to identify the resource. If the media type remains unknown, the recipient SHOULD treat it as type "application/octet-stream". http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1

So, if no Content-Type is indicated, the client *might* try to guess the contents, or if not, it *should* treat the response body as binary data. I see neither of these options as preferable to explicitly labeling the blank response body as text/plain, which saves the client from having to guess, or incorrectly treating the contents as binary data.

(follow-up: ↓ 5 ) 09/19/07 12:36:05 changed by nzkoz

For API clients (i.e. format.xml) head :no_content seems like a reasonable change to make. The incompatibility with XHR implementations doesn't seem to matter as we have format.js for them?

(in reply to: ↑ 4 ) 09/20/07 17:14:06 changed by gbuesing

Replying to nzkoz:

For API clients (i.e. format.xml) head :no_content seems like a reasonable change to make. The incompatibility with XHR implementations doesn't seem to matter as we have format.js for them?

Not sure I understand your point about format.js. The XmlHttpRequest object can make requests for any content type -- HTML, XML, JSON, etc. -- not just Javascript. And it's reasonable to expect that client-side libraries would want to interact with the server via the .xml api, which returns data as XML (or an empty response, when appropriate) instead of the .js api, which returns no structured data, just a glob of Javascript to evaluate.

As far as what Rails should deliver as a default, via the out-of-the-box scaffold -- I think it should err on the side of compatibility and correctness (i.e., an empty 200 OK with a Content-Type: text/plain) as opposed to "best form" without full compatibility (i.e., 204 No Content).

(follow-up: ↓ 7 ) 09/21/07 15:49:36 changed by nzkoz

To ask for .xml and receive a text/plain reply seems completely broken, and not something I'd be interested in applying.

I can definitely see the problem with serving 'invalid xml', but as I've yet to see any reports of problems with this in the wild, I'm not too worried about it.

The 204 response is the 'right' thing to do, and that's what I'd prefer to commit if we're going to be worrying about correctness. I don't think I know of anyone who's actually requesting XML with ajax any more, but if that's something you're concerned about perhaps we just leave the scaffolds as is.

(in reply to: ↑ 6 ) 09/21/07 18:34:36 changed by gbuesing

Replying to nzkoz:

I can definitely see the problem with serving 'invalid xml', but as I've yet to see any reports of problems with this in the wild, I'm not too worried about it.

I ran into this problem in the wild with the most recent release of jQuery, which raises an error when it receives a blank 200 OK with an XML content type from an XHR request, because it's checking the responseXML for a <parsererror>.

This behavior is strict, but it's technically correct, which makes me think that there would be other clients out there that implement the same strict behavior.

I don't think I know of anyone who's actually requesting XML with ajax any more

I would be curious to know -- if you want to use a structured data format on the client, the two popular formats for this are XML and JSON, and the built-in Rails XML tools (#to_xml, Builder templates, assert_select) are more mature than the JSON tools (just #to_json, which doesn't support :include yet).

The 204 response is the 'right' thing to do, and that's what I'd prefer to commit if we're going to be worrying about correctness

Putting aside than the XHR issues with the 204 response, and given that the attached patch seems like a no-go, I think this would be an improvement to the current scaffold behavior, which is neither correct nor totally compatible. Since it's generated code, it would certainly be easy enough to change, if needed for a specific situation.

One other option to consider: as an XHR-friendly alternative to the 204, what about changing the scaffolds to something like this --

format.xml { render :xml => :success }

which would return a simple XML success document:

<?xml version="1.0" encoding="UTF-8"?>
<success/>

...with this behavior, every time you requested .xml, you would receive an XML document.

So, if the solution provided in this patch is definitely a no-go, I'll close it out, and if there's any interest in either the 204 or the render :xml => :success options, I'd be happy to provide a patch.

09/22/07 16:00:24 changed by nzkoz

I'd be happy to take a patch for the 204 behaviour, that definitely seems like the right approach, assuming of course it works with active resource ;)

09/23/07 01:35:36 changed by dkubb

+1 for the idea of scaffold returning 204 No Content when responding to a DELETE request in the format.xml block.

I'm using ActiveResource with controllers that do this now and it works just fine.

(follow-up: ↓ 11 ) 09/24/07 05:22:10 changed by gbuesing

The ActiveResource test suite includes a couple tests for 204 responses, so I think we're covered there.

One last issue to bring up before I submit the patch: I came across this line in the W3C HTTP spec:

"The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields." http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5

Currently, Rails *does* return a response body -- one single space -- which is there as a hack for Safari (according to the inline source code comment in the render method.)

So, technically, we'd be delivering invalid HTTP -- "MUST NOT include a message-body" is certainly unambiguous.

Opinions on this?

(in reply to: ↑ 10 ) 09/24/07 05:25:33 changed by gbuesing

Currently, Rails *does* return a response body -- one single space -- which is there as a hack for Safari (according to the inline source code comment in the render method.)

...to be specific, when doing render :nothing => true (which is what the head method calls.)

12/06/07 00:16:03 changed by norbert

See also #9481. Apparently a 204 with the single space body causes problems in Safari.

01/31/08 21:05:41 changed by wzph

I opened 10974.