Browse code

requestdecorator: repurpose the package and rename to useragent

Signed-off-by: Tibor Vass <tibor@docker.com>

Tibor Vass authored on 2015/05/16 07:03:08
Showing 7 changed files
1 1
deleted file mode 100644
... ...
@@ -1,2 +0,0 @@
1
-This package provides helper functions for decorating a request with user agent
2
-versions, auth, meta headers.
3 1
deleted file mode 100644
... ...
@@ -1,172 +0,0 @@
1
-// Package requestdecorator provides helper functions to decorate a request with
2
-// user agent versions, auth, meta headers.
3
-package requestdecorator
4
-
5
-import (
6
-	"errors"
7
-	"io"
8
-	"net/http"
9
-	"strings"
10
-
11
-	"github.com/Sirupsen/logrus"
12
-)
13
-
14
-var (
15
-	ErrNilRequest = errors.New("request cannot be nil")
16
-)
17
-
18
-// UAVersionInfo is used to model UserAgent versions.
19
-type UAVersionInfo struct {
20
-	Name    string
21
-	Version string
22
-}
23
-
24
-func NewUAVersionInfo(name, version string) UAVersionInfo {
25
-	return UAVersionInfo{
26
-		Name:    name,
27
-		Version: version,
28
-	}
29
-}
30
-
31
-func (vi *UAVersionInfo) isValid() bool {
32
-	const stopChars = " \t\r\n/"
33
-	name := vi.Name
34
-	vers := vi.Version
35
-	if len(name) == 0 || strings.ContainsAny(name, stopChars) {
36
-		return false
37
-	}
38
-	if len(vers) == 0 || strings.ContainsAny(vers, stopChars) {
39
-		return false
40
-	}
41
-	return true
42
-}
43
-
44
-// Convert versions to a string and append the string to the string base.
45
-//
46
-// Each UAVersionInfo will be converted to a string in the format of
47
-// "product/version", where the "product" is get from the name field, while
48
-// version is get from the version field. Several pieces of verson information
49
-// will be concatinated and separated by space.
50
-func AppendVersions(base string, versions ...UAVersionInfo) string {
51
-	if len(versions) == 0 {
52
-		return base
53
-	}
54
-
55
-	verstrs := make([]string, 0, 1+len(versions))
56
-	if len(base) > 0 {
57
-		verstrs = append(verstrs, base)
58
-	}
59
-
60
-	for _, v := range versions {
61
-		if !v.isValid() {
62
-			continue
63
-		}
64
-		verstrs = append(verstrs, v.Name+"/"+v.Version)
65
-	}
66
-	return strings.Join(verstrs, " ")
67
-}
68
-
69
-// Decorator is used to change an instance of
70
-// http.Request. It could be used to add more header fields,
71
-// change body, etc.
72
-type Decorator interface {
73
-	// ChangeRequest() changes the request accordingly.
74
-	// The changed request will be returned or err will be non-nil
75
-	// if an error occur.
76
-	ChangeRequest(req *http.Request) (newReq *http.Request, err error)
77
-}
78
-
79
-// UserAgentDecorator appends the product/version to the user agent field
80
-// of a request.
81
-type UserAgentDecorator struct {
82
-	Versions []UAVersionInfo
83
-}
84
-
85
-func (h *UserAgentDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
86
-	if req == nil {
87
-		return req, ErrNilRequest
88
-	}
89
-
90
-	userAgent := AppendVersions(req.UserAgent(), h.Versions...)
91
-	if len(userAgent) > 0 {
92
-		req.Header.Set("User-Agent", userAgent)
93
-	}
94
-	return req, nil
95
-}
96
-
97
-type MetaHeadersDecorator struct {
98
-	Headers map[string][]string
99
-}
100
-
101
-func (h *MetaHeadersDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
102
-	if h.Headers == nil {
103
-		return req, ErrNilRequest
104
-	}
105
-	for k, v := range h.Headers {
106
-		req.Header[k] = v
107
-	}
108
-	return req, nil
109
-}
110
-
111
-type AuthDecorator struct {
112
-	login    string
113
-	password string
114
-}
115
-
116
-func NewAuthDecorator(login, password string) Decorator {
117
-	return &AuthDecorator{
118
-		login:    login,
119
-		password: password,
120
-	}
121
-}
122
-
123
-func (self *AuthDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
124
-	if req == nil {
125
-		return req, ErrNilRequest
126
-	}
127
-	req.SetBasicAuth(self.login, self.password)
128
-	return req, nil
129
-}
130
-
131
-// RequestFactory creates an HTTP request
132
-// and applies a list of decorators on the request.
133
-type RequestFactory struct {
134
-	decorators []Decorator
135
-}
136
-
137
-func NewRequestFactory(d ...Decorator) *RequestFactory {
138
-	return &RequestFactory{
139
-		decorators: d,
140
-	}
141
-}
142
-
143
-func (f *RequestFactory) AddDecorator(d ...Decorator) {
144
-	f.decorators = append(f.decorators, d...)
145
-}
146
-
147
-func (f *RequestFactory) GetDecorators() []Decorator {
148
-	return f.decorators
149
-}
150
-
151
-// NewRequest() creates a new *http.Request,
152
-// applies all decorators in the Factory on the request,
153
-// then applies decorators provided by d on the request.
154
-func (h *RequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...Decorator) (*http.Request, error) {
155
-	req, err := http.NewRequest(method, urlStr, body)
156
-	if err != nil {
157
-		return nil, err
158
-	}
159
-
160
-	// By default, a nil factory should work.
161
-	if h == nil {
162
-		return req, nil
163
-	}
164
-	for _, dec := range h.decorators {
165
-		req, _ = dec.ChangeRequest(req)
166
-	}
167
-	for _, dec := range d {
168
-		req, _ = dec.ChangeRequest(req)
169
-	}
170
-	logrus.Debugf("%v -- HEADERS: %v", req.URL, req.Header)
171
-	return req, err
172
-}
173 1
deleted file mode 100644
... ...
@@ -1,222 +0,0 @@
1
-package requestdecorator
2
-
3
-import (
4
-	"net/http"
5
-	"strings"
6
-	"testing"
7
-)
8
-
9
-func TestUAVersionInfo(t *testing.T) {
10
-	uavi := NewUAVersionInfo("foo", "bar")
11
-	if !uavi.isValid() {
12
-		t.Fatalf("UAVersionInfo should be valid")
13
-	}
14
-	uavi = NewUAVersionInfo("", "bar")
15
-	if uavi.isValid() {
16
-		t.Fatalf("Expected UAVersionInfo to be invalid")
17
-	}
18
-	uavi = NewUAVersionInfo("foo", "")
19
-	if uavi.isValid() {
20
-		t.Fatalf("Expected UAVersionInfo to be invalid")
21
-	}
22
-}
23
-
24
-func TestUserAgentDecorator(t *testing.T) {
25
-	httpVersion := make([]UAVersionInfo, 2)
26
-	httpVersion = append(httpVersion, NewUAVersionInfo("testname", "testversion"))
27
-	httpVersion = append(httpVersion, NewUAVersionInfo("name", "version"))
28
-	uad := &UserAgentDecorator{
29
-		Versions: httpVersion,
30
-	}
31
-
32
-	req, err := http.NewRequest("GET", "/something", strings.NewReader("test"))
33
-	if err != nil {
34
-		t.Fatal(err)
35
-	}
36
-	reqDecorated, err := uad.ChangeRequest(req)
37
-	if err != nil {
38
-		t.Fatal(err)
39
-	}
40
-
41
-	if reqDecorated.Header.Get("User-Agent") != "testname/testversion name/version" {
42
-		t.Fatalf("Request should have User-Agent 'testname/testversion name/version'")
43
-	}
44
-}
45
-
46
-func TestUserAgentDecoratorErr(t *testing.T) {
47
-	httpVersion := make([]UAVersionInfo, 0)
48
-	uad := &UserAgentDecorator{
49
-		Versions: httpVersion,
50
-	}
51
-
52
-	var req *http.Request
53
-	_, err := uad.ChangeRequest(req)
54
-	if err == nil {
55
-		t.Fatalf("Expected to get ErrNilRequest instead no error was returned")
56
-	}
57
-}
58
-
59
-func TestMetaHeadersDecorator(t *testing.T) {
60
-	var headers = map[string][]string{
61
-		"key1": {"value1"},
62
-		"key2": {"value2"},
63
-	}
64
-	mhd := &MetaHeadersDecorator{
65
-		Headers: headers,
66
-	}
67
-
68
-	req, err := http.NewRequest("GET", "/something", strings.NewReader("test"))
69
-	if err != nil {
70
-		t.Fatal(err)
71
-	}
72
-	reqDecorated, err := mhd.ChangeRequest(req)
73
-	if err != nil {
74
-		t.Fatal(err)
75
-	}
76
-
77
-	v, ok := reqDecorated.Header["key1"]
78
-	if !ok {
79
-		t.Fatalf("Expected to have header key1")
80
-	}
81
-	if v[0] != "value1" {
82
-		t.Fatalf("Expected value for key1 isn't value1")
83
-	}
84
-
85
-	v, ok = reqDecorated.Header["key2"]
86
-	if !ok {
87
-		t.Fatalf("Expected to have header key2")
88
-	}
89
-	if v[0] != "value2" {
90
-		t.Fatalf("Expected value for key2 isn't value2")
91
-	}
92
-}
93
-
94
-func TestMetaHeadersDecoratorErr(t *testing.T) {
95
-	mhd := &MetaHeadersDecorator{}
96
-
97
-	var req *http.Request
98
-	_, err := mhd.ChangeRequest(req)
99
-	if err == nil {
100
-		t.Fatalf("Expected to get ErrNilRequest instead no error was returned")
101
-	}
102
-}
103
-
104
-func TestAuthDecorator(t *testing.T) {
105
-	ad := NewAuthDecorator("test", "password")
106
-
107
-	req, err := http.NewRequest("GET", "/something", strings.NewReader("test"))
108
-	if err != nil {
109
-		t.Fatal(err)
110
-	}
111
-	reqDecorated, err := ad.ChangeRequest(req)
112
-	if err != nil {
113
-		t.Fatal(err)
114
-	}
115
-
116
-	username, password, ok := reqDecorated.BasicAuth()
117
-	if !ok {
118
-		t.Fatalf("Cannot retrieve basic auth info from request")
119
-	}
120
-	if username != "test" {
121
-		t.Fatalf("Expected username to be test, got %s", username)
122
-	}
123
-	if password != "password" {
124
-		t.Fatalf("Expected password to be password, got %s", password)
125
-	}
126
-}
127
-
128
-func TestAuthDecoratorErr(t *testing.T) {
129
-	ad := &AuthDecorator{}
130
-
131
-	var req *http.Request
132
-	_, err := ad.ChangeRequest(req)
133
-	if err == nil {
134
-		t.Fatalf("Expected to get ErrNilRequest instead no error was returned")
135
-	}
136
-}
137
-
138
-func TestRequestFactory(t *testing.T) {
139
-	ad := NewAuthDecorator("test", "password")
140
-	httpVersion := make([]UAVersionInfo, 2)
141
-	httpVersion = append(httpVersion, NewUAVersionInfo("testname", "testversion"))
142
-	httpVersion = append(httpVersion, NewUAVersionInfo("name", "version"))
143
-	uad := &UserAgentDecorator{
144
-		Versions: httpVersion,
145
-	}
146
-
147
-	requestFactory := NewRequestFactory(ad, uad)
148
-
149
-	if l := len(requestFactory.GetDecorators()); l != 2 {
150
-		t.Fatalf("Expected to have two decorators, got %d", l)
151
-	}
152
-
153
-	req, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"))
154
-	if err != nil {
155
-		t.Fatal(err)
156
-	}
157
-
158
-	username, password, ok := req.BasicAuth()
159
-	if !ok {
160
-		t.Fatalf("Cannot retrieve basic auth info from request")
161
-	}
162
-	if username != "test" {
163
-		t.Fatalf("Expected username to be test, got %s", username)
164
-	}
165
-	if password != "password" {
166
-		t.Fatalf("Expected password to be password, got %s", password)
167
-	}
168
-	if req.Header.Get("User-Agent") != "testname/testversion name/version" {
169
-		t.Fatalf("Request should have User-Agent 'testname/testversion name/version'")
170
-	}
171
-}
172
-
173
-func TestRequestFactoryNewRequestWithDecorators(t *testing.T) {
174
-	ad := NewAuthDecorator("test", "password")
175
-
176
-	requestFactory := NewRequestFactory(ad)
177
-
178
-	if l := len(requestFactory.GetDecorators()); l != 1 {
179
-		t.Fatalf("Expected to have one decorators, got %d", l)
180
-	}
181
-
182
-	ad2 := NewAuthDecorator("test2", "password2")
183
-
184
-	req, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"), ad2)
185
-	if err != nil {
186
-		t.Fatal(err)
187
-	}
188
-
189
-	username, password, ok := req.BasicAuth()
190
-	if !ok {
191
-		t.Fatalf("Cannot retrieve basic auth info from request")
192
-	}
193
-	if username != "test2" {
194
-		t.Fatalf("Expected username to be test, got %s", username)
195
-	}
196
-	if password != "password2" {
197
-		t.Fatalf("Expected password to be password, got %s", password)
198
-	}
199
-}
200
-
201
-func TestRequestFactoryAddDecorator(t *testing.T) {
202
-	requestFactory := NewRequestFactory()
203
-
204
-	if l := len(requestFactory.GetDecorators()); l != 0 {
205
-		t.Fatalf("Expected to have zero decorators, got %d", l)
206
-	}
207
-
208
-	ad := NewAuthDecorator("test", "password")
209
-	requestFactory.AddDecorator(ad)
210
-
211
-	if l := len(requestFactory.GetDecorators()); l != 1 {
212
-		t.Fatalf("Expected to have one decorators, got %d", l)
213
-	}
214
-}
215
-
216
-func TestRequestFactoryNil(t *testing.T) {
217
-	var requestFactory RequestFactory
218
-	_, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"))
219
-	if err != nil {
220
-		t.Fatalf("Expected not to get and error, got %s", err)
221
-	}
222
-}
223 1
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+This package provides helper functions to pack version information into a single User-Agent header.
0 1
new file mode 100644
... ...
@@ -0,0 +1,60 @@
0
+// Package useragent provides helper functions to pack
1
+// version information into a single User-Agent header.
2
+package useragent
3
+
4
+import (
5
+	"errors"
6
+	"strings"
7
+)
8
+
9
+var (
10
+	ErrNilRequest = errors.New("request cannot be nil")
11
+)
12
+
13
+// VersionInfo is used to model UserAgent versions.
14
+type VersionInfo struct {
15
+	Name    string
16
+	Version string
17
+}
18
+
19
+func (vi *VersionInfo) isValid() bool {
20
+	const stopChars = " \t\r\n/"
21
+	name := vi.Name
22
+	vers := vi.Version
23
+	if len(name) == 0 || strings.ContainsAny(name, stopChars) {
24
+		return false
25
+	}
26
+	if len(vers) == 0 || strings.ContainsAny(vers, stopChars) {
27
+		return false
28
+	}
29
+	return true
30
+}
31
+
32
+// Convert versions to a string and append the string to the string base.
33
+//
34
+// Each VersionInfo will be converted to a string in the format of
35
+// "product/version", where the "product" is get from the name field, while
36
+// version is get from the version field. Several pieces of verson information
37
+// will be concatinated and separated by space.
38
+//
39
+// Example:
40
+// AppendVersions("base", VersionInfo{"foo", "1.0"}, VersionInfo{"bar", "2.0"})
41
+// results in "base foo/1.0 bar/2.0".
42
+func AppendVersions(base string, versions ...VersionInfo) string {
43
+	if len(versions) == 0 {
44
+		return base
45
+	}
46
+
47
+	verstrs := make([]string, 0, 1+len(versions))
48
+	if len(base) > 0 {
49
+		verstrs = append(verstrs, base)
50
+	}
51
+
52
+	for _, v := range versions {
53
+		if !v.isValid() {
54
+			continue
55
+		}
56
+		verstrs = append(verstrs, v.Name+"/"+v.Version)
57
+	}
58
+	return strings.Join(verstrs, " ")
59
+}
0 60
new file mode 100644
... ...
@@ -0,0 +1,31 @@
0
+package useragent
1
+
2
+import "testing"
3
+
4
+func TestVersionInfo(t *testing.T) {
5
+	vi := VersionInfo{"foo", "bar"}
6
+	if !vi.isValid() {
7
+		t.Fatalf("VersionInfo should be valid")
8
+	}
9
+	vi = VersionInfo{"", "bar"}
10
+	if vi.isValid() {
11
+		t.Fatalf("Expected VersionInfo to be invalid")
12
+	}
13
+	vi = VersionInfo{"foo", ""}
14
+	if vi.isValid() {
15
+		t.Fatalf("Expected VersionInfo to be invalid")
16
+	}
17
+}
18
+
19
+func TestAppendVersions(t *testing.T) {
20
+	vis := []VersionInfo{
21
+		{"foo", "1.0"},
22
+		{"bar", "0.1"},
23
+		{"pi", "3.1.4"},
24
+	}
25
+	v := AppendVersions("base", vis...)
26
+	expect := "base foo/1.0 bar/0.1 pi/3.1.4"
27
+	if v != expect {
28
+		t.Fatalf("expected %q, got %q", expect, v)
29
+	}
30
+}
... ...
@@ -18,8 +18,8 @@ import (
18 18
 	"github.com/Sirupsen/logrus"
19 19
 	"github.com/docker/docker/autogen/dockerversion"
20 20
 	"github.com/docker/docker/pkg/parsers/kernel"
21
-	"github.com/docker/docker/pkg/requestdecorator"
22 21
 	"github.com/docker/docker/pkg/timeoutconn"
22
+	"github.com/docker/docker/pkg/useragent"
23 23
 )
