gorilla/sessions と gorilla/context と net/http.Request.WithContext で気をつけること
net/http
には net/http.Request.WithContext という context を紐付け直すメソッドがある。
実装はこうなっている。
func (r *Request) WithContext(ctx context.Context) *Request { if ctx == nil { panic("nil context") } r2 := new(Request) *r2 = *r r2.ctx = ctx // Deep copy the URL because it isn't // a map and the URL is mutable by users // of WithContext. if r.URL != nil { r2URL := new(url.URL) *r2URL = *r.URL r2.URL = r2URL } return r2 }
ところで、session を扱おうとすると gorilla/sessions というのが出てきたりする。 echo*1 とか gin*2 のコミュニティ?の session ライブラリでも使われたりする。のでまだメジャなライブラリと言えるかもしれない。
gorilla/sessions は gorilla/context という標準の context package が出てくる前に生まれた request scope な値を入れるためのライブラリに依存している。
これは map[*http.Request]map[interface{}]interface{}
という map で管理するというもの。
また
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler or else you will leak memory! An easy way to do this is to wrap the top-level mux when calling http.ListenAndServe:
という記述があり、要は最後にこの map から request object を消去しないとメモリリークするという話。
ただ、ここで上の Request.WithContext
の実装見てわかるのだがポインタが変わるので、gorilla/context の map に入った request object を消すことが出来ない。
なので、context.ClearHandler
を使ってもメモリリークする。
echo とか gin にはライブラリの context object があってそこに request やメタデータを入れられるのでそちらで管理している限りは request object のポインタは変わらず、最後に context.ClearHandler
を呼べば無事 map から消去出来る。
もし Request.WithContext
を使いたいのであれば gorilla/context の map に request object を入れなければ良い。 gorilla/sessions
の store interface には Get
と New
(https://godoc.org/github.com/gorilla/sessions#Store) という method があり、Get は gorilla/context を利用してしまうので New の方を呼び、その session object 自体を Request.WithContext
で紐づけてあげれば良い。
結論
gorilla/sessions v2 頑張ってほしい。