net/url の Parse()
に #
を含む文字列を渡す時は以下の点に注意する。
-
#
以降は削除される。 (フラグメント識別子と見なされる) -
#
を文字列に含めたい場合は%23
で記述する。 (エスケープシーケンス)
以下はこの記事を書くに至った経緯なので、多くの方にとって有用な情報はないと思います。
発端
Go で PostgreSQL に繋いでみようと思い、以下のようなコードを書いていた。
package main
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres", "postgres://user1:@localhost:5432/postgres?sslmode=disable&password=asdf#1234")
if err != nil {
panic(err)
}
fmt.Println(db)
rows, err := db.Query("SELECT * FROM table1")
if err != nil {
panic(err)
}
fmt.Println(rows)
}
実行してみると...
panic: pq: password authentication failed for user "user1"
認証に失敗してしまった。
調査
panic
が起きた db.Query
から辿って行くと、 lib/pq が DSN をパースする際に net/url
の Parse()
を呼んでいた。
-
#
以降の削除 - RFC 2396 に基づいた URL 文字列のアンエスケープ
Parse()
では上記 2 点の処理がされている。
修正
#
をエスケープシーケンスに置き換えた。
db, err := sql.Open("postgres", "postgres://user1:@localhost:5432/postgres?sslmode=disable&password=asdf#zxcv")
これを
db, err := sql.Open("postgres", "postgres://user1:@localhost:5432/postgres?sslmode=disable&password=asdf%23zxcv")
こうすることで無事に動きました。