Browse code

http utils

Nan Monnand Deng authored on 2013/08/02 15:47:58
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,129 @@
0
+package utils
1
+
2
+import (
3
+	"bytes"
4
+	"io"
5
+	"net/http"
6
+	"strings"
7
+)
8
+
9
+// VersionInfo is used to model entities which has a version.
10
+// It is basically a tupple with name and version.
11
+type VersionInfo interface {
12
+	Name() string
13
+	Version() string
14
+}
15
+
16
+func validVersion(version VersionInfo) bool {
17
+	stopChars := " \t\r\n/"
18
+	if strings.ContainsAny(version.Name(), stopChars) {
19
+		return false
20
+	}
21
+	if strings.ContainsAny(version.Version(), stopChars) {
22
+		return false
23
+	}
24
+	return true
25
+}
26
+
27
+// Convert versions to a string and append the string to the string base.
28
+//
29
+// Each VersionInfo will be converted to a string in the format of
30
+// "product/version", where the "product" is get from the Name() method, while
31
+// version is get from the Version() method. Several pieces of verson information
32
+// will be concatinated and separated by space.
33
+func appendVersions(base string, versions ...VersionInfo) string {
34
+	if len(versions) == 0 {
35
+		return base
36
+	}
37
+
38
+	var buf bytes.Buffer
39
+	if len(base) > 0 {
40
+		buf.Write([]byte(base))
41
+	}
42
+
43
+	for _, v := range versions {
44
+		name := []byte(v.Name())
45
+		version := []byte(v.Version())
46
+
47
+		if len(name) == 0 || len(version) == 0 {
48
+			continue
49
+		}
50
+		if !validVersion(v) {
51
+			continue
52
+		}
53
+		buf.Write([]byte(v.Name()))
54
+		buf.Write([]byte("/"))
55
+		buf.Write([]byte(v.Version()))
56
+		buf.Write([]byte(" "))
57
+	}
58
+	return buf.String()
59
+}
60
+
61
+// HTTPRequestDecorator is used to change an instance of
62
+// http.Request. It could be used to add more header fields,
63
+// change body, etc.
64
+type HTTPRequestDecorator interface {
65
+	// ChangeRequest() changes the request accordingly.
66
+	// The changed request will be returned or err will be non-nil
67
+	// if an error occur.
68
+	ChangeRequest(req *http.Request) (newReq *http.Request, err error)
69
+}
70
+
71
+// HTTPUserAgentDecorator appends the product/version to the user agent field
72
+// of a request.
73
+type HTTPUserAgentDecorator struct {
74
+	versions []VersionInfo
75
+}
76
+
77
+func NewHTTPUserAgentDecorator(versions ...VersionInfo) HTTPRequestDecorator {
78
+	ret := new(HTTPUserAgentDecorator)
79
+	ret.versions = versions
80
+	return ret
81
+}
82
+
83
+func (self *HTTPUserAgentDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
84
+	if req == nil {
85
+		return req, nil
86
+	}
87
+
88
+	userAgent := appendVersions(req.UserAgent(), self.versions...)
89
+	if len(userAgent) > 0 {
90
+		req.Header.Set("User-Agent", userAgent)
91
+	}
92
+	return req, nil
93
+}
94
+
95
+// HTTPRequestFactory creates an HTTP request
96
+// and applies a list of decorators on the request.
97
+type HTTPRequestFactory struct {
98
+	decorators []HTTPRequestDecorator
99
+}
100
+
101
+func NewHTTPRequestFactory(d ...HTTPRequestDecorator) *HTTPRequestFactory {
102
+	ret := new(HTTPRequestFactory)
103
+	ret.decorators = d
104
+	return ret
105
+}
106
+
107
+// NewRequest() creates a new *http.Request,
108
+// applies all decorators in the HTTPRequestFactory on the request,
109
+// then applies decorators provided by d on the request.
110
+func (self *HTTPRequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...HTTPRequestDecorator) (*http.Request, error) {
111
+	req, err := http.NewRequest(method, urlStr, body)
112
+	if err != nil {
113
+		return nil, err
114
+	}
115
+	for _, dec := range self.decorators {
116
+		req, err = dec.ChangeRequest(req)
117
+		if err != nil {
118
+			return nil, err
119
+		}
120
+	}
121
+	for _, dec := range d {
122
+		req, err = dec.ChangeRequest(req)
123
+		if err != nil {
124
+			return nil, err
125
+		}
126
+	}
127
+	return req, err
128
+}