鱒身(Masu_mi)のブログ

知った事をメモする場所。

Go1.5.x から URL.RawPath が追加

Goがv1.5(.1?) になりWebサーバーで undecodedなURLのpathを取得する定番の方法が決まったのでメモしておく。 解決策は URL.EscapedPath() を使う事。また確認した所ではURL.RequestURI() の挙動にも変更が加えられており、これでも解決してしまう。

背景

少し前はWebサーバーを書く時Undecodedなリクエストパスを取得したくなると少し困っていた。 以下の様にリクエストパスを得るがv1.4.3 以前では後述する問題があった。

func sampleHandler(w http.ResponseWriter, r * http.Request) {
  fmt.Fprintf(w, "r.RequestURI:%s\n", r.RequestURI)
  fmt.Fprintf(w, "r.URL.RequestURI():%s\n", r.URL.RequestURI())
  // fmt.Fprintf(w, "r.URL.RawPath:%s\n", r.URL.RawPath)
  // fmt.Fprintf(w, "r.URL.EscapedPath():%s\n", r.URL.EscapedPath())
}
func main() {
  http.HandleFunc("/", sampleHandler)
  http.ListenAndServe(":18080", nil)
}

v1.4.3の時の挙動

r.RequestURI はリクエスト内容が直接出力されるが、absolute-formrequest-target に使われているとスキーマなどが含まれて扱いにくい。 URL.RequestURI() はGoがエンコードした内容が含まれる。そのため%24 で転送された部分が$ として残る事になる。

$ export GOROOT=${HOME}/go1.4/
$ ~/go1.4/bin/go run ./main.go &
$ telnet localhost 18080
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /test_%24host%20$ HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Date: Thu, 29 Oct 2015 13:46:11 GMT
Content-Length: 66
Content-Type: text/plain; charset=utf-8

r.RequestURI:/test_%24host%20$
r.URL.RequestURI():/test_$host%20$
GET http://localhost/test_%24host%20$ HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Date: Thu, 29 Oct 2015 13:46:40 GMT
Content-Length: 82
Content-Type: text/plain; charset=utf-8

r.RequestURI:http://localhost/test_%24host%20$
r.URL.RequestURI():/test_$host%20$
^C
Connection closed by foreign host.

v1.5.1の時の挙動

r.RequestURI の挙動は変わらない。変わったのはURL.RequestURI() だ。

URL.RequestURI() はリクエストされたrequest-target が妥当な内容の場合、該当リクエストのパス部分を返す。 これはリクエストから取られGoが再エンコードする訳ではない。さらにabsolute-form であってもパス部分が取得できる。 ただし、この変更が正しいのかわからない。

困っていた内容を解決する意図のURL.EscapedPath() が追加されていた。 これを使うのが良さそう。

とにかく、これで解決した。やったぜ。

$ telnet localhost 18080
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /test_%24host%20$ HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Date: Thu, 29 Oct 2015 14:18:40 GMT
Content-Length: 68
Content-Type: text/plain; charset=utf-8

r.RequestURI:/test_%24host%20$
r.URL.RequestURI():/test_%24host%20$
GET http://localhost/test_%24host%20$ HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Date: Thu, 29 Oct 2015 14:19:03 GMT
Content-Length: 84
Content-Type: text/plain; charset=utf-8

r.RequestURI:http://localhost/test_%24host%20$
r.URL.RequestURI():/test_%24host%20$
^C
Connection closed by foreign host.