build.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. // Package rest provides RESTful serialisation of AWS requests and responses.
  2. package rest
  3. import (
  4. "bytes"
  5. "encoding/base64"
  6. "fmt"
  7. "github.com/ks3sdklib/aws-sdk-go/aws"
  8. "github.com/ks3sdklib/aws-sdk-go/internal/apierr"
  9. "io"
  10. "net/url"
  11. "path"
  12. "reflect"
  13. "strconv"
  14. "strings"
  15. "time"
  16. )
  17. // RFC822 returns an RFC822 formatted timestamp for AWS protocols
  18. const RFC822 = "Mon, 2 Jan 2006 15:04:05 GMT"
  19. // Whether the byte value can be sent without escaping in AWS URLs
  20. var noEscape [256]bool
  21. func init() {
  22. for i := 0; i < len(noEscape); i++ {
  23. // AWS expects every character except these to be escaped
  24. noEscape[i] = (i >= 'A' && i <= 'Z') ||
  25. (i >= 'a' && i <= 'z') ||
  26. (i >= '0' && i <= '9') ||
  27. i == '-' ||
  28. i == '.' ||
  29. i == '_' ||
  30. i == '~'
  31. }
  32. }
  33. var isTokenTable = [256]bool{
  34. '!': true,
  35. '#': true,
  36. '$': true,
  37. '%': true,
  38. '&': true,
  39. '\'': true,
  40. '*': true,
  41. '+': true,
  42. '-': true,
  43. '.': true,
  44. '0': true,
  45. '1': true,
  46. '2': true,
  47. '3': true,
  48. '4': true,
  49. '5': true,
  50. '6': true,
  51. '7': true,
  52. '8': true,
  53. '9': true,
  54. 'A': true,
  55. 'B': true,
  56. 'C': true,
  57. 'D': true,
  58. 'E': true,
  59. 'F': true,
  60. 'G': true,
  61. 'H': true,
  62. 'I': true,
  63. 'J': true,
  64. 'K': true,
  65. 'L': true,
  66. 'M': true,
  67. 'N': true,
  68. 'O': true,
  69. 'P': true,
  70. 'Q': true,
  71. 'R': true,
  72. 'S': true,
  73. 'T': true,
  74. 'U': true,
  75. 'W': true,
  76. 'V': true,
  77. 'X': true,
  78. 'Y': true,
  79. 'Z': true,
  80. '^': true,
  81. '_': true,
  82. '`': true,
  83. 'a': true,
  84. 'b': true,
  85. 'c': true,
  86. 'd': true,
  87. 'e': true,
  88. 'f': true,
  89. 'g': true,
  90. 'h': true,
  91. 'i': true,
  92. 'j': true,
  93. 'k': true,
  94. 'l': true,
  95. 'm': true,
  96. 'n': true,
  97. 'o': true,
  98. 'p': true,
  99. 'q': true,
  100. 'r': true,
  101. 's': true,
  102. 't': true,
  103. 'u': true,
  104. 'v': true,
  105. 'w': true,
  106. 'x': true,
  107. 'y': true,
  108. 'z': true,
  109. '|': true,
  110. '~': true,
  111. }
  112. func ValidHeaderFieldName(v string) bool {
  113. if len(v) == 0 {
  114. return false
  115. }
  116. for i := 0; i < len(v); i++ {
  117. if !isTokenTable[v[i]] {
  118. return false
  119. }
  120. }
  121. return true
  122. }
  123. // Build builds the REST component of a service request.
  124. func Build(r *aws.Request) {
  125. if r.ParamsFilled() {
  126. v := reflect.ValueOf(r.Params).Elem()
  127. buildLocationElements(r, v)
  128. buildBody(r, v)
  129. }
  130. }
  131. func buildLocationElements(r *aws.Request, v reflect.Value) {
  132. query := r.HTTPRequest.URL.Query()
  133. for i := 0; i < v.NumField(); i++ {
  134. m := v.Field(i)
  135. if n := v.Type().Field(i).Name; n[0:1] == strings.ToLower(n[0:1]) {
  136. continue
  137. }
  138. if m.IsValid() {
  139. field := v.Type().Field(i)
  140. name := field.Tag.Get("locationName")
  141. if name == "" {
  142. name = field.Name
  143. }
  144. if m.Kind() == reflect.Ptr {
  145. m = m.Elem()
  146. }
  147. if !m.IsValid() {
  148. continue
  149. }
  150. switch field.Tag.Get("location") {
  151. case "headers": // header maps
  152. buildHeaderMap(r, m, field.Tag.Get("locationName"))
  153. case "header":
  154. buildHeader(r, m, name)
  155. case "uri":
  156. buildURI(r, m, name)
  157. case "querystring":
  158. buildQueryString(r, m, name, query)
  159. case "querystrings":
  160. buildQueryStrings(r, m, name, query)
  161. }
  162. }
  163. if r.Error != nil {
  164. return
  165. }
  166. }
  167. buildExtendHeaders(r, v)
  168. buildExtendQueryParams(v, query)
  169. r.HTTPRequest.URL.RawQuery = query.Encode()
  170. updatePath(r.HTTPRequest.URL, r.Config)
  171. }
  172. func buildBody(r *aws.Request, v reflect.Value) {
  173. if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
  174. if payloadName := field.Tag.Get("payload"); payloadName != "" {
  175. pfield, _ := v.Type().FieldByName(payloadName)
  176. if ptag := pfield.Tag.Get("type"); ptag != "" && ptag != "structure" {
  177. payload := reflect.Indirect(v.FieldByName(payloadName))
  178. if payload.IsValid() && payload.Interface() != nil {
  179. switch reader := payload.Interface().(type) {
  180. case io.ReadSeeker:
  181. r.SetReaderBody(reader)
  182. case []byte:
  183. r.SetBufferBody(reader)
  184. case string:
  185. r.SetStringBody(reader)
  186. default:
  187. r.Error = apierr.New("Marshal",
  188. "failed to encode REST request",
  189. fmt.Errorf("unknown payload type %s", payload.Type()))
  190. }
  191. }
  192. }
  193. }
  194. }
  195. }
  196. func buildHeader(r *aws.Request, v reflect.Value, name string) {
  197. str, err := convertType(v)
  198. if err != nil {
  199. r.Error = apierr.New("Marshal", "failed to encode REST request", err)
  200. } else if str != nil {
  201. r.HTTPRequest.Header.Add(name, *str)
  202. }
  203. }
  204. func buildHeaderMap(r *aws.Request, v reflect.Value, prefix string) {
  205. for _, key := range v.MapKeys() {
  206. str, err := convertType(v.MapIndex(key))
  207. if err != nil {
  208. r.Error = apierr.New("Marshal", "failed to encode REST request", err)
  209. } else if str != nil {
  210. if strings.HasPrefix(strings.ToLower(key.String()), strings.ToLower(prefix)) {
  211. r.HTTPRequest.Header.Add(key.String(), *str)
  212. } else {
  213. r.HTTPRequest.Header.Add(prefix+key.String(), *str)
  214. }
  215. }
  216. }
  217. }
  218. func buildExtendHeaders(r *aws.Request, v reflect.Value) {
  219. extendHeaders := v.FieldByName("ExtendHeaders")
  220. if extendHeaders.IsValid() {
  221. iter := extendHeaders.MapRange()
  222. for iter.Next() {
  223. key := iter.Key().String()
  224. value := iter.Value()
  225. if ValidHeaderFieldName(key) {
  226. if !value.IsNil() {
  227. r.HTTPRequest.Header.Set(key, value.Elem().String())
  228. } else {
  229. r.HTTPRequest.Header.Set(key, "")
  230. }
  231. } else {
  232. r.Error = apierr.New("Marshal", fmt.Sprintf("invalid extend header field name \"%s\"", key), nil)
  233. return
  234. }
  235. }
  236. }
  237. }
  238. func buildExtendQueryParams(v reflect.Value, query url.Values) {
  239. extendQueryParams := v.FieldByName("ExtendQueryParams")
  240. if extendQueryParams.IsValid() {
  241. iter := extendQueryParams.MapRange()
  242. for iter.Next() {
  243. key := iter.Key().String()
  244. if key == "" {
  245. continue
  246. }
  247. value := iter.Value()
  248. if !value.IsNil() {
  249. query.Set(key, value.Elem().String())
  250. } else {
  251. query.Set(key, "")
  252. }
  253. }
  254. }
  255. }
  256. func buildURI(r *aws.Request, v reflect.Value, name string) {
  257. value, err := convertType(v)
  258. if err != nil {
  259. r.Error = apierr.New("Marshal", "failed to encode REST request", err)
  260. } else if value != nil {
  261. uri := r.HTTPRequest.URL.Path
  262. uri = strings.Replace(uri, "{"+name+"}", EscapePath(*value, true), -1)
  263. uri = strings.Replace(uri, "{"+name+"+}", EscapePath(*value, false), -1)
  264. r.HTTPRequest.URL.Path = uri
  265. }
  266. }
  267. func buildQueryString(r *aws.Request, v reflect.Value, name string, query url.Values) {
  268. str, err := convertType(v)
  269. if err != nil {
  270. r.Error = apierr.New("Marshal", "failed to encode REST request", err)
  271. } else if str != nil {
  272. query.Set(name, *str)
  273. } else if str == nil {
  274. query.Set(name, "")
  275. }
  276. }
  277. func buildQueryStrings(r *aws.Request, v reflect.Value, name string, query url.Values) {
  278. if v.Kind() == reflect.Slice {
  279. var valSlice []string
  280. for i := 0; i < v.Len(); i++ {
  281. str, err := convertType(v.Index(i))
  282. if err != nil {
  283. r.Error = apierr.New("Marshal", "failed to encode REST request", err)
  284. return
  285. }
  286. valSlice = append(valSlice, *str)
  287. }
  288. if len(valSlice) > 0 {
  289. query.Set(name, strings.Join(valSlice, ","))
  290. }
  291. }
  292. }
  293. func updatePath(url *url.URL, cfg *aws.Config) {
  294. urlPath := url.Path
  295. scheme, query := url.Scheme, url.RawQuery
  296. // path.Clean will remove duplicate leading /
  297. // this will make deleting / started key impossible
  298. // so escape it here first
  299. urlPath = strings.Replace(urlPath, "//", "/%2F", -1)
  300. // 新增参数控制path clean,默认值为true
  301. if !cfg.DisableRestProtocolURICleaning {
  302. urlPath = cleanPath(urlPath)
  303. }
  304. // get formatted URL minus scheme, so we can build this into Opaque
  305. url.Scheme, url.Path, url.RawQuery = "", "", ""
  306. s := url.String()
  307. url.Scheme = scheme
  308. url.RawQuery = query
  309. // build opaque URI
  310. url.Opaque = s + urlPath
  311. }
  312. func cleanPath(urlPath string) string {
  313. // path.Clean会去掉最后的斜杠,导致无法创建目录。所以添加以下逻辑
  314. add := false
  315. if urlPath[len(urlPath)-1] == '/' && len(urlPath) > 1 {
  316. add = true
  317. }
  318. // clean up path
  319. urlPath = path.Clean(urlPath)
  320. if add {
  321. urlPath += "/"
  322. }
  323. return urlPath
  324. }
  325. // EscapePath escapes part of a URL path in Amazon style
  326. //
  327. // path The path segment to escape
  328. // encodeSep If true, '/' will be encoded, otherwise they will not
  329. func EscapePath(path string, encodeSep bool) string {
  330. var buf bytes.Buffer
  331. for i := 0; i < len(path); i++ {
  332. c := path[i]
  333. if noEscape[c] || (c == '/' && !encodeSep) {
  334. buf.WriteByte(c)
  335. } else {
  336. fmt.Fprintf(&buf, "%%%02X", c)
  337. }
  338. }
  339. return buf.String()
  340. }
  341. func convertType(v reflect.Value) (*string, error) {
  342. v = reflect.Indirect(v)
  343. if !v.IsValid() {
  344. return nil, nil
  345. }
  346. var str string
  347. switch value := v.Interface().(type) {
  348. case string:
  349. str = value
  350. case []byte:
  351. str = base64.StdEncoding.EncodeToString(value)
  352. case bool:
  353. str = strconv.FormatBool(value)
  354. case int64:
  355. str = strconv.FormatInt(value, 10)
  356. case float64:
  357. str = strconv.FormatFloat(value, 'f', -1, 64)
  358. case time.Time:
  359. str = value.UTC().Format(RFC822)
  360. default:
  361. err := fmt.Errorf("unsupported value for param %v (%s)", v.Interface(), v.Type())
  362. return nil, err
  363. }
  364. return &str, nil
  365. }