mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
vendor: cockroachdb/cmux -> soheilhy/cmux
Official release is ahead of the fork.
This commit is contained in:
parent
35c5dcefc2
commit
603f84bb6d
@ -26,15 +26,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"project": "github.com/cockroachdb/cmux",
|
|
||||||
"licenses": [
|
|
||||||
{
|
|
||||||
"type": "Apache License 2.0",
|
|
||||||
"confidence": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"project": "github.com/coreos/bbolt",
|
"project": "github.com/coreos/bbolt",
|
||||||
"licenses": [
|
"licenses": [
|
||||||
@ -264,6 +255,15 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"project": "github.com/soheilhy/cmux",
|
||||||
|
"licenses": [
|
||||||
|
{
|
||||||
|
"type": "Apache License 2.0",
|
||||||
|
"confidence": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"project": "github.com/spf13/cobra",
|
"project": "github.com/spf13/cobra",
|
||||||
"licenses": [
|
"licenses": [
|
||||||
|
|||||||
35
cmd/vendor/github.com/cockroachdb/cmux/buffer.go
generated
vendored
35
cmd/vendor/github.com/cockroachdb/cmux/buffer.go
generated
vendored
@ -1,35 +0,0 @@
|
|||||||
package cmux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// bufferedReader is an optimized implementation of io.Reader that behaves like
|
|
||||||
// ```
|
|
||||||
// io.MultiReader(bytes.NewReader(buffer.Bytes()), io.TeeReader(source, buffer))
|
|
||||||
// ```
|
|
||||||
// without allocating.
|
|
||||||
type bufferedReader struct {
|
|
||||||
source io.Reader
|
|
||||||
buffer *bytes.Buffer
|
|
||||||
bufferRead int
|
|
||||||
bufferSize int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *bufferedReader) Read(p []byte) (int, error) {
|
|
||||||
// Functionality of bytes.Reader.
|
|
||||||
bn := copy(p, s.buffer.Bytes()[s.bufferRead:s.bufferSize])
|
|
||||||
s.bufferRead += bn
|
|
||||||
|
|
||||||
p = p[bn:]
|
|
||||||
|
|
||||||
// Funtionality of io.TeeReader.
|
|
||||||
sn, sErr := s.source.Read(p)
|
|
||||||
if sn > 0 {
|
|
||||||
if wn, wErr := s.buffer.Write(p[:sn]); wErr != nil {
|
|
||||||
return bn + wn, wErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bn + sn, sErr
|
|
||||||
}
|
|
||||||
150
cmd/vendor/github.com/cockroachdb/cmux/matchers.go
generated
vendored
150
cmd/vendor/github.com/cockroachdb/cmux/matchers.go
generated
vendored
@ -1,150 +0,0 @@
|
|||||||
package cmux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
|
||||||
"golang.org/x/net/http2/hpack"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Any is a Matcher that matches any connection.
|
|
||||||
func Any() Matcher {
|
|
||||||
return func(r io.Reader) bool { return true }
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrefixMatcher returns a matcher that matches a connection if it
|
|
||||||
// starts with any of the strings in strs.
|
|
||||||
func PrefixMatcher(strs ...string) Matcher {
|
|
||||||
pt := newPatriciaTreeString(strs...)
|
|
||||||
return pt.matchPrefix
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultHTTPMethods = []string{
|
|
||||||
"OPTIONS",
|
|
||||||
"GET",
|
|
||||||
"HEAD",
|
|
||||||
"POST",
|
|
||||||
"PUT",
|
|
||||||
"DELETE",
|
|
||||||
"TRACE",
|
|
||||||
"CONNECT",
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP1Fast only matches the methods in the HTTP request.
|
|
||||||
//
|
|
||||||
// This matcher is very optimistic: if it returns true, it does not mean that
|
|
||||||
// the request is a valid HTTP response. If you want a correct but slower HTTP1
|
|
||||||
// matcher, use HTTP1 instead.
|
|
||||||
func HTTP1Fast(extMethods ...string) Matcher {
|
|
||||||
return PrefixMatcher(append(defaultHTTPMethods, extMethods...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxHTTPRead = 4096
|
|
||||||
|
|
||||||
// HTTP1 parses the first line or upto 4096 bytes of the request to see if
|
|
||||||
// the conection contains an HTTP request.
|
|
||||||
func HTTP1() Matcher {
|
|
||||||
return func(r io.Reader) bool {
|
|
||||||
br := bufio.NewReader(&io.LimitedReader{R: r, N: maxHTTPRead})
|
|
||||||
l, part, err := br.ReadLine()
|
|
||||||
if err != nil || part {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, proto, ok := parseRequestLine(string(l))
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _, ok := http.ParseHTTPVersion(proto)
|
|
||||||
return ok && v == 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// grabbed from net/http.
|
|
||||||
func parseRequestLine(line string) (method, uri, proto string, ok bool) {
|
|
||||||
s1 := strings.Index(line, " ")
|
|
||||||
s2 := strings.Index(line[s1+1:], " ")
|
|
||||||
if s1 < 0 || s2 < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s2 += s1 + 1
|
|
||||||
return line[:s1], line[s1+1 : s2], line[s2+1:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP2 parses the frame header of the first frame to detect whether the
|
|
||||||
// connection is an HTTP2 connection.
|
|
||||||
func HTTP2() Matcher {
|
|
||||||
return hasHTTP2Preface
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP1HeaderField returns a matcher matching the header fields of the first
|
|
||||||
// request of an HTTP 1 connection.
|
|
||||||
func HTTP1HeaderField(name, value string) Matcher {
|
|
||||||
return func(r io.Reader) bool {
|
|
||||||
return matchHTTP1Field(r, name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP2HeaderField resturns a matcher matching the header fields of the first
|
|
||||||
// headers frame.
|
|
||||||
func HTTP2HeaderField(name, value string) Matcher {
|
|
||||||
return func(r io.Reader) bool {
|
|
||||||
return matchHTTP2Field(r, name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasHTTP2Preface(r io.Reader) bool {
|
|
||||||
var b [len(http2.ClientPreface)]byte
|
|
||||||
if _, err := io.ReadFull(r, b[:]); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(b[:]) == http2.ClientPreface
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchHTTP1Field(r io.Reader, name, value string) (matched bool) {
|
|
||||||
req, err := http.ReadRequest(bufio.NewReader(r))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return req.Header.Get(name) == value
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchHTTP2Field(r io.Reader, name, value string) (matched bool) {
|
|
||||||
if !hasHTTP2Preface(r) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
framer := http2.NewFramer(ioutil.Discard, r)
|
|
||||||
hdec := hpack.NewDecoder(uint32(4<<10), func(hf hpack.HeaderField) {
|
|
||||||
if hf.Name == name && hf.Value == value {
|
|
||||||
matched = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
for {
|
|
||||||
f, err := framer.ReadFrame()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch f := f.(type) {
|
|
||||||
case *http2.HeadersFrame:
|
|
||||||
if _, err := hdec.Write(f.HeaderBlockFragment()); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if matched {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.FrameHeader.Flags&http2.FlagHeadersEndHeaders != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
67
cmd/vendor/github.com/soheilhy/cmux/buffer.go
generated
vendored
Normal file
67
cmd/vendor/github.com/soheilhy/cmux/buffer.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2016 The CMux Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 cmux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// bufferedReader is an optimized implementation of io.Reader that behaves like
|
||||||
|
// ```
|
||||||
|
// io.MultiReader(bytes.NewReader(buffer.Bytes()), io.TeeReader(source, buffer))
|
||||||
|
// ```
|
||||||
|
// without allocating.
|
||||||
|
type bufferedReader struct {
|
||||||
|
source io.Reader
|
||||||
|
buffer bytes.Buffer
|
||||||
|
bufferRead int
|
||||||
|
bufferSize int
|
||||||
|
sniffing bool
|
||||||
|
lastErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *bufferedReader) Read(p []byte) (int, error) {
|
||||||
|
if s.bufferSize > s.bufferRead {
|
||||||
|
// If we have already read something from the buffer before, we return the
|
||||||
|
// same data and the last error if any. We need to immediately return,
|
||||||
|
// otherwise we may block for ever, if we try to be smart and call
|
||||||
|
// source.Read() seeking a little bit of more data.
|
||||||
|
bn := copy(p, s.buffer.Bytes()[s.bufferRead:s.bufferSize])
|
||||||
|
s.bufferRead += bn
|
||||||
|
return bn, s.lastErr
|
||||||
|
} else if !s.sniffing && s.buffer.Cap() != 0 {
|
||||||
|
// We don't need the buffer anymore.
|
||||||
|
// Reset it to release the internal slice.
|
||||||
|
s.buffer = bytes.Buffer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is nothing more to return in the sniffed buffer, read from the
|
||||||
|
// source.
|
||||||
|
sn, sErr := s.source.Read(p)
|
||||||
|
if sn > 0 && s.sniffing {
|
||||||
|
s.lastErr = sErr
|
||||||
|
if wn, wErr := s.buffer.Write(p[:sn]); wErr != nil {
|
||||||
|
return wn, wErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sn, sErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *bufferedReader) reset(snif bool) {
|
||||||
|
s.sniffing = snif
|
||||||
|
s.bufferRead = 0
|
||||||
|
s.bufferSize = s.buffer.Len()
|
||||||
|
}
|
||||||
101
cmd/vendor/github.com/cockroachdb/cmux/cmux.go → cmd/vendor/github.com/soheilhy/cmux/cmux.go
generated
vendored
101
cmd/vendor/github.com/cockroachdb/cmux/cmux.go → cmd/vendor/github.com/soheilhy/cmux/cmux.go
generated
vendored
@ -1,16 +1,33 @@
|
|||||||
|
// Copyright 2016 The CMux Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 cmux
|
package cmux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Matcher matches a connection based on its content.
|
// Matcher matches a connection based on its content.
|
||||||
type Matcher func(io.Reader) bool
|
type Matcher func(io.Reader) bool
|
||||||
|
|
||||||
|
// MatchWriter is a match that can also write response (say to do handshake).
|
||||||
|
type MatchWriter func(io.Writer, io.Reader) bool
|
||||||
|
|
||||||
// ErrorHandler handles an error and returns whether
|
// ErrorHandler handles an error and returns whether
|
||||||
// the mux should continue serving the listener.
|
// the mux should continue serving the listener.
|
||||||
type ErrorHandler func(error) bool
|
type ErrorHandler func(error) bool
|
||||||
@ -44,13 +61,17 @@ func (e errListenerClosed) Timeout() bool { return false }
|
|||||||
// listener is closed.
|
// listener is closed.
|
||||||
var ErrListenerClosed = errListenerClosed("mux: listener closed")
|
var ErrListenerClosed = errListenerClosed("mux: listener closed")
|
||||||
|
|
||||||
|
// for readability of readTimeout
|
||||||
|
var noTimeout time.Duration
|
||||||
|
|
||||||
// New instantiates a new connection multiplexer.
|
// New instantiates a new connection multiplexer.
|
||||||
func New(l net.Listener) CMux {
|
func New(l net.Listener) CMux {
|
||||||
return &cMux{
|
return &cMux{
|
||||||
root: l,
|
root: l,
|
||||||
bufLen: 1024,
|
bufLen: 1024,
|
||||||
errh: func(_ error) bool { return true },
|
errh: func(_ error) bool { return true },
|
||||||
donec: make(chan struct{}),
|
donec: make(chan struct{}),
|
||||||
|
readTimeout: noTimeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,27 +82,53 @@ type CMux interface {
|
|||||||
//
|
//
|
||||||
// The order used to call Match determines the priority of matchers.
|
// The order used to call Match determines the priority of matchers.
|
||||||
Match(...Matcher) net.Listener
|
Match(...Matcher) net.Listener
|
||||||
|
// MatchWithWriters returns a net.Listener that accepts only the
|
||||||
|
// connections that matched by at least of the matcher writers.
|
||||||
|
//
|
||||||
|
// Prefer Matchers over MatchWriters, since the latter can write on the
|
||||||
|
// connection before the actual handler.
|
||||||
|
//
|
||||||
|
// The order used to call Match determines the priority of matchers.
|
||||||
|
MatchWithWriters(...MatchWriter) net.Listener
|
||||||
// Serve starts multiplexing the listener. Serve blocks and perhaps
|
// Serve starts multiplexing the listener. Serve blocks and perhaps
|
||||||
// should be invoked concurrently within a go routine.
|
// should be invoked concurrently within a go routine.
|
||||||
Serve() error
|
Serve() error
|
||||||
// HandleError registers an error handler that handles listener errors.
|
// HandleError registers an error handler that handles listener errors.
|
||||||
HandleError(ErrorHandler)
|
HandleError(ErrorHandler)
|
||||||
|
// sets a timeout for the read of matchers
|
||||||
|
SetReadTimeout(time.Duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
type matchersListener struct {
|
type matchersListener struct {
|
||||||
ss []Matcher
|
ss []MatchWriter
|
||||||
l muxListener
|
l muxListener
|
||||||
}
|
}
|
||||||
|
|
||||||
type cMux struct {
|
type cMux struct {
|
||||||
root net.Listener
|
root net.Listener
|
||||||
bufLen int
|
bufLen int
|
||||||
errh ErrorHandler
|
errh ErrorHandler
|
||||||
donec chan struct{}
|
donec chan struct{}
|
||||||
sls []matchersListener
|
sls []matchersListener
|
||||||
|
readTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchersToMatchWriters(matchers []Matcher) []MatchWriter {
|
||||||
|
mws := make([]MatchWriter, 0, len(matchers))
|
||||||
|
for _, m := range matchers {
|
||||||
|
mws = append(mws, func(w io.Writer, r io.Reader) bool {
|
||||||
|
return m(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return mws
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *cMux) Match(matchers ...Matcher) net.Listener {
|
func (m *cMux) Match(matchers ...Matcher) net.Listener {
|
||||||
|
mws := matchersToMatchWriters(matchers)
|
||||||
|
return m.MatchWithWriters(mws...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *cMux) MatchWithWriters(matchers ...MatchWriter) net.Listener {
|
||||||
ml := muxListener{
|
ml := muxListener{
|
||||||
Listener: m.root,
|
Listener: m.root,
|
||||||
connc: make(chan net.Conn, m.bufLen),
|
connc: make(chan net.Conn, m.bufLen),
|
||||||
@ -90,6 +137,10 @@ func (m *cMux) Match(matchers ...Matcher) net.Listener {
|
|||||||
return ml
|
return ml
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *cMux) SetReadTimeout(t time.Duration) {
|
||||||
|
m.readTimeout = t
|
||||||
|
}
|
||||||
|
|
||||||
func (m *cMux) Serve() error {
|
func (m *cMux) Serve() error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
@ -124,10 +175,17 @@ func (m *cMux) serve(c net.Conn, donec <-chan struct{}, wg *sync.WaitGroup) {
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
muc := newMuxConn(c)
|
muc := newMuxConn(c)
|
||||||
|
if m.readTimeout > noTimeout {
|
||||||
|
_ = c.SetReadDeadline(time.Now().Add(m.readTimeout))
|
||||||
|
}
|
||||||
for _, sl := range m.sls {
|
for _, sl := range m.sls {
|
||||||
for _, s := range sl.ss {
|
for _, s := range sl.ss {
|
||||||
matched := s(muc.getSniffer())
|
matched := s(muc.Conn, muc.startSniffing())
|
||||||
if matched {
|
if matched {
|
||||||
|
muc.doneSniffing()
|
||||||
|
if m.readTimeout > noTimeout {
|
||||||
|
_ = c.SetReadDeadline(time.Time{})
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case sl.l.connc <- muc:
|
case sl.l.connc <- muc:
|
||||||
case <-donec:
|
case <-donec:
|
||||||
@ -177,13 +235,13 @@ func (l muxListener) Accept() (net.Conn, error) {
|
|||||||
// MuxConn wraps a net.Conn and provides transparent sniffing of connection data.
|
// MuxConn wraps a net.Conn and provides transparent sniffing of connection data.
|
||||||
type MuxConn struct {
|
type MuxConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
buf bytes.Buffer
|
buf bufferedReader
|
||||||
sniffer bufferedReader
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMuxConn(c net.Conn) *MuxConn {
|
func newMuxConn(c net.Conn) *MuxConn {
|
||||||
return &MuxConn{
|
return &MuxConn{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
|
buf: bufferedReader{source: c},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,13 +256,14 @@ func newMuxConn(c net.Conn) *MuxConn {
|
|||||||
// return either err == EOF or err == nil. The next Read should
|
// return either err == EOF or err == nil. The next Read should
|
||||||
// return 0, EOF.
|
// return 0, EOF.
|
||||||
func (m *MuxConn) Read(p []byte) (int, error) {
|
func (m *MuxConn) Read(p []byte) (int, error) {
|
||||||
if n, err := m.buf.Read(p); err != io.EOF {
|
return m.buf.Read(p)
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
return m.Conn.Read(p)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MuxConn) getSniffer() io.Reader {
|
func (m *MuxConn) startSniffing() io.Reader {
|
||||||
m.sniffer = bufferedReader{source: m.Conn, buffer: &m.buf, bufferSize: m.buf.Len()}
|
m.buf.reset(true)
|
||||||
return &m.sniffer
|
return &m.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MuxConn) doneSniffing() {
|
||||||
|
m.buf.reset(false)
|
||||||
}
|
}
|
||||||
18
cmd/vendor/github.com/soheilhy/cmux/doc.go
generated
vendored
Normal file
18
cmd/vendor/github.com/soheilhy/cmux/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2016 The CMux Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 cmux is a library to multiplex network connections based on
|
||||||
|
// their payload. Using cmux, you can serve different protocols from the
|
||||||
|
// same listener.
|
||||||
|
package cmux
|
||||||
262
cmd/vendor/github.com/soheilhy/cmux/matchers.go
generated
vendored
Normal file
262
cmd/vendor/github.com/soheilhy/cmux/matchers.go
generated
vendored
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
// Copyright 2016 The CMux Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 cmux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/hpack"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Any is a Matcher that matches any connection.
|
||||||
|
func Any() Matcher {
|
||||||
|
return func(r io.Reader) bool { return true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefixMatcher returns a matcher that matches a connection if it
|
||||||
|
// starts with any of the strings in strs.
|
||||||
|
func PrefixMatcher(strs ...string) Matcher {
|
||||||
|
pt := newPatriciaTreeString(strs...)
|
||||||
|
return pt.matchPrefix
|
||||||
|
}
|
||||||
|
|
||||||
|
func prefixByteMatcher(list ...[]byte) Matcher {
|
||||||
|
pt := newPatriciaTree(list...)
|
||||||
|
return pt.matchPrefix
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultHTTPMethods = []string{
|
||||||
|
"OPTIONS",
|
||||||
|
"GET",
|
||||||
|
"HEAD",
|
||||||
|
"POST",
|
||||||
|
"PUT",
|
||||||
|
"DELETE",
|
||||||
|
"TRACE",
|
||||||
|
"CONNECT",
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP1Fast only matches the methods in the HTTP request.
|
||||||
|
//
|
||||||
|
// This matcher is very optimistic: if it returns true, it does not mean that
|
||||||
|
// the request is a valid HTTP response. If you want a correct but slower HTTP1
|
||||||
|
// matcher, use HTTP1 instead.
|
||||||
|
func HTTP1Fast(extMethods ...string) Matcher {
|
||||||
|
return PrefixMatcher(append(defaultHTTPMethods, extMethods...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLS matches HTTPS requests.
|
||||||
|
//
|
||||||
|
// By default, any TLS handshake packet is matched. An optional whitelist
|
||||||
|
// of versions can be passed in to restrict the matcher, for example:
|
||||||
|
// TLS(tls.VersionTLS11, tls.VersionTLS12)
|
||||||
|
func TLS(versions ...int) Matcher {
|
||||||
|
if len(versions) == 0 {
|
||||||
|
versions = []int{
|
||||||
|
tls.VersionSSL30,
|
||||||
|
tls.VersionTLS10,
|
||||||
|
tls.VersionTLS11,
|
||||||
|
tls.VersionTLS12,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefixes := [][]byte{}
|
||||||
|
for _, v := range versions {
|
||||||
|
prefixes = append(prefixes, []byte{22, byte(v >> 8 & 0xff), byte(v & 0xff)})
|
||||||
|
}
|
||||||
|
return prefixByteMatcher(prefixes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxHTTPRead = 4096
|
||||||
|
|
||||||
|
// HTTP1 parses the first line or upto 4096 bytes of the request to see if
|
||||||
|
// the conection contains an HTTP request.
|
||||||
|
func HTTP1() Matcher {
|
||||||
|
return func(r io.Reader) bool {
|
||||||
|
br := bufio.NewReader(&io.LimitedReader{R: r, N: maxHTTPRead})
|
||||||
|
l, part, err := br.ReadLine()
|
||||||
|
if err != nil || part {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, proto, ok := parseRequestLine(string(l))
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _, ok := http.ParseHTTPVersion(proto)
|
||||||
|
return ok && v == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// grabbed from net/http.
|
||||||
|
func parseRequestLine(line string) (method, uri, proto string, ok bool) {
|
||||||
|
s1 := strings.Index(line, " ")
|
||||||
|
s2 := strings.Index(line[s1+1:], " ")
|
||||||
|
if s1 < 0 || s2 < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s2 += s1 + 1
|
||||||
|
return line[:s1], line[s1+1 : s2], line[s2+1:], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2 parses the frame header of the first frame to detect whether the
|
||||||
|
// connection is an HTTP2 connection.
|
||||||
|
func HTTP2() Matcher {
|
||||||
|
return hasHTTP2Preface
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP1HeaderField returns a matcher matching the header fields of the first
|
||||||
|
// request of an HTTP 1 connection.
|
||||||
|
func HTTP1HeaderField(name, value string) Matcher {
|
||||||
|
return func(r io.Reader) bool {
|
||||||
|
return matchHTTP1Field(r, name, func(gotValue string) bool {
|
||||||
|
return gotValue == value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP1HeaderFieldPrefix returns a matcher matching the header fields of the
|
||||||
|
// first request of an HTTP 1 connection. If the header with key name has a
|
||||||
|
// value prefixed with valuePrefix, this will match.
|
||||||
|
func HTTP1HeaderFieldPrefix(name, valuePrefix string) Matcher {
|
||||||
|
return func(r io.Reader) bool {
|
||||||
|
return matchHTTP1Field(r, name, func(gotValue string) bool {
|
||||||
|
return strings.HasPrefix(gotValue, valuePrefix)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2HeaderField returns a matcher matching the header fields of the first
|
||||||
|
// headers frame.
|
||||||
|
func HTTP2HeaderField(name, value string) Matcher {
|
||||||
|
return func(r io.Reader) bool {
|
||||||
|
return matchHTTP2Field(ioutil.Discard, r, name, func(gotValue string) bool {
|
||||||
|
return gotValue == value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2HeaderFieldPrefix returns a matcher matching the header fields of the
|
||||||
|
// first headers frame. If the header with key name has a value prefixed with
|
||||||
|
// valuePrefix, this will match.
|
||||||
|
func HTTP2HeaderFieldPrefix(name, valuePrefix string) Matcher {
|
||||||
|
return func(r io.Reader) bool {
|
||||||
|
return matchHTTP2Field(ioutil.Discard, r, name, func(gotValue string) bool {
|
||||||
|
return strings.HasPrefix(gotValue, valuePrefix)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2MatchHeaderFieldSendSettings matches the header field and writes the
|
||||||
|
// settings to the server. Prefer HTTP2HeaderField over this one, if the client
|
||||||
|
// does not block on receiving a SETTING frame.
|
||||||
|
func HTTP2MatchHeaderFieldSendSettings(name, value string) MatchWriter {
|
||||||
|
return func(w io.Writer, r io.Reader) bool {
|
||||||
|
return matchHTTP2Field(w, r, name, func(gotValue string) bool {
|
||||||
|
return gotValue == value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2MatchHeaderFieldPrefixSendSettings matches the header field prefix
|
||||||
|
// and writes the settings to the server. Prefer HTTP2HeaderFieldPrefix over
|
||||||
|
// this one, if the client does not block on receiving a SETTING frame.
|
||||||
|
func HTTP2MatchHeaderFieldPrefixSendSettings(name, valuePrefix string) MatchWriter {
|
||||||
|
return func(w io.Writer, r io.Reader) bool {
|
||||||
|
return matchHTTP2Field(w, r, name, func(gotValue string) bool {
|
||||||
|
return strings.HasPrefix(gotValue, valuePrefix)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasHTTP2Preface(r io.Reader) bool {
|
||||||
|
var b [len(http2.ClientPreface)]byte
|
||||||
|
last := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := r.Read(b[last:])
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
last += n
|
||||||
|
eq := string(b[:last]) == http2.ClientPreface[:last]
|
||||||
|
if last == len(http2.ClientPreface) {
|
||||||
|
return eq
|
||||||
|
}
|
||||||
|
if !eq {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchHTTP1Field(r io.Reader, name string, matches func(string) bool) (matched bool) {
|
||||||
|
req, err := http.ReadRequest(bufio.NewReader(r))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches(req.Header.Get(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchHTTP2Field(w io.Writer, r io.Reader, name string, matches func(string) bool) (matched bool) {
|
||||||
|
if !hasHTTP2Preface(r) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
done := false
|
||||||
|
framer := http2.NewFramer(w, r)
|
||||||
|
hdec := hpack.NewDecoder(uint32(4<<10), func(hf hpack.HeaderField) {
|
||||||
|
if hf.Name == name {
|
||||||
|
done = true
|
||||||
|
if matches(hf.Value) {
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
for {
|
||||||
|
f, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch f := f.(type) {
|
||||||
|
case *http2.SettingsFrame:
|
||||||
|
if err := framer.WriteSettings(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case *http2.ContinuationFrame:
|
||||||
|
if _, err := hdec.Write(f.HeaderBlockFragment()); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
done = done || f.FrameHeader.Flags&http2.FlagHeadersEndHeaders != 0
|
||||||
|
case *http2.HeadersFrame:
|
||||||
|
if _, err := hdec.Write(f.HeaderBlockFragment()); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
done = done || f.FrameHeader.Flags&http2.FlagHeadersEndHeaders != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if done {
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,17 @@
|
|||||||
|
// Copyright 2016 The CMux Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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 cmux
|
package cmux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -8,12 +22,20 @@ import (
|
|||||||
// patriciaTree is a simple patricia tree that handles []byte instead of string
|
// patriciaTree is a simple patricia tree that handles []byte instead of string
|
||||||
// and cannot be changed after instantiation.
|
// and cannot be changed after instantiation.
|
||||||
type patriciaTree struct {
|
type patriciaTree struct {
|
||||||
root *ptNode
|
root *ptNode
|
||||||
|
maxDepth int // max depth of the tree.
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPatriciaTree(b ...[]byte) *patriciaTree {
|
func newPatriciaTree(bs ...[]byte) *patriciaTree {
|
||||||
|
max := 0
|
||||||
|
for _, b := range bs {
|
||||||
|
if max < len(b) {
|
||||||
|
max = len(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
return &patriciaTree{
|
return &patriciaTree{
|
||||||
root: newNode(b),
|
root: newNode(bs),
|
||||||
|
maxDepth: max + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,17 +44,19 @@ func newPatriciaTreeString(strs ...string) *patriciaTree {
|
|||||||
for i, s := range strs {
|
for i, s := range strs {
|
||||||
b[i] = []byte(s)
|
b[i] = []byte(s)
|
||||||
}
|
}
|
||||||
return &patriciaTree{
|
return newPatriciaTree(b...)
|
||||||
root: newNode(b),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *patriciaTree) matchPrefix(r io.Reader) bool {
|
func (t *patriciaTree) matchPrefix(r io.Reader) bool {
|
||||||
return t.root.match(r, true)
|
buf := make([]byte, t.maxDepth)
|
||||||
|
n, _ := io.ReadFull(r, buf)
|
||||||
|
return t.root.match(buf[:n], true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *patriciaTree) match(r io.Reader) bool {
|
func (t *patriciaTree) match(r io.Reader) bool {
|
||||||
return t.root.match(r, false)
|
buf := make([]byte, t.maxDepth)
|
||||||
|
n, _ := io.ReadFull(r, buf)
|
||||||
|
return t.root.match(buf[:n], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ptNode struct {
|
type ptNode struct {
|
||||||
@ -122,52 +146,34 @@ func splitPrefix(bss [][]byte) (prefix []byte, rest [][]byte) {
|
|||||||
return prefix, rest
|
return prefix, rest
|
||||||
}
|
}
|
||||||
|
|
||||||
func readBytes(r io.Reader, n int) (b []byte, err error) {
|
func (n *ptNode) match(b []byte, prefix bool) bool {
|
||||||
b = make([]byte, n)
|
l := len(n.prefix)
|
||||||
o := 0
|
if l > 0 {
|
||||||
for o < n {
|
if l > len(b) {
|
||||||
nr, err := r.Read(b[o:])
|
l = len(b)
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return b, err
|
|
||||||
}
|
}
|
||||||
|
if !bytes.Equal(b[:l], n.prefix) {
|
||||||
o += nr
|
|
||||||
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b[:o], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *ptNode) match(r io.Reader, prefix bool) bool {
|
|
||||||
if l := len(n.prefix); l > 0 {
|
|
||||||
b, err := readBytes(r, l)
|
|
||||||
if err != nil || len(b) != l || !bytes.Equal(b, n.prefix) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if prefix && n.terminal {
|
if n.terminal && (prefix || len(n.prefix) == len(b)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
b := make([]byte, 1)
|
if l >= len(b) {
|
||||||
for {
|
return false
|
||||||
nr, err := r.Read(b)
|
|
||||||
if nr != 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == io.EOF {
|
|
||||||
return n.terminal
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextN, ok := n.next[b[0]]
|
nextN, ok := n.next[b[l]]
|
||||||
return ok && nextN.match(r, prefix)
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if l == len(b) {
|
||||||
|
b = b[l:l]
|
||||||
|
} else {
|
||||||
|
b = b[l+1:]
|
||||||
|
}
|
||||||
|
return nextN.match(b, prefix)
|
||||||
}
|
}
|
||||||
2
glide.lock
generated
2
glide.lock
generated
@ -97,6 +97,8 @@ imports:
|
|||||||
- xfs
|
- xfs
|
||||||
- name: github.com/russross/blackfriday
|
- name: github.com/russross/blackfriday
|
||||||
version: 4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c
|
version: 4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c
|
||||||
|
- name: github.com/soheilhy/cmux
|
||||||
|
version: bb79a83465015a27a175925ebd155e660f55e9f1
|
||||||
- name: github.com/spf13/cobra
|
- name: github.com/spf13/cobra
|
||||||
version: 1c44ec8d3f1552cac48999f9306da23c4d8a288b
|
version: 1c44ec8d3f1552cac48999f9306da23c4d8a288b
|
||||||
- name: github.com/spf13/pflag
|
- name: github.com/spf13/pflag
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
package: github.com/coreos/etcd
|
package: github.com/coreos/etcd
|
||||||
|
ignore:
|
||||||
|
- google.golang.org/appengine
|
||||||
import:
|
import:
|
||||||
- package: github.com/bgentry/speakeasy
|
- package: github.com/bgentry/speakeasy
|
||||||
version: v0.1.0
|
version: v0.1.0
|
||||||
@ -128,5 +130,5 @@ import:
|
|||||||
- unicode/norm
|
- unicode/norm
|
||||||
- package: github.com/russross/blackfriday
|
- package: github.com/russross/blackfriday
|
||||||
version: 4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c
|
version: 4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c
|
||||||
ignore:
|
- package: github.com/soheilhy/cmux
|
||||||
- google.golang.org/appengine
|
version: v0.1.3
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user