mirror of
https://github.com/Luzifer/nginx-sso.git
synced 2024-12-21 13:21:17 +00:00
215 lines
4.6 KiB
Go
215 lines
4.6 KiB
Go
/*
|
|
Copyright 2015 Google LLC
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"cloud.google.com/go/bigtable"
|
|
)
|
|
|
|
// Parse a GC policy. Valid policies include
|
|
// never
|
|
// maxage = 5d
|
|
// maxversions = 3
|
|
// maxage = 5d || maxversions = 3
|
|
// maxage=30d || (maxage=3d && maxversions=100)
|
|
func parseGCPolicy(s string) (bigtable.GCPolicy, error) {
|
|
if strings.TrimSpace(s) == "never" {
|
|
return bigtable.NoGcPolicy(), nil
|
|
}
|
|
r := strings.NewReader(s)
|
|
p, err := parsePolicyExpr(r)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid GC policy: %v", err)
|
|
}
|
|
tok, err := getToken(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tok != "" {
|
|
return nil, fmt.Errorf("invalid GC policy: want end of input, got %q", tok)
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// expr ::= term (op term)*
|
|
// op ::= "and" | "or" | "&&" | "||"
|
|
func parsePolicyExpr(r io.RuneScanner) (bigtable.GCPolicy, error) {
|
|
policy, err := parsePolicyTerm(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for {
|
|
tok, err := getToken(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var f func(...bigtable.GCPolicy) bigtable.GCPolicy
|
|
switch tok {
|
|
case "and", "&&":
|
|
f = bigtable.IntersectionPolicy
|
|
case "or", "||":
|
|
f = bigtable.UnionPolicy
|
|
default:
|
|
ungetToken(tok)
|
|
return policy, nil
|
|
}
|
|
p2, err := parsePolicyTerm(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
policy = f(policy, p2)
|
|
}
|
|
}
|
|
|
|
// term ::= "maxage" "=" duration | "maxversions" "=" int | "(" policy ")"
|
|
func parsePolicyTerm(r io.RuneScanner) (bigtable.GCPolicy, error) {
|
|
tok, err := getToken(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch tok {
|
|
case "":
|
|
return nil, errors.New("empty GC policy term")
|
|
|
|
case "maxage", "maxversions":
|
|
if err := expectToken(r, "="); err != nil {
|
|
return nil, err
|
|
}
|
|
tok2, err := getToken(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tok2 == "" {
|
|
return nil, errors.New("expected a token after '='")
|
|
}
|
|
if tok == "maxage" {
|
|
dur, err := parseDuration(tok2)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bigtable.MaxAgePolicy(dur), nil
|
|
}
|
|
n, err := strconv.ParseUint(tok2, 10, 16)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bigtable.MaxVersionsPolicy(int(n)), nil
|
|
|
|
case "(":
|
|
p, err := parsePolicyExpr(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := expectToken(r, ")"); err != nil {
|
|
return nil, err
|
|
}
|
|
return p, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unexpected token: %q", tok)
|
|
}
|
|
|
|
}
|
|
|
|
func expectToken(r io.RuneScanner, want string) error {
|
|
got, err := getToken(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if got != want {
|
|
return fmt.Errorf("expected %q, saw %q", want, got)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const noToken = "_" // empty token is valid, so use "_" instead
|
|
|
|
// If not noToken, getToken will return this instead of reading a new token
|
|
// from the input.
|
|
var ungotToken = noToken
|
|
|
|
// getToken extracts the first token from the input. Valid tokens include
|
|
// any sequence of letters and digits, and these symbols: &&, ||, =, ( and ).
|
|
// getToken returns ("", nil) at end of input.
|
|
func getToken(r io.RuneScanner) (string, error) {
|
|
if ungotToken != noToken {
|
|
t := ungotToken
|
|
ungotToken = noToken
|
|
return t, nil
|
|
}
|
|
var err error
|
|
// Skip leading whitespace.
|
|
c := ' '
|
|
for unicode.IsSpace(c) {
|
|
c, _, err = r.ReadRune()
|
|
if err == io.EOF {
|
|
return "", nil
|
|
}
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
switch {
|
|
case c == '=' || c == '(' || c == ')':
|
|
return string(c), nil
|
|
|
|
case c == '&' || c == '|':
|
|
c2, _, err := r.ReadRune()
|
|
if err != nil && err != io.EOF {
|
|
return "", err
|
|
}
|
|
if c != c2 {
|
|
return "", fmt.Errorf("expected %c%c", c, c)
|
|
}
|
|
return string([]rune{c, c}), nil
|
|
|
|
case unicode.IsLetter(c) || unicode.IsDigit(c):
|
|
// Collect an alphanumeric token.
|
|
var b bytes.Buffer
|
|
for unicode.IsLetter(c) || unicode.IsDigit(c) {
|
|
b.WriteRune(c)
|
|
c, _, err = r.ReadRune()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
r.UnreadRune()
|
|
return b.String(), nil
|
|
|
|
default:
|
|
return "", fmt.Errorf("bad rune %q", c)
|
|
}
|
|
}
|
|
|
|
// "unget" a token so the next call to getToken will return it.
|
|
func ungetToken(tok string) {
|
|
if ungotToken != noToken {
|
|
panic("ungetToken called twice")
|
|
}
|
|
ungotToken = tok
|
|
}
|