pattern.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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 third_party/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. }
  34. // NewPattern returns a new Pattern from the given definition values.
  35. // "ops" is a sequence of op codes. "pool" is a constant pool.
  36. // "verb" is the verb part of the pattern. It is empty if the pattern does not have the part.
  37. // "version" must be 1 for now.
  38. // It returns an error if the given definition is invalid.
  39. func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, error) {
  40. if version != 1 {
  41. grpclog.Printf("unsupported version: %d", version)
  42. return Pattern{}, ErrInvalidPattern
  43. }
  44. l := len(ops)
  45. if l%2 != 0 {
  46. grpclog.Printf("odd number of ops codes: %d", l)
  47. return Pattern{}, ErrInvalidPattern
  48. }
  49. var (
  50. typedOps []op
  51. stack, maxstack int
  52. tailLen int
  53. pushMSeen bool
  54. vars []string
  55. )
  56. for i := 0; i < l; i += 2 {
  57. op := op{code: utilities.OpCode(ops[i]), operand: ops[i+1]}
  58. switch op.code {
  59. case utilities.OpNop:
  60. continue
  61. case utilities.OpPush:
  62. if pushMSeen {
  63. tailLen++
  64. }
  65. stack++
  66. case utilities.OpPushM:
  67. if pushMSeen {
  68. grpclog.Printf("pushM appears twice")
  69. return Pattern{}, ErrInvalidPattern
  70. }
  71. pushMSeen = true
  72. stack++
  73. case utilities.OpLitPush:
  74. if op.operand < 0 || len(pool) <= op.operand {
  75. grpclog.Printf("negative literal index: %d", op.operand)
  76. return Pattern{}, ErrInvalidPattern
  77. }
  78. if pushMSeen {
  79. tailLen++
  80. }
  81. stack++
  82. case utilities.OpConcatN:
  83. if op.operand <= 0 {
  84. grpclog.Printf("negative concat size: %d", op.operand)
  85. return Pattern{}, ErrInvalidPattern
  86. }
  87. stack -= op.operand
  88. if stack < 0 {
  89. grpclog.Print("stack underflow")
  90. return Pattern{}, ErrInvalidPattern
  91. }
  92. stack++
  93. case utilities.OpCapture:
  94. if op.operand < 0 || len(pool) <= op.operand {
  95. grpclog.Printf("variable name index out of bound: %d", op.operand)
  96. return Pattern{}, ErrInvalidPattern
  97. }
  98. v := pool[op.operand]
  99. op.operand = len(vars)
  100. vars = append(vars, v)
  101. stack--
  102. if stack < 0 {
  103. grpclog.Printf("stack underflow")
  104. return Pattern{}, ErrInvalidPattern
  105. }
  106. default:
  107. grpclog.Printf("invalid opcode: %d", op.code)
  108. return Pattern{}, ErrInvalidPattern
  109. }
  110. if maxstack < stack {
  111. maxstack = stack
  112. }
  113. typedOps = append(typedOps, op)
  114. }
  115. return Pattern{
  116. ops: typedOps,
  117. pool: pool,
  118. vars: vars,
  119. stacksize: maxstack,
  120. tailLen: tailLen,
  121. verb: verb,
  122. }, nil
  123. }
  124. // MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.
  125. func MustPattern(p Pattern, err error) Pattern {
  126. if err != nil {
  127. grpclog.Fatalf("Pattern initialization failed: %v", err)
  128. }
  129. return p
  130. }
  131. // Match examines components if it matches to the Pattern.
  132. // If it matches, the function returns a mapping from field paths to their captured values.
  133. // If otherwise, the function returns an error.
  134. func (p Pattern) Match(components []string, verb string) (map[string]string, error) {
  135. if p.verb != verb {
  136. return nil, ErrNotMatch
  137. }
  138. var pos int
  139. stack := make([]string, 0, p.stacksize)
  140. captured := make([]string, len(p.vars))
  141. l := len(components)
  142. for _, op := range p.ops {
  143. switch op.code {
  144. case utilities.OpNop:
  145. continue
  146. case utilities.OpPush, utilities.OpLitPush:
  147. if pos >= l {
  148. return nil, ErrNotMatch
  149. }
  150. c := components[pos]
  151. if op.code == utilities.OpLitPush {
  152. if lit := p.pool[op.operand]; c != lit {
  153. return nil, ErrNotMatch
  154. }
  155. }
  156. stack = append(stack, c)
  157. pos++
  158. case utilities.OpPushM:
  159. end := len(components)
  160. if end < pos+p.tailLen {
  161. return nil, ErrNotMatch
  162. }
  163. end -= p.tailLen
  164. stack = append(stack, strings.Join(components[pos:end], "/"))
  165. pos = end
  166. case utilities.OpConcatN:
  167. n := op.operand
  168. l := len(stack) - n
  169. stack = append(stack[:l], strings.Join(stack[l:], "/"))
  170. case utilities.OpCapture:
  171. n := len(stack) - 1
  172. captured[op.operand] = stack[n]
  173. stack = stack[:n]
  174. }
  175. }
  176. if pos < l {
  177. return nil, ErrNotMatch
  178. }
  179. bindings := make(map[string]string)
  180. for i, val := range captured {
  181. bindings[p.vars[i]] = val
  182. }
  183. return bindings, nil
  184. }
  185. // Verb returns the verb part of the Pattern.
  186. func (p Pattern) Verb() string { return p.verb }
  187. func (p Pattern) String() string {
  188. var stack []string
  189. for _, op := range p.ops {
  190. switch op.code {
  191. case utilities.OpNop:
  192. continue
  193. case utilities.OpPush:
  194. stack = append(stack, "*")
  195. case utilities.OpLitPush:
  196. stack = append(stack, p.pool[op.operand])
  197. case utilities.OpPushM:
  198. stack = append(stack, "**")
  199. case utilities.OpConcatN:
  200. n := op.operand
  201. l := len(stack) - n
  202. stack = append(stack[:l], strings.Join(stack[l:], "/"))
  203. case utilities.OpCapture:
  204. n := len(stack) - 1
  205. stack[n] = fmt.Sprintf("{%s=%s}", p.vars[op.operand], stack[n])
  206. }
  207. }
  208. segs := strings.Join(stack, "/")
  209. if p.verb != "" {
  210. return fmt.Sprintf("/%s:%s", segs, p.verb)
  211. }
  212. return "/" + segs
  213. }