src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

path_replace.go (2506B)


      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 }