path_replace.go raw

   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  	// search for "{<key>}". If not found, search for the greedy version "{<key>+}". If none are found, return error
  26  	fieldBuf = bufCap(fieldBuf, len(key)+2) // { <key> }
  27  	fieldBuf = append(fieldBuf, uriTokenStart)
  28  	fieldBuf = append(fieldBuf, key...)
  29  	fieldBuf = append(fieldBuf, uriTokenStop)
  30  
  31  	start := bytes.Index(path, fieldBuf)
  32  	encodeSep := true
  33  	if start < 0 {
  34  		fieldBuf = bufCap(fieldBuf, len(key)+3) // { <key> [+] }
  35  		fieldBuf = append(fieldBuf, uriTokenStart)
  36  		fieldBuf = append(fieldBuf, key...)
  37  		fieldBuf = append(fieldBuf, uriTokenSkip)
  38  		fieldBuf = append(fieldBuf, uriTokenStop)
  39  
  40  		start = bytes.Index(path, fieldBuf)
  41  		if start < 0 {
  42  			return path, fieldBuf, fmt.Errorf("invalid path index, start=%d. %s", start, path)
  43  		}
  44  		encodeSep = false
  45  	}
  46  	end := start + len(fieldBuf)
  47  
  48  	if escape {
  49  		val = EscapePath(val, encodeSep)
  50  	}
  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  }
 109