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  }