はじめに
URL正規化リダイレクト自体は正常な動作で、無効化する事による悪影響もあるので、気をつけて下さい。なんとなく黒魔術っぽい。
困った現象
- http://〜/path?param の path に // があると / にマージした URL にリダイレクトされる。
通常は問題ないけど、path 部をパラメータとして使おうという場合、
http://〜/w=100,h=100/http://〜
にアクセスすると、http://〜/w=100,h=100/http:/〜
にリダイレクトされる。尚、URLエンコードしても無駄。
ちなみに、go-thumber の元画像URLにプロトコル部を試しに追加したらこの現象に遭遇しました。
原因
- URL は正規化された方に Permanent Redirect される。
- http://? の path の 正規化で // はマージされる
対応するコードは net/http/server.go:cleanPat => path.go:Clean
回避方法
- http://stackoverflow.com/questions/16888285/overriding-gos-default-http-sever-redirect-behaviour
- https://gist.github.com/johnboxall/5698940
HandleFunc を使わずに http.ListenAndServe の第二引数でハンドルを指定して、そこから自前で URL ルーティングを行う。
- go-thumber でパラメータに // を使えるようにする改造
listenAndServe.go
type Handler struct{}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
uri := r.URL.Path
switch uri {
case "/server-status":
statusServer(w, r)
case "/favicon.ico":
errorServer(w, r)
default:
thumbServer(w, r)
}
}
func main() {
flag.Parse()
if *show_version {
fmt.Printf("thumberd %s\n", version)
return
}
client.Timeout = time.Duration(*timeout) * time.Second
var err error
if *local != "" { // Run as a local web server
handler := new(Handler)
// err = http.ListenAndServe(*local, nil)
err = http.ListenAndServe(*local, handler)
} else { // Run as FCGI via standard I/O
err = fcgi.Serve(nil, nil)
}
if err != nil {
log.Fatal(err)
}
}
http.ListenAndServe(*local, nil)
を
http.ListenAndServe(*local, handle)
にして、handle で URI を見て処理をスイッチしてます。
ハマりどころ
- Permanent Redirect はブラウザが覚えてしまうので、一度アクセスしたURLだとサーバ側で対処してもブラウザ側で勝手にアドレスを書き換えるかもしれない。(なので、当初対処に失敗したと勘違いしてた)
- Chrome のシークレット・ウィンドウを使えば一時的に忘れてくれるので、動作確認に便利です。
使用上の注意
- プロキシー等の経路で勝手に正規化されて無駄かもしれない
- 正規化されない事でキャッシュ率が落ちるかもしれない