{"id":2043,"date":"2012-02-26T20:53:39","date_gmt":"2012-02-26T20:53:39","guid":{"rendered":"http:\/\/dalelane.co.uk\/blog\/?p=2043"},"modified":"2012-02-27T20:27:23","modified_gmt":"2012-02-27T20:27:23","slug":"etag-header-missing-in-http-no-content-responses-in-internet-explorer","status":"publish","type":"post","link":"https:\/\/dalelane.co.uk\/blog\/?p=2043","title":{"rendered":"ETag header missing in HTTP No Content responses in Internet Explorer"},"content":{"rendered":"<p><em>If you&#8217;re one of that exceedingly rare breed who regularly check or subscribe to my blog, you probably want to give this post a miss. This one is more for people who find me through Google. A specific solution to a specific, geeky, problem.<\/em> <\/p>\n<p><strong>Background<\/strong><\/p>\n<p>First a little scene setting&#8230;<\/p>\n<p><em><strong>Server side<\/strong><\/em><\/p>\n<p>I have a REST API that uses <a href=\"http:\/\/en.wikipedia.org\/wiki\/HTTP_ETag\">ETags<\/a> for, amongst other things, concurrency control. That is, the version of an entity is (opaquely) identified by an ETag. You need to specify that ETag when you try and make any changes to that entity. If someone else changes the entity before you do, your ETag won&#8217;t match, so your update will fail, and you won&#8217;t unintentionally roll-back their change.<\/p>\n<p>The REST API returns no content (HTTP 204) in response to a successful PUT request to edit an entity, and includes the new ETag representing the version of the updated entity. <\/p>\n<p><em><strong>Client side<\/strong><\/em><\/p>\n<p>I have a Dojo web tool that uses xhr.put to submit edits to the REST API. In order to make further subsequent edits to an entity without reloading the page, it stores the ETag that it gets back in the response header after every PUT. <\/p>\n<p><strong>The problem<\/strong><\/p>\n<p>In short, Internet Explorer. \ud83d\ude42<\/p>\n<p><!--more-->In more detail, it seems that there is <a href=\"http:\/\/www.enhanceie.com\/ie\/bugs.asp\">a bug in Internet Explorer<\/a> when it gets a 204 no-content response to an xhr request from Javascript.<\/p>\n<p>IE does drop whatever it uses to store the response content. Unhelpfully, it also drops all of the response headers.<\/p>\n<p>This means my client side Javascript doesn&#8217;t get the updated ETag returned by the server. <\/p>\n<p>It means my Javascript can make one update to the entity. After this, it doesn&#8217;t have the correct ETag required to make any subsequent updates. <\/p>\n<p>This bug seems to be present on all versions of IE that I tried, including the latest-and-greatest. <\/p>\n<p><strong>A workaround<\/strong><\/p>\n<p>One way of handling this without needing to modify the REST API is to extend the behaviour of xhr.put so that, when running on Internet Explorer only, it follows up a successful PUT with a HEAD call to the same entity. This is just enough to get the updated response headers &#8211; to fetch the updated ETag that IE dropped from the PUT. <\/p>\n<p>By daisy-chaining the two requests, this is invisible to code that calls xhr.put. <\/p>\n<p>This is implemented as a module <code>myproject\/xhr<\/code> to use in place of <code>dojo\/_base\/xhr<\/code>. It behaves in the same way as <code>dojo\/_base\/xhr<\/code>, with the exception of this additional workaround. <\/p>\n<pre style=\"border: thin solid silver; background-color: #eeeeee; padding: 0.7em; font-size: 1.1em; overflow: auto;\">define([\"dojo\/_base\/declare\", \"dojo\/_base\/xhr\", \"dojo\/_base\/sniff\", \"dojo\/_base\/lang\"],\r\n    function (declare, xhr, sniff, lang) {\r\n   \r\n    function myXhr() {\r\n        \/\/\r\n        \/\/ if we are using a JSON with ETags handler, then we are interested\r\n        \/\/  in the the entity tag contained in the response header of the POST\/PUT\r\n        \/\/  requests\r\n        \/\/\r\n        \/\/ however, on Internet Explorer, this response header is missing,\r\n        \/\/  because of the bug described at http:\/\/www.enhanceie.com\/ie\/bugs.asp\r\n        \/\/  (IE0013: IE XMLHTTP implementation turns 204 response code into bogus 1223 status code)\r\n        \/\/  so we have to perform an additional GET to fetch the updated\r\n        \/\/  entity tag.\r\n        \/\/\r\n        \/\/ this bug is present on IE7-IE9 so we do this for any version of IE\r\n        \/\/\r\n\r\n        this.put = function (request) {\r\n           if (sniff(\"ie\")) {\r\n               return xhr.put.apply(this, arguments).then(lang.hitch(this, function(response){\r\n                   delete request.headers['If-Match'];\r\n                   return xhr(\"HEAD\", request);\r\n               }));\r\n           }\r\n           else {\r\n               return xhr.put.apply(this, arguments);\r\n           }\r\n        };       \r\n    };\r\n   \r\n    myXhr.prototype = xhr;\r\n       \r\n    return new myXhr();\r\n});<\/pre>\n<p><strong>Postscript<\/strong><\/p>\n<p>Oh IE, for crying out loud. How much more could we get done if we didn&#8217;t have to spend so much time working around your little quirks?!<\/p>\n<p><strong>Update (27\/02\/12):<\/strong> The comments below are worth a look. <a href=\"http:\/\/adrianspender.com\/blog\">Adrian<\/a> makes the very good point that this workaround is not appropriate for all situations. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;re one of that exceedingly rare breed who regularly check or subscribe to my blog, you probably want to give this post a miss. This one is more for people who find me through Google. A specific solution to a specific, geeky, problem. Background First a little scene setting&#8230; Server side I have a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[514,516,515],"class_list":["post-2043","post","type-post","status-publish","format-standard","hentry","category-code","tag-dojo","tag-etag","tag-xhr"],"_links":{"self":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2043","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2043"}],"version-history":[{"count":0,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2043\/revisions"}],"wp:attachment":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2043"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2043"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2043"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}