GoのClient Hintsパーサを書いたので使い方メモ
インストール
go get github.com/cateiru/go-client-hints/v2
使い方
goclienthints.Parse(&r.Header)
のようにして引数にhttp.Header
を渡すことでパースをし、structで返却します。
clienthint.IsSupportClientHints(&r.Header)
というメソッドも追加しており、こちらはClient Hintsのヘッダが存在しているかをチェックしてboolで返却します。
このメソッドはFireFoxでClient Hintsが実装されていないため追加したものです。実際にこのパッケージを使用する場合、IsSupportClientHints
でClientHintsがサポートされているかを判定してtrueの場合はパース、falseの場合はUser-Agentから判定するといいかと思います。
例
package example import ( "fmt" "net/http" clienthint "github.com/cateiru/go-client-hints/v2" ) func Handler(w http.ResponseWriter, r *http.Request) { clientHints, err := clienthint.Parse(&r.Header) if err != nil { return } // Sec-CH-UA field fmt.Println("Brand: ", clientHints.Brand.Brand) fmt.Println("Brand Version: ", clientHints.BrandVersion) fmt.Println("Brands: ", clientHints.Brands) // Sec-Ch-Ua-Platform filed fmt.Println("Platform: ", clientHints.Platform) // Sec-CH-UA-Platform-Version filed fmt.Println("Platform Version: ", clientHints.PlatformVersion) // Sec-Ch-Ua-Mobile filed fmt.Println("IsMobile: ", clientHints.IsMobile) // Sec-CH-UA-Arch filed fmt.Println("Arch: ", clientHints.Architecture) // Sec-CH-UA-Bitness filed fmt.Println("Bitness: ", clientHints.Bitness) // Sec-CH-UA-Model filed fmt.Println("Model: ", clientHints.Model) // Sec-Ch-Ua-Full-Version filed fmt.Println("Full Version: ", clientHints.FullVersion) }
技術的な話
Client Hintsのヘッダの値はRFC 8941で記述されています。このパッケージでは、github.com/dunglas/httpsfvを使用してRFC 8941の形式をパースしています。
また、Sec-CH-UA
ヘッダでは" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
のように複数のブランド名とダミーのブランド名が混在しています。全てをブランド名-バージョンの配列でパースできても使いづらいと感じたため、予めブランド名を配列として定義しておきそれに一致するものを優先的に返却するようにしています。
現在は以下のようなブランド名をサポートしています。
// Brand array // A array of brands that Sec-Ch-Ua prefers to compare. var PrimaryBrands = []string{ "Google Chrome", "Chrome", "Microsoft Edge", "Edge", "Brave Browser", "Brave", "Yandex", "CocCoc", } // This array will be used to try to get the brand name // if it is not in the PrimaryBrands array. var SecondaryBrands = []string{ "Chromium", }
Secondary
はPrimary
のブランド名が全て一致しなかった場合に比較されます。
バグ報告など
issueに投稿していただけるとありがたいです。
特に、Sec-Ch-Ua
ヘッダのブランド名は既存のブラウザ名を配列で保持しているので…