From 4b4923487d0bfdea16681d68e3329d153883c663 Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Fri, 15 May 2015 15:03:08 -0700 Subject: [PATCH] requestdecorator: repurpose the package and rename to useragent Signed-off-by: Tibor Vass --- requestdecorator/README.md | 2 - requestdecorator/requestdecorator.go | 172 ----------------- requestdecorator/requestdecorator_test.go | 222 ---------------------- useragent/README.md | 1 + useragent/useragent.go | 60 ++++++ useragent/useragent_test.go | 31 +++ 6 files changed, 92 insertions(+), 396 deletions(-) delete mode 100644 requestdecorator/README.md delete mode 100644 requestdecorator/requestdecorator.go delete mode 100644 requestdecorator/requestdecorator_test.go create mode 100644 useragent/README.md create mode 100644 useragent/useragent.go create mode 100644 useragent/useragent_test.go diff --git a/requestdecorator/README.md b/requestdecorator/README.md deleted file mode 100644 index 76f8ca7..0000000 --- a/requestdecorator/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This package provides helper functions for decorating a request with user agent -versions, auth, meta headers. diff --git a/requestdecorator/requestdecorator.go b/requestdecorator/requestdecorator.go deleted file mode 100644 index 079e38b..0000000 --- a/requestdecorator/requestdecorator.go +++ /dev/null @@ -1,172 +0,0 @@ -// Package requestdecorator provides helper functions to decorate a request with -// user agent versions, auth, meta headers. -package requestdecorator - -import ( - "errors" - "io" - "net/http" - "strings" - - "github.com/Sirupsen/logrus" -) - -var ( - ErrNilRequest = errors.New("request cannot be nil") -) - -// UAVersionInfo is used to model UserAgent versions. -type UAVersionInfo struct { - Name string - Version string -} - -func NewUAVersionInfo(name, version string) UAVersionInfo { - return UAVersionInfo{ - Name: name, - Version: version, - } -} - -func (vi *UAVersionInfo) isValid() bool { - const stopChars = " \t\r\n/" - name := vi.Name - vers := vi.Version - if len(name) == 0 || strings.ContainsAny(name, stopChars) { - return false - } - if len(vers) == 0 || strings.ContainsAny(vers, stopChars) { - return false - } - return true -} - -// Convert versions to a string and append the string to the string base. -// -// Each UAVersionInfo will be converted to a string in the format of -// "product/version", where the "product" is get from the name field, while -// version is get from the version field. Several pieces of verson information -// will be concatinated and separated by space. -func AppendVersions(base string, versions ...UAVersionInfo) string { - if len(versions) == 0 { - return base - } - - verstrs := make([]string, 0, 1+len(versions)) - if len(base) > 0 { - verstrs = append(verstrs, base) - } - - for _, v := range versions { - if !v.isValid() { - continue - } - verstrs = append(verstrs, v.Name+"/"+v.Version) - } - return strings.Join(verstrs, " ") -} - -// Decorator is used to change an instance of -// http.Request. It could be used to add more header fields, -// change body, etc. -type Decorator interface { - // ChangeRequest() changes the request accordingly. - // The changed request will be returned or err will be non-nil - // if an error occur. - ChangeRequest(req *http.Request) (newReq *http.Request, err error) -} - -// UserAgentDecorator appends the product/version to the user agent field -// of a request. -type UserAgentDecorator struct { - Versions []UAVersionInfo -} - -func (h *UserAgentDecorator) ChangeRequest(req *http.Request) (*http.Request, error) { - if req == nil { - return req, ErrNilRequest - } - - userAgent := AppendVersions(req.UserAgent(), h.Versions...) - if len(userAgent) > 0 { - req.Header.Set("User-Agent", userAgent) - } - return req, nil -} - -type MetaHeadersDecorator struct { - Headers map[string][]string -} - -func (h *MetaHeadersDecorator) ChangeRequest(req *http.Request) (*http.Request, error) { - if h.Headers == nil { - return req, ErrNilRequest - } - for k, v := range h.Headers { - req.Header[k] = v - } - return req, nil -} - -type AuthDecorator struct { - login string - password string -} - -func NewAuthDecorator(login, password string) Decorator { - return &AuthDecorator{ - login: login, - password: password, - } -} - -func (self *AuthDecorator) ChangeRequest(req *http.Request) (*http.Request, error) { - if req == nil { - return req, ErrNilRequest - } - req.SetBasicAuth(self.login, self.password) - return req, nil -} - -// RequestFactory creates an HTTP request -// and applies a list of decorators on the request. -type RequestFactory struct { - decorators []Decorator -} - -func NewRequestFactory(d ...Decorator) *RequestFactory { - return &RequestFactory{ - decorators: d, - } -} - -func (f *RequestFactory) AddDecorator(d ...Decorator) { - f.decorators = append(f.decorators, d...) -} - -func (f *RequestFactory) GetDecorators() []Decorator { - return f.decorators -} - -// NewRequest() creates a new *http.Request, -// applies all decorators in the Factory on the request, -// then applies decorators provided by d on the request. -func (h *RequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...Decorator) (*http.Request, error) { - req, err := http.NewRequest(method, urlStr, body) - if err != nil { - return nil, err - } - - // By default, a nil factory should work. - if h == nil { - return req, nil - } - for _, dec := range h.decorators { - req, _ = dec.ChangeRequest(req) - } - for _, dec := range d { - req, _ = dec.ChangeRequest(req) - } - logrus.Debugf("%v -- HEADERS: %v", req.URL, req.Header) - return req, err -} diff --git a/requestdecorator/requestdecorator_test.go b/requestdecorator/requestdecorator_test.go deleted file mode 100644 index ed61135..0000000 --- a/requestdecorator/requestdecorator_test.go +++ /dev/null @@ -1,222 +0,0 @@ -package requestdecorator - -import ( - "net/http" - "strings" - "testing" -) - -func TestUAVersionInfo(t *testing.T) { - uavi := NewUAVersionInfo("foo", "bar") - if !uavi.isValid() { - t.Fatalf("UAVersionInfo should be valid") - } - uavi = NewUAVersionInfo("", "bar") - if uavi.isValid() { - t.Fatalf("Expected UAVersionInfo to be invalid") - } - uavi = NewUAVersionInfo("foo", "") - if uavi.isValid() { - t.Fatalf("Expected UAVersionInfo to be invalid") - } -} - -func TestUserAgentDecorator(t *testing.T) { - httpVersion := make([]UAVersionInfo, 2) - httpVersion = append(httpVersion, NewUAVersionInfo("testname", "testversion")) - httpVersion = append(httpVersion, NewUAVersionInfo("name", "version")) - uad := &UserAgentDecorator{ - Versions: httpVersion, - } - - req, err := http.NewRequest("GET", "/something", strings.NewReader("test")) - if err != nil { - t.Fatal(err) - } - reqDecorated, err := uad.ChangeRequest(req) - if err != nil { - t.Fatal(err) - } - - if reqDecorated.Header.Get("User-Agent") != "testname/testversion name/version" { - t.Fatalf("Request should have User-Agent 'testname/testversion name/version'") - } -} - -func TestUserAgentDecoratorErr(t *testing.T) { - httpVersion := make([]UAVersionInfo, 0) - uad := &UserAgentDecorator{ - Versions: httpVersion, - } - - var req *http.Request - _, err := uad.ChangeRequest(req) - if err == nil { - t.Fatalf("Expected to get ErrNilRequest instead no error was returned") - } -} - -func TestMetaHeadersDecorator(t *testing.T) { - var headers = map[string][]string{ - "key1": {"value1"}, - "key2": {"value2"}, - } - mhd := &MetaHeadersDecorator{ - Headers: headers, - } - - req, err := http.NewRequest("GET", "/something", strings.NewReader("test")) - if err != nil { - t.Fatal(err) - } - reqDecorated, err := mhd.ChangeRequest(req) - if err != nil { - t.Fatal(err) - } - - v, ok := reqDecorated.Header["key1"] - if !ok { - t.Fatalf("Expected to have header key1") - } - if v[0] != "value1" { - t.Fatalf("Expected value for key1 isn't value1") - } - - v, ok = reqDecorated.Header["key2"] - if !ok { - t.Fatalf("Expected to have header key2") - } - if v[0] != "value2" { - t.Fatalf("Expected value for key2 isn't value2") - } -} - -func TestMetaHeadersDecoratorErr(t *testing.T) { - mhd := &MetaHeadersDecorator{} - - var req *http.Request - _, err := mhd.ChangeRequest(req) - if err == nil { - t.Fatalf("Expected to get ErrNilRequest instead no error was returned") - } -} - -func TestAuthDecorator(t *testing.T) { - ad := NewAuthDecorator("test", "password") - - req, err := http.NewRequest("GET", "/something", strings.NewReader("test")) - if err != nil { - t.Fatal(err) - } - reqDecorated, err := ad.ChangeRequest(req) - if err != nil { - t.Fatal(err) - } - - username, password, ok := reqDecorated.BasicAuth() - if !ok { - t.Fatalf("Cannot retrieve basic auth info from request") - } - if username != "test" { - t.Fatalf("Expected username to be test, got %s", username) - } - if password != "password" { - t.Fatalf("Expected password to be password, got %s", password) - } -} - -func TestAuthDecoratorErr(t *testing.T) { - ad := &AuthDecorator{} - - var req *http.Request - _, err := ad.ChangeRequest(req) - if err == nil { - t.Fatalf("Expected to get ErrNilRequest instead no error was returned") - } -} - -func TestRequestFactory(t *testing.T) { - ad := NewAuthDecorator("test", "password") - httpVersion := make([]UAVersionInfo, 2) - httpVersion = append(httpVersion, NewUAVersionInfo("testname", "testversion")) - httpVersion = append(httpVersion, NewUAVersionInfo("name", "version")) - uad := &UserAgentDecorator{ - Versions: httpVersion, - } - - requestFactory := NewRequestFactory(ad, uad) - - if l := len(requestFactory.GetDecorators()); l != 2 { - t.Fatalf("Expected to have two decorators, got %d", l) - } - - req, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test")) - if err != nil { - t.Fatal(err) - } - - username, password, ok := req.BasicAuth() - if !ok { - t.Fatalf("Cannot retrieve basic auth info from request") - } - if username != "test" { - t.Fatalf("Expected username to be test, got %s", username) - } - if password != "password" { - t.Fatalf("Expected password to be password, got %s", password) - } - if req.Header.Get("User-Agent") != "testname/testversion name/version" { - t.Fatalf("Request should have User-Agent 'testname/testversion name/version'") - } -} - -func TestRequestFactoryNewRequestWithDecorators(t *testing.T) { - ad := NewAuthDecorator("test", "password") - - requestFactory := NewRequestFactory(ad) - - if l := len(requestFactory.GetDecorators()); l != 1 { - t.Fatalf("Expected to have one decorators, got %d", l) - } - - ad2 := NewAuthDecorator("test2", "password2") - - req, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"), ad2) - if err != nil { - t.Fatal(err) - } - - username, password, ok := req.BasicAuth() - if !ok { - t.Fatalf("Cannot retrieve basic auth info from request") - } - if username != "test2" { - t.Fatalf("Expected username to be test, got %s", username) - } - if password != "password2" { - t.Fatalf("Expected password to be password, got %s", password) - } -} - -func TestRequestFactoryAddDecorator(t *testing.T) { - requestFactory := NewRequestFactory() - - if l := len(requestFactory.GetDecorators()); l != 0 { - t.Fatalf("Expected to have zero decorators, got %d", l) - } - - ad := NewAuthDecorator("test", "password") - requestFactory.AddDecorator(ad) - - if l := len(requestFactory.GetDecorators()); l != 1 { - t.Fatalf("Expected to have one decorators, got %d", l) - } -} - -func TestRequestFactoryNil(t *testing.T) { - var requestFactory RequestFactory - _, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test")) - if err != nil { - t.Fatalf("Expected not to get and error, got %s", err) - } -} diff --git a/useragent/README.md b/useragent/README.md new file mode 100644 index 0000000..d9cb367 --- /dev/null +++ b/useragent/README.md @@ -0,0 +1 @@ +This package provides helper functions to pack version information into a single User-Agent header. diff --git a/useragent/useragent.go b/useragent/useragent.go new file mode 100644 index 0000000..9e35d1c --- /dev/null +++ b/useragent/useragent.go @@ -0,0 +1,60 @@ +// Package useragent provides helper functions to pack +// version information into a single User-Agent header. +package useragent + +import ( + "errors" + "strings" +) + +var ( + ErrNilRequest = errors.New("request cannot be nil") +) + +// VersionInfo is used to model UserAgent versions. +type VersionInfo struct { + Name string + Version string +} + +func (vi *VersionInfo) isValid() bool { + const stopChars = " \t\r\n/" + name := vi.Name + vers := vi.Version + if len(name) == 0 || strings.ContainsAny(name, stopChars) { + return false + } + if len(vers) == 0 || strings.ContainsAny(vers, stopChars) { + return false + } + return true +} + +// Convert versions to a string and append the string to the string base. +// +// Each VersionInfo will be converted to a string in the format of +// "product/version", where the "product" is get from the name field, while +// version is get from the version field. Several pieces of verson information +// will be concatinated and separated by space. +// +// Example: +// AppendVersions("base", VersionInfo{"foo", "1.0"}, VersionInfo{"bar", "2.0"}) +// results in "base foo/1.0 bar/2.0". +func AppendVersions(base string, versions ...VersionInfo) string { + if len(versions) == 0 { + return base + } + + verstrs := make([]string, 0, 1+len(versions)) + if len(base) > 0 { + verstrs = append(verstrs, base) + } + + for _, v := range versions { + if !v.isValid() { + continue + } + verstrs = append(verstrs, v.Name+"/"+v.Version) + } + return strings.Join(verstrs, " ") +} diff --git a/useragent/useragent_test.go b/useragent/useragent_test.go new file mode 100644 index 0000000..0ad7243 --- /dev/null +++ b/useragent/useragent_test.go @@ -0,0 +1,31 @@ +package useragent + +import "testing" + +func TestVersionInfo(t *testing.T) { + vi := VersionInfo{"foo", "bar"} + if !vi.isValid() { + t.Fatalf("VersionInfo should be valid") + } + vi = VersionInfo{"", "bar"} + if vi.isValid() { + t.Fatalf("Expected VersionInfo to be invalid") + } + vi = VersionInfo{"foo", ""} + if vi.isValid() { + t.Fatalf("Expected VersionInfo to be invalid") + } +} + +func TestAppendVersions(t *testing.T) { + vis := []VersionInfo{ + {"foo", "1.0"}, + {"bar", "0.1"}, + {"pi", "3.1.4"}, + } + v := AppendVersions("base", vis...) + expect := "base foo/1.0 bar/0.1 pi/3.1.4" + if v != expect { + t.Fatalf("expected %q, got %q", expect, v) + } +}