pattern.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. package runtime
  2. import (
  3. "errors"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
  8. "google.golang.org/grpc/grpclog"
  9. )
  10. var (
  11. // ErrNotMatch indicates that the given HTTP request path does not match to the pattern.
  12. ErrNotMatch = errors.New("not match to the path pattern")
  13. // ErrInvalidPattern indicates that the given definition of Pattern is not valid.
  14. ErrInvalidPattern = errors.New("invalid pattern")
  15. )
  16. type MalformedSequenceError string
  17. func (e MalformedSequenceError) Error() string {
  18. return "malformed path escape " + strconv.Quote(string(e))
  19. }
  20. type op struct {
  21. code utilities.OpCode
  22. operand int
  23. }
  24. // Pattern is a template pattern of http request paths defined in
  25. // https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
  26. type Pattern struct {
  27. // ops is a list of operations
  28. ops []op
  29. // pool is a constant pool indexed by the operands or vars.
  30. pool []string
  31. // vars is a list of variables names to be bound by this pattern
  32. vars []string
  33. // stacksize is the max depth of the stack
  34. stacksize int
  35. // tailLen is the length of the fixed-size segments after a deep wildcard
  36. tailLen int
  37. // verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
  38. verb string
  39. }
  40. // NewPattern returns a new Pattern from the given definition values.
  41. // "ops" is a sequence of op codes. "pool" is a constant pool.
  42. // "verb" is the verb part of the pattern. It is empty if the pattern does not have the part.
  43. // "version" must be 1 for now.
  44. // It returns an error if the given definition is invalid.
  45. func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, error) {
  46. if version != 1 {
  47. grpclog.Infof("unsupported version: %d", version)
  48. return Pattern{}, ErrInvalidPattern
  49. }
  50. l := len(ops)
  51. if l%2 != 0 {
  52. grpclog.Infof("odd number of ops codes: %d", l)
  53. return Pattern{}, ErrInvalidPattern
  54. }
  55. var (
  56. typedOps []op
  57. stack, maxstack int
  58. tailLen int
  59. pushMSeen bool
  60. vars []string
  61. )
  62. for i := 0; i < l; i += 2 {
  63. op := op{code: utilities.OpCode(ops[i]), operand: ops[i+1]}
  64. switch op.code {
  65. case utilities.OpNop:
  66. continue
  67. case utilities.OpPush:
  68. if pushMSeen {
  69. tailLen++
  70. }
  71. stack++
  72. case utilities.OpPushM:
  73. if pushMSeen {
  74. grpclog.Infof("pushM appears twice")
  75. return Pattern{}, ErrInvalidPattern
  76. }
  77. pushMSeen = true
  78. stack++
  79. case utilities.OpLitPush:
  80. if op.operand < 0 || len(pool) <= op.operand {
  81. grpclog.Infof("negative literal index: %d", op.operand)
  82. return Pattern{}, ErrInvalidPattern
  83. }
  84. if pushMSeen {
  85. tailLen++
  86. }
  87. stack++
  88. case utilities.OpConcatN:
  89. if op.operand <= 0 {
  90. grpclog.Infof("negative concat size: %d", op.operand)
  91. return Pattern{}, ErrInvalidPattern
  92. }
  93. stack -= op.operand
  94. if stack < 0 {
  95. grpclog.Info("stack underflow")
  96. return Pattern{}, ErrInvalidPattern
  97. }
  98. stack++
  99. case utilities.OpCapture:
  100. if op.operand < 0 || len(pool) <= op.operand {
  101. grpclog.Infof("variable name index out of bound: %d", op.operand)
  102. return Pattern{}, ErrInvalidPattern
  103. }
  104. v := pool[op.operand]
  105. op.operand = len(vars)
  106. vars = append(vars, v)
  107. stack--
  108. if stack < 0 {
  109. grpclog.Infof("stack underflow")
  110. return Pattern{}, ErrInvalidPattern
  111. }
  112. default:
  113. grpclog.Infof("invalid opcode: %d", op.code)
  114. return Pattern{}, ErrInvalidPattern
  115. }
  116. if maxstack < stack {
  117. maxstack = stack
  118. }
  119. typedOps = append(typedOps, op)
  120. }
  121. return Pattern{
  122. ops: typedOps,
  123. pool: pool,
  124. vars: vars,
  125. stacksize: maxstack,
  126. tailLen: tailLen,
  127. verb: verb,
  128. }, nil
  129. }
  130. // MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.
  131. func MustPattern(p Pattern, err error) Pattern {
  132. if err != nil {
  133. grpclog.Fatalf("Pattern initialization failed: %v", err)
  134. }
  135. return p
  136. }
  137. // MatchAndEscape examines components to determine if they match to a Pattern.
  138. // MatchAndEscape will return an error if no Patterns matched or if a pattern
  139. // matched but contained malformed escape sequences. If successful, the function
  140. // returns a mapping from field paths to their captured values.
  141. func (p Pattern) MatchAndEscape(components []string, verb string, unescapingMode UnescapingMode) (map[string]string, error) {
  142. if p.verb != verb {
  143. if p.verb != "" {
  144. return nil, ErrNotMatch
  145. }
  146. if len(components) == 0 {
  147. components = []string{":" + verb}
  148. } else {
  149. components = append([]string{}, components...)
  150. components[len(components)-1] += ":" + verb
  151. }
  152. }
  153. var pos int
  154. stack := make([]string, 0, p.stacksize)
  155. captured := make([]string, len(p.vars))
  156. l := len(components)
  157. for _, op := range p.ops {
  158. var err error
  159. switch op.code {
  160. case utilities.OpNop:
  161. continue
  162. case utilities.OpPush, utilities.OpLitPush:
  163. if pos >= l {
  164. return nil, ErrNotMatch
  165. }
  166. c := components[pos]
  167. if op.code == utilities.OpLitPush {
  168. if lit := p.pool[op.operand]; c != lit {
  169. return nil, ErrNotMatch
  170. }
  171. } else if op.code == utilities.OpPush {
  172. if c, err = unescape(c, unescapingMode, false); err != nil {
  173. return nil, err
  174. }
  175. }
  176. stack = append(stack, c)
  177. pos++
  178. case utilities.OpPushM:
  179. end := len(components)
  180. if end < pos+p.tailLen {
  181. return nil, ErrNotMatch
  182. }
  183. end -= p.tailLen
  184. c := strings.Join(components[pos:end], "/")
  185. if c, err = unescape(c, unescapingMode, true); err != nil {
  186. return nil, err
  187. }
  188. stack = append(stack, c)
  189. pos = end
  190. case utilities.OpConcatN:
  191. n := op.operand
  192. l := len(stack) - n
  193. stack = append(stack[:l], strings.Join(stack[l:], "/"))
  194. case utilities.OpCapture:
  195. n := len(stack) - 1
  196. captured[op.operand] = stack[n]
  197. stack = stack[:n]
  198. }
  199. }
  200. if pos < l {
  201. return nil, ErrNotMatch
  202. }
  203. bindings := make(map[string]string)
  204. for i, val := range captured {
  205. bindings[p.vars[i]] = val
  206. }
  207. return bindings, nil
  208. }
  209. // MatchAndEscape examines components to determine if they match to a Pattern.
  210. // It will never perform per-component unescaping (see: UnescapingModeLegacy).
  211. // MatchAndEscape will return an error if no Patterns matched. If successful,
  212. // the function returns a mapping from field paths to their captured values.
  213. //
  214. // Deprecated: Use MatchAndEscape.
  215. func (p Pattern) Match(components []string, verb string) (map[string]string, error) {
  216. return p.MatchAndEscape(components, verb, UnescapingModeDefault)
  217. }
  218. // Verb returns the verb part of the Pattern.
  219. func (p Pattern) Verb() string { return p.verb }
  220. func (p Pattern) String() string {
  221. var stack []string
  222. for _, op := range p.ops {
  223. switch op.code {
  224. case utilities.OpNop:
  225. continue
  226. case utilities.OpPush:
  227. stack = append(stack, "*")
  228. case utilities.OpLitPush:
  229. stack = append(stack, p.pool[op.operand])
  230. case utilities.OpPushM:
  231. stack = append(stack, "**")
  232. case utilities.OpConcatN:
  233. n := op.operand
  234. l := len(stack) - n
  235. stack = append(stack[:l], strings.Join(stack[l:], "/"))
  236. case utilities.OpCapture:
  237. n := len(stack) - 1
  238. stack[n] = fmt.Sprintf("{%s=%s}", p.vars[op.operand], stack[n])
  239. }
  240. }
  241. segs := strings.Join(stack, "/")
  242. if p.verb != "" {
  243. return fmt.Sprintf("/%s:%s", segs, p.verb)
  244. }
  245. return "/" + segs
  246. }
  247. /*
  248. * The following code is adopted and modified from Go's standard library
  249. * and carries the attached license.
  250. *
  251. * Copyright 2009 The Go Authors. All rights reserved.
  252. * Use of this source code is governed by a BSD-style
  253. * license that can be found in the LICENSE file.
  254. */
  255. // ishex returns whether or not the given byte is a valid hex character
  256. func ishex(c byte) bool {
  257. switch {
  258. case '0' <= c && c <= '9':
  259. return true
  260. case 'a' <= c && c <= 'f':
  261. return true
  262. case 'A' <= c && c <= 'F':
  263. return true
  264. }
  265. return false
  266. }
  267. func isRFC6570Reserved(c byte) bool {
  268. switch c {
  269. case '!', '#', '$', '&', '\'', '(', ')', '*',
  270. '+', ',', '/', ':', ';', '=', '?', '@', '[', ']':
  271. return true
  272. default:
  273. return false
  274. }
  275. }
  276. // unhex converts a hex point to the bit representation
  277. func unhex(c byte) byte {
  278. switch {
  279. case '0' <= c && c <= '9':
  280. return c - '0'
  281. case 'a' <= c && c <= 'f':
  282. return c - 'a' + 10
  283. case 'A' <= c && c <= 'F':
  284. return c - 'A' + 10
  285. }
  286. return 0
  287. }
  288. // shouldUnescapeWithMode returns true if the character is escapable with the
  289. // given mode
  290. func shouldUnescapeWithMode(c byte, mode UnescapingMode) bool {
  291. switch mode {
  292. case UnescapingModeAllExceptReserved:
  293. if isRFC6570Reserved(c) {
  294. return false
  295. }
  296. case UnescapingModeAllExceptSlash:
  297. if c == '/' {
  298. return false
  299. }
  300. case UnescapingModeAllCharacters:
  301. return true
  302. }
  303. return true
  304. }
  305. // unescape unescapes a path string using the provided mode
  306. func unescape(s string, mode UnescapingMode, multisegment bool) (string, error) {
  307. // TODO(v3): remove UnescapingModeLegacy
  308. if mode == UnescapingModeLegacy {
  309. return s, nil
  310. }
  311. if !multisegment {
  312. mode = UnescapingModeAllCharacters
  313. }
  314. // Count %, check that they're well-formed.
  315. n := 0
  316. for i := 0; i < len(s); {
  317. if s[i] == '%' {
  318. n++
  319. if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
  320. s = s[i:]
  321. if len(s) > 3 {
  322. s = s[:3]
  323. }
  324. return "", MalformedSequenceError(s)
  325. }
  326. i += 3
  327. } else {
  328. i++
  329. }
  330. }
  331. if n == 0 {
  332. return s, nil
  333. }
  334. var t strings.Builder
  335. t.Grow(len(s))
  336. for i := 0; i < len(s); i++ {
  337. switch s[i] {
  338. case '%':
  339. c := unhex(s[i+1])<<4 | unhex(s[i+2])
  340. if shouldUnescapeWithMode(c, mode) {
  341. t.WriteByte(c)
  342. i += 2
  343. continue
  344. }
  345. fallthrough
  346. default:
  347. t.WriteByte(s[i])
  348. }
  349. }
  350. return t.String(), nil
  351. }