8 "github.com/grpc-ecosystem/grpc-gateway/utilities"
9 "google.golang.org/grpc/grpclog"
13 // ErrNotMatch indicates that the given HTTP request path does not match to the pattern.
14 ErrNotMatch = errors.New("not match to the path pattern")
15 // ErrInvalidPattern indicates that the given definition of Pattern is not valid.
16 ErrInvalidPattern = errors.New("invalid pattern")
24 // Pattern is a template pattern of http request paths defined in github.com/googleapis/googleapis/google/api/http.proto.
26 // ops is a list of operations
28 // pool is a constant pool indexed by the operands or vars.
30 // vars is a list of variables names to be bound by this pattern
32 // stacksize is the max depth of the stack
34 // tailLen is the length of the fixed-size segments after a deep wildcard
36 // verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
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) {
47 grpclog.Infof("unsupported version: %d", version)
48 return Pattern{}, ErrInvalidPattern
53 grpclog.Infof("odd number of ops codes: %d", l)
54 return Pattern{}, ErrInvalidPattern
64 for i := 0; i < l; i += 2 {
65 op := op{code: utilities.OpCode(ops[i]), operand: ops[i+1]}
69 case utilities.OpPush:
74 case utilities.OpPushM:
76 grpclog.Infof("pushM appears twice")
77 return Pattern{}, ErrInvalidPattern
81 case utilities.OpLitPush:
82 if op.operand < 0 || len(pool) <= op.operand {
83 grpclog.Infof("negative literal index: %d", op.operand)
84 return Pattern{}, ErrInvalidPattern
90 case utilities.OpConcatN:
92 grpclog.Infof("negative concat size: %d", op.operand)
93 return Pattern{}, ErrInvalidPattern
97 grpclog.Print("stack underflow")
98 return Pattern{}, ErrInvalidPattern
101 case utilities.OpCapture:
102 if op.operand < 0 || len(pool) <= op.operand {
103 grpclog.Infof("variable name index out of bound: %d", op.operand)
104 return Pattern{}, ErrInvalidPattern
106 v := pool[op.operand]
107 op.operand = len(vars)
108 vars = append(vars, v)
111 grpclog.Infof("stack underflow")
112 return Pattern{}, ErrInvalidPattern
115 grpclog.Infof("invalid opcode: %d", op.code)
116 return Pattern{}, ErrInvalidPattern
119 if maxstack < stack {
122 typedOps = append(typedOps, op)
134 // MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.
135 func MustPattern(p Pattern, err error) Pattern {
137 grpclog.Fatalf("Pattern initialization failed: %v", err)
142 // Match examines components if it matches to the Pattern.
143 // If it matches, the function returns a mapping from field paths to their captured values.
144 // If otherwise, the function returns an error.
145 func (p Pattern) Match(components []string, verb string) (map[string]string, error) {
147 return nil, ErrNotMatch
151 stack := make([]string, 0, p.stacksize)
152 captured := make([]string, len(p.vars))
154 for _, op := range p.ops {
156 case utilities.OpNop:
158 case utilities.OpPush, utilities.OpLitPush:
160 return nil, ErrNotMatch
163 if op.code == utilities.OpLitPush {
164 if lit := p.pool[op.operand]; c != lit {
165 return nil, ErrNotMatch
168 stack = append(stack, c)
170 case utilities.OpPushM:
171 end := len(components)
172 if end < pos+p.tailLen {
173 return nil, ErrNotMatch
176 stack = append(stack, strings.Join(components[pos:end], "/"))
178 case utilities.OpConcatN:
181 stack = append(stack[:l], strings.Join(stack[l:], "/"))
182 case utilities.OpCapture:
184 captured[op.operand] = stack[n]
189 return nil, ErrNotMatch
191 bindings := make(map[string]string)
192 for i, val := range captured {
193 bindings[p.vars[i]] = val
198 // Verb returns the verb part of the Pattern.
199 func (p Pattern) Verb() string { return p.verb }
201 func (p Pattern) String() string {
203 for _, op := range p.ops {
205 case utilities.OpNop:
207 case utilities.OpPush:
208 stack = append(stack, "*")
209 case utilities.OpLitPush:
210 stack = append(stack, p.pool[op.operand])
211 case utilities.OpPushM:
212 stack = append(stack, "**")
213 case utilities.OpConcatN:
216 stack = append(stack[:l], strings.Join(stack[l:], "/"))
217 case utilities.OpCapture:
219 stack[n] = fmt.Sprintf("{%s=%s}", p.vars[op.operand], stack[n])
222 segs := strings.Join(stack, "/")
224 return fmt.Sprintf("/%s:%s", segs, p.verb)