Why not login to Qiita and try out its useful features?

We'll deliver articles that match you.

You can read useful information later.

2
1

More than 3 years have passed since last update.

unicornにパッチを当ててカスタムgemを作る

Posted at

概要

RubyのアプリケーションサーバunicornにはURLの最大長の制限がC言語でハードコードされており、実行時に変更できないようだ。

DEF_MAX_LENGTH(REQUEST_URI, 1024 * 15);
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
DEF_MAX_LENGTH(REQUEST_PATH, 4096); /* common PATH_MAX on modern systems */
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));

この上限値を書き換えてカスタムunicorn gemを作ってみようと思う。

unicornのgemを作成

まずはunicornのリポジトリをclone。

git clone https://github.com/defunkt/unicorn.git
cd unicorn

リポジトリ内のHACKINGファイルを読むとgemのビルドに必要なツールが色々書いてある。

  1. GNU make
  2. Ragel
  3. pandoc
  4. olddoc(書いてないのだが必要)

1~3、そしてgccなどはお使いのOSのパッケージマネージャでインストールしていただきたい。Arch Linuxの場合はpacmanで全部そろう。
問題はolddocというマイナーなツールで、これは

gem install olddoc

でインストールできる。
以上で準備は整った。

gitにタグを打つ。これがgemのバージョン番号になる(この辺の仕組みはGIT-VERSION-GENファイルを参照)。

git tag -a v5.5.1.custom -m 'v5.5.1.custom'

gemをビルド:

make gem

pkgディレクトリの中にunicorn-5.5.1.custom.gemといファイルが作成されているはずだ。

ビルドしたgemを試してみる

gemコマンドでインストールしてみる

gem install pkg/unicorn-5.5.1.custom.gem

下記を実行してバージョンが5.5.1.customと表示されればインストール成功だ。

ruby -r unicorn -e 'puts Unicorn::Const::UNICORN_VERSION'

URL最大長の確認

下記のファイルを任意のディレクトリに作り、unicorn -p 9000を実行すればunicornがポート9000番で起動するはずだ。

config.ru
class TestApp
  def call(env)
    [ 200,                                          # ステータス(Integer)
      { 'Content-Type' => 'text/plain' },           # レスポンスヘッダ(Hash)
      env.keys.sort.map {|k| "#{k} = #{env[k]}\n" } # body(StringのArray)
    ]
  end
end

run TestApp.new

curlで適当な長さのURLを生成して投げてみよう。

curl -si "http://localhost:9000/?aaa=$(ruby -e 'puts "x"*10')"

"x"*10の部分が10300バイトくらいになると

HTTP/1.1 414 URI Too Long

が返るようになるはずだ。

unicornのソースを書き換えてURL最大長を上げる

下記のように書き換える。

diff --git ext/unicorn_http/global_variables.h ext/unicorn_http/global_variables.h
index f8e694c..0964209 100644
--- ext/unicorn_http/global_variables.h
+++ ext/unicorn_http/global_variables.h
@@ -62,10 +62,10 @@ NORETURN(static void parser_raise(VALUE klass, const char *));
 /* Defines the maximum allowed lengths for various input elements.*/
 DEF_MAX_LENGTH(FIELD_NAME, 256);
 DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
-DEF_MAX_LENGTH(REQUEST_URI, 1024 * 15);
+DEF_MAX_LENGTH(REQUEST_URI, 1024 * 64);
 DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
 DEF_MAX_LENGTH(REQUEST_PATH, 4096); /* common PATH_MAX on modern systems */
-DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
+DEF_MAX_LENGTH(QUERY_STRING, (1024 * 64));

 static void init_globals(void)
 {

再度make gemでビルドすると今度はgemファイル名がunicorn-5.5.1.custom.dirty.gemのようにdirtyとついているかもしれない。これは最終コミットから変更があることを意味する。これが嫌ならコミットして再度タグを打ってからビルドしよう。
前回のgemをアンインストールしてから新しいgemをインストールする。

gem uninstall unicorn -v 5.5.1.custom
gem install pkg/unicorn-5.5.1.custom.dirty.gem

またunicornを立ち上げてcurlで投げると、今度は65536バイト近くまでいけるようになっているはずだ。

ビルドしたgemを他のプロジェクトのGemfileから利用できるようにする

gemをS3に置く方法

これが良いのではないか。
http://fly1tkg.github.io/2014/09/s3-gem/

Gemfileでgitリポジトリを指定する方法

修正したソースコードを独自リポジトリに置いてGemfileで

gem "unicorn", git: 'git@github.com:MY_NAME/unicorn.git', branch: 'bbd270b'

のように指定したくなるかもしれない。しかしこのままではうまくいかない
ビルドに必要な下記ファイルが欠けているからだ。

ext/unicorn_http/unicorn_http.c
lib/unicorn/version.rb

これらはmake gem時に生成される。.gitignoreでも無視するように指定されている。これらもリポジトリに含めればGemfileから上記の記述で使えるようになる。厄介なところだ。いい方法があったら教えていただきたい。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

@aosho235's pickup articles

aosho235

@aosho235

1981年生まれ。駅すぱあとの会社で新規サービスを開発しています。興味のあるレイヤーはOS~ミドルウェア。好きなことは自分が便利と思うものを作ること。トレンドを追うより、使い慣れた技術で効率的に開発するのを好みます。そのためライブラリやサービスの挙動は仔細に把握しておきたいものです。嫌いなものは定義がさっぱり分からない言葉。
val
経路検索システム「駅すぱあと」をはじめ、全国のデータと高い信頼性をベースにさまざまなサービスを展開。

Comments

No comments

Let's comment your feelings that are more than good

Being held Article posting campaign

2
1

Login to continue?

Login or Sign up with social account

Login or Sign up with your email address