src

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

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 }