github.com/cnotch/ipchub@v1.1.0/network/socket/listener/matcher.go (about) 1 /********************************************************************************** 2 * Copyright (c) 2009-2017 Misakai Ltd. 3 * This program is free software: you can redistribute it and/or modify it under the 4 * terms of the GNU Affero General Public License as published by the Free Software 5 * Foundation, either version 3 of the License, or(at your option) any later version. 6 * 7 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 9 * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 10 * 11 * You should have received a copy of the GNU Affero General Public License along 12 * with this program. If not, see<http://www.gnu.org/licenses/>. 13 * 14 * This file was originally developed by The CMux Authors and released under Apache 15 * License, Version 2.0 in 2016. 16 ************************************************************************************/ 17 18 package listener 19 20 import ( 21 "bytes" 22 "io" 23 ) 24 25 var defaultHTTPMethods = []string{ 26 "OPTIONS", 27 "GET", 28 "HEAD", 29 "POST", 30 "PATCH", 31 "PUT", 32 "DELETE", 33 "TRACE", 34 "CONNECT", 35 } 36 37 // ------------------------------------------------------------------------------------ 38 39 // MatchAny matches any connection. 40 func MatchAny() Matcher { 41 return func(r io.Reader) bool { return true } 42 } 43 44 // MatchPrefix returns a matcher that matches a connection if it 45 // starts with any of the strings in strs. 46 func MatchPrefix(strs ...string) Matcher { 47 pt := newPatriciaTreeString(strs...) 48 return pt.matchPrefix 49 } 50 51 // MatchHTTP only matches the methods in the HTTP request. 52 func MatchHTTP(extMethods ...string) Matcher { 53 return MatchPrefix(append(defaultHTTPMethods, extMethods...)...) 54 } 55 56 // MatchPrefixBytes 匹配前缀字节数组 57 func MatchPrefixBytes(bs ...[]byte) Matcher { 58 pt := newPatriciaTree(bs...) 59 return pt.matchPrefix 60 } 61 62 // ------------------------------------------------------------------------------------ 63 64 // patriciaTree is a simple patricia tree that handles []byte instead of string 65 // and cannot be changed after instantiation. 66 type patriciaTree struct { 67 root *ptNode 68 maxDepth int // max depth of the tree. 69 } 70 71 func newPatriciaTree(bs ...[]byte) *patriciaTree { 72 max := 0 73 for _, b := range bs { 74 if max < len(b) { 75 max = len(b) 76 } 77 } 78 return &patriciaTree{ 79 root: newNode(bs), 80 maxDepth: max + 1, 81 } 82 } 83 84 func newPatriciaTreeString(strs ...string) *patriciaTree { 85 b := make([][]byte, len(strs)) 86 for i, s := range strs { 87 b[i] = []byte(s) 88 } 89 return newPatriciaTree(b...) 90 } 91 92 func (t *patriciaTree) matchPrefix(r io.Reader) bool { 93 buf := make([]byte, t.maxDepth) 94 n, _ := io.ReadFull(r, buf) 95 return t.root.match(buf[:n], true) 96 } 97 98 func (t *patriciaTree) match(r io.Reader) bool { 99 buf := make([]byte, t.maxDepth) 100 n, _ := io.ReadFull(r, buf) 101 return t.root.match(buf[:n], false) 102 } 103 104 type ptNode struct { 105 prefix []byte 106 next map[byte]*ptNode 107 terminal bool 108 } 109 110 func newNode(strs [][]byte) *ptNode { 111 if len(strs) == 0 { 112 return &ptNode{ 113 prefix: []byte{}, 114 terminal: true, 115 } 116 } 117 118 if len(strs) == 1 { 119 return &ptNode{ 120 prefix: strs[0], 121 terminal: true, 122 } 123 } 124 125 p, strs := splitPrefix(strs) 126 n := &ptNode{ 127 prefix: p, 128 } 129 130 nexts := make(map[byte][][]byte) 131 for _, s := range strs { 132 if len(s) == 0 { 133 n.terminal = true 134 continue 135 } 136 nexts[s[0]] = append(nexts[s[0]], s[1:]) 137 } 138 139 n.next = make(map[byte]*ptNode) 140 for first, rests := range nexts { 141 n.next[first] = newNode(rests) 142 } 143 144 return n 145 } 146 147 func splitPrefix(bss [][]byte) (prefix []byte, rest [][]byte) { 148 if len(bss) == 0 || len(bss[0]) == 0 { 149 return prefix, bss 150 } 151 152 if len(bss) == 1 { 153 return bss[0], [][]byte{{}} 154 } 155 156 for i := 0; ; i++ { 157 var cur byte 158 eq := true 159 for j, b := range bss { 160 if len(b) <= i { 161 eq = false 162 break 163 } 164 165 if j == 0 { 166 cur = b[i] 167 continue 168 } 169 170 if cur != b[i] { 171 eq = false 172 break 173 } 174 } 175 176 if !eq { 177 break 178 } 179 180 prefix = append(prefix, cur) 181 } 182 183 rest = make([][]byte, 0, len(bss)) 184 for _, b := range bss { 185 rest = append(rest, b[len(prefix):]) 186 } 187 188 return prefix, rest 189 } 190 191 func (n *ptNode) match(b []byte, prefix bool) bool { 192 l := len(n.prefix) 193 if l > 0 { 194 if l > len(b) { 195 l = len(b) 196 } 197 if !bytes.Equal(b[:l], n.prefix) { 198 return false 199 } 200 } 201 202 if n.terminal && (prefix || len(n.prefix) == len(b)) { 203 return true 204 } 205 206 if l >= len(b) { 207 return false 208 } 209 210 nextN, ok := n.next[b[l]] 211 if !ok { 212 return false 213 } 214 215 if l == len(b) { 216 b = b[l:l] 217 } else { 218 b = b[l+1:] 219 } 220 return nextN.match(b, prefix) 221 }