短縮URLの展開処理を各言語で書いてみた
別にぜんぜんなんてことはないのだけど、なんとなく(謎)。
展開処理とは言っても、リクエストを出してLocationヘッダの内容を取っているだけだけど。
Java
java.net.HttpURLConnectionクラスを使用して、こんな感じで。
import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; import java.util.HashSet; import java.util.Set; public class ShortURL { private static final Set<URL> services; static { try { Set<URL> tmp = new HashSet<URL>(); tmp.add(new URL("http://t.co/")); tmp.add(new URL("http://bit.ly/")); tmp.add(new URL("http://goo.gl/")); services = Collections.unmodifiableSet(tmp); } catch (MalformedURLException e) { throw new ExceptionInInitializerError(e); } } public static URL expand(URL shortUrl) throws IOException { boolean isTarget = false; for (URL service : services) { if (service.getProtocol().equals(shortUrl.getProtocol()) && service.getHost().equals(shortUrl.getHost())) { isTarget = true; break; } } if (!isTarget) { return shortUrl; } HttpURLConnection conn = (HttpURLConnection) shortUrl.openConnection(); try { conn.setInstanceFollowRedirects(false); conn.setRequestMethod("HEAD"); conn.setDoOutput(false); int statusCode = conn.getResponseCode(); if (statusCode == 301) { String tmpUrlStr = conn.getHeaderField("Location"); return new URL(tmpUrlStr); } else { return shortUrl; } } finally { conn.disconnect(); } } }
呼び出しのサンプル。
try { String urlStr = "http://t.co/vXCdwgSB"; URL url = ShortURL.expand(new URL(urlStr)); System.out.println(urlStr); System.out.println(" => " + url); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
PHP
追加モジュールなしでのベタ実装(謎)。
<?php class ShortURL { private static $services = null; private static function initServices() { if (!is_null(self::$services)) { return; } self::$services = array(); self::$services[] = parse_url("http://t.co/"); self::$services[] = parse_url("http://bit.ly/"); self::$services[] = parse_url("http://goo.gl/"); } public static function expand($shortUrl) { self::initServices(); $parsed = parse_url($shortUrl); if (!$parsed) { return $shortUrl; } $isTarget = false; foreach (self::$services as $service) { if ($service['scheme'] === $parsed['scheme'] && $service['host'] === $parsed['host']) { $isTarget = true; break; } } if (!$isTarget) { return $shortUrl; } $port = isset($parsed['port']) && $parsed['port'] > 0 ? $parsed['port'] : 80; $sock = fsockopen($parsed['host'], $port); if (!$sock) { throw new Exception($shortUrl . ': Connection failed.'); } fwrite($sock, "GET {$parsed['path']} HTTP/1.1\r\n"); fwrite($sock, "Host: {$parsed['host']}\r\n"); fwrite($sock, "Accept: */*\r\n"); fwrite($sock, "\r\n"); $returnUrl = null; while (($header = fgets($sock))) { $header = trim($header); if (!$header) { break; } if (preg_match("/^Location: /", $header)) { $returnUrl = preg_replace("/^Location: /", "", $header); } } fclose($sock); if ($returnUrl) { return $returnUrl; } else { return $shortUrl; } } }
呼び出しのサンプル。
$urlStr = 'http://bit.ly/sopihq';
printf("%s\n", $urlStr);
printf(" => %s\n", ShortURL::expand($urlStr));
JavaScript(Node.js)
ネットワーク通信が発生するので、コールバック関数を指定する以下のような形で書いてみたけど、これでいいのかな(謎)。
var http = require('http'); var url = require('url'); var services = Array( url.parse('http://t.co/'), url.parse('http://bit.ly/'), url.parse('http://goo.gl/') ); module.exports = { expand: function(urlStr, callback) { var parsed = url.parse(urlStr); var isTarget = false; for (var i in services) { if (services[i].protocol == parsed.protocol && services[i].hostname == parsed.hostname) { isTarget = true; break; } } if (!isTarget) { callback(urlStr); } var target = { host: parsed.hostname, port: parsed.port, path: parsed.pathname + (parsed.search || '')}; http.get(target, function(_res) { if (_res.statusCode == 301) { callback(_res.headers.location); } else { callback(urlStr); } }); }, };
呼び出しのサンプル。(xxx.xxx.xxx.xxx は環境に合わせたIPアドレスに、yyyy はポート番号にそれぞれ書き換え)
var http = require('http'); var ShortURL = require('./ShortURL'); http.createServer(function (req, res) { var urlStr = 'http://goo.gl/r1yBJ'; ShortURL.expand(urlStr, function(expanded) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.write(urlStr + '\n'); res.write(' => ' + expanded + '\n'); res.end(); }); }).listen(yyyy, "xxx.xxx.xxx.xxx"); console.log('Server running at http://xxx.xxx.xxx.xxx:yyyy/');