path_replace.go (2361B)
1 package httpbinding 2 3 import ( 4 "bytes" 5 "fmt" 6 ) 7 8 const ( 9 uriTokenStart = '{' 10 uriTokenStop = '}' 11 uriTokenSkip = '+' 12 ) 13 14 func 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. 24 func 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. 82 func 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 95 var noEscape [256]bool 96 97 func 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 }