From 603f84bb6d81c5aa7dd972a03ae555a23f59926e Mon Sep 17 00:00:00 2001 From: Anthony Romano Date: Sat, 19 Aug 2017 19:19:40 -0700 Subject: [PATCH] vendor: cockroachdb/cmux -> soheilhy/cmux Official release is ahead of the fork. --- bill-of-materials.json | 18 +- .../github.com/cockroachdb/cmux/buffer.go | 35 --- .../github.com/cockroachdb/cmux/matchers.go | 150 ---------- .../{cockroachdb => soheilhy}/cmux/LICENSE | 0 cmd/vendor/github.com/soheilhy/cmux/buffer.go | 67 +++++ .../{cockroachdb => soheilhy}/cmux/cmux.go | 101 +++++-- cmd/vendor/github.com/soheilhy/cmux/doc.go | 18 ++ .../github.com/soheilhy/cmux/matchers.go | 262 ++++++++++++++++++ .../cmux/patricia.go | 96 ++++--- glide.lock | 2 + glide.yaml | 6 +- 11 files changed, 493 insertions(+), 262 deletions(-) delete mode 100644 cmd/vendor/github.com/cockroachdb/cmux/buffer.go delete mode 100644 cmd/vendor/github.com/cockroachdb/cmux/matchers.go rename cmd/vendor/github.com/{cockroachdb => soheilhy}/cmux/LICENSE (100%) create mode 100644 cmd/vendor/github.com/soheilhy/cmux/buffer.go rename cmd/vendor/github.com/{cockroachdb => soheilhy}/cmux/cmux.go (62%) create mode 100644 cmd/vendor/github.com/soheilhy/cmux/doc.go create mode 100644 cmd/vendor/github.com/soheilhy/cmux/matchers.go rename cmd/vendor/github.com/{cockroachdb => soheilhy}/cmux/patricia.go (55%) diff --git a/bill-of-materials.json b/bill-of-materials.json index 20fc5200228..cd518ece334 100644 --- a/bill-of-materials.json +++ b/bill-of-materials.json @@ -26,15 +26,6 @@ } ] }, - { - "project": "github.com/cockroachdb/cmux", - "licenses": [ - { - "type": "Apache License 2.0", - "confidence": 1 - } - ] - }, { "project": "github.com/coreos/bbolt", "licenses": [ @@ -264,6 +255,15 @@ } ] }, + { + "project": "github.com/soheilhy/cmux", + "licenses": [ + { + "type": "Apache License 2.0", + "confidence": 1 + } + ] + }, { "project": "github.com/spf13/cobra", "licenses": [ diff --git a/cmd/vendor/github.com/cockroachdb/cmux/buffer.go b/cmd/vendor/github.com/cockroachdb/cmux/buffer.go deleted file mode 100644 index 5c178585363..00000000000 --- a/cmd/vendor/github.com/cockroachdb/cmux/buffer.go +++ /dev/null @@ -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 -} diff --git a/cmd/vendor/github.com/cockroachdb/cmux/matchers.go b/cmd/vendor/github.com/cockroachdb/cmux/matchers.go deleted file mode 100644 index abc30f6e0ad..00000000000 --- a/cmd/vendor/github.com/cockroachdb/cmux/matchers.go +++ /dev/null @@ -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 - } - } - } -} diff --git a/cmd/vendor/github.com/cockroachdb/cmux/LICENSE b/cmd/vendor/github.com/soheilhy/cmux/LICENSE similarity index 100% rename from cmd/vendor/github.com/cockroachdb/cmux/LICENSE rename to cmd/vendor/github.com/soheilhy/cmux/LICENSE diff --git a/cmd/vendor/github.com/soheilhy/cmux/buffer.go b/cmd/vendor/github.com/soheilhy/cmux/buffer.go new file mode 100644 index 00000000000..f8cf30a1e66 --- /dev/null +++ b/cmd/vendor/github.com/soheilhy/cmux/buffer.go @@ -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() +} diff --git a/cmd/vendor/github.com/cockroachdb/cmux/cmux.go b/cmd/vendor/github.com/soheilhy/cmux/cmux.go similarity index 62% rename from cmd/vendor/github.com/cockroachdb/cmux/cmux.go rename to cmd/vendor/github.com/soheilhy/cmux/cmux.go index 89cc910b024..9de6b0a3c2a 100644 --- a/cmd/vendor/github.com/cockroachdb/cmux/cmux.go +++ b/cmd/vendor/github.com/soheilhy/cmux/cmux.go @@ -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 import ( - "bytes" "fmt" "io" "net" "sync" + "time" ) // Matcher matches a connection based on its content. 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 // the mux should continue serving the listener. type ErrorHandler func(error) bool @@ -44,13 +61,17 @@ func (e errListenerClosed) Timeout() bool { return false } // listener is closed. var ErrListenerClosed = errListenerClosed("mux: listener closed") +// for readability of readTimeout +var noTimeout time.Duration + // New instantiates a new connection multiplexer. func New(l net.Listener) CMux { return &cMux{ - root: l, - bufLen: 1024, - errh: func(_ error) bool { return true }, - donec: make(chan struct{}), + root: l, + bufLen: 1024, + errh: func(_ error) bool { return true }, + donec: make(chan struct{}), + readTimeout: noTimeout, } } @@ -61,27 +82,53 @@ type CMux interface { // // The order used to call Match determines the priority of matchers. 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 // should be invoked concurrently within a go routine. Serve() error // HandleError registers an error handler that handles listener errors. HandleError(ErrorHandler) + // sets a timeout for the read of matchers + SetReadTimeout(time.Duration) } type matchersListener struct { - ss []Matcher + ss []MatchWriter l muxListener } type cMux struct { - root net.Listener - bufLen int - errh ErrorHandler - donec chan struct{} - sls []matchersListener + root net.Listener + bufLen int + errh ErrorHandler + donec chan struct{} + 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 { + mws := matchersToMatchWriters(matchers) + return m.MatchWithWriters(mws...) +} + +func (m *cMux) MatchWithWriters(matchers ...MatchWriter) net.Listener { ml := muxListener{ Listener: m.root, connc: make(chan net.Conn, m.bufLen), @@ -90,6 +137,10 @@ func (m *cMux) Match(matchers ...Matcher) net.Listener { return ml } +func (m *cMux) SetReadTimeout(t time.Duration) { + m.readTimeout = t +} + func (m *cMux) Serve() error { var wg sync.WaitGroup @@ -124,10 +175,17 @@ func (m *cMux) serve(c net.Conn, donec <-chan struct{}, wg *sync.WaitGroup) { defer wg.Done() muc := newMuxConn(c) + if m.readTimeout > noTimeout { + _ = c.SetReadDeadline(time.Now().Add(m.readTimeout)) + } for _, sl := range m.sls { for _, s := range sl.ss { - matched := s(muc.getSniffer()) + matched := s(muc.Conn, muc.startSniffing()) if matched { + muc.doneSniffing() + if m.readTimeout > noTimeout { + _ = c.SetReadDeadline(time.Time{}) + } select { case sl.l.connc <- muc: 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. type MuxConn struct { net.Conn - buf bytes.Buffer - sniffer bufferedReader + buf bufferedReader } func newMuxConn(c net.Conn) *MuxConn { return &MuxConn{ 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 0, EOF. func (m *MuxConn) Read(p []byte) (int, error) { - if n, err := m.buf.Read(p); err != io.EOF { - return n, err - } - return m.Conn.Read(p) + return m.buf.Read(p) +} + +func (m *MuxConn) startSniffing() io.Reader { + m.buf.reset(true) + return &m.buf } -func (m *MuxConn) getSniffer() io.Reader { - m.sniffer = bufferedReader{source: m.Conn, buffer: &m.buf, bufferSize: m.buf.Len()} - return &m.sniffer +func (m *MuxConn) doneSniffing() { + m.buf.reset(false) } diff --git a/cmd/vendor/github.com/soheilhy/cmux/doc.go b/cmd/vendor/github.com/soheilhy/cmux/doc.go new file mode 100644 index 00000000000..aaa8f315899 --- /dev/null +++ b/cmd/vendor/github.com/soheilhy/cmux/doc.go @@ -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 diff --git a/cmd/vendor/github.com/soheilhy/cmux/matchers.go b/cmd/vendor/github.com/soheilhy/cmux/matchers.go new file mode 100644 index 00000000000..652fd869178 --- /dev/null +++ b/cmd/vendor/github.com/soheilhy/cmux/matchers.go @@ -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 + } + } +} diff --git a/cmd/vendor/github.com/cockroachdb/cmux/patricia.go b/cmd/vendor/github.com/soheilhy/cmux/patricia.go similarity index 55% rename from cmd/vendor/github.com/cockroachdb/cmux/patricia.go rename to cmd/vendor/github.com/soheilhy/cmux/patricia.go index 56ec4e7b287..c3e3d85bdea 100644 --- a/cmd/vendor/github.com/cockroachdb/cmux/patricia.go +++ b/cmd/vendor/github.com/soheilhy/cmux/patricia.go @@ -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 import ( @@ -8,12 +22,20 @@ import ( // patriciaTree is a simple patricia tree that handles []byte instead of string // and cannot be changed after instantiation. 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{ - root: newNode(b), + root: newNode(bs), + maxDepth: max + 1, } } @@ -22,17 +44,19 @@ func newPatriciaTreeString(strs ...string) *patriciaTree { for i, s := range strs { b[i] = []byte(s) } - return &patriciaTree{ - root: newNode(b), - } + return newPatriciaTree(b...) } 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 { - 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 { @@ -122,52 +146,34 @@ func splitPrefix(bss [][]byte) (prefix []byte, rest [][]byte) { return prefix, rest } -func readBytes(r io.Reader, n int) (b []byte, err error) { - b = make([]byte, n) - o := 0 - for o < n { - nr, err := r.Read(b[o:]) - if err != nil && err != io.EOF { - return b, err +func (n *ptNode) match(b []byte, prefix bool) bool { + l := len(n.prefix) + if l > 0 { + if l > len(b) { + l = len(b) } - - 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) { + if !bytes.Equal(b[:l], n.prefix) { return false } } - if prefix && n.terminal { + if n.terminal && (prefix || len(n.prefix) == len(b)) { return true } - b := make([]byte, 1) - for { - nr, err := r.Read(b) - if nr != 0 { - break - } - - if err == io.EOF { - return n.terminal - } + if l >= len(b) { + return false + } - if err != nil { - return false - } + nextN, ok := n.next[b[l]] + if !ok { + return false } - nextN, ok := n.next[b[0]] - return ok && nextN.match(r, prefix) + if l == len(b) { + b = b[l:l] + } else { + b = b[l+1:] + } + return nextN.match(b, prefix) } diff --git a/glide.lock b/glide.lock index 32138d23113..528cd4d36fa 100644 --- a/glide.lock +++ b/glide.lock @@ -97,6 +97,8 @@ imports: - xfs - name: github.com/russross/blackfriday version: 4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c +- name: github.com/soheilhy/cmux + version: bb79a83465015a27a175925ebd155e660f55e9f1 - name: github.com/spf13/cobra version: 1c44ec8d3f1552cac48999f9306da23c4d8a288b - name: github.com/spf13/pflag diff --git a/glide.yaml b/glide.yaml index 513d767da07..1fb5602bb16 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,4 +1,6 @@ package: github.com/coreos/etcd +ignore: +- google.golang.org/appengine import: - package: github.com/bgentry/speakeasy version: v0.1.0 @@ -128,5 +130,5 @@ import: - unicode/norm - package: github.com/russross/blackfriday version: 4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c -ignore: - - google.golang.org/appengine +- package: github.com/soheilhy/cmux + version: v0.1.3