{"id":597,"date":"2009-05-21T23:02:34","date_gmt":"2009-05-22T04:02:34","guid":{"rendered":"http:\/\/www.jasonmorrison.net\/content\/?p=597"},"modified":"2009-05-21T23:03:26","modified_gmt":"2009-05-22T04:03:26","slug":"tinyurl-trouble-greasemonkey-drops-the-location-header-in-gm_xmlhttprequest","status":"publish","type":"post","link":"http:\/\/www.jasonmorrison.net\/content\/2009\/tinyurl-trouble-greasemonkey-drops-the-location-header-in-gm_xmlhttprequest\/","title":{"rendered":"TinyUrl Trouble: Greasemonkey drops the location header in GM_xmlhttpRequest"},"content":{"rendered":"<p>I get a lot of ideas.  Most of them wander aimlessly in my head until they become obsolete, but once in a while I&#8217;ll get an idea that seems useful and simple enough to do in my free time.<\/p>\n<p>If you&#8217;ve used Twitter, you&#8217;ve seen the myriad of url shortening services like TinyUrl and Bit.ly.  <a href=\"http:\/\/gregable.com\/2009\/05\/why-do-we-even-need-url-shorteners.html\">Url shortening services are a kludge<\/a> and they break one useful, built-in feature of the web, which is the ability to <a href=\"http:\/\/www.jasonmorrison.net\/content\/2009\/open-redirects-under-attack-by-spammers\/\">know where you&#8217;re going when you click a link<\/a>.<\/p>\n<p>So I thought, this is something that I could fix in an hour or so with a Greasemonkey script.  If you have no idea what I&#8217;m talking about, Greasemonkey is a Firefox Plugin that runs in your browser and lets you run your own Javascript on pages you load.  Greasemonkey comes with a handy-dandy AJAX function called GM_xmlhttpRequest.<\/p>\n<p>I figured all I have to do is grab all the anchors on the page, see if they match a list of shortener urls, do an xmlhttpRequest for each one and grab the final location (after the service finishes with it&#8217;s redirecting) from the headers.<\/p>\n<p>Something along these lines:<br \/>\n<code><\/p>\n<pre>\r\nfunction getTargetUrl(short_url) {\r\n\r\n  GM_log('Getting '+short_url);\r\n\r\n  GM_xmlhttpRequest({\r\n      method: 'GET',\r\n      url: short_url,\r\n      headers: {\r\n          'User-agent': 'Mozilla\/4.0 (compatible) Greasemonkey',\r\n          'Accept': 'text\/html'\r\n      },\r\n      onload: function(responseDetails) {\r\n          GM_log('Done.  Status ' + responseDetails.status +\r\n                ' Text ' + responseDetails.statusText + '\\n\\n' +\r\n                ' Headers:\\n' + responseDetails.responseHeaders);\r\n      }\r\n  });\r\n}<\/pre>\n<p><\/code><\/p>\n<p> <!--more--> <\/p>\n<p>As you can see, it should just be a matter of plucking <a href=\"http:\/\/www.w3.org\/Protocols\/rfc2616\/rfc2616-sec14.html#sec14.30\">the location header<\/a> from responseDetails.responseHeaders. <\/p>\n<p>But every time I fetched a url, even though Firefox <a href=\"http:\/\/www.mnot.net\/javascript\/xmlhttprequest\/\">theoretically handles GET redirects correctly<\/a>, I never found a location in the headers returned.<\/p>\n<p>If you can&#8217;t do an AJAX call, you can do things the old-fashioned way with hidden iframes.<\/p>\n<p><code><\/p>\n<pre>\r\n  var the_iframe = document.createElement('iframe');\r\n  the_iframe.setAttribute('style', 'height:10px;width:10px;');\r\n  the_iframe.setAttribute('src', short_url);\r\n  document.body.appendChild(the_iframe);\r\n<\/pre>\n<p><\/code><\/p>\n<p>The problem there is that although I can easily create the iframe, it&#8217;s hard to get the final location.  If I get the value of the src attribute, it&#8217;s still set to whatever I set it to in the first place.  I tried using the location of the contentWindow:<\/p>\n<p><code><\/p>\n<pre>\r\nGM_log('Current href: '+\r\n      document.getElementById('embiggen'+num).contentWindow.location.href);\r\n<\/pre>\n<p><\/code><\/p>\n<p>&#8230;but that gives me &#8220;about:blank&#8221; since my GM script runs before the iframes are loaded.<\/p>\n<p>I played around a bit adding and event listener to the iframe but got access errors.  Then it got late so I gave up. It turns out <a href=\"http:\/\/lifehacker.com\/5196454\/tinyurl-decoder-displays-the-real-addresses-behind-shrunken-urls\">lots of other people have already had the same idea<\/a>.  <\/p>\n<p>Here&#8217;s a <a href=\"http:\/\/userscripts.org\/scripts\/show\/46822\">short url expanding GM script<\/a> that looks promising, if you&#8217;re here to get actual help and not just listen to my whining.  <\/p>\n<p>If you&#8217;re also annoyed by tinyurls or if you&#8217;re a fellow Javascripter, and you have found a glaring error in my plan, let me know in the comments below.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I get a lot of ideas. Most of them wander aimlessly in my head until they become obsolete, but once in a while I&#8217;ll get an idea that seems useful and simple enough to do in my free time. If you&#8217;ve used Twitter, you&#8217;ve seen the myriad of url shortening services like TinyUrl and Bit.ly. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[19],"tags":[262,645,20,646,270,233,260,643,644,642,269,647,641],"class_list":["post-597","post","type-post","status-publish","format-standard","hentry","category-blog","tag-ajax","tag-bitly","tag-firefox","tag-firefox-add-on","tag-greasemonkey","tag-iframe","tag-javascript","tag-kludge","tag-redirects","tag-tinyurl","tag-twitter","tag-url-shorteners","tag-xmlhttprequest"],"aioseo_notices":[],"_links":{"self":[{"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/posts\/597","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/comments?post=597"}],"version-history":[{"count":2,"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/posts\/597\/revisions"}],"predecessor-version":[{"id":599,"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/posts\/597\/revisions\/599"}],"wp:attachment":[{"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/media?parent=597"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/categories?post=597"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.jasonmorrison.net\/content\/wp-json\/wp\/v2\/tags?post=597"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}