build.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Package rest provides RESTful serialization of AWS requests and responses.
  2. package rest
  3. import (
  4. "bytes"
  5. "encoding/base64"
  6. "fmt"
  7. "io"
  8. "net/url"
  9. "path"
  10. "reflect"
  11. "strconv"
  12. "strings"
  13. "time"
  14. "github.com/aws/aws-sdk-go/aws/awserr"
  15. "github.com/aws/aws-sdk-go/aws/request"
  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. // Build builds the REST component of a service request.
  34. func Build(r *request.Request) {
  35. if r.ParamsFilled() {
  36. v := reflect.ValueOf(r.Params).Elem()
  37. buildLocationElements(r, v)
  38. buildBody(r, v)
  39. }
  40. }
  41. func buildLocationElements(r *request.Request, v reflect.Value) {
  42. query := r.HTTPRequest.URL.Query()
  43. for i := 0; i < v.NumField(); i++ {
  44. m := v.Field(i)
  45. if n := v.Type().Field(i).Name; n[0:1] == strings.ToLower(n[0:1]) {
  46. continue
  47. }
  48. if m.IsValid() {
  49. field := v.Type().Field(i)
  50. name := field.Tag.Get("locationName")
  51. if name == "" {
  52. name = field.Name
  53. }
  54. if m.Kind() == reflect.Ptr {
  55. m = m.Elem()
  56. }
  57. if !m.IsValid() {
  58. continue
  59. }
  60. switch field.Tag.Get("location") {
  61. case "headers": // header maps
  62. buildHeaderMap(r, m, field.Tag.Get("locationName"))
  63. case "header":
  64. buildHeader(r, m, name)
  65. case "uri":
  66. buildURI(r, m, name)
  67. case "querystring":
  68. buildQueryString(r, m, name, query)
  69. }
  70. }
  71. if r.Error != nil {
  72. return
  73. }
  74. }
  75. r.HTTPRequest.URL.RawQuery = query.Encode()
  76. updatePath(r.HTTPRequest.URL, r.HTTPRequest.URL.Path)
  77. }
  78. func buildBody(r *request.Request, v reflect.Value) {
  79. if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
  80. if payloadName := field.Tag.Get("payload"); payloadName != "" {
  81. pfield, _ := v.Type().FieldByName(payloadName)
  82. if ptag := pfield.Tag.Get("type"); ptag != "" && ptag != "structure" {
  83. payload := reflect.Indirect(v.FieldByName(payloadName))
  84. if payload.IsValid() && payload.Interface() != nil {
  85. switch reader := payload.Interface().(type) {
  86. case io.ReadSeeker:
  87. r.SetReaderBody(reader)
  88. case []byte:
  89. r.SetBufferBody(reader)
  90. case string:
  91. r.SetStringBody(reader)
  92. default:
  93. r.Error = awserr.New("SerializationError",
  94. "failed to encode REST request",
  95. fmt.Errorf("unknown payload type %s", payload.Type()))
  96. }
  97. }
  98. }
  99. }
  100. }
  101. }
  102. func buildHeader(r *request.Request, v reflect.Value, name string) {
  103. str, err := convertType(v)
  104. if err != nil {
  105. r.Error = awserr.New("SerializationError", "failed to encode REST request", err)
  106. } else if str != nil {
  107. r.HTTPRequest.Header.Add(name, *str)
  108. }
  109. }
  110. func buildHeaderMap(r *request.Request, v reflect.Value, prefix string) {
  111. for _, key := range v.MapKeys() {
  112. str, err := convertType(v.MapIndex(key))
  113. if err != nil {
  114. r.Error = awserr.New("SerializationError", "failed to encode REST request", err)
  115. } else if str != nil {
  116. r.HTTPRequest.Header.Add(prefix+key.String(), *str)
  117. }
  118. }
  119. }
  120. func buildURI(r *request.Request, v reflect.Value, name string) {
  121. value, err := convertType(v)
  122. if err != nil {
  123. r.Error = awserr.New("SerializationError", "failed to encode REST request", err)
  124. } else if value != nil {
  125. uri := r.HTTPRequest.URL.Path
  126. uri = strings.Replace(uri, "{"+name+"}", EscapePath(*value, true), -1)
  127. uri = strings.Replace(uri, "{"+name+"+}", EscapePath(*value, false), -1)
  128. r.HTTPRequest.URL.Path = uri
  129. }
  130. }
  131. func buildQueryString(r *request.Request, v reflect.Value, name string, query url.Values) {
  132. str, err := convertType(v)
  133. if err != nil {
  134. r.Error = awserr.New("SerializationError", "failed to encode REST request", err)
  135. } else if str != nil {
  136. query.Set(name, *str)
  137. }
  138. }
  139. func updatePath(url *url.URL, urlPath string) {
  140. scheme, query := url.Scheme, url.RawQuery
  141. hasSlash := strings.HasSuffix(urlPath, "/")
  142. // clean up path
  143. urlPath = path.Clean(urlPath)
  144. if hasSlash && !strings.HasSuffix(urlPath, "/") {
  145. urlPath += "/"
  146. }
  147. // get formatted URL minus scheme so we can build this into Opaque
  148. url.Scheme, url.Path, url.RawQuery = "", "", ""
  149. s := url.String()
  150. url.Scheme = scheme
  151. url.RawQuery = query
  152. // build opaque URI
  153. url.Opaque = s + urlPath
  154. }
  155. // EscapePath escapes part of a URL path in Amazon style
  156. func EscapePath(path string, encodeSep bool) string {
  157. var buf bytes.Buffer
  158. for i := 0; i < len(path); i++ {
  159. c := path[i]
  160. if noEscape[c] || (c == '/' && !encodeSep) {
  161. buf.WriteByte(c)
  162. } else {
  163. buf.WriteByte('%')
  164. buf.WriteString(strings.ToUpper(strconv.FormatUint(uint64(c), 16)))
  165. }
  166. }
  167. return buf.String()
  168. }
  169. func convertType(v reflect.Value) (*string, error) {
  170. v = reflect.Indirect(v)
  171. if !v.IsValid() {
  172. return nil, nil
  173. }
  174. var str string
  175. switch value := v.Interface().(type) {
  176. case string:
  177. str = value
  178. case []byte:
  179. str = base64.StdEncoding.EncodeToString(value)
  180. case bool:
  181. str = strconv.FormatBool(value)
  182. case int64:
  183. str = strconv.FormatInt(value, 10)
  184. case float64:
  185. str = strconv.FormatFloat(value, 'f', -1, 64)
  186. case time.Time:
  187. str = value.UTC().Format(RFC822)
  188. default:
  189. err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
  190. return nil, err
  191. }
  192. return &str, nil
  193. }