読者です 読者をやめる 読者になる 読者になる

Block Rockin’ Codes

back with another one of those block rockin' codes

GO の SPDY パッケージのアップグレード

go spdy

追記

2013/02/13
パッチが本家に取り込まれました

https://code.google.com/p/go/source/detail?r=71409a1c89f0&repo=net

レビューでもの凄くお世話になった mikio 先生には頭が上がりません。
本当にありがとうございました。

intro

あけましておめでとうございます。
このお正月は、ずっと Go を触りながら、 Go の準標準モジュール? である SPDY パッケージのアップグレードをやっていました。


もともとは、 HTTP2 のサーバを書いてみようと思い、色々あって Go を選んだら、spdy パッケージがあるけどバージョンが古かったというのが始まりです。


SPDY の知識はある程度ありますが、 Go 自体はほとんどはじめてなので、こっから Go にも詳しくなれればというモチベーションです。

Go の SPDY パッケージ

もともと、 spdy や websocket などのパッケージは Go の標準パッケージだったんですが、Go の version 1 が出るタイミングでサブリポジトリに移されたようです。
(でも、管理自体は Golang Project がやっているみたい。)

http://golang.org/doc/go1.html#subrepo

なので、 import するときは下記のように指定します。

import "code.google.com/p/go.net/spdy"


このパッケージが提供するのは、 SPDY の各フレームに対応した型とその型への write/read のインタフェースの実装です。


で、この spdy パッケージは spdy/2 準拠になっています。これを spdy/3 にするというのが今回のスコープ。

成果

spdy/2 から spdy/3 への変化は色々ありますが、このパッケージに関わる大きな変化は以下です。

  • 圧縮に使う zlib の辞書データ
  • NOOP frame が消えた
  • CREDENTIAL frame が増えた
  • 各 frame の定義がちょこちょこ変わった


ということで、これらを実装したんですが CREDENTIAL frame 以外は実装が終わりました。


(CREDENTIAL frame はドキュメントの記述が他と整合して無く、内容も割と半端で、よくわからないところがあるし、しかも次のバージョンでは消えるかもしれない、ということでちょっと後回しになってる)


あとは、テストを直しながら明らかに SPDY の仕様変更に弱すぎるところがあるので、そこはペンディングにしたという点を除いては、一通り終わったかなと。
(ほとんどが、 Go のリーディングに費やされました。)

ソースはとりあえず下記に上げています。

https://github.com/Jxck/go-spdy

Contribute

最終的には本家に取り込んでもらえればと思うので、そのための手順を踏む必要があります。
手順は以下。

http://golang.org/doc/contribute.html


とりあえず ML でやってることの旨を伝えたら、Welcome とのことでした。
なので、あとはこの流儀に沿って本家に投げていこうと思います。


Git じゃなくて Mercurial 必須なんで、そっからですが、ちゃんとやれば Go のコミッタ達にレビューをしてもらえるということなので、十分得る物があるだろうと。

Go を触ってみて

今回の作業歴=Go 歴な訳ですが、個人的な感想を箇条書きで。

go fmt

go fmt が最初からあるのは良い。そして、そんなに違和感のあるフォーマットでもなかったし。
こういう流派で無駄な争いやらがおこるのってホント無駄なんで、公式が(長ったらしい説明でなく)ツールで提供してしまうので良いと思う。

ちなみに vimプラグインとかも提供されてて、コード書く環境自体に迷いは無く始められた。

go test

test がパッケージもコマンドもも最初から組み込まれているので、今一番生きのいい testing FW を探すというこの上なく無駄な時間が省けるのもいいですね。


test パッケージくらい最初からある言語は多いけど、ベンチやらコマンドやらも込みで、テスト関数やテストファイルの命名も決まっていてあとは書くだけっていうのがいいと思います。


ただ、 test にはちょっと癖があって、いわゆる assert.Equals な検証ではなく、 Go のコード自体に頼っておかしかったら落とす(Fail) という点が割と面白い。
比較に使うのは、 reflect と型アサーションが多いみたい。

  // 型アサーション
  v, ok := x.(T)
  if !ok {
    t.Fatal("Incorrect type:", T)
  }
  // 同値検証
  if !reflect.DeepEqual(expected, actual) {
    t.Fatal("got: ", actual, "\nwant: ", expected)
  }


あとテストを指定する -test.run という直感的じゃないオプションという点(というか go のオプションパーサのせいで、全体的にコマンドが UNIX ぽくない)と落ちたテストの関数名とかの出し方が -v だってことを覚えておけばまあいいかと。


細かいけど、型があるために Fixture 的なものを作る時にごまかしが効かないというか、 Ruby や JS ではテストしたいところに合わせた半端なオブジェクトをでっち上げたりできたけど、そういうことが出来ないなと思った。
別に、出来ないなと思っただけで GO が悪いとかそういうことは無い。単なる感想。
型があるために減るテストもあるし、Mock みたいな仕組みもあるらしいので、その辺を上手く使って行きたい。

go run

これがあるのが、最大の強みかなと思う。それが無ければちょっと便利な C とか C++ になるのかもしれないけど、ちょっと走らせるという点のコストの低さが非常に嬉しい。


同じコマンド体系で go build もできるので、すぐにバイナリもできるし。この辺がモダンだなぁと思う。
(複雑になってきたら、やっぱり make を書くのかな?その辺は知らない)


ただ、ちょっと試したい時にも、 import や変数の不使用チェックが必ず行われるのがちょっときつい。
カジュアルにコメントアウトしただけでは動かないことがあるので、 go run の時だけでもオプションが欲しいところだけど、
そういうことはしないで、 _ (アンダースコア) を使うようにお達しが出ている。

http://golang.org/doc/go_faq.html#unused_variables_and_imports

むしろ、run でオプションにしておいて build 時にはエラーにしちゃう方が、消し忘れなくていいと思うんだけどどうなんだろう。

GOPATH

最初よくわからずにハマった。全てはこれをきちっと設定してあげる事が重要。ここが規約になっていることが、 make みたいなツールが無くても go build/go install で色々解決できる助けになっているよう。(build のプロセスはまだブラックボックスでいる)


ちなみに、ここはプロジェクトをまたぐと設定しなおさないといけないのが面倒。
それをはかどらせるツールはある(Goのレポジトリ管理をするgenvというものを作りました - YAMAGUCHI::weblog) が、今回はプロジェクトが一個だし以下だけで済んだので入れていない。そのうち色々やるようになったら入れよう。

export GOPATH=$HOME/path-to-project/spdy-go
export PATH=$PATH:$GOPATH/bin
名前空間

パッケージを分けて、大文字で始まる関数がパブリックという覚えやすいルールなんだけど、慣れないと迷う(迷子になる)。


パッケージが複数のファイルから構成されていて、それを構成するファイルの命名規則は無いので、ある関数がどこで定義されているかは、 grep しないとわからない。ファイル自体がモジュールになるのはそれはそれで利点だったなとも思った。


今回のパッケージは、大きく 3 つにわかれていて、型(ベースとなるクラス的な)、 Read、 Write とあるけど、この分け方から外れる帯域定数とかは、grep しないとわからない。まあ、 Java とかも似た感じだけど eclipse があるから困らなかったのか。型と IDE の相性がここで出てくるかも。
あとは、慣れだと思う。

その他

思い出したら後で書く。



ということで、しばらくはこのパッケージのコントリビュートに取り組みたいと思います。