2012/10/23


手軽に使える forward http proxy : stone, Tinyproxy - 酒日記 はてな支店

直接グローバルに繋がる経路をもたないホストから http アクセスしたいので http proxy を使いたい。Squidは定番ですが、もう少し手軽なのはなにかないかと思っていたところ twitter で教えていただきました。

http://d.hatena.ne.jp/sfujiwara/20121023/1350990163
本題からそれますが、go言語で小さいプロキシを使う場合に僕がお勧めしたいのが goproxy です。
elazarl/goproxy - GitHub

An HTTP proxy library for Go

https://github.com/elazarl/goproxy
goproxy 自身は実は実行モジュールを提供していません。ですので goproxy を使った実行モジュールは自分で作ります。
package main

import (
    "github.com/elazarl/goproxy"
    "log"
    "net/http"
)

func main() {
    proxy := goproxy.NewProxyHttpServer()
    proxy.Verbose = true
    log.Fatal(http.ListenAndServe(":8080", proxy))
}
このソースを proxy.go という名前で保存して、go build proxy.go とすれば実行モジュールが出来上がります。
ポートを変えたいであったり、監視機能を付けたいという人は勝手にソースを弄ります。
goproxy はプログラマブルなプロキシです。なので例えば package main

import (
    "github.com/elazarl/goproxy"
    "log"
    . "net/http"
    "regexp"
    "time"
)

func main() {
    proxy := goproxy.NewProxyHttpServer()
    proxy.Verbose = true
    re := regexp.MustCompile(`^(www\.)?2ch\.net/?`)
    proxy.OnRequest(goproxy.UrlMatches(re)).DoFunc(
        func(r *Request, ctx *goproxy.ProxyCtx) (*Request, *Response) {
            h,_,_ := time.Now().Clock()
            if h >= 9 && h <= 18 {
                return r,goproxy.NewResponse(r,
                    goproxy.ContentTypeText, StatusForbidden,
                    "就業時間中です。通報しました。")
            }
            return r, nil
    })
    log.Fatal(ListenAndServe(":8888", proxy))
}
こう書けば就業時間中の2ch閲覧禁止を行うプロキシが出来上がります。もちろん外部の設定を参照して制御を行うのも良いと思います。
また squid の様にキャッシュする事で高速に動作させる事も出来るかと思います。オリジナルのプロキシを作ってみたいという方は試してみる価値あるかと思います。

Posted at by



2012/10/19


