path_replace.go 2.4 KB

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