24 24
 
25 25
 var (
... ...
@@ -186,17 +186,17 @@ func cloneRequest(r *http.Request) *http.Request {
186 186
 
187 187
 func (tr *DockerHeaders) RoundTrip(req *http.Request) (*http.Response, error) {
188 188
 	req = cloneRequest(req)
189
-	httpVersion := make([]requestdecorator.UAVersionInfo, 0, 4)
190
-	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("docker", dockerversion.VERSION))
191
-	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("go", runtime.Version()))
192
-	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("git-commit", dockerversion.GITCOMMIT))
189
+	httpVersion := make([]useragent.VersionInfo, 0, 4)
190
+	httpVersion = append(httpVersion, useragent.VersionInfo{"docker", dockerversion.VERSION})
191
+	httpVersion = append(httpVersion, useragent.VersionInfo{"go", runtime.Version()})
192
+	httpVersion = append(httpVersion, useragent.VersionInfo{"git-commit", dockerversion.GITCOMMIT})
193 193
 	if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
194
-		httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("kernel", kernelVersion.String()))
194
+		httpVersion = append(httpVersion, useragent.VersionInfo{"kernel", kernelVersion.String()})
195 195
 	}
196
-	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("os", runtime.GOOS))
197
-	httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("arch", runtime.GOARCH))
196
+	httpVersion = append(httpVersion, useragent.VersionInfo{"os", runtime.GOOS})
197
+	httpVersion = append(httpVersion, useragent.VersionInfo{"arch", runtime.GOARCH})
198 198
 
199
-	userAgent := requestdecorator.AppendVersions(req.UserAgent(), httpVersion...)
199
+	userAgent := useragent.AppendVersions(req.UserAgent(), httpVersion...)
200 200
 
201 201
 	req.Header.Set("User-Agent", userAgent)
202 202