// Copyright (c) 2017 Uber Technologies, Inc. // // 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 rpcmetrics // NameNormalizer is used to convert the endpoint names to strings // that can be safely used as tags in the metrics. type NameNormalizer interface { Normalize(name string) string } // DefaultNameNormalizer converts endpoint names so that they contain only characters // from the safe charset [a-zA-Z0-9-./_]. All other characters are replaced with '-'. var DefaultNameNormalizer = &SimpleNameNormalizer{ SafeSets: []SafeCharacterSet{ &Range{From: 'a', To: 'z'}, &Range{From: 'A', To: 'Z'}, &Range{From: '0', To: '9'}, &Char{'-'}, &Char{'_'}, &Char{'/'}, &Char{'.'}, }, Replacement: '-', } // SimpleNameNormalizer uses a set of safe character sets. type SimpleNameNormalizer struct { SafeSets []SafeCharacterSet Replacement byte } // SafeCharacterSet determines if the given character is "safe" type SafeCharacterSet interface { IsSafe(c byte) bool } // Range implements SafeCharacterSet type Range struct { From, To byte } // IsSafe implements SafeCharacterSet func (r *Range) IsSafe(c byte) bool { return c >= r.From && c <= r.To } // Char implements SafeCharacterSet type Char struct { Val byte } // IsSafe implements SafeCharacterSet func (ch *Char) IsSafe(c byte) bool { return c == ch.Val } // Normalize checks each character in the string against SafeSets, // and if it's not safe substitutes it with Replacement. func (n *SimpleNameNormalizer) Normalize(name string) string { var retMe []byte nameBytes := []byte(name) for i, b := range nameBytes { if n.safeByte(b) { if retMe != nil { retMe[i] = b } } else { if retMe == nil { retMe = make([]byte, len(nameBytes)) copy(retMe[0:i], nameBytes[0:i]) } retMe[i] = n.Replacement } } if retMe == nil { return name } return string(retMe) } // safeByte checks if b against all safe charsets. func (n *SimpleNameNormalizer) safeByte(b byte) bool { for i := range n.SafeSets { if n.SafeSets[i].IsSafe(b) { return true } } return false }