« 2010年4月 | トップページ | 2011年5月 »

2010年8月

2010年8月 8日 (日)

webページをproxyしてくれるwebアプリのソース

悪用されそうで、自重してサービスを止めていたのですが、同じようなツール
を作ってすでに公開(Google App Engineを使って無料で自宅用プロキシを運用)
されているので、私もサービスを再開し、ソースを公開することにしました。
下記で公開中です。
google code webappproxy
メインのプログラム1本とHTML1本の小さなプログラムなので全部掲載します。
guess_charsetに関しては、たばさの:urlfetchとmemcacheを使ってみるテスト
のソースを流用させていただきました。
Python歴3ヶ月程度なのでPythonのほうは滅茶苦茶。

hello.py

 import os
import re
import urllib
import logging
import urlparse
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import urlfetch
from google.appengine.ext.webapp import template

def guess_charset(data):
   f = lambda d, enc: d.decode(enc) and enc 
 
   try: return f(data, 'utf-8') 
   except: pass 
   try: return f(data, 'shift-jis') 
   except: pass 
   try: return f(data, 'euc-jp') 
   except: pass 
   try: return f(data, 'iso2022-jp') 
   except: pass 
   return None 

class MainPage(webapp.RequestHandler):
  def get(self):
    path = os.path.join(os.path.dirname(__file__), 'main.html')
    self.response.out.write(template.render(path, {}))

class NotePage(webapp.RequestHandler):
  def get(self):
    url=""
    if(self.request.get("url")):
      if re.compile("^\s*http://").match(self.request.get("url")):
        url = self.request.get("url").strip()
      else:
        url = "http://"+self.request.get("url").strip()
    else:
      if 'host' in self.request.cookies:
        url = self.request.cookies['host'] + self.request.path
    if url!="" :
      result = urlfetch.fetch(url)
      enc=guess_charset(result.content)
      if enc == None or enc == "shift_jis":
        enc="cp932"
      dec_content = result.content.decode(enc)
      enc_content = dec_content.encode("utf_8")
      result.content = re.sub("charset=([a-zA-Z0-9_]*)","charset=UTF-8",enc_content,1)
      if result.status_code == 200:
        for k,v in result.headers.iteritems():
          self.response.headers.add_header(k, v)
        parseString=urlparse.urlparse(url)
        cookie_val = 'host' + '=' + parseString[0]+"://"+parseString[1]
        self.response.headers.add_header('Set-Cookie',cookie_val)
        cookie_val = 'path' + '=' + re.sub("/[^/]*$","",parseString[2])
        self.response.headers.add_header('Set-Cookie',cookie_val)
        self.response.out.write(result.content)
    else:
      self.response.out.write("")

application = webapp.WSGIApplication(
                                     [('/main', MainPage),(r'/[^?]*',NotePage)],
                                     debug=True)
def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

/mainでアクセスされたら、MainPageのgetを呼び出し、main.htmlを返して
います。
それ以外のアドレスはすべてNotePageのgetを呼び出します。
こうすることには 訳があり、後ほど説明します。
main.html画面にurlを入力し、displayボタンを押下すると、/?url=指定したurl
の形式でアクセスされます。
指定されたurlからurl_fetchで内容をごっそり持ってきて、utf-8に変換します。
また、クッキーに指定されたURLのHostをセットしておきます。
こうすることで、表示した画面のパスのみで記述されたリンクをクリックされた
際に、 クッキーに保存しているHost名にそのパスをくっつけることで、再び
コンテンツを 取得することができるようになります。
/main以外のアドレスをすべてNotePageのgetを呼び出すようにしたのもそういう
訳です。

