util.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. package storage
  2. // Copyright 2017 Microsoft Corporation
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import (
  16. "bytes"
  17. "crypto/hmac"
  18. "crypto/sha256"
  19. "encoding/base64"
  20. "encoding/xml"
  21. "fmt"
  22. "io"
  23. "io/ioutil"
  24. "net/http"
  25. "net/url"
  26. "reflect"
  27. "strconv"
  28. "strings"
  29. "time"
  30. )
  31. var (
  32. fixedTime = time.Date(2050, time.December, 20, 21, 55, 0, 0, time.FixedZone("GMT", -6))
  33. accountSASOptions = AccountSASTokenOptions{
  34. Services: Services{
  35. Blob: true,
  36. },
  37. ResourceTypes: ResourceTypes{
  38. Service: true,
  39. Container: true,
  40. Object: true,
  41. },
  42. Permissions: Permissions{
  43. Read: true,
  44. Write: true,
  45. Delete: true,
  46. List: true,
  47. Add: true,
  48. Create: true,
  49. Update: true,
  50. Process: true,
  51. },
  52. Expiry: fixedTime,
  53. UseHTTPS: true,
  54. }
  55. )
  56. func (c Client) computeHmac256(message string) string {
  57. h := hmac.New(sha256.New, c.accountKey)
  58. h.Write([]byte(message))
  59. return base64.StdEncoding.EncodeToString(h.Sum(nil))
  60. }
  61. func currentTimeRfc1123Formatted() string {
  62. return timeRfc1123Formatted(time.Now().UTC())
  63. }
  64. func timeRfc1123Formatted(t time.Time) string {
  65. return t.Format(http.TimeFormat)
  66. }
  67. func timeRFC3339Formatted(t time.Time) string {
  68. return t.Format("2006-01-02T15:04:05.0000000Z")
  69. }
  70. func mergeParams(v1, v2 url.Values) url.Values {
  71. out := url.Values{}
  72. for k, v := range v1 {
  73. out[k] = v
  74. }
  75. for k, v := range v2 {
  76. vals, ok := out[k]
  77. if ok {
  78. vals = append(vals, v...)
  79. out[k] = vals
  80. } else {
  81. out[k] = v
  82. }
  83. }
  84. return out
  85. }
  86. func prepareBlockListRequest(blocks []Block) string {
  87. s := `<?xml version="1.0" encoding="utf-8"?><BlockList>`
  88. for _, v := range blocks {
  89. s += fmt.Sprintf("<%s>%s</%s>", v.Status, v.ID, v.Status)
  90. }
  91. s += `</BlockList>`
  92. return s
  93. }
  94. func xmlUnmarshal(body io.Reader, v interface{}) error {
  95. data, err := ioutil.ReadAll(body)
  96. if err != nil {
  97. return err
  98. }
  99. return xml.Unmarshal(data, v)
  100. }
  101. func xmlMarshal(v interface{}) (io.Reader, int, error) {
  102. b, err := xml.Marshal(v)
  103. if err != nil {
  104. return nil, 0, err
  105. }
  106. return bytes.NewReader(b), len(b), nil
  107. }
  108. func headersFromStruct(v interface{}) map[string]string {
  109. headers := make(map[string]string)
  110. value := reflect.ValueOf(v)
  111. for i := 0; i < value.NumField(); i++ {
  112. key := value.Type().Field(i).Tag.Get("header")
  113. if key != "" {
  114. reflectedValue := reflect.Indirect(value.Field(i))
  115. var val string
  116. if reflectedValue.IsValid() {
  117. switch reflectedValue.Type() {
  118. case reflect.TypeOf(fixedTime):
  119. val = timeRfc1123Formatted(reflectedValue.Interface().(time.Time))
  120. case reflect.TypeOf(uint64(0)), reflect.TypeOf(uint(0)):
  121. val = strconv.FormatUint(reflectedValue.Uint(), 10)
  122. case reflect.TypeOf(int(0)):
  123. val = strconv.FormatInt(reflectedValue.Int(), 10)
  124. default:
  125. val = reflectedValue.String()
  126. }
  127. }
  128. if val != "" {
  129. headers[key] = val
  130. }
  131. }
  132. }
  133. return headers
  134. }
  135. // merges extraHeaders into headers and returns headers
  136. func mergeHeaders(headers, extraHeaders map[string]string) map[string]string {
  137. for k, v := range extraHeaders {
  138. headers[k] = v
  139. }
  140. return headers
  141. }
  142. func addToHeaders(h map[string]string, key, value string) map[string]string {
  143. if value != "" {
  144. h[key] = value
  145. }
  146. return h
  147. }
  148. func addTimeToHeaders(h map[string]string, key string, value *time.Time) map[string]string {
  149. if value != nil {
  150. h = addToHeaders(h, key, timeRfc1123Formatted(*value))
  151. }
  152. return h
  153. }
  154. func addTimeout(params url.Values, timeout uint) url.Values {
  155. if timeout > 0 {
  156. params.Add("timeout", fmt.Sprintf("%v", timeout))
  157. }
  158. return params
  159. }
  160. func addSnapshot(params url.Values, snapshot *time.Time) url.Values {
  161. if snapshot != nil {
  162. params.Add("snapshot", timeRFC3339Formatted(*snapshot))
  163. }
  164. return params
  165. }
  166. func getTimeFromHeaders(h http.Header, key string) (*time.Time, error) {
  167. var out time.Time
  168. var err error
  169. outStr := h.Get(key)
  170. if outStr != "" {
  171. out, err = time.Parse(time.RFC1123, outStr)
  172. if err != nil {
  173. return nil, err
  174. }
  175. }
  176. return &out, nil
  177. }
  178. // TimeRFC1123 is an alias for time.Time needed for custom Unmarshalling
  179. type TimeRFC1123 time.Time
  180. // UnmarshalXML is a custom unmarshaller that overrides the default time unmarshal which uses a different time layout.
  181. func (t *TimeRFC1123) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  182. var value string
  183. d.DecodeElement(&value, &start)
  184. parse, err := time.Parse(time.RFC1123, value)
  185. if err != nil {
  186. return err
  187. }
  188. *t = TimeRFC1123(parse)
  189. return nil
  190. }
  191. // MarshalXML marshals using time.RFC1123.
  192. func (t *TimeRFC1123) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  193. return e.EncodeElement(time.Time(*t).Format(time.RFC1123), start)
  194. }
  195. // returns a map of custom metadata values from the specified HTTP header
  196. func getMetadataFromHeaders(header http.Header) map[string]string {
  197. metadata := make(map[string]string)
  198. for k, v := range header {
  199. // Can't trust CanonicalHeaderKey() to munge case
  200. // reliably. "_" is allowed in identifiers:
  201. // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
  202. // https://msdn.microsoft.com/library/aa664670(VS.71).aspx
  203. // http://tools.ietf.org/html/rfc7230#section-3.2
  204. // ...but "_" is considered invalid by
  205. // CanonicalMIMEHeaderKey in
  206. // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542
  207. // so k can be "X-Ms-Meta-Lol" or "x-ms-meta-lol_rofl".
  208. k = strings.ToLower(k)
  209. if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) {
  210. continue
  211. }
  212. // metadata["lol"] = content of the last X-Ms-Meta-Lol header
  213. k = k[len(userDefinedMetadataHeaderPrefix):]
  214. metadata[k] = v[len(v)-1]
  215. }
  216. if len(metadata) == 0 {
  217. return nil
  218. }
  219. return metadata
  220. }