...

Source file src/github.com/soheilhy/cmux/matchers.go

Documentation: github.com/soheilhy/cmux

     1  // Copyright 2016 The CMux Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied. See the License for the specific language governing
    13  // permissions and limitations under the License.
    14  
    15  package cmux
    16  
    17  import (
    18  	"bufio"
    19  	"crypto/tls"
    20  	"io"
    21  	"io/ioutil"
    22  	"net/http"
    23  	"strings"
    24  
    25  	"golang.org/x/net/http2"
    26  	"golang.org/x/net/http2/hpack"
    27  )
    28  
    29  // Any is a Matcher that matches any connection.
    30  func Any() Matcher {
    31  	return func(r io.Reader) bool { return true }
    32  }
    33  
    34  // PrefixMatcher returns a matcher that matches a connection if it
    35  // starts with any of the strings in strs.
    36  func PrefixMatcher(strs ...string) Matcher {
    37  	pt := newPatriciaTreeString(strs...)
    38  	return pt.matchPrefix
    39  }
    40  
    41  func prefixByteMatcher(list ...[]byte) Matcher {
    42  	pt := newPatriciaTree(list...)
    43  	return pt.matchPrefix
    44  }
    45  
    46  var defaultHTTPMethods = []string{
    47  	"OPTIONS",
    48  	"GET",
    49  	"HEAD",
    50  	"POST",
    51  	"PUT",
    52  	"DELETE",
    53  	"TRACE",
    54  	"CONNECT",
    55  }
    56  
    57  // HTTP1Fast only matches the methods in the HTTP request.
    58  //
    59  // This matcher is very optimistic: if it returns true, it does not mean that
    60  // the request is a valid HTTP response. If you want a correct but slower HTTP1
    61  // matcher, use HTTP1 instead.
    62  func HTTP1Fast(extMethods ...string) Matcher {
    63  	return PrefixMatcher(append(defaultHTTPMethods, extMethods...)...)
    64  }
    65  
    66  // TLS matches HTTPS requests.
    67  //
    68  // By default, any TLS handshake packet is matched. An optional whitelist
    69  // of versions can be passed in to restrict the matcher, for example:
    70  //  TLS(tls.VersionTLS11, tls.VersionTLS12)
    71  func TLS(versions ...int) Matcher {
    72  	if len(versions) == 0 {
    73  		versions = []int{
    74  			tls.VersionSSL30,
    75  			tls.VersionTLS10,
    76  			tls.VersionTLS11,
    77  			tls.VersionTLS12,
    78  		}
    79  	}
    80  	prefixes := [][]byte{}
    81  	for _, v := range versions {
    82  		prefixes = append(prefixes, []byte{22, byte(v >> 8 & 0xff), byte(v & 0xff)})
    83  	}
    84  	return prefixByteMatcher(prefixes...)
    85  }
    86  
    87  const maxHTTPRead = 4096
    88  
    89  // HTTP1 parses the first line or upto 4096 bytes of the request to see if
    90  // the conection contains an HTTP request.
    91  func HTTP1() Matcher {
    92  	return func(r io.Reader) bool {
    93  		br := bufio.NewReader(&io.LimitedReader{R: r, N: maxHTTPRead})
    94  		l, part, err := br.ReadLine()
    95  		if err != nil || part {
    96  			return false
    97  		}
    98  
    99  		_, _, proto, ok := parseRequestLine(string(l))
   100  		if !ok {
   101  			return false
   102  		}
   103  
   104  		v, _, ok := http.ParseHTTPVersion(proto)
   105  		return ok && v == 1
   106  	}
   107  }
   108  
   109  // grabbed from net/http.
   110  func parseRequestLine(line string) (method, uri, proto string, ok bool) {
   111  	s1 := strings.Index(line, " ")
   112  	s2 := strings.Index(line[s1+1:], " ")
   113  	if s1 < 0 || s2 < 0 {
   114  		return
   115  	}
   116  	s2 += s1 + 1
   117  	return line[:s1], line[s1+1 : s2], line[s2+1:], true
   118  }
   119  
   120  // HTTP2 parses the frame header of the first frame to detect whether the
   121  // connection is an HTTP2 connection.
   122  func HTTP2() Matcher {
   123  	return hasHTTP2Preface
   124  }
   125  
   126  // HTTP1HeaderField returns a matcher matching the header fields of the first
   127  // request of an HTTP 1 connection.
   128  func HTTP1HeaderField(name, value string) Matcher {
   129  	return func(r io.Reader) bool {
   130  		return matchHTTP1Field(r, name, func(gotValue string) bool {
   131  			return gotValue == value
   132  		})
   133  	}
   134  }
   135  
   136  // HTTP1HeaderFieldPrefix returns a matcher matching the header fields of the
   137  // first request of an HTTP 1 connection. If the header with key name has a
   138  // value prefixed with valuePrefix, this will match.
   139  func HTTP1HeaderFieldPrefix(name, valuePrefix string) Matcher {
   140  	return func(r io.Reader) bool {
   141  		return matchHTTP1Field(r, name, func(gotValue string) bool {
   142  			return strings.HasPrefix(gotValue, valuePrefix)
   143  		})
   144  	}
   145  }
   146  
   147  // HTTP2HeaderField returns a matcher matching the header fields of the first
   148  // headers frame.
   149  func HTTP2HeaderField(name, value string) Matcher {
   150  	return func(r io.Reader) bool {
   151  		return matchHTTP2Field(ioutil.Discard, r, name, func(gotValue string) bool {
   152  			return gotValue == value
   153  		})
   154  	}
   155  }
   156  
   157  // HTTP2HeaderFieldPrefix returns a matcher matching the header fields of the
   158  // first headers frame. If the header with key name has a value prefixed with
   159  // valuePrefix, this will match.
   160  func HTTP2HeaderFieldPrefix(name, valuePrefix string) Matcher {
   161  	return func(r io.Reader) bool {
   162  		return matchHTTP2Field(ioutil.Discard, r, name, func(gotValue string) bool {
   163  			return strings.HasPrefix(gotValue, valuePrefix)
   164  		})
   165  	}
   166  }
   167  
   168  // HTTP2MatchHeaderFieldSendSettings matches the header field and writes the
   169  // settings to the server. Prefer HTTP2HeaderField over this one, if the client
   170  // does not block on receiving a SETTING frame.
   171  func HTTP2MatchHeaderFieldSendSettings(name, value string) MatchWriter {
   172  	return func(w io.Writer, r io.Reader) bool {
   173  		return matchHTTP2Field(w, r, name, func(gotValue string) bool {
   174  			return gotValue == value
   175  		})
   176  	}
   177  }
   178  
   179  // HTTP2MatchHeaderFieldPrefixSendSettings matches the header field prefix
   180  // and writes the settings to the server. Prefer HTTP2HeaderFieldPrefix over
   181  // this one, if the client does not block on receiving a SETTING frame.
   182  func HTTP2MatchHeaderFieldPrefixSendSettings(name, valuePrefix string) MatchWriter {
   183  	return func(w io.Writer, r io.Reader) bool {
   184  		return matchHTTP2Field(w, r, name, func(gotValue string) bool {
   185  			return strings.HasPrefix(gotValue, valuePrefix)
   186  		})
   187  	}
   188  }
   189  
   190  func hasHTTP2Preface(r io.Reader) bool {
   191  	var b [len(http2.ClientPreface)]byte
   192  	last := 0
   193  
   194  	for {
   195  		n, err := r.Read(b[last:])
   196  		if err != nil {
   197  			return false
   198  		}
   199  
   200  		last += n
   201  		eq := string(b[:last]) == http2.ClientPreface[:last]
   202  		if last == len(http2.ClientPreface) {
   203  			return eq
   204  		}
   205  		if !eq {
   206  			return false
   207  		}
   208  	}
   209  }
   210  
   211  func matchHTTP1Field(r io.Reader, name string, matches func(string) bool) (matched bool) {
   212  	req, err := http.ReadRequest(bufio.NewReader(r))
   213  	if err != nil {
   214  		return false
   215  	}
   216  
   217  	return matches(req.Header.Get(name))
   218  }
   219  
   220  func matchHTTP2Field(w io.Writer, r io.Reader, name string, matches func(string) bool) (matched bool) {
   221  	if !hasHTTP2Preface(r) {
   222  		return false
   223  	}
   224  
   225  	done := false
   226  	framer := http2.NewFramer(w, r)
   227  	hdec := hpack.NewDecoder(uint32(4<<10), func(hf hpack.HeaderField) {
   228  		if hf.Name == name {
   229  			done = true
   230  			if matches(hf.Value) {
   231  				matched = true
   232  			}
   233  		}
   234  	})
   235  	for {
   236  		f, err := framer.ReadFrame()
   237  		if err != nil {
   238  			return false
   239  		}
   240  
   241  		switch f := f.(type) {
   242  		case *http2.SettingsFrame:
   243  			// Sender acknoweldged the SETTINGS frame. No need to write
   244  			// SETTINGS again.
   245  			if f.IsAck() {
   246  				break
   247  			}
   248  			if err := framer.WriteSettings(); err != nil {
   249  				return false
   250  			}
   251  		case *http2.ContinuationFrame:
   252  			if _, err := hdec.Write(f.HeaderBlockFragment()); err != nil {
   253  				return false
   254  			}
   255  			done = done || f.FrameHeader.Flags&http2.FlagHeadersEndHeaders != 0
   256  		case *http2.HeadersFrame:
   257  			if _, err := hdec.Write(f.HeaderBlockFragment()); err != nil {
   258  				return false
   259  			}
   260  			done = done || f.FrameHeader.Flags&http2.FlagHeadersEndHeaders != 0
   261  		}
   262  
   263  		if done {
   264  			return matched
   265  		}
   266  	}
   267  }
   268  

View as plain text