From 0cd84ada1e66b6d917ea47c0c88693707f92f405 Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Sat, 27 May 2017 15:31:50 +0200 Subject: [PATCH] Extract useragent package Fixes #30 --- api/api.go | 13 +++++++------ api/api_test.go | 1 + useragent/useragent.go | 38 +++++++++++++++++++++++++++++++++++++ useragent/useragent_test.go | 36 +++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 useragent/useragent.go create mode 100644 useragent/useragent_test.go diff --git a/api/api.go b/api/api.go index 74f7932..aa7ba35 100644 --- a/api/api.go +++ b/api/api.go @@ -6,13 +6,13 @@ import ( "html/template" "io" + "github.com/mpolden/ipd/useragent" "github.com/sirupsen/logrus" "math/big" "net" "net/http" "path/filepath" - "regexp" "strconv" "strings" @@ -24,10 +24,6 @@ const ( textMediaType = "text/plain" ) -var userAgentPattern = regexp.MustCompile( - `^(?:curl|Wget|fetch\slibfetch|ddclient|Go-http-client|HTTPie)\/.*|Go\s1\.1\spackage\shttp$`, -) - type API struct { Template string IPHeader string @@ -210,7 +206,12 @@ func (a *API) NotFoundHandler(w http.ResponseWriter, r *http.Request) *appError } func cliMatcher(r *http.Request, rm *mux.RouteMatch) bool { - return userAgentPattern.MatchString(r.UserAgent()) + ua := useragent.Parse(r.UserAgent()) + switch ua.Product { + case "curl", "HTTPie", "Wget", "fetch libfetch", "Go", "Go-http-client", "ddclient": + return true + } + return false } type appHandler func(http.ResponseWriter, *http.Request) *appError diff --git a/api/api_test.go b/api/api_test.go index bb92ea5..fec6236 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -150,6 +150,7 @@ func TestCLIMatcher(t *testing.T) { }{ {"curl/7.26.0", true}, {"Wget/1.13.4 (linux-gnu)", true}, + {"Wget", true}, {"fetch libfetch/2.0", true}, {"HTTPie/0.9.3", true}, {"Go 1.1 package http", true}, diff --git a/useragent/useragent.go b/useragent/useragent.go new file mode 100644 index 0000000..e533662 --- /dev/null +++ b/useragent/useragent.go @@ -0,0 +1,38 @@ +package useragent + +import ( + "strings" +) + +type UserAgent struct { + Product string + Version string + Comment string +} + +func Parse(s string) UserAgent { + parts := strings.SplitN(s, "/", 2) + var version, comment string + if len(parts) > 1 { + // If first character is a number, treat it as version + if parts[1][0] >= 48 && parts[1][0] <= 57 { + rest := strings.SplitN(parts[1], " ", 2) + version = rest[0] + if len(rest) > 1 { + comment = rest[1] + } + } else { + comment = parts[1] + } + } else { + parts = strings.SplitN(s, " ", 2) + if len(parts) > 1 { + comment = parts[1] + } + } + return UserAgent{ + Product: parts[0], + Version: version, + Comment: comment, + } +} diff --git a/useragent/useragent_test.go b/useragent/useragent_test.go new file mode 100644 index 0000000..14ff09a --- /dev/null +++ b/useragent/useragent_test.go @@ -0,0 +1,36 @@ +package useragent + +import ( + "testing" +) + +func TestParse(t *testing.T) { + var tests = []struct { + in string + out UserAgent + }{ + {"", UserAgent{}}, + {"curl/7.26.0", UserAgent{Product: "curl", Version: "7.26.0"}}, + {"Wget/1.13.4 (linux-gnu)", UserAgent{Product: "Wget", Version: "1.13.4", Comment: "(linux-gnu)"}}, + {"Wget", UserAgent{Product: "Wget"}}, + {"fetch libfetch/2.0", UserAgent{Product: "fetch libfetch", Version: "2.0"}}, + {"Go 1.1 package http", UserAgent{Product: "Go", Comment: "1.1 package http"}}, + {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) " + + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.28 " + + "Safari/537.36", UserAgent{Product: "Mozilla", Version: "5.0", Comment: "(Macintosh; Intel Mac OS X 10_8_4) " + + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.28 " + + "Safari/537.36"}}, + } + for _, tt := range tests { + ua := Parse(tt.in) + if got := ua.Product; got != tt.out.Product { + t.Errorf("got Product=%q for %q, want %q", got, tt.in, tt.out.Product) + } + if got := ua.Version; got != tt.out.Version { + t.Errorf("got Version=%q for %q, want %q", got, tt.in, tt.out.Version) + } + if got := ua.Comment; got != tt.out.Comment { + t.Errorf("got Comment=%q for %q, want %q", got, tt.in, tt.out.Comment) + } + } +}