main.html

 <html>
  <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>title</title>
 <link rel="shortcut icon" href="favicon.ico">
 <script type="text/javascript">
 function parseCooke(){
      cookies = document.cookie.split(";");
      obj = {};
 for(var i=0;i<cookies.length;i++){
        pair = cookies[i].split("=");
        obj[pair[0].replace(/^\s+/,"")] = pair[1];
 }
 return obj;
 }
 function loadData()
 {
 document.getElementById('ifm').src = "/?url="+escape(document.getElementById('url').value);
 }
 function modLink()
 {
 var doc;
 var ifm = document.getElementById("ifm");
 if (document.all) {
 // IE
        doc  = ifm.contentWindow.document;
 } else {
 // Mozilla
        doc = ifm.contentDocument;
 }
 var aElems=doc.getElementsByTagName("a");
 var cookie_info = parseCooke();
 var nHost = cookie_info.host;
 var path = cookie_info.path;
 var reg = RegExp(nHost);
 var reg2 = RegExp("http://"+location.host);
 for(var i=0;i<aElems.length;i++)
 {
 var aElem = aElems[i];
 if (document.all) {
 if(aElem.target!=null){
            aElem.target=null;
 }
 }
 else if(aElem.target){
 delete(aElem.target);
 }
 if(aElem.href.match(reg)){
          aElem.href= aElem.href.replace(nHost,"http://"+location.host);
 }
 else if(!aElem.href.match(reg2)){
          aElem.href="http://"+location.host+"?url="+escape(aElem.href);
 }else{
 if(!aElem.getAttributeNode("href").nodeValue.match(/^[\/#]/)){
            aElem.href =  path + "/" + aElem.getAttributeNode("href").nodeValue;
 }
 }
        aElem.onclick=function(ev){
 var elem = null;
 if(ifm.contentWindow.event){
            elem=ifm.contentWindow.event.srcElement;
 }else
 {
            elem=ev.target;
 }
          ifm.src = elem.href;return false;
 };
 }
 var imgElems=doc.getElementsByTagName("img");
 for(var i=0;i<imgElems.length;i++)
 {
 var imgElem = imgElems[i];
 if(!imgElem.getAttributeNode("src").nodeValue.match(/^http:/)){
 if(imgElem.getAttributeNode("src").nodeValue.match(/^\//)){
            imgElem.src = nHost + imgElem.getAttributeNode("src").nodeValue;
 }else{
            imgElem.src = nHost + "/" + path + "/" + imgElem.getAttributeNode("src").nodeValue;
 }
 }
 }
 }
 </script>
 </head>
<body>
  <form id="note_form" target="#" onsubmit="loadData(); return false;">
    URL:<input type="text" name="url" id="url" size="150"/>
    <input type="submit" value="display"/>
  </form>
  <iframe id="ifm" frameborder='0' style="width:100%;height:100%;border:none;" onload="modLink();" >
  </iframe>
</body>
</html>

main.htmlでは、displayボタンが押された時に/?url="URLのテキストボックスに
書かれた値" の形式でwebappproxyのURLを呼び出し、応答をiframeに書き出し
ています。
その際、iframeに書き出されたaタグのリンクをすべて抽出し、srcが同一ホストの
場合は、ホスト名を削除し(クッキーにホストが保存されているため),パスだけに
します。
違うホスト名の場合は/?url=違うホストのURLにします。
上記のようにすることで、proxyして表示されたリンクをたどっていっても、
proxyされ続けるようにしています。

| | コメント (0) | トラックバック (0)

iPhoneのsafariで文字化けしているサイトを読めるようにする

iPhoneのsafariは文字のエンコーディングを指定できることができないので、
文字化けしたサイトを簡単に読めるようにすることができない。
そこで、以前Google App Engineで作ったWeb proxyを活用することを
思いついた。webページをproxyしてくれるwebアプリ
iPhoneで下記サイトにアクセスし、URL:となっているtextboxに文字化けした
URLを記述し、displayボタンを押下。
webappproxy

ちょっとフォーマットが変わるが、うまくいった。
文字化けするWebページ
1
webappproxy適用後
Photo

| | コメント (0) | トラックバック (0)

LinuxでchrootによるJail環境を構築2

前にも書いたLinuxでchrootによるJail環境を構築 ですが、現在のプロジェクト
でもかなり役にたったので、再掲載。
今回はUbuntu 10.04で検証しました。
今回のプロジェクトで必要になった理由は、Rails2.8とRails3のWebアプリを1台
のサーバで開発、動作させたいけど、rails3が依存する1.0系のbundlerが現在主流
の0.9系とGemファイルで互換性がないため、bundler1.0系をインストールでき
ないよというジレンマを解消するため、Jail環境でbundler1.0系やRails3をインス
トールするようにしました。ポート番号のバッティングさえ気をつければ、サーバ
が増えたような環境ができます。
KVMやXen等を使えば、OSさえ違うもっと完全な仮想マシンを作れますが、よく
知らないので、そこまでしたくないなーという時に便利です。

sudo su -l

rootになる。

mkdir /chroot

適当なchroot用のディレクトリの作成(どこでもいい。以降は/chrootと仮定)

mkdir /chroot/proc

procディレクトリの作成

mkdir /chroot/root

rootのホームディレクトリの作成

mkdir /chroot/bin

chroot用の/binディレクトリの作成

cp -r /bin/* /chroot/bin/

/bin配下のコマンドをchroot環境にコピー

mkdir /chroot/lib

chroot用の/libディレクトリの作成

cp -r /lib/* /chroot/lib/

/lib配下のライブラリをchroot環境にコピー

mkdir /chroot/usr

chroot用の/usrディレクトリの作成

cp -r /usr/include /chroot/usr/

/usr/include配下のヘッダをchroot環境にコピー

cp -r /usr/lib /chroot/usr/

/usr/lib配下のライブラリをchroot環境にコピー

mkdir /chroot/tmp

chroot用の/tmpディレクトリの作成

chmod 777 /chroot/tmp

chroot用の/tmpディレクトリのモード変更

mkdir /chroot/dev

chroot用の/devディレクトリの作成

mknod  -m 666 /chroot/dev/null c 1 3

chroot環境に/dev/nullデバイスの作成

mknod  -m 666 /chroot/dev/zero c 1 5

chroot環境に/dev/zeroデバイスの作成

mknod  -m 644 /chroot/dev/random c 1 8

chroot環境に/dev/randomデバイスの作成

mknod  -m 644 /chroot/dev/urandom c 1 9

chroot環境に/dev/urandomデバイスの作成

mknod  -m 666 /chroot/dev/tty c 5 0

chroot環境に/dev/ttyデバイスの作成

mknod  -m 660 /chroot/dev/tty0 c 4 0

chroot環境に/dev/tty0デバイスの作成

mknod  -m 600 /chroot/dev/tty1 c 4 1

chroot環境に/dev/tty1デバイスの作成

mkdir /chroot/etc

chroot環境に/etcディレクトリの作成

cp -r /etc/* /chroot/etc

chroot環境に/etc配下をコピー

mkdir /chroot/usr/bin

chroot環境に/usr/binディレクトリの作成

cp -r /usr/bin/* /chroot/usr/bin/

/usr/bin以下のコマンドをchroot環境にコピー

mkdir /chroot/sbin

chroot環境に/sbinディレクトリの作成

cp -r /sbin/* /chroot/sbin/

/sbin以下のコマンドをchroot環境にコピー

chmod 4511 /chroot/usr/bin/sudo

chroot環境のsudoにrootのsetuidをセット

mkdir /chroot/usr/sbin

chroot環境に/usr/sbinディレクトリの作成

cp -r /usr/sbin/* /chroot/usr/sbin

chroot環境に/usr/sbinをコピー

mkdir /chroot/var
mkdir /chroot/var/lock
mkdir /chroot/var/log
mkdir /chroot/var/run

chroot環境に各種/varディレクトリの作成

mkdir /chroot/usr/share
cp -r /usr/share/perl /chroot/usr/share/
cp -r /usr/share/perl5 /chroot/usr/share/
cp -r /usr/share/git-core /chroot/usr/share/
cp -r /usr/share/vim /chroot/usr/share/

chroot環境に/usr/shareを必要な分だけコピー(環境によって違う)

sudo cp /usr/share/zoneinfo/Japan /chroot/etc/localtime

chroot環境の時刻のlocaleを設定

mkdir -p /chroot/home/ユーザー名
chown -R ユーザ名:ユーザ名 /chroot/home/ユーザー名

chroot環境に自分のホームディレクトリを作成(作成済みユーザーであること)

chroot /chroot

chrootの実行!!

mount -t proc proc /proc

/procのmount

su -l ユーザー名
ssh-keygen

これで、aptなり、gemを本体に影響与えることなく、好きにインストールし放題。
chrootのディレクトリの名前さえ変えれば、任意の数だけJail環境を構築できる。
仮想化によるオーバーヘッドもないし、もっとみんな知っといたほうがいいんじゃ
ないかな。

| | コメント (1) | トラックバック (0)

« 2010年4月 | トップページ | 2011年5月 »