フューチャーアーキテクトアドベントカレンダーの5日目の
サーバーサイドレンダリングの代替としてPrerenderを試してみたの最後で、「Go実装について 長くなったので説明は省略します。だれかがGoアドベントカレンダーを落としたら書くかも?」と書いていたら、早速落とす人がいたらしいので、ハイジャックします。
リバースプロキシーはnet/http/httputilパッケージの ReverseProxyを使えば簡単につくれますよ、という説明はよく見かけるのですが、レスポンスを書き換える方法はまとまった情報がなかったので紹介します。
リクエストを書き換える
go言語でリバースプロキシというブログのエントリーにある通りです。
向き先を書き換える、Directorというフック関数を設定してあげることで、向き先を変えられます。なお、このエントリーにあるように、この関数の中でrequestを書き換えることでドメインごと入れ替えたりとかもできますし、試してはないですが、当然ヘッダーを足すとかもできるでしょう。
director := func(request *http.Request) {
request.URL.Scheme = "http"
request.URL.Host = ":9001"
}
rp := &httputil.ReverseProxy{Director: director}
server := http.Server{
Addr: ":9000",
Handler: rp,
}
レスポンスを書き換える
レスポンスを書き換えるフック関数も用意されています。ModifyResponse
というフィールドにこの関数を設定します。
modifier := func(res *http.Response) error {
// 別途用意したheadless chromeの出力を分析したDOMのソース
// を元に、goqueryを使ってレスポンスを書き換えたstring型のhtmlという
// 変数を用意しています
document, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
return err
}
:
document.Find("head").AppendHtml(result.OGP)
body := document.Find(route.BodySelector)
body.SetHtml("")
body.AppendHtml(result.InnerHTML)
html, err := document.Html()
if err != nil {
return err
}
// ここからが汎用的なところで、この文字列を``io.ReadCloser``にして
// res.Bodyを差し替えます
b := []byte(html)
res.Body = ioutil.NopCloser(bytes.NewReader(b))
res.Header.Set("Content-Length", strconv.Itoa(len(b)))
return nil
}
rp := &httputil.ReverseProxy{
Director: director,
ModifyResponse: modifier,
}
[]byte
にしてからbytes.NewReader
で初期化していたのは、strings.NewReader
で良かった予感。
なお、内容を書き換える場合、Content-Length
をわざわざ書き換えて上げる必要はありません。Body
だけ置き換えればあとはhttputilパッケージが勝手にやってくれます。
追記: やっぱりContent-Length書き換え必要っぽい
追記:github.com/elazarl/goproxyというのもあるらしい
GoでReverse Proxy、ぼくは https://t.co/Eit8Q803Te ってやつを使って書いてます。もう4年くらい運用してる
— ( null) (@yuroyoro) December 6, 2017