emahiro/b.log

Drastically Repeat Yourself !!!!

GAE/GOのversionを上げたらContextが違ってコードが動かなくなってた話

有名な話です。が、いざ自分が体験したので備忘録としてまとめます。

github.com

上記で上げられている netcontextとcontext周りで死ぬ というのに引っかかりました。

課題

go1.6上で以下のようなリクエスト比較するコードを書いてました。 ※ コードはあくまでサンプルです。

url := "https://www.gooogle.com"
values := url.Values{}
req_1, _ := http.NewRequest("POST", url, strings.NewReader(values.Encode()))
req_2, _ := http.NewRequest("POST", url, strings.NewReader(values.Encode()))

if reflect.DeepEqual(req_1, req_2) {
  // requestが同値である。
} else {
  // requestは同値でない
}

req_1req_2 をdeepEqualで比較して、同値性を判別したいという意図でしたが、このコードはgo1.6だと同値だと判定されますが、go1.8だと同値だと判定されません。

理由は最初に書いた netcontextとcontext周りで死ぬ が原因だと思われます。

goのx/net/context パッケージが標準の context に入ったというのは有名です。
refs: Go 1.7 Release Notes - The Go Programming Language

net/context も標準のcontextとして扱われるようになったので、req_1req_2 は別々のリクエスト、すなわち異なるcontextを持っていると判定され、それを reflect.DeepEqual にかけた場合、標準の context 違いがあるので、同値判定されません。

go1.7のcontextについては以下のブログがすごく勉強になりました。

Go1.7のcontextパッケージ | SOTA

同値性の比較

では、requestの比較をしたい場合はどうすればいいかというと、 httputil.DumpRequest を使います。 refs: httputil - The Go Programming Language

url := "https://www.gooogle.com"
values := url.Values{}
req_1, _ := http.NewRequest("POST", url, strings.NewReader(values.Encode()))
req_2, _ := http.NewRequest("POST", url, strings.NewReader(values.Encode()))

// dumpは[]byte型
dump_1, _ := httputil.DumpRequest(req_1, true)
dump_2, _ := httputil.DumpRequest(req_2, true) 

// DeepEqual
if reflect.DeepEqual(dump_1, dump_2){
  // requestの同値判定
}

// bytes.Equal
if bytes.Equal(dump_1, dump_2) {
  // requestの同値判定
}

// stringに変換して文字列比較
if string(dump_1) == string(dump_2) {
  // requestの同値判定
}

DumpRequest することで context ではなくリクエストそのものを比較出来ます。
比較方法は、以前同様 DeepEqual を使ってもいいですし、 []byte 型に変換されるので、それに合わせて bytes packageを使ってもいいですし、文字列に変換して文字列一致をしても同値性を取ることが出来ると思います。