path_replace.go

  1package httpbinding
  2
  3import (
  4	"bytes"
  5	"fmt"
  6)
  7
  8const (
  9	uriTokenStart = '{'
 10	uriTokenStop  = '}'
 11	uriTokenSkip  = '+'
 12)
 13
 14func bufCap(b []byte, n int) []byte {
 15	if cap(b) < n {
 16		return make([]byte, 0, n)
 17	}
 18
 19	return b[0:0]
 20}
 21
 22// replacePathElement replaces a single element in the path []byte.
 23// Escape is used to control whether the value will be escaped using Amazon path escape style.
 24func replacePathElement(path, fieldBuf []byte, key, val string, escape bool) ([]byte, []byte, error) {
 25	fieldBuf = bufCap(fieldBuf, len(key)+3) // { <key> [+] }
 26	fieldBuf = append(fieldBuf, uriTokenStart)
 27	fieldBuf = append(fieldBuf, key...)
 28
 29	start := bytes.Index(path, fieldBuf)
 30	end := start + len(fieldBuf)
 31	if start < 0 || len(path[end:]) == 0 {
 32		// TODO what to do about error?
 33		return path, fieldBuf, fmt.Errorf("invalid path index, start=%d,end=%d. %s", start, end, path)
 34	}
 35
 36	encodeSep := true
 37	if path[end] == uriTokenSkip {
 38		// '+' token means do not escape slashes
 39		encodeSep = false
 40		end++
 41	}
 42
 43	if escape {
 44		val = EscapePath(val, encodeSep)
 45	}
 46
 47	if path[end] != uriTokenStop {
 48		return path, fieldBuf, fmt.Errorf("invalid path element, does not contain token stop, %s", path)
 49	}
 50	end++
 51
 52	fieldBuf = bufCap(fieldBuf, len(val))
 53	fieldBuf = append(fieldBuf, val...)
 54
 55	keyLen := end - start
 56	valLen := len(fieldBuf)
 57
 58	if keyLen == valLen {
 59		copy(path[start:], fieldBuf)
 60		return path, fieldBuf, nil
 61	}
 62
 63	newLen := len(path) + (valLen - keyLen)
 64	if len(path) < newLen {
 65		path = path[:cap(path)]
 66	}
 67	if cap(path) < newLen {
 68		newURI := make([]byte, newLen)
 69		copy(newURI, path)
 70		path = newURI
 71	}
 72
 73	// shift
 74	copy(path[start+valLen:], path[end:])
 75	path = path[:newLen]
 76	copy(path[start:], fieldBuf)
 77
 78	return path, fieldBuf, nil
 79}
 80
 81// EscapePath escapes part of a URL path in Amazon style.
 82func EscapePath(path string, encodeSep bool) string {
 83	var buf bytes.Buffer
 84	for i := 0; i < len(path); i++ {
 85		c := path[i]
 86		if noEscape[c] || (c == '/' && !encodeSep) {
 87			buf.WriteByte(c)
 88		} else {
 89			fmt.Fprintf(&buf, "%%%02X", c)
 90		}
 91	}
 92	return buf.String()
 93}
 94
 95var noEscape [256]bool
 96
 97func init() {
 98	for i := 0; i < len(noEscape); i++ {
 99		// AWS expects every character except these to be escaped
100		noEscape[i] = (i >= 'A' && i <= 'Z') ||
101			(i >= 'a' && i <= 'z') ||
102			(i >= '0' && i <= '9') ||
103			i == '-' ||
104			i == '.' ||
105			i == '_' ||
106			i == '~'
107	}
108}