Jekyllを使っていていつも思うのが、githubの様な ```perl
use strict;
use warnings;

warn "foo";
```
という Triple Backtick が使えない事なんだけど、まぁこれは Markdown の仕様に準拠していない拡張なのでしょうがないと思いつつも、最近 github に慣れすぎていてイライラしてきた。Jekyll Plugin では Markdown の変換をプラグインとして書くことは出来ないので、Jekyll::MarkdownConverter の convert メソッドを上書きしてやるプラグイン(と呼べるかどうかはあるが)を書いた。
module Jekyll
  class MarkdownConverter
    alias :old_convert :convert
  
    def convert(content)
      content.gsub!(/(?:^|\n)```(\w*)\n(.*?\n)```\n/mdo |text|
        cls = $1.empty? ? "prettyprint" : "prettyprint lang-#{$1}"
        "\n<pre class=\"#{cls}\"><code>#{CGI.escapeHTML($2)}</code></pre>"
      end
      old_convert(content)
    end
  end
end
これを _plugins/triple-backtick.rb として入れておけば、Google Code Prettify が検知できるクラス名を付けてコード出力してくれ、みんな大好き ``` が使える様になって幸せになれる。
ちなみにこの方式を使って、maruku や redcloth で出力させたい場合はLiquidのコンバータまでさかのぼって上書きしないといけなくなるので諦めた。
Posted at by



2012/10/11


タイトルなげーーーーーーーーーーー!
プロキシ環境下でしか動きませんが、使いたい人は勝手に書き換えて下さい。
boost::bind 使わずラムダだけでやってんの、オレオレルールだから気にした奴は負け。
#define _WIN32_WINNT 0x0501
#include <iostream>
#include <string>
#include <sstream>
#include <system_error>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/regex.hpp>
#include <boost/foreach.hpp>
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <boost/property_tree/json_parser.hpp>

using namespace boost::asio;
using namespace boost::asio::ip;
using namespace boost::property_tree;
using namespace boost::system;

int
main(int argc, char* argv[]) {
  if (argc == 1) {
    std::cerr << "usage: " << argv[0] << " [words]" << std::endl;
    return -1;
  }

  // リクエストする URL を作る
  std::string get_url =
    "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=";

  for (int i = 1; i < argc; i++) {
    std::string arg = argv[i];
    BOOST_FOREACH(auto c, arg) {
      if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
          (c >= '0' && c <= '9') ||
          c == '-' || c == '_' || c == '.' || c == '~') {
        get_url += c;
      } else if (c == ' ') {
        get_url += '+';
      } else {
        std::stringstream ss;
        ss << "%" << std::hex << std::setw(2) <<
          std::setfill('0') << (int) (c & 0xff);
        get_url += ss.str();
      }
    }
  }

  // HTTP_PROXY 環境変数からホスト名とポート番号を得る
  std::string http_proxy = std::getenv("HTTP_PROXY");
  boost::regex pattern("^(?:http://)?([^/]+):([0-9]+)");
  boost::match_results<std::string::const_iterator> it;
  if (!regex_search(http_proxy, it, pattern)) {
    std::cerr << "プロキシを使っていません" << std::endl;
    return -1;
  }

  io_service service;
  tcp::resolver resolver(service);

  // ホスト名から名前解決を行う
  tcp::resolver::query resolver_query(
    std::string(it[1].first, it[1].second), // ホスト名
    std::string(it[2].first, it[2].second)  // ポート番号
  );

  std::string data;
  resolver.async_resolve(
    resolver_query,
    [&](const error_code& ec, tcp::resolver::iterator it) {
      if (ec) {
        std::cerr << ec.message() << std::endl;
      } else {
        // プロキシに接続する
        tcp::socket socket(service);
        socket.async_connect(
          it->endpoint(),
          [&](const error_code& ec) {
            if (ec) {
              std::cerr << ec.message() << std::endl;
            } else {
              // プロキシに GET 命令を送信する
              std::string query = "GET ";
              query += get_url;
              query += " HTTP/1.0\r\n\r\n";

              async_write(
                socket,
                buffer(query.c_str(), query.size()),
                [&](const error_code& ec, std::size_t len) {
                  if (ec) {
                    std::cerr << ec.message() << std::endl;
                  } else {
                    boost::array<char1024> buf;
                    std::stringstream ss;
                    // HTTP/1.0 なので切断までループ
                    while (socket.is_open()) {
                      socket.async_read_some(
                        buffer(buf),
                        [&](const error_code& ec, std::size_t len) {
                          if (ec) {
                            socket.close();
                          } else if (len == 0) {
                            socket.close();
                          } else {
                            ss << std::string(buf.data(), len);
                            buf.assign(0);
                          }
                        });
                      service.run_one();
                    }
                    data = ss.str();
                    // ヘッダを取り除く
                    std::size_t pos = data.find("\r\n\r\n");
                    if (pos != std::string::npos) {
                      data = data.substr(pos + 4);
                    }
                  }
                });
              service.run_one();
            }
          });
        service.run_one();
      }
    });
  service.run_one();

  if (!data.empty()) {
    // 結果で得られた JSON をパースする
    std::stringstream ss(data);
    ptree pt;
    read_json(ss, pt);

    if (pt.get<int>("responseStatus") == 200) {
      // 結果配列でループしながら表示
      ptree& results = pt.get_child("responseData.results");
      BOOST_FOREACH(const ptree::value_type& result, results) {
        std::string url = result.second.get<std::string>("url");
        std::string title = result.second.get<std::string>("titleNoFormatting");
        std::cout << url << std::endl << "  " << title << std::endl;
      }
    }
  }
  return 0;
}
Posted at by