123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- package storage
- // Copyright 2017 Microsoft Corporation
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- import (
- "bytes"
- "crypto/hmac"
- "crypto/sha256"
- "encoding/base64"
- "encoding/xml"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "net/url"
- "reflect"
- "strconv"
- "strings"
- "time"
- )
- var (
- fixedTime = time.Date(2050, time.December, 20, 21, 55, 0, 0, time.FixedZone("GMT", -6))
- accountSASOptions = AccountSASTokenOptions{
- Services: Services{
- Blob: true,
- },
- ResourceTypes: ResourceTypes{
- Service: true,
- Container: true,
- Object: true,
- },
- Permissions: Permissions{
- Read: true,
- Write: true,
- Delete: true,
- List: true,
- Add: true,
- Create: true,
- Update: true,
- Process: true,
- },
- Expiry: fixedTime,
- UseHTTPS: true,
- }
- )
- func (c Client) computeHmac256(message string) string {
- h := hmac.New(sha256.New, c.accountKey)
- h.Write([]byte(message))
- return base64.StdEncoding.EncodeToString(h.Sum(nil))
- }
- func currentTimeRfc1123Formatted() string {
- return timeRfc1123Formatted(time.Now().UTC())
- }
- func timeRfc1123Formatted(t time.Time) string {
- return t.Format(http.TimeFormat)
- }
- func timeRFC3339Formatted(t time.Time) string {
- return t.Format("2006-01-02T15:04:05.0000000Z")
- }
- func mergeParams(v1, v2 url.Values) url.Values {
- out := url.Values{}
- for k, v := range v1 {
- out[k] = v
- }
- for k, v := range v2 {
- vals, ok := out[k]
- if ok {
- vals = append(vals, v...)
- out[k] = vals
- } else {
- out[k] = v
- }
- }
- return out
- }
- func prepareBlockListRequest(blocks []Block) string {
- s := `<?xml version="1.0" encoding="utf-8"?><BlockList>`
- for _, v := range blocks {
- s += fmt.Sprintf("<%s>%s</%s>", v.Status, v.ID, v.Status)
- }
- s += `</BlockList>`
- return s
- }
- func xmlUnmarshal(body io.Reader, v interface{}) error {
- data, err := ioutil.ReadAll(body)
- if err != nil {
- return err
- }
- return xml.Unmarshal(data, v)
- }
- func xmlMarshal(v interface{}) (io.Reader, int, error) {
- b, err := xml.Marshal(v)
- if err != nil {
- return nil, 0, err
- }
- return bytes.NewReader(b), len(b), nil
- }
- func headersFromStruct(v interface{}) map[string]string {
- headers := make(map[string]string)
- value := reflect.ValueOf(v)
- for i := 0; i < value.NumField(); i++ {
- key := value.Type().Field(i).Tag.Get("header")
- if key != "" {
- reflectedValue := reflect.Indirect(value.Field(i))
- var val string
- if reflectedValue.IsValid() {
- switch reflectedValue.Type() {
- case reflect.TypeOf(fixedTime):
- val = timeRfc1123Formatted(reflectedValue.Interface().(time.Time))
- case reflect.TypeOf(uint64(0)), reflect.TypeOf(uint(0)):
- val = strconv.FormatUint(reflectedValue.Uint(), 10)
- case reflect.TypeOf(int(0)):
- val = strconv.FormatInt(reflectedValue.Int(), 10)
- default:
- val = reflectedValue.String()
- }
- }
- if val != "" {
- headers[key] = val
- }
- }
- }
- return headers
- }
- // merges extraHeaders into headers and returns headers
- func mergeHeaders(headers, extraHeaders map[string]string) map[string]string {
- for k, v := range extraHeaders {
- headers[k] = v
- }
- return headers
- }
- func addToHeaders(h map[string]string, key, value string) map[string]string {
- if value != "" {
- h[key] = value
- }
- return h
- }
- func addTimeToHeaders(h map[string]string, key string, value *time.Time) map[string]string {
- if value != nil {
- h = addToHeaders(h, key, timeRfc1123Formatted(*value))
- }
- return h
- }
- func addTimeout(params url.Values, timeout uint) url.Values {
- if timeout > 0 {
- params.Add("timeout", fmt.Sprintf("%v", timeout))
- }
- return params
- }
- func addSnapshot(params url.Values, snapshot *time.Time) url.Values {
- if snapshot != nil {
- params.Add("snapshot", timeRFC3339Formatted(*snapshot))
- }
- return params
- }
- func getTimeFromHeaders(h http.Header, key string) (*time.Time, error) {
- var out time.Time
- var err error
- outStr := h.Get(key)
- if outStr != "" {
- out, err = time.Parse(time.RFC1123, outStr)
- if err != nil {
- return nil, err
- }
- }
- return &out, nil
- }
- // TimeRFC1123 is an alias for time.Time needed for custom Unmarshalling
- type TimeRFC1123 time.Time
- // UnmarshalXML is a custom unmarshaller that overrides the default time unmarshal which uses a different time layout.
- func (t *TimeRFC1123) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- var value string
- d.DecodeElement(&value, &start)
- parse, err := time.Parse(time.RFC1123, value)
- if err != nil {
- return err
- }
- *t = TimeRFC1123(parse)
- return nil
- }
- // MarshalXML marshals using time.RFC1123.
- func (t *TimeRFC1123) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
- return e.EncodeElement(time.Time(*t).Format(time.RFC1123), start)
- }
- // returns a map of custom metadata values from the specified HTTP header
- func getMetadataFromHeaders(header http.Header) map[string]string {
- metadata := make(map[string]string)
- for k, v := range header {
- // Can't trust CanonicalHeaderKey() to munge case
- // reliably. "_" is allowed in identifiers:
- // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
- // https://msdn.microsoft.com/library/aa664670(VS.71).aspx
- // http://tools.ietf.org/html/rfc7230#section-3.2
- // ...but "_" is considered invalid by
- // CanonicalMIMEHeaderKey in
- // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542
- // so k can be "X-Ms-Meta-Lol" or "x-ms-meta-lol_rofl".
- k = strings.ToLower(k)
- if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) {
- continue
- }
- // metadata["lol"] = content of the last X-Ms-Meta-Lol header
- k = k[len(userDefinedMetadataHeaderPrefix):]
- metadata[k] = v[len(v)-1]
- }
- if len(metadata) == 0 {
- return nil
- }
- return metadata
- }
|