diff options
Diffstat (limited to 'vendor/github.com/aws/aws-sdk-go/private/protocol/rest/build.go')
-rw-r--r-- | vendor/github.com/aws/aws-sdk-go/private/protocol/rest/build.go | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/build.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/build.go new file mode 100644 index 0000000..7161835 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/build.go | |||
@@ -0,0 +1,290 @@ | |||
1 | // Package rest provides RESTful serialization of AWS requests and responses. | ||
2 | package rest | ||
3 | |||
4 | import ( | ||
5 | "bytes" | ||
6 | "encoding/base64" | ||
7 | "encoding/json" | ||
8 | "fmt" | ||
9 | "io" | ||
10 | "net/http" | ||
11 | "net/url" | ||
12 | "path" | ||
13 | "reflect" | ||
14 | "strconv" | ||
15 | "strings" | ||
16 | "time" | ||
17 | |||
18 | "github.com/aws/aws-sdk-go/aws" | ||
19 | "github.com/aws/aws-sdk-go/aws/awserr" | ||
20 | "github.com/aws/aws-sdk-go/aws/request" | ||
21 | ) | ||
22 | |||
23 | // RFC822 returns an RFC822 formatted timestamp for AWS protocols | ||
24 | const RFC822 = "Mon, 2 Jan 2006 15:04:05 GMT" | ||
25 | |||
26 | // Whether the byte value can be sent without escaping in AWS URLs | ||
27 | var noEscape [256]bool | ||
28 | |||
29 | var errValueNotSet = fmt.Errorf("value not set") | ||
30 | |||
31 | func init() { | ||
32 | for i := 0; i < len(noEscape); i++ { | ||
33 | // AWS expects every character except these to be escaped | ||
34 | noEscape[i] = (i >= 'A' && i <= 'Z') || | ||
35 | (i >= 'a' && i <= 'z') || | ||
36 | (i >= '0' && i <= '9') || | ||
37 | i == '-' || | ||
38 | i == '.' || | ||
39 | i == '_' || | ||
40 | i == '~' | ||
41 | } | ||
42 | } | ||
43 | |||
44 | // BuildHandler is a named request handler for building rest protocol requests | ||
45 | var BuildHandler = request.NamedHandler{Name: "awssdk.rest.Build", Fn: Build} | ||
46 | |||
47 | // Build builds the REST component of a service request. | ||
48 | func Build(r *request.Request) { | ||
49 | if r.ParamsFilled() { | ||
50 | v := reflect.ValueOf(r.Params).Elem() | ||
51 | buildLocationElements(r, v, false) | ||
52 | buildBody(r, v) | ||
53 | } | ||
54 | } | ||
55 | |||
56 | // BuildAsGET builds the REST component of a service request with the ability to hoist | ||
57 | // data from the body. | ||
58 | func BuildAsGET(r *request.Request) { | ||
59 | if r.ParamsFilled() { | ||
60 | v := reflect.ValueOf(r.Params).Elem() | ||
61 | buildLocationElements(r, v, true) | ||
62 | buildBody(r, v) | ||
63 | } | ||
64 | } | ||
65 | |||
66 | func buildLocationElements(r *request.Request, v reflect.Value, buildGETQuery bool) { | ||
67 | query := r.HTTPRequest.URL.Query() | ||
68 | |||
69 | // Setup the raw path to match the base path pattern. This is needed | ||
70 | // so that when the path is mutated a custom escaped version can be | ||
71 | // stored in RawPath that will be used by the Go client. | ||
72 | r.HTTPRequest.URL.RawPath = r.HTTPRequest.URL.Path | ||
73 | |||
74 | for i := 0; i < v.NumField(); i++ { | ||
75 | m := v.Field(i) | ||
76 | if n := v.Type().Field(i).Name; n[0:1] == strings.ToLower(n[0:1]) { | ||
77 | continue | ||
78 | } | ||
79 | |||
80 | if m.IsValid() { | ||
81 | field := v.Type().Field(i) | ||
82 | name := field.Tag.Get("locationName") | ||
83 | if name == "" { | ||
84 | name = field.Name | ||
85 | } | ||
86 | if kind := m.Kind(); kind == reflect.Ptr { | ||
87 | m = m.Elem() | ||
88 | } else if kind == reflect.Interface { | ||
89 | if !m.Elem().IsValid() { | ||
90 | continue | ||
91 | } | ||
92 | } | ||
93 | if !m.IsValid() { | ||
94 | continue | ||
95 | } | ||
96 | if field.Tag.Get("ignore") != "" { | ||
97 | continue | ||
98 | } | ||
99 | |||
100 | var err error | ||
101 | switch field.Tag.Get("location") { | ||
102 | case "headers": // header maps | ||
103 | err = buildHeaderMap(&r.HTTPRequest.Header, m, field.Tag) | ||
104 | case "header": | ||
105 | err = buildHeader(&r.HTTPRequest.Header, m, name, field.Tag) | ||
106 | case "uri": | ||
107 | err = buildURI(r.HTTPRequest.URL, m, name, field.Tag) | ||
108 | case "querystring": | ||
109 | err = buildQueryString(query, m, name, field.Tag) | ||
110 | default: | ||
111 | if buildGETQuery { | ||
112 | err = buildQueryString(query, m, name, field.Tag) | ||
113 | } | ||
114 | } | ||
115 | r.Error = err | ||
116 | } | ||
117 | if r.Error != nil { | ||
118 | return | ||
119 | } | ||
120 | } | ||
121 | |||
122 | r.HTTPRequest.URL.RawQuery = query.Encode() | ||
123 | if !aws.BoolValue(r.Config.DisableRestProtocolURICleaning) { | ||
124 | cleanPath(r.HTTPRequest.URL) | ||
125 | } | ||
126 | } | ||
127 | |||
128 | func buildBody(r *request.Request, v reflect.Value) { | ||
129 | if field, ok := v.Type().FieldByName("_"); ok { | ||
130 | if payloadName := field.Tag.Get("payload"); payloadName != "" { | ||
131 | pfield, _ := v.Type().FieldByName(payloadName) | ||
132 | if ptag := pfield.Tag.Get("type"); ptag != "" && ptag != "structure" { | ||
133 | payload := reflect.Indirect(v.FieldByName(payloadName)) | ||
134 | if payload.IsValid() && payload.Interface() != nil { | ||
135 | switch reader := payload.Interface().(type) { | ||
136 | case io.ReadSeeker: | ||
137 | r.SetReaderBody(reader) | ||
138 | case []byte: | ||
139 | r.SetBufferBody(reader) | ||
140 | case string: | ||
141 | r.SetStringBody(reader) | ||
142 | default: | ||
143 | r.Error = awserr.New("SerializationError", | ||
144 | "failed to encode REST request", | ||
145 | fmt.Errorf("unknown payload type %s", payload.Type())) | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | func buildHeader(header *http.Header, v reflect.Value, name string, tag reflect.StructTag) error { | ||
154 | str, err := convertType(v, tag) | ||
155 | if err == errValueNotSet { | ||
156 | return nil | ||
157 | } else if err != nil { | ||
158 | return awserr.New("SerializationError", "failed to encode REST request", err) | ||
159 | } | ||
160 | |||
161 | header.Add(name, str) | ||
162 | |||
163 | return nil | ||
164 | } | ||
165 | |||
166 | func buildHeaderMap(header *http.Header, v reflect.Value, tag reflect.StructTag) error { | ||
167 | prefix := tag.Get("locationName") | ||
168 | for _, key := range v.MapKeys() { | ||
169 | str, err := convertType(v.MapIndex(key), tag) | ||
170 | if err == errValueNotSet { | ||
171 | continue | ||
172 | } else if err != nil { | ||
173 | return awserr.New("SerializationError", "failed to encode REST request", err) | ||
174 | |||
175 | } | ||
176 | |||
177 | header.Add(prefix+key.String(), str) | ||
178 | } | ||
179 | return nil | ||
180 | } | ||
181 | |||
182 | func buildURI(u *url.URL, v reflect.Value, name string, tag reflect.StructTag) error { | ||
183 | value, err := convertType(v, tag) | ||
184 | if err == errValueNotSet { | ||
185 | return nil | ||
186 | } else if err != nil { | ||
187 | return awserr.New("SerializationError", "failed to encode REST request", err) | ||
188 | } | ||
189 | |||
190 | u.Path = strings.Replace(u.Path, "{"+name+"}", value, -1) | ||
191 | u.Path = strings.Replace(u.Path, "{"+name+"+}", value, -1) | ||
192 | |||
193 | u.RawPath = strings.Replace(u.RawPath, "{"+name+"}", EscapePath(value, true), -1) | ||
194 | u.RawPath = strings.Replace(u.RawPath, "{"+name+"+}", EscapePath(value, false), -1) | ||
195 | |||
196 | return nil | ||
197 | } | ||
198 | |||
199 | func buildQueryString(query url.Values, v reflect.Value, name string, tag reflect.StructTag) error { | ||
200 | switch value := v.Interface().(type) { | ||
201 | case []*string: | ||
202 | for _, item := range value { | ||
203 | query.Add(name, *item) | ||
204 | } | ||
205 | case map[string]*string: | ||
206 | for key, item := range value { | ||
207 | query.Add(key, *item) | ||
208 | } | ||
209 | case map[string][]*string: | ||
210 | for key, items := range value { | ||
211 | for _, item := range items { | ||
212 | query.Add(key, *item) | ||
213 | } | ||
214 | } | ||
215 | default: | ||
216 | str, err := convertType(v, tag) | ||
217 | if err == errValueNotSet { | ||
218 | return nil | ||
219 | } else if err != nil { | ||
220 | return awserr.New("SerializationError", "failed to encode REST request", err) | ||
221 | } | ||
222 | query.Set(name, str) | ||
223 | } | ||
224 | |||
225 | return nil | ||
226 | } | ||
227 | |||
228 | func cleanPath(u *url.URL) { | ||
229 | hasSlash := strings.HasSuffix(u.Path, "/") | ||
230 | |||
231 | // clean up path, removing duplicate `/` | ||
232 | u.Path = path.Clean(u.Path) | ||
233 | u.RawPath = path.Clean(u.RawPath) | ||
234 | |||
235 | if hasSlash && !strings.HasSuffix(u.Path, "/") { | ||
236 | u.Path += "/" | ||
237 | u.RawPath += "/" | ||
238 | } | ||
239 | } | ||
240 | |||
241 | // EscapePath escapes part of a URL path in Amazon style | ||
242 | func EscapePath(path string, encodeSep bool) string { | ||
243 | var buf bytes.Buffer | ||
244 | for i := 0; i < len(path); i++ { | ||
245 | c := path[i] | ||
246 | if noEscape[c] || (c == '/' && !encodeSep) { | ||
247 | buf.WriteByte(c) | ||
248 | } else { | ||
249 | fmt.Fprintf(&buf, "%%%02X", c) | ||
250 | } | ||
251 | } | ||
252 | return buf.String() | ||
253 | } | ||
254 | |||
255 | func convertType(v reflect.Value, tag reflect.StructTag) (string, error) { | ||
256 | v = reflect.Indirect(v) | ||
257 | if !v.IsValid() { | ||
258 | return "", errValueNotSet | ||
259 | } | ||
260 | |||
261 | var str string | ||
262 | switch value := v.Interface().(type) { | ||
263 | case string: | ||
264 | str = value | ||
265 | case []byte: | ||
266 | str = base64.StdEncoding.EncodeToString(value) | ||
267 | case bool: | ||
268 | str = strconv.FormatBool(value) | ||
269 | case int64: | ||
270 | str = strconv.FormatInt(value, 10) | ||
271 | case float64: | ||
272 | str = strconv.FormatFloat(value, 'f', -1, 64) | ||
273 | case time.Time: | ||
274 | str = value.UTC().Format(RFC822) | ||
275 | case aws.JSONValue: | ||
276 | b, err := json.Marshal(value) | ||
277 | if err != nil { | ||
278 | return "", err | ||
279 | } | ||
280 | if tag.Get("location") == "header" { | ||
281 | str = base64.StdEncoding.EncodeToString(b) | ||
282 | } else { | ||
283 | str = string(b) | ||
284 | } | ||
285 | default: | ||
286 | err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type()) | ||
287 | return "", err | ||
288 | } | ||
289 | return str, nil | ||
290 | } | ||