pattern.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. package runtime
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "github.com/grpc-ecosystem/grpc-gateway/utilities"
  7. "google.golang.org/grpc/grpclog"
  8. )
  9. var (
  10. // ErrNotMatch indicates that the given HTTP request path does not match to the pattern.
  11. ErrNotMatch = errors.New("not match to the path pattern")
  12. // ErrInvalidPattern indicates that the given definition of Pattern is not valid.
  13. ErrInvalidPattern = errors.New("invalid pattern")
  14. )
  15. type op struct {
  16. code utilities.OpCode
  17. operand int
  18. }
  19. // Pattern is a template pattern of http request paths defined in github.com/googleapis/googleapis/google/api/http.proto.
  20. type Pattern struct {
  21. // ops is a list of operations
  22. ops []op
  23. // pool is a constant pool indexed by the operands or vars.
  24. pool []string
  25. // vars is a list of variables names to be bound by this pattern
  26. vars []string
  27. // stacksize is the max depth of the stack
  28. stacksize int
  29. // tailLen is the length of the fixed-size segments after a deep wildcard
  30. tailLen int
  31. // verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
  32. verb string
  33. // assumeColonVerb indicates whether a path suffix after a final
  34. // colon may only be interpreted as a verb.
  35. assumeColonVerb bool
  36. }
  37. type patternOptions struct {
  38. assumeColonVerb bool
  39. }
  40. // PatternOpt is an option for creating Patterns.
  41. type PatternOpt func(*patternOptions)
  42. // NewPattern returns a new Pattern from the given definition values.
  43. // "ops" is a sequence of op codes. "pool" is a constant pool.
  44. // "verb" is the verb part of the pattern. It is empty if the pattern does not have the part.
  45. // "version" must be 1 for now.
  46. // It returns an error if the given definition is invalid.
  47. func NewPattern(version int, ops []int, pool []string, verb string, opts ...PatternOpt) (Pattern, error) {
  48. options := patternOptions{
  49. assumeColonVerb: true,
  50. }
  51. for _, o := range opts {
  52. o(&options)
  53. }
  54. if version != 1 {
  55. grpclog.Infof("unsupported version: %d", version)
  56. return Pattern{}, ErrInvalidPattern
  57. }
  58. l := len(ops)
  59. if l%2 != 0 {
  60. grpclog.Infof("odd number of ops codes: %d", l)
  61. return Pattern{}, ErrInvalidPattern
  62. }
  63. var (
  64. typedOps []op
  65. stack, maxstack int
  66. tailLen int
  67. pushMSeen bool
  68. vars []string
  69. )
  70. for i := 0; i < l; i += 2 {
  71. op := op{code: utilities.OpCode(ops[i]), operand: ops[i+1]}
  72. switch op.code {
  73. case utilities.OpNop:
  74. continue
  75. case utilities.OpPush:
  76. if pushMSeen {
  77. tailLen++
  78. }
  79. stack++
  80. case utilities.OpPushM:
  81. if pushMSeen {
  82. grpclog.Infof("pushM appears twice")
  83. return Pattern{}, ErrInvalidPattern
  84. }
  85. pushMSeen = true
  86. stack++
  87. case utilities.OpLitPush:
  88. if op.operand < 0 || len(pool) <= op.operand {
  89. grpclog.Infof("negative literal index: %d", op.operand)
  90. return Pattern{}, ErrInvalidPattern
  91. }
  92. if pushMSeen {
  93. tailLen++
  94. }
  95. stack++
  96. case utilities.OpConcatN:
  97. if op.operand <= 0 {
  98. grpclog.Infof("negative concat size: %d", op.operand)
  99. return Pattern{}, ErrInvalidPattern
  100. }
  101. stack -= op.operand
  102. if stack < 0 {
  103. grpclog.Print("stack underflow")
  104. return Pattern{}, ErrInvalidPattern
  105. }
  106. stack++
  107. case utilities.OpCapture:
  108. if op.operand < 0 || len(pool) <= op.operand {
  109. grpclog.Infof("variable name index out of bound: %d", op.operand)
  110. return Pattern{}, ErrInvalidPattern
  111. }
  112. v := pool[op.operand]
  113. op.operand = len(vars)
  114. vars = append(vars, v)
  115. stack--
  116. if stack < 0 {
  117. grpclog.Infof("stack underflow")
  118. return Pattern{}, ErrInvalidPattern
  119. }
  120. default:
  121. grpclog.Infof("invalid opcode: %d", op.code)
  122. return Pattern{}, ErrInvalidPattern
  123. }
  124. if maxstack < stack {
  125. maxstack = stack
  126. }
  127. typedOps = append(typedOps, op)
  128. }
  129. return Pattern{
  130. ops: typedOps,
  131. pool: pool,
  132. vars: vars,
  133. stacksize: maxstack,
  134. tailLen: tailLen,
  135. verb: verb,
  136. assumeColonVerb: options.assumeColonVerb,
  137. }, nil
  138. }
  139. // MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.
  140. func MustPattern(p Pattern, err error) Pattern {
  141. if err != nil {
  142. grpclog.Fatalf("Pattern initialization failed: %v", err)
  143. }
  144. return p
  145. }
  146. // Match examines components if it matches to the Pattern.
  147. // If it matches, the function returns a mapping from field paths to their captured values.
  148. // If otherwise, the function returns an error.
  149. func (p Pattern) Match(components []string, verb string) (map[string]string, error) {
  150. if p.verb != verb {
  151. if p.assumeColonVerb || p.verb != "" {
  152. return nil, ErrNotMatch
  153. }
  154. if len(components) == 0 {
  155. components = []string{":" + verb}
  156. } else {
  157. components = append([]string{}, components...)
  158. components[len(components)-1] += ":" + verb
  159. }
  160. verb = ""
  161. }
  162. var pos int
  163. stack := make([]string, 0, p.stacksize)
  164. captured := make([]string, len(p.vars))
  165. l := len(components)
  166. for _, op := range p.ops {
  167. switch op.code {
  168. case utilities.OpNop:
  169. continue
  170. case utilities.OpPush, utilities.OpLitPush:
  171. if pos >= l {
  172. return nil, ErrNotMatch
  173. }
  174. c := components[pos]
  175. if op.code == utilities.OpLitPush {
  176. if lit := p.pool[op.operand]; c != lit {
  177. return nil, ErrNotMatch
  178. }
  179. }
  180. stack = append(stack, c)
  181. pos++
  182. case utilities.OpPushM:
  183. end := len(components)
  184. if end < pos+p.tailLen {
  185. return nil, ErrNotMatch
  186. }
  187. end -= p.tailLen
  188. stack = append(stack, strings.Join(components[pos:end], "/"))
  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. // Verb returns the verb part of the Pattern.
  210. func (p Pattern) Verb() string { return p.verb }
  211. func (p Pattern) String() string {
  212. var stack []string
  213. for _, op := range p.ops {
  214. switch op.code {
  215. case utilities.OpNop:
  216. continue
  217. case utilities.OpPush:
  218. stack = append(stack, "*")
  219. case utilities.OpLitPush:
  220. stack = append(stack, p.pool[op.operand])
  221. case utilities.OpPushM:
  222. stack = append(stack, "**")
  223. case utilities.OpConcatN:
  224. n := op.operand
  225. l := len(stack) - n
  226. stack = append(stack[:l], strings.Join(stack[l:], "/"))
  227. case utilities.OpCapture:
  228. n := len(stack) - 1
  229. stack[n] = fmt.Sprintf("{%s=%s}", p.vars[op.operand], stack[n])
  230. }
  231. }
  232. segs := strings.Join(stack, "/")
  233. if p.verb != "" {
  234. return fmt.Sprintf("/%s:%s", segs, p.verb)
  235. }
  236. return "/" + segs
  237. }
  238. // AssumeColonVerbOpt indicates whether a path suffix after a final
  239. // colon may only be interpreted as a verb.
  240. func AssumeColonVerbOpt(val bool) PatternOpt {
  241. return PatternOpt(func(o *patternOptions) {
  242. o.assumeColonVerb = val
  243. })
  244. }