├── .gitignore ├── .travis.yml ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── bench_test.go ├── buffer.go ├── cmux.go ├── cmux_test.go ├── doc.go ├── example ├── example_recursive_test.go ├── example_test.go ├── example_tls_test.go ├── go.mod └── go.sum ├── go.mod ├── go.sum ├── matchers.go ├── patricia.go └── patricia_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.6 5 | - 1.7 6 | - 1.8 7 | - tip 8 | 9 | matrix: 10 | allow_failures: 11 | - go: tip 12 | 13 | gobuild_args: -race 14 | 15 | before_install: 16 | - if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then go get -u github.com/kisielk/errcheck; fi 17 | - if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then go get -u golang.org/x/lint/golint; fi 18 | 19 | before_script: 20 | - '! gofmt -s -l . | read' 21 | - echo $TRAVIS_GO_VERSION 22 | - if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then golint ./...; fi 23 | - if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then errcheck ./...; fi 24 | - if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then go tool vet .; fi 25 | - if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then go tool vet --shadow .; fi 26 | 27 | script: 28 | - go test -bench . -v ./... 29 | - go test -race -bench . -v ./... 30 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # The list of people who have contributed code to the cmux repository. 2 | # 3 | # Auto-generated with: 4 | # git log --oneline --pretty=format:'%an <%aE>' | sort -u 5 | # 6 | Andreas Jaekle 7 | Dmitri Shuralyov 8 | Ethan Mosbaugh 9 | Soheil Hassas Yeganeh 10 | Soheil Hassas Yeganeh 11 | Tamir Duberstein 12 | Tamir Duberstein 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cmux: Connection Mux ![Travis Build Status](https://api.travis-ci.org/soheilhy/args.svg?branch=master "Travis Build Status") [![GoDoc](https://godoc.org/github.com/soheilhy/cmux?status.svg)](http://godoc.org/github.com/soheilhy/cmux) 2 | 3 | cmux is a generic Go library to multiplex connections based on 4 | their payload. Using cmux, you can serve gRPC, SSH, HTTPS, HTTP, 5 | Go RPC, and pretty much any other protocol on the same TCP listener. 6 | 7 | ## How-To 8 | Simply create your main listener, create a cmux for that listener, 9 | and then match connections: 10 | ```go 11 | // Create the main listener. 12 | l, err := net.Listen("tcp", ":23456") 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | 17 | // Create a cmux. 18 | m := cmux.New(l) 19 | 20 | // Match connections in order: 21 | // First grpc, then HTTP, and otherwise Go RPC/TCP. 22 | grpcL := m.Match(cmux.HTTP2HeaderField("content-type", "application/grpc")) 23 | httpL := m.Match(cmux.HTTP1Fast()) 24 | trpcL := m.Match(cmux.Any()) // Any means anything that is not yet matched. 25 | 26 | // Create your protocol servers. 27 | grpcS := grpc.NewServer() 28 | grpchello.RegisterGreeterServer(grpcS, &server{}) 29 | 30 | httpS := &http.Server{ 31 | Handler: &helloHTTP1Handler{}, 32 | } 33 | 34 | trpcS := rpc.NewServer() 35 | trpcS.Register(&ExampleRPCRcvr{}) 36 | 37 | // Use the muxed listeners for your servers. 38 | go grpcS.Serve(grpcL) 39 | go httpS.Serve(httpL) 40 | go trpcS.Accept(trpcL) 41 | 42 | // Start serving! 43 | m.Serve() 44 | ``` 45 | 46 | Take a look at [other examples in the GoDoc](http://godoc.org/github.com/soheilhy/cmux/#pkg-examples). 47 | 48 | ## Docs 49 | * [GoDocs](https://godoc.org/github.com/soheilhy/cmux) 50 | 51 | ## Performance 52 | There is room for improvment but, since we are only matching 53 | the very first bytes of a connection, the performance overheads on 54 | long-lived connections (i.e., RPCs and pipelined HTTP streams) 55 | is negligible. 56 | 57 | *TODO(soheil)*: Add benchmarks. 58 | 59 | ## Limitations 60 | * *TLS*: `net/http` uses a type assertion to identify TLS connections; since 61 | cmux's lookahead-implementing connection wraps the underlying TLS connection, 62 | this type assertion fails. 63 | Because of that, you can serve HTTPS using cmux but `http.Request.TLS` 64 | would not be set in your handlers. 65 | 66 | * *Different Protocols on The Same Connection*: `cmux` matches the connection 67 | when it's accepted. For example, one connection can be either gRPC or REST, but 68 | not both. That is, we assume that a client connection is either used for gRPC 69 | or REST. 70 | 71 | * *Java gRPC Clients*: Java gRPC client blocks until it receives a SETTINGS 72 | frame from the server. If you are using the Java client to connect to a cmux'ed 73 | gRPC server please match with writers: 74 | ```go 75 | grpcl := m.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) 76 | ``` 77 | 78 | # Copyright and License 79 | Copyright 2016 The CMux Authors. All rights reserved. 80 | 81 | See [CONTRIBUTORS](https://github.com/soheilhy/cmux/blob/master/CONTRIBUTORS) 82 | for the CMux Authors. Code is released under 83 | [the Apache 2 license](https://github.com/soheilhy/cmux/blob/master/LICENSE). 84 | -------------------------------------------------------------------------------- /bench_test.go: -------------------------------------------------------------------------------- 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 | "bytes" 19 | "io" 20 | "net" 21 | "sync" 22 | "testing" 23 | "time" 24 | 25 | "golang.org/x/net/http2" 26 | ) 27 | 28 | var ( 29 | benchHTTP1Payload = make([]byte, 4096) 30 | benchHTTP2Payload = make([]byte, 4096) 31 | ) 32 | 33 | func init() { 34 | copy(benchHTTP1Payload, []byte("GET http://www.w3.org/ HTTP/1.1")) 35 | copy(benchHTTP2Payload, http2.ClientPreface) 36 | } 37 | 38 | type mockConn struct { 39 | net.Conn 40 | r io.Reader 41 | } 42 | 43 | func (c *mockConn) Read(b []byte) (n int, err error) { 44 | return c.r.Read(b) 45 | } 46 | 47 | func (c *mockConn) SetReadDeadline(time.Time) error { 48 | return nil 49 | } 50 | 51 | func discard(l net.Listener) { 52 | for { 53 | if _, err := l.Accept(); err != nil { 54 | return 55 | } 56 | } 57 | } 58 | 59 | func BenchmarkCMuxConnHTTP1(b *testing.B) { 60 | m := New(nil).(*cMux) 61 | l := m.Match(HTTP1Fast()) 62 | 63 | go discard(l) 64 | 65 | donec := make(chan struct{}) 66 | var wg sync.WaitGroup 67 | wg.Add(b.N) 68 | 69 | b.ResetTimer() 70 | b.RunParallel(func(pb *testing.PB) { 71 | for pb.Next() { 72 | wg.Add(1) 73 | m.serve(&mockConn{ 74 | r: bytes.NewReader(benchHTTP1Payload), 75 | }, donec, &wg) 76 | } 77 | }) 78 | } 79 | 80 | func BenchmarkCMuxConnHTTP2(b *testing.B) { 81 | m := New(nil).(*cMux) 82 | l := m.Match(HTTP2()) 83 | go discard(l) 84 | 85 | donec := make(chan struct{}) 86 | var wg sync.WaitGroup 87 | wg.Add(b.N) 88 | 89 | b.ResetTimer() 90 | b.RunParallel(func(pb *testing.PB) { 91 | for pb.Next() { 92 | wg.Add(1) 93 | m.serve(&mockConn{ 94 | r: bytes.NewReader(benchHTTP2Payload), 95 | }, donec, &wg) 96 | } 97 | }) 98 | } 99 | 100 | func BenchmarkCMuxConnHTTP1n2(b *testing.B) { 101 | m := New(nil).(*cMux) 102 | l1 := m.Match(HTTP1Fast()) 103 | l2 := m.Match(HTTP2()) 104 | 105 | go discard(l1) 106 | go discard(l2) 107 | 108 | donec := make(chan struct{}) 109 | var wg sync.WaitGroup 110 | 111 | b.ResetTimer() 112 | b.RunParallel(func(pb *testing.PB) { 113 | for pb.Next() { 114 | wg.Add(1) 115 | m.serve(&mockConn{ 116 | r: bytes.NewReader(benchHTTP2Payload), 117 | }, donec, &wg) 118 | } 119 | }) 120 | } 121 | 122 | func BenchmarkCMuxConnHTTP2n1(b *testing.B) { 123 | m := New(nil).(*cMux) 124 | l2 := m.Match(HTTP2()) 125 | l1 := m.Match(HTTP1Fast()) 126 | 127 | go discard(l1) 128 | go discard(l2) 129 | 130 | donec := make(chan struct{}) 131 | var wg sync.WaitGroup 132 | 133 | b.ResetTimer() 134 | b.RunParallel(func(pb *testing.PB) { 135 | for pb.Next() { 136 | wg.Add(1) 137 | m.serve(&mockConn{ 138 | r: bytes.NewReader(benchHTTP1Payload), 139 | }, donec, &wg) 140 | } 141 | }) 142 | } 143 | -------------------------------------------------------------------------------- /buffer.go: -------------------------------------------------------------------------------- 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 | "bytes" 19 | "io" 20 | ) 21 | 22 | // bufferedReader is an optimized implementation of io.Reader that behaves like 23 | // ``` 24 | // io.MultiReader(bytes.NewReader(buffer.Bytes()), io.TeeReader(source, buffer)) 25 | // ``` 26 | // without allocating. 27 | type bufferedReader struct { 28 | source io.Reader 29 | buffer bytes.Buffer 30 | bufferRead int 31 | bufferSize int 32 | sniffing bool 33 | lastErr error 34 | } 35 | 36 | func (s *bufferedReader) Read(p []byte) (int, error) { 37 | if s.bufferSize > s.bufferRead { 38 | // If we have already read something from the buffer before, we return the 39 | // same data and the last error if any. We need to immediately return, 40 | // otherwise we may block for ever, if we try to be smart and call 41 | // source.Read() seeking a little bit of more data. 42 | bn := copy(p, s.buffer.Bytes()[s.bufferRead:s.bufferSize]) 43 | s.bufferRead += bn 44 | return bn, s.lastErr 45 | } else if !s.sniffing && s.buffer.Cap() != 0 { 46 | // We don't need the buffer anymore. 47 | // Reset it to release the internal slice. 48 | s.buffer = bytes.Buffer{} 49 | } 50 | 51 | // If there is nothing more to return in the sniffed buffer, read from the 52 | // source. 53 | sn, sErr := s.source.Read(p) 54 | if sn > 0 && s.sniffing { 55 | s.lastErr = sErr 56 | if wn, wErr := s.buffer.Write(p[:sn]); wErr != nil { 57 | return wn, wErr 58 | } 59 | } 60 | return sn, sErr 61 | } 62 | 63 | func (s *bufferedReader) reset(snif bool) { 64 | s.sniffing = snif 65 | s.bufferRead = 0 66 | s.bufferSize = s.buffer.Len() 67 | } 68 | -------------------------------------------------------------------------------- /cmux.go: -------------------------------------------------------------------------------- 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 | "errors" 19 | "fmt" 20 | "io" 21 | "net" 22 | "sync" 23 | "time" 24 | ) 25 | 26 | // Matcher matches a connection based on its content. 27 | type Matcher func(io.Reader) bool 28 | 29 | // MatchWriter is a match that can also write response (say to do handshake). 30 | type MatchWriter func(io.Writer, io.Reader) bool 31 | 32 | // ErrorHandler handles an error and returns whether 33 | // the mux should continue serving the listener. 34 | type ErrorHandler func(error) bool 35 | 36 | var _ net.Error = ErrNotMatched{} 37 | 38 | // ErrNotMatched is returned whenever a connection is not matched by any of 39 | // the matchers registered in the multiplexer. 40 | type ErrNotMatched struct { 41 | c net.Conn 42 | } 43 | 44 | func (e ErrNotMatched) Error() string { 45 | return fmt.Sprintf("mux: connection %v not matched by an matcher", 46 | e.c.RemoteAddr()) 47 | } 48 | 49 | // Temporary implements the net.Error interface. 50 | func (e ErrNotMatched) Temporary() bool { return true } 51 | 52 | // Timeout implements the net.Error interface. 53 | func (e ErrNotMatched) Timeout() bool { return false } 54 | 55 | type errListenerClosed string 56 | 57 | func (e errListenerClosed) Error() string { return string(e) } 58 | func (e errListenerClosed) Temporary() bool { return false } 59 | func (e errListenerClosed) Timeout() bool { return false } 60 | 61 | // ErrListenerClosed is returned from muxListener.Accept when the underlying 62 | // listener is closed. 63 | var ErrListenerClosed = errListenerClosed("mux: listener closed") 64 | 65 | // ErrServerClosed is returned from muxListener.Accept when mux server is closed. 66 | var ErrServerClosed = errors.New("mux: server closed") 67 | 68 | // for readability of readTimeout 69 | var noTimeout time.Duration 70 | 71 | // New instantiates a new connection multiplexer. 72 | func New(l net.Listener) CMux { 73 | return &cMux{ 74 | root: l, 75 | bufLen: 1024, 76 | errh: func(_ error) bool { return true }, 77 | donec: make(chan struct{}), 78 | readTimeout: noTimeout, 79 | } 80 | } 81 | 82 | // CMux is a multiplexer for network connections. 83 | type CMux interface { 84 | // Match returns a net.Listener that sees (i.e., accepts) only 85 | // the connections matched by at least one of the matcher. 86 | // 87 | // The order used to call Match determines the priority of matchers. 88 | Match(...Matcher) net.Listener 89 | // MatchWithWriters returns a net.Listener that accepts only the 90 | // connections that matched by at least of the matcher writers. 91 | // 92 | // Prefer Matchers over MatchWriters, since the latter can write on the 93 | // connection before the actual handler. 94 | // 95 | // The order used to call Match determines the priority of matchers. 96 | MatchWithWriters(...MatchWriter) net.Listener 97 | // Serve starts multiplexing the listener. Serve blocks and perhaps 98 | // should be invoked concurrently within a go routine. 99 | Serve() error 100 | // Closes cmux server and stops accepting any connections on listener 101 | Close() 102 | // HandleError registers an error handler that handles listener errors. 103 | HandleError(ErrorHandler) 104 | // sets a timeout for the read of matchers 105 | SetReadTimeout(time.Duration) 106 | } 107 | 108 | type matchersListener struct { 109 | ss []MatchWriter 110 | l muxListener 111 | } 112 | 113 | type cMux struct { 114 | root net.Listener 115 | bufLen int 116 | errh ErrorHandler 117 | sls []matchersListener 118 | readTimeout time.Duration 119 | donec chan struct{} 120 | mu sync.Mutex 121 | } 122 | 123 | func matchersToMatchWriters(matchers []Matcher) []MatchWriter { 124 | mws := make([]MatchWriter, 0, len(matchers)) 125 | for _, m := range matchers { 126 | cm := m 127 | mws = append(mws, func(w io.Writer, r io.Reader) bool { 128 | return cm(r) 129 | }) 130 | } 131 | return mws 132 | } 133 | 134 | func (m *cMux) Match(matchers ...Matcher) net.Listener { 135 | mws := matchersToMatchWriters(matchers) 136 | return m.MatchWithWriters(mws...) 137 | } 138 | 139 | func (m *cMux) MatchWithWriters(matchers ...MatchWriter) net.Listener { 140 | ml := muxListener{ 141 | Listener: m.root, 142 | connc: make(chan net.Conn, m.bufLen), 143 | donec: make(chan struct{}), 144 | } 145 | m.sls = append(m.sls, matchersListener{ss: matchers, l: ml}) 146 | return ml 147 | } 148 | 149 | func (m *cMux) SetReadTimeout(t time.Duration) { 150 | m.readTimeout = t 151 | } 152 | 153 | func (m *cMux) Serve() error { 154 | var wg sync.WaitGroup 155 | 156 | defer func() { 157 | m.closeDoneChans() 158 | wg.Wait() 159 | 160 | for _, sl := range m.sls { 161 | close(sl.l.connc) 162 | // Drain the connections enqueued for the listener. 163 | for c := range sl.l.connc { 164 | _ = c.Close() 165 | } 166 | } 167 | }() 168 | 169 | for { 170 | c, err := m.root.Accept() 171 | if err != nil { 172 | if !m.handleErr(err) { 173 | return err 174 | } 175 | continue 176 | } 177 | 178 | wg.Add(1) 179 | go m.serve(c, m.donec, &wg) 180 | } 181 | } 182 | 183 | func (m *cMux) serve(c net.Conn, donec <-chan struct{}, wg *sync.WaitGroup) { 184 | defer wg.Done() 185 | 186 | muc := newMuxConn(c) 187 | if m.readTimeout > noTimeout { 188 | _ = c.SetReadDeadline(time.Now().Add(m.readTimeout)) 189 | } 190 | for _, sl := range m.sls { 191 | for _, s := range sl.ss { 192 | matched := s(muc.Conn, muc.startSniffing()) 193 | if matched { 194 | muc.doneSniffing() 195 | if m.readTimeout > noTimeout { 196 | _ = c.SetReadDeadline(time.Time{}) 197 | } 198 | select { 199 | case sl.l.connc <- muc: 200 | case <-donec: 201 | _ = c.Close() 202 | } 203 | return 204 | } 205 | } 206 | } 207 | 208 | _ = c.Close() 209 | err := ErrNotMatched{c: c} 210 | if !m.handleErr(err) { 211 | _ = m.root.Close() 212 | } 213 | } 214 | 215 | func (m *cMux) Close() { 216 | m.closeDoneChans() 217 | } 218 | 219 | func (m *cMux) closeDoneChans() { 220 | m.mu.Lock() 221 | defer m.mu.Unlock() 222 | 223 | select { 224 | case <-m.donec: 225 | // Already closed. Don't close again 226 | default: 227 | close(m.donec) 228 | } 229 | for _, sl := range m.sls { 230 | select { 231 | case <-sl.l.donec: 232 | // Already closed. Don't close again 233 | default: 234 | close(sl.l.donec) 235 | } 236 | } 237 | } 238 | 239 | func (m *cMux) HandleError(h ErrorHandler) { 240 | m.errh = h 241 | } 242 | 243 | func (m *cMux) handleErr(err error) bool { 244 | if !m.errh(err) { 245 | return false 246 | } 247 | 248 | if ne, ok := err.(net.Error); ok { 249 | return ne.Temporary() 250 | } 251 | 252 | return false 253 | } 254 | 255 | type muxListener struct { 256 | net.Listener 257 | connc chan net.Conn 258 | donec chan struct{} 259 | } 260 | 261 | func (l muxListener) Accept() (net.Conn, error) { 262 | select { 263 | case c, ok := <-l.connc: 264 | if !ok { 265 | return nil, ErrListenerClosed 266 | } 267 | return c, nil 268 | case <-l.donec: 269 | return nil, ErrServerClosed 270 | } 271 | } 272 | 273 | // MuxConn wraps a net.Conn and provides transparent sniffing of connection data. 274 | type MuxConn struct { 275 | net.Conn 276 | buf bufferedReader 277 | } 278 | 279 | func newMuxConn(c net.Conn) *MuxConn { 280 | return &MuxConn{ 281 | Conn: c, 282 | buf: bufferedReader{source: c}, 283 | } 284 | } 285 | 286 | // From the io.Reader documentation: 287 | // 288 | // When Read encounters an error or end-of-file condition after 289 | // successfully reading n > 0 bytes, it returns the number of 290 | // bytes read. It may return the (non-nil) error from the same call 291 | // or return the error (and n == 0) from a subsequent call. 292 | // An instance of this general case is that a Reader returning 293 | // a non-zero number of bytes at the end of the input stream may 294 | // return either err == EOF or err == nil. The next Read should 295 | // return 0, EOF. 296 | func (m *MuxConn) Read(p []byte) (int, error) { 297 | return m.buf.Read(p) 298 | } 299 | 300 | func (m *MuxConn) startSniffing() io.Reader { 301 | m.buf.reset(true) 302 | return &m.buf 303 | } 304 | 305 | func (m *MuxConn) doneSniffing() { 306 | m.buf.reset(false) 307 | } 308 | -------------------------------------------------------------------------------- /cmux_test.go: -------------------------------------------------------------------------------- 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 | "bytes" 19 | "crypto/rand" 20 | "crypto/tls" 21 | "errors" 22 | "fmt" 23 | "go/build" 24 | "io" 25 | "io/ioutil" 26 | "log" 27 | "net" 28 | "net/http" 29 | "net/rpc" 30 | "os" 31 | "os/exec" 32 | "runtime" 33 | "sort" 34 | "strings" 35 | "sync" 36 | "sync/atomic" 37 | "testing" 38 | "time" 39 | 40 | "golang.org/x/net/http2" 41 | "golang.org/x/net/http2/hpack" 42 | ) 43 | 44 | const ( 45 | testHTTP1Resp = "http1" 46 | rpcVal = 1234 47 | ) 48 | 49 | func safeServe(errCh chan<- error, muxl CMux) { 50 | if err := muxl.Serve(); !strings.Contains(err.Error(), "use of closed") { 51 | errCh <- err 52 | } 53 | } 54 | 55 | func safeDial(t *testing.T, addr net.Addr) (*rpc.Client, func()) { 56 | c, err := rpc.Dial(addr.Network(), addr.String()) 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | return c, func() { 61 | if err := c.Close(); err != nil { 62 | t.Fatal(err) 63 | } 64 | } 65 | } 66 | 67 | type chanListener struct { 68 | net.Listener 69 | connCh chan net.Conn 70 | } 71 | 72 | func newChanListener() *chanListener { 73 | return &chanListener{connCh: make(chan net.Conn, 1)} 74 | } 75 | 76 | func (l *chanListener) Accept() (net.Conn, error) { 77 | if c, ok := <-l.connCh; ok { 78 | return c, nil 79 | } 80 | return nil, errors.New("use of closed network connection") 81 | } 82 | 83 | func testListener(t *testing.T) (net.Listener, func()) { 84 | l, err := net.Listen("tcp", "127.0.0.1:0") 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | var once sync.Once 89 | return l, func() { 90 | once.Do(func() { 91 | if err := l.Close(); err != nil { 92 | t.Fatal(err) 93 | } 94 | }) 95 | } 96 | } 97 | 98 | type testHTTP1Handler struct{} 99 | 100 | func (h *testHTTP1Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 101 | fmt.Fprintf(w, testHTTP1Resp) 102 | } 103 | 104 | func runTestHTTPServer(errCh chan<- error, l net.Listener) { 105 | var mu sync.Mutex 106 | conns := make(map[net.Conn]struct{}) 107 | 108 | defer func() { 109 | mu.Lock() 110 | for c := range conns { 111 | if err := c.Close(); err != nil { 112 | errCh <- err 113 | } 114 | } 115 | mu.Unlock() 116 | }() 117 | 118 | s := &http.Server{ 119 | Handler: &testHTTP1Handler{}, 120 | ConnState: func(c net.Conn, state http.ConnState) { 121 | mu.Lock() 122 | switch state { 123 | case http.StateNew: 124 | conns[c] = struct{}{} 125 | case http.StateClosed: 126 | delete(conns, c) 127 | } 128 | mu.Unlock() 129 | }, 130 | } 131 | if err := s.Serve(l); err != ErrListenerClosed && err != ErrServerClosed { 132 | errCh <- err 133 | } 134 | } 135 | 136 | func generateTLSCert(t *testing.T) { 137 | err := exec.Command("go", "run", build.Default.GOROOT+"/src/crypto/tls/generate_cert.go", "--host", "*").Run() 138 | if err != nil { 139 | t.Fatal(err) 140 | } 141 | } 142 | 143 | func cleanupTLSCert(t *testing.T) { 144 | err := os.Remove("cert.pem") 145 | if err != nil { 146 | t.Error(err) 147 | } 148 | err = os.Remove("key.pem") 149 | if err != nil { 150 | t.Error(err) 151 | } 152 | } 153 | 154 | func runTestTLSServer(errCh chan<- error, l net.Listener) { 155 | certificate, err := tls.LoadX509KeyPair("cert.pem", "key.pem") 156 | if err != nil { 157 | errCh <- err 158 | log.Printf("1") 159 | return 160 | } 161 | 162 | config := &tls.Config{ 163 | Certificates: []tls.Certificate{certificate}, 164 | Rand: rand.Reader, 165 | } 166 | 167 | tlsl := tls.NewListener(l, config) 168 | runTestHTTPServer(errCh, tlsl) 169 | } 170 | 171 | func runTestHTTP1Client(t *testing.T, addr net.Addr) { 172 | runTestHTTPClient(t, "http", addr) 173 | } 174 | 175 | func runTestTLSClient(t *testing.T, addr net.Addr) { 176 | runTestHTTPClient(t, "https", addr) 177 | } 178 | 179 | func runTestHTTPClient(t *testing.T, proto string, addr net.Addr) { 180 | client := http.Client{ 181 | Timeout: 5 * time.Second, 182 | Transport: &http.Transport{ 183 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 184 | }, 185 | } 186 | r, err := client.Get(proto + "://" + addr.String()) 187 | if err != nil { 188 | t.Fatal(err) 189 | } 190 | 191 | defer func() { 192 | if err = r.Body.Close(); err != nil { 193 | t.Fatal(err) 194 | } 195 | }() 196 | 197 | b, err := ioutil.ReadAll(r.Body) 198 | if err != nil { 199 | t.Fatal(err) 200 | } 201 | if string(b) != testHTTP1Resp { 202 | t.Fatalf("invalid response: want=%s got=%s", testHTTP1Resp, b) 203 | } 204 | } 205 | 206 | type TestRPCRcvr struct{} 207 | 208 | func (r TestRPCRcvr) Test(i int, j *int) error { 209 | *j = i 210 | return nil 211 | } 212 | 213 | func runTestRPCServer(errCh chan<- error, l net.Listener) { 214 | s := rpc.NewServer() 215 | if err := s.Register(TestRPCRcvr{}); err != nil { 216 | errCh <- err 217 | } 218 | for { 219 | c, err := l.Accept() 220 | if err != nil { 221 | if err != ErrListenerClosed && err != ErrServerClosed { 222 | errCh <- err 223 | } 224 | return 225 | } 226 | go s.ServeConn(c) 227 | } 228 | } 229 | 230 | func runTestRPCClient(t *testing.T, addr net.Addr) { 231 | c, cleanup := safeDial(t, addr) 232 | defer cleanup() 233 | 234 | var num int 235 | if err := c.Call("TestRPCRcvr.Test", rpcVal, &num); err != nil { 236 | t.Fatal(err) 237 | } 238 | 239 | if num != rpcVal { 240 | t.Errorf("wrong rpc response: want=%d got=%v", rpcVal, num) 241 | } 242 | } 243 | 244 | const ( 245 | handleHTTP1Close = 1 246 | handleHTTP1Request = 2 247 | handleAnyClose = 3 248 | handleAnyRequest = 4 249 | ) 250 | 251 | func TestTimeout(t *testing.T) { 252 | defer leakCheck(t)() 253 | lis, Close := testListener(t) 254 | defer Close() 255 | result := make(chan int, 5) 256 | testDuration := time.Millisecond * 500 257 | m := New(lis) 258 | m.SetReadTimeout(testDuration) 259 | http1 := m.Match(HTTP1Fast()) 260 | any := m.Match(Any()) 261 | go func() { 262 | _ = m.Serve() 263 | }() 264 | go func() { 265 | con, err := http1.Accept() 266 | if err != nil { 267 | result <- handleHTTP1Close 268 | } else { 269 | _, _ = con.Write([]byte("http1")) 270 | _ = con.Close() 271 | result <- handleHTTP1Request 272 | } 273 | }() 274 | go func() { 275 | con, err := any.Accept() 276 | if err != nil { 277 | result <- handleAnyClose 278 | } else { 279 | _, _ = con.Write([]byte("any")) 280 | _ = con.Close() 281 | result <- handleAnyRequest 282 | } 283 | }() 284 | time.Sleep(testDuration) // wait to prevent timeouts on slow test-runners 285 | client, err := net.Dial("tcp", lis.Addr().String()) 286 | if err != nil { 287 | log.Fatal("testTimeout client failed: ", err) 288 | } 289 | defer func() { 290 | _ = client.Close() 291 | }() 292 | time.Sleep(testDuration / 2) 293 | if len(result) != 0 { 294 | log.Print("tcp ") 295 | t.Fatal("testTimeout failed: accepted to fast: ", len(result)) 296 | } 297 | _ = client.SetReadDeadline(time.Now().Add(testDuration * 3)) 298 | buffer := make([]byte, 10) 299 | rl, err := client.Read(buffer) 300 | if err != nil { 301 | t.Fatal("testTimeout failed: client error: ", err, rl) 302 | } 303 | Close() 304 | if rl != 3 { 305 | log.Print("testTimeout failed: response from wrong sevice ", rl) 306 | } 307 | if string(buffer[0:3]) != "any" { 308 | log.Print("testTimeout failed: response from wrong sevice ") 309 | } 310 | time.Sleep(testDuration * 2) 311 | if len(result) != 2 { 312 | t.Fatal("testTimeout failed: accepted to less: ", len(result)) 313 | } 314 | if a := <-result; a != handleAnyRequest { 315 | t.Fatal("testTimeout failed: any rule did not match") 316 | } 317 | if a := <-result; a != handleHTTP1Close { 318 | t.Fatal("testTimeout failed: no close an http rule") 319 | } 320 | } 321 | 322 | func TestRead(t *testing.T) { 323 | defer leakCheck(t)() 324 | errCh := make(chan error) 325 | defer func() { 326 | select { 327 | case err := <-errCh: 328 | t.Fatal(err) 329 | default: 330 | } 331 | }() 332 | const payload = "hello world\r\n" 333 | const mult = 2 334 | 335 | writer, reader := net.Pipe() 336 | go func() { 337 | if _, err := io.WriteString(writer, strings.Repeat(payload, mult)); err != nil { 338 | t.Fatal(err) 339 | } 340 | if err := writer.Close(); err != nil { 341 | t.Fatal(err) 342 | } 343 | }() 344 | 345 | l := newChanListener() 346 | defer close(l.connCh) 347 | l.connCh <- reader 348 | muxl := New(l) 349 | // Register a bogus matcher to force buffering exactly the right amount. 350 | // Before this fix, this would trigger a bug where `Read` would incorrectly 351 | // report `io.EOF` when only the buffer had been consumed. 352 | muxl.Match(func(r io.Reader) bool { 353 | var b [len(payload)]byte 354 | _, _ = r.Read(b[:]) 355 | return false 356 | }) 357 | anyl := muxl.Match(Any()) 358 | go safeServe(errCh, muxl) 359 | muxedConn, err := anyl.Accept() 360 | if err != nil { 361 | t.Fatal(err) 362 | } 363 | for i := 0; i < mult; i++ { 364 | var b [len(payload)]byte 365 | n, err := muxedConn.Read(b[:]) 366 | if err != nil { 367 | t.Error(err) 368 | continue 369 | } 370 | if e := len(b); n != e { 371 | t.Errorf("expected to read %d bytes, but read %d bytes", e, n) 372 | } 373 | } 374 | var b [1]byte 375 | if _, err := muxedConn.Read(b[:]); err != io.EOF { 376 | t.Errorf("unexpected error %v, expected %v", err, io.EOF) 377 | } 378 | } 379 | 380 | func TestAny(t *testing.T) { 381 | defer leakCheck(t)() 382 | errCh := make(chan error) 383 | defer func() { 384 | select { 385 | case err := <-errCh: 386 | t.Fatal(err) 387 | default: 388 | } 389 | }() 390 | l, cleanup := testListener(t) 391 | defer cleanup() 392 | 393 | muxl := New(l) 394 | httpl := muxl.Match(Any()) 395 | 396 | go runTestHTTPServer(errCh, httpl) 397 | go safeServe(errCh, muxl) 398 | 399 | runTestHTTP1Client(t, l.Addr()) 400 | } 401 | 402 | func TestTLS(t *testing.T) { 403 | generateTLSCert(t) 404 | defer cleanupTLSCert(t) 405 | defer leakCheck(t)() 406 | errCh := make(chan error) 407 | defer func() { 408 | select { 409 | case err := <-errCh: 410 | t.Fatal(err) 411 | default: 412 | } 413 | }() 414 | l, cleanup := testListener(t) 415 | defer cleanup() 416 | 417 | muxl := New(l) 418 | tlsl := muxl.Match(TLS()) 419 | httpl := muxl.Match(Any()) 420 | 421 | go runTestTLSServer(errCh, tlsl) 422 | go runTestHTTPServer(errCh, httpl) 423 | go safeServe(errCh, muxl) 424 | 425 | runTestHTTP1Client(t, l.Addr()) 426 | runTestTLSClient(t, l.Addr()) 427 | } 428 | 429 | func TestHTTP2(t *testing.T) { 430 | defer leakCheck(t)() 431 | errCh := make(chan error) 432 | defer func() { 433 | select { 434 | case err := <-errCh: 435 | t.Fatal(err) 436 | default: 437 | } 438 | }() 439 | writer, reader := net.Pipe() 440 | go func() { 441 | if _, err := io.WriteString(writer, http2.ClientPreface); err != nil { 442 | t.Fatal(err) 443 | } 444 | if err := writer.Close(); err != nil { 445 | t.Fatal(err) 446 | } 447 | }() 448 | 449 | l := newChanListener() 450 | l.connCh <- reader 451 | muxl := New(l) 452 | // Register a bogus matcher that only reads one byte. 453 | muxl.Match(func(r io.Reader) bool { 454 | var b [1]byte 455 | _, _ = r.Read(b[:]) 456 | return false 457 | }) 458 | h2l := muxl.Match(HTTP2()) 459 | go safeServe(errCh, muxl) 460 | muxedConn, err := h2l.Accept() 461 | close(l.connCh) 462 | if err != nil { 463 | t.Fatal(err) 464 | } 465 | var b [len(http2.ClientPreface)]byte 466 | var n int 467 | // We have the sniffed buffer first... 468 | if n, err = muxedConn.Read(b[:]); err == io.EOF { 469 | t.Fatal(err) 470 | } 471 | // and then we read from the source. 472 | if _, err = muxedConn.Read(b[n:]); err != io.EOF { 473 | t.Fatal(err) 474 | } 475 | if string(b[:]) != http2.ClientPreface { 476 | t.Errorf("got unexpected read %s, expected %s", b, http2.ClientPreface) 477 | } 478 | } 479 | 480 | func TestHTTP2MatchHeaderField(t *testing.T) { 481 | testHTTP2MatchHeaderField(t, HTTP2HeaderField, "value", "value", "anothervalue") 482 | } 483 | 484 | func TestHTTP2MatchHeaderFieldPrefix(t *testing.T) { 485 | testHTTP2MatchHeaderField(t, HTTP2HeaderFieldPrefix, "application/grpc+proto", "application/grpc", "application/json") 486 | } 487 | 488 | func testHTTP2MatchHeaderField( 489 | t *testing.T, 490 | matcherConstructor func(string, string) Matcher, 491 | headerValue string, 492 | matchValue string, 493 | notMatchValue string, 494 | ) { 495 | defer leakCheck(t)() 496 | errCh := make(chan error) 497 | defer func() { 498 | select { 499 | case err := <-errCh: 500 | t.Fatal(err) 501 | default: 502 | } 503 | }() 504 | name := "name" 505 | writer, reader := net.Pipe() 506 | go func() { 507 | if _, err := io.WriteString(writer, http2.ClientPreface); err != nil { 508 | t.Fatal(err) 509 | } 510 | var buf bytes.Buffer 511 | enc := hpack.NewEncoder(&buf) 512 | if err := enc.WriteField(hpack.HeaderField{Name: name, Value: headerValue}); err != nil { 513 | t.Fatal(err) 514 | } 515 | framer := http2.NewFramer(writer, nil) 516 | err := framer.WriteHeaders(http2.HeadersFrameParam{ 517 | StreamID: 1, 518 | BlockFragment: buf.Bytes(), 519 | EndStream: true, 520 | EndHeaders: true, 521 | }) 522 | if err != nil { 523 | t.Fatal(err) 524 | } 525 | if err := writer.Close(); err != nil { 526 | t.Fatal(err) 527 | } 528 | }() 529 | 530 | l := newChanListener() 531 | l.connCh <- reader 532 | muxl := New(l) 533 | // Register a bogus matcher that only reads one byte. 534 | muxl.Match(func(r io.Reader) bool { 535 | var b [1]byte 536 | _, _ = r.Read(b[:]) 537 | return false 538 | }) 539 | // Create a matcher that cannot match the response. 540 | muxl.Match(matcherConstructor(name, notMatchValue)) 541 | // Then match with the expected field. 542 | h2l := muxl.Match(matcherConstructor(name, matchValue)) 543 | go safeServe(errCh, muxl) 544 | muxedConn, err := h2l.Accept() 545 | close(l.connCh) 546 | if err != nil { 547 | t.Fatal(err) 548 | } 549 | var b [len(http2.ClientPreface)]byte 550 | // We have the sniffed buffer first... 551 | if _, err := muxedConn.Read(b[:]); err == io.EOF { 552 | t.Fatal(err) 553 | } 554 | if string(b[:]) != http2.ClientPreface { 555 | t.Errorf("got unexpected read %s, expected %s", b, http2.ClientPreface) 556 | } 557 | } 558 | 559 | func TestHTTPGoRPC(t *testing.T) { 560 | defer leakCheck(t)() 561 | errCh := make(chan error) 562 | defer func() { 563 | select { 564 | case err := <-errCh: 565 | t.Fatal(err) 566 | default: 567 | } 568 | }() 569 | l, cleanup := testListener(t) 570 | defer cleanup() 571 | 572 | muxl := New(l) 573 | httpl := muxl.Match(HTTP2(), HTTP1Fast()) 574 | rpcl := muxl.Match(Any()) 575 | 576 | go runTestHTTPServer(errCh, httpl) 577 | go runTestRPCServer(errCh, rpcl) 578 | go safeServe(errCh, muxl) 579 | 580 | runTestHTTP1Client(t, l.Addr()) 581 | runTestRPCClient(t, l.Addr()) 582 | } 583 | 584 | func TestErrorHandler(t *testing.T) { 585 | defer leakCheck(t)() 586 | errCh := make(chan error) 587 | defer func() { 588 | select { 589 | case err := <-errCh: 590 | t.Fatal(err) 591 | default: 592 | } 593 | }() 594 | l, cleanup := testListener(t) 595 | defer cleanup() 596 | 597 | muxl := New(l) 598 | httpl := muxl.Match(HTTP2(), HTTP1Fast()) 599 | 600 | go runTestHTTPServer(errCh, httpl) 601 | go safeServe(errCh, muxl) 602 | 603 | var errCount uint32 604 | muxl.HandleError(func(err error) bool { 605 | if atomic.AddUint32(&errCount, 1) == 1 { 606 | if _, ok := err.(ErrNotMatched); !ok { 607 | t.Errorf("unexpected error: %v", err) 608 | } 609 | } 610 | return true 611 | }) 612 | 613 | c, cleanup := safeDial(t, l.Addr()) 614 | defer cleanup() 615 | 616 | var num int 617 | for atomic.LoadUint32(&errCount) == 0 { 618 | if err := c.Call("TestRPCRcvr.Test", rpcVal, &num); err == nil { 619 | // The connection is simply closed. 620 | t.Errorf("unexpected rpc success after %d errors", atomic.LoadUint32(&errCount)) 621 | } 622 | } 623 | } 624 | 625 | func TestMultipleMatchers(t *testing.T) { 626 | defer leakCheck(t)() 627 | errCh := make(chan error) 628 | defer func() { 629 | select { 630 | case err := <-errCh: 631 | t.Fatal(err) 632 | default: 633 | } 634 | }() 635 | l, cleanup := testListener(t) 636 | defer cleanup() 637 | 638 | matcher := func(r io.Reader) bool { 639 | return true 640 | } 641 | unmatcher := func(r io.Reader) bool { 642 | return false 643 | } 644 | 645 | muxl := New(l) 646 | lis := muxl.Match(unmatcher, matcher, unmatcher) 647 | 648 | go runTestHTTPServer(errCh, lis) 649 | go safeServe(errCh, muxl) 650 | 651 | runTestHTTP1Client(t, l.Addr()) 652 | } 653 | 654 | func TestListenerClose(t *testing.T) { 655 | defer leakCheck(t)() 656 | errCh := make(chan error) 657 | defer func() { 658 | select { 659 | case err := <-errCh: 660 | t.Fatal(err) 661 | default: 662 | } 663 | }() 664 | l := newChanListener() 665 | 666 | c1, c2 := net.Pipe() 667 | 668 | muxl := New(l) 669 | anyl := muxl.Match(Any()) 670 | 671 | go safeServe(errCh, muxl) 672 | 673 | l.connCh <- c1 674 | 675 | // First connection goes through. 676 | if _, err := anyl.Accept(); err != nil { 677 | t.Fatal(err) 678 | } 679 | 680 | // Second connection is sent 681 | l.connCh <- c2 682 | 683 | // Listener is closed. 684 | close(l.connCh) 685 | 686 | // Second connection either goes through or it is closed. 687 | if _, err := anyl.Accept(); err != nil { 688 | if err != ErrListenerClosed && err != ErrServerClosed { 689 | t.Fatal(err) 690 | } 691 | // The error is either io.ErrClosedPipe or net.OpError wrapping 692 | // a net.pipeError depending on the go version. 693 | if _, err := c2.Read([]byte{}); !strings.Contains(err.Error(), "closed") { 694 | t.Fatalf("connection is not closed and is leaked: %v", err) 695 | } 696 | } 697 | } 698 | 699 | func TestClose(t *testing.T) { 700 | defer leakCheck(t)() 701 | errCh := make(chan error) 702 | defer func() { 703 | select { 704 | case err := <-errCh: 705 | t.Fatal(err) 706 | default: 707 | } 708 | }() 709 | l, cleanup := testListener(t) 710 | defer cleanup() 711 | 712 | muxl := New(l) 713 | anyl := muxl.Match(Any()) 714 | 715 | go safeServe(errCh, muxl) 716 | 717 | muxl.Close() 718 | 719 | if _, err := anyl.Accept(); err != ErrServerClosed { 720 | t.Fatal(err) 721 | } 722 | } 723 | 724 | // Cribbed from google.golang.org/grpc/test/end2end_test.go. 725 | 726 | // interestingGoroutines returns all goroutines we care about for the purpose 727 | // of leak checking. It excludes testing or runtime ones. 728 | func interestingGoroutines() (gs []string) { 729 | buf := make([]byte, 2<<20) 730 | buf = buf[:runtime.Stack(buf, true)] 731 | for _, g := range strings.Split(string(buf), "\n\n") { 732 | sl := strings.SplitN(g, "\n", 2) 733 | if len(sl) != 2 { 734 | continue 735 | } 736 | stack := strings.TrimSpace(sl[1]) 737 | if strings.HasPrefix(stack, "testing.RunTests") { 738 | continue 739 | } 740 | 741 | if stack == "" || 742 | strings.Contains(stack, "main.main()") || 743 | strings.Contains(stack, "testing.Main(") || 744 | strings.Contains(stack, "runtime.goexit") || 745 | strings.Contains(stack, "created by runtime.gc") || 746 | strings.Contains(stack, "interestingGoroutines") || 747 | strings.Contains(stack, "runtime.MHeap_Scavenger") { 748 | continue 749 | } 750 | gs = append(gs, g) 751 | } 752 | sort.Strings(gs) 753 | return 754 | } 755 | 756 | // leakCheck snapshots the currently-running goroutines and returns a 757 | // function to be run at the end of tests to see whether any 758 | // goroutines leaked. 759 | func leakCheck(t testing.TB) func() { 760 | orig := map[string]bool{} 761 | for _, g := range interestingGoroutines() { 762 | orig[g] = true 763 | } 764 | return func() { 765 | // Loop, waiting for goroutines to shut down. 766 | // Wait up to 5 seconds, but finish as quickly as possible. 767 | deadline := time.Now().Add(5 * time.Second) 768 | for { 769 | var leaked []string 770 | for _, g := range interestingGoroutines() { 771 | if !orig[g] { 772 | leaked = append(leaked, g) 773 | } 774 | } 775 | if len(leaked) == 0 { 776 | return 777 | } 778 | if time.Now().Before(deadline) { 779 | time.Sleep(50 * time.Millisecond) 780 | continue 781 | } 782 | for _, g := range leaked { 783 | t.Errorf("Leaked goroutine: %v", g) 784 | } 785 | return 786 | } 787 | } 788 | } 789 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 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 is a library to multiplex network connections based on 16 | // their payload. Using cmux, you can serve different protocols from the 17 | // same listener. 18 | package cmux 19 | -------------------------------------------------------------------------------- /example/example_recursive_test.go: -------------------------------------------------------------------------------- 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_test 16 | 17 | import ( 18 | "crypto/rand" 19 | "crypto/tls" 20 | "fmt" 21 | "log" 22 | "net" 23 | "net/http" 24 | "net/rpc" 25 | "strings" 26 | 27 | "github.com/soheilhy/cmux" 28 | ) 29 | 30 | type recursiveHTTPHandler struct{} 31 | 32 | func (h *recursiveHTTPHandler) ServeHTTP(w http.ResponseWriter, 33 | r *http.Request) { 34 | 35 | fmt.Fprintf(w, "example http response") 36 | } 37 | 38 | func recursiveServeHTTP(l net.Listener) { 39 | s := &http.Server{ 40 | Handler: &recursiveHTTPHandler{}, 41 | } 42 | if err := s.Serve(l); err != cmux.ErrListenerClosed { 43 | panic(err) 44 | } 45 | } 46 | 47 | func tlsListener(l net.Listener) net.Listener { 48 | // Load certificates. 49 | certificate, err := tls.LoadX509KeyPair("cert.pem", "key.pem") 50 | if err != nil { 51 | log.Panic(err) 52 | } 53 | 54 | config := &tls.Config{ 55 | Certificates: []tls.Certificate{certificate}, 56 | Rand: rand.Reader, 57 | } 58 | 59 | // Create TLS listener. 60 | tlsl := tls.NewListener(l, config) 61 | return tlsl 62 | } 63 | 64 | type RecursiveRPCRcvr struct{} 65 | 66 | func (r *RecursiveRPCRcvr) Cube(i int, j *int) error { 67 | *j = i * i 68 | return nil 69 | } 70 | 71 | func recursiveServeRPC(l net.Listener) { 72 | s := rpc.NewServer() 73 | if err := s.Register(&RecursiveRPCRcvr{}); err != nil { 74 | panic(err) 75 | } 76 | for { 77 | conn, err := l.Accept() 78 | if err != nil { 79 | if err != cmux.ErrListenerClosed { 80 | panic(err) 81 | } 82 | return 83 | } 84 | go s.ServeConn(conn) 85 | } 86 | } 87 | 88 | // This is an example for serving HTTP, HTTPS, and GoRPC/TLS on the same port. 89 | func Example_recursiveCmux() { 90 | // Create the TCP listener. 91 | l, err := net.Listen("tcp", "127.0.0.1:50051") 92 | if err != nil { 93 | log.Panic(err) 94 | } 95 | 96 | // Create a mux. 97 | tcpm := cmux.New(l) 98 | 99 | // We first match on HTTP 1.1 methods. 100 | httpl := tcpm.Match(cmux.HTTP1Fast()) 101 | 102 | // If not matched, we assume that its TLS. 103 | tlsl := tcpm.Match(cmux.Any()) 104 | tlsl = tlsListener(tlsl) 105 | 106 | // Now, we build another mux recursively to match HTTPS and GoRPC. 107 | // You can use the same trick for SSH. 108 | tlsm := cmux.New(tlsl) 109 | httpsl := tlsm.Match(cmux.HTTP1Fast()) 110 | gorpcl := tlsm.Match(cmux.Any()) 111 | go recursiveServeHTTP(httpl) 112 | go recursiveServeHTTP(httpsl) 113 | go recursiveServeRPC(gorpcl) 114 | 115 | go func() { 116 | if err := tlsm.Serve(); err != cmux.ErrListenerClosed { 117 | panic(err) 118 | } 119 | }() 120 | if err := tcpm.Serve(); !strings.Contains(err.Error(), "use of closed network connection") { 121 | panic(err) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /example/example_test.go: -------------------------------------------------------------------------------- 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_test 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "log" 21 | "net" 22 | "net/http" 23 | "net/rpc" 24 | "strings" 25 | 26 | "google.golang.org/grpc" 27 | 28 | "golang.org/x/net/context" 29 | "golang.org/x/net/websocket" 30 | 31 | "github.com/soheilhy/cmux" 32 | "google.golang.org/grpc/examples/helloworld/helloworld" 33 | grpchello "google.golang.org/grpc/examples/helloworld/helloworld" 34 | ) 35 | 36 | type exampleHTTPHandler struct{} 37 | 38 | func (h *exampleHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 39 | fmt.Fprintf(w, "example http response") 40 | } 41 | 42 | func serveHTTP(l net.Listener) { 43 | s := &http.Server{ 44 | Handler: &exampleHTTPHandler{}, 45 | } 46 | if err := s.Serve(l); err != cmux.ErrListenerClosed { 47 | panic(err) 48 | } 49 | } 50 | 51 | func EchoServer(ws *websocket.Conn) { 52 | if _, err := io.Copy(ws, ws); err != nil { 53 | panic(err) 54 | } 55 | } 56 | 57 | func serveWS(l net.Listener) { 58 | s := &http.Server{ 59 | Handler: websocket.Handler(EchoServer), 60 | } 61 | if err := s.Serve(l); err != cmux.ErrListenerClosed { 62 | panic(err) 63 | } 64 | } 65 | 66 | type ExampleRPCRcvr struct{} 67 | 68 | func (r *ExampleRPCRcvr) Cube(i int, j *int) error { 69 | *j = i * i 70 | return nil 71 | } 72 | 73 | func serveRPC(l net.Listener) { 74 | s := rpc.NewServer() 75 | if err := s.Register(&ExampleRPCRcvr{}); err != nil { 76 | panic(err) 77 | } 78 | for { 79 | conn, err := l.Accept() 80 | if err != nil { 81 | if err != cmux.ErrListenerClosed { 82 | panic(err) 83 | } 84 | return 85 | } 86 | go s.ServeConn(conn) 87 | } 88 | } 89 | 90 | type grpcServer struct { 91 | helloworld.UnimplementedGreeterServer 92 | } 93 | 94 | func (s *grpcServer) SayHello(ctx context.Context, in *grpchello.HelloRequest) ( 95 | *grpchello.HelloReply, error) { 96 | 97 | return &grpchello.HelloReply{Message: "Hello " + in.Name + " from cmux"}, nil 98 | } 99 | 100 | func serveGRPC(l net.Listener) { 101 | grpcs := grpc.NewServer() 102 | grpchello.RegisterGreeterServer(grpcs, &grpcServer{}) 103 | if err := grpcs.Serve(l); err != cmux.ErrListenerClosed { 104 | panic(err) 105 | } 106 | } 107 | 108 | func Example() { 109 | l, err := net.Listen("tcp", "127.0.0.1:50051") 110 | if err != nil { 111 | log.Panic(err) 112 | } 113 | 114 | m := cmux.New(l) 115 | 116 | // We first match the connection against HTTP2 fields. If matched, the 117 | // connection will be sent through the "grpcl" listener. 118 | grpcl := m.Match(cmux.HTTP2HeaderFieldPrefix("content-type", "application/grpc")) 119 | //Otherwise, we match it againts a websocket upgrade request. 120 | wsl := m.Match(cmux.HTTP1HeaderField("Upgrade", "websocket")) 121 | 122 | // Otherwise, we match it againts HTTP1 methods. If matched, 123 | // it is sent through the "httpl" listener. 124 | httpl := m.Match(cmux.HTTP1Fast()) 125 | // If not matched by HTTP, we assume it is an RPC connection. 126 | rpcl := m.Match(cmux.Any()) 127 | 128 | // Then we used the muxed listeners. 129 | go serveGRPC(grpcl) 130 | go serveWS(wsl) 131 | go serveHTTP(httpl) 132 | go serveRPC(rpcl) 133 | 134 | if err := m.Serve(); !strings.Contains(err.Error(), "use of closed network connection") { 135 | panic(err) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /example/example_tls_test.go: -------------------------------------------------------------------------------- 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_test 16 | 17 | import ( 18 | "crypto/rand" 19 | "crypto/tls" 20 | "fmt" 21 | "log" 22 | "net" 23 | "net/http" 24 | "strings" 25 | 26 | "github.com/soheilhy/cmux" 27 | ) 28 | 29 | type anotherHTTPHandler struct{} 30 | 31 | func (h *anotherHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 32 | fmt.Fprintf(w, "example http response") 33 | } 34 | 35 | func serveHTTP1(l net.Listener) { 36 | s := &http.Server{ 37 | Handler: &anotherHTTPHandler{}, 38 | } 39 | if err := s.Serve(l); err != cmux.ErrListenerClosed { 40 | panic(err) 41 | } 42 | } 43 | 44 | func serveHTTPS(l net.Listener) { 45 | // Load certificates. 46 | certificate, err := tls.LoadX509KeyPair("cert.pem", "key.pem") 47 | if err != nil { 48 | log.Panic(err) 49 | } 50 | 51 | config := &tls.Config{ 52 | Certificates: []tls.Certificate{certificate}, 53 | Rand: rand.Reader, 54 | } 55 | 56 | // Create TLS listener. 57 | tlsl := tls.NewListener(l, config) 58 | 59 | // Serve HTTP over TLS. 60 | serveHTTP1(tlsl) 61 | } 62 | 63 | // This is an example for serving HTTP and HTTPS on the same port. 64 | func Example_bothHTTPAndHTTPS() { 65 | // Create the TCP listener. 66 | l, err := net.Listen("tcp", "127.0.0.1:50051") 67 | if err != nil { 68 | log.Panic(err) 69 | } 70 | 71 | // Create a mux. 72 | m := cmux.New(l) 73 | 74 | // We first match on HTTP 1.1 methods. 75 | httpl := m.Match(cmux.HTTP1Fast()) 76 | 77 | // If not matched, we assume that its TLS. 78 | // 79 | // Note that you can take this listener, do TLS handshake and 80 | // create another mux to multiplex the connections over TLS. 81 | tlsl := m.Match(cmux.Any()) 82 | 83 | go serveHTTP1(httpl) 84 | go serveHTTPS(tlsl) 85 | 86 | if err := m.Serve(); !strings.Contains(err.Error(), "use of closed network connection") { 87 | panic(err) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /example/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/soheilhy/cmux/example 2 | 3 | go 1.11 4 | 5 | require ( 6 | github.com/golang/protobuf v1.4.3 // indirect 7 | github.com/soheilhy/cmux v0.0.0-00010101000000-000000000000 8 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb 9 | google.golang.org/genproto v0.0.0-20201207150747-9ee31aac76e7 // indirect 10 | google.golang.org/grpc v1.27.0 11 | ) 12 | 13 | replace github.com/soheilhy/cmux => ../ 14 | -------------------------------------------------------------------------------- /example/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 4 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 5 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 6 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 7 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 8 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 9 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 10 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 11 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 12 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 13 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 14 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 15 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 16 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 17 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 18 | github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= 19 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 20 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 21 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 22 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 23 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 24 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 25 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 26 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 27 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 28 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 29 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 30 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 31 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 32 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 33 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 34 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 35 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 36 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 37 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= 38 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 39 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 40 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 41 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 42 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 43 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 44 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 45 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 46 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= 47 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 48 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 49 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 50 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 51 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 52 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 53 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 54 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 55 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 56 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 57 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 58 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 59 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 60 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 61 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 62 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 63 | google.golang.org/genproto v0.0.0-20201207150747-9ee31aac76e7 h1:MrlntRhz7JNWmR2J5pRYZFgfR0IuuhELDhxo2aBZVsg= 64 | google.golang.org/genproto v0.0.0-20201207150747-9ee31aac76e7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 65 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 66 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 67 | google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= 68 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 69 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 70 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 71 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 72 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 73 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 74 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 75 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 76 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 77 | google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= 78 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 79 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 80 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 81 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/soheilhy/cmux 2 | 3 | go 1.11 4 | 5 | require golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 2 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 3 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 4 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= 5 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 6 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 7 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 8 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 9 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 10 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 11 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 12 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 13 | -------------------------------------------------------------------------------- /matchers.go: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /patricia.go: -------------------------------------------------------------------------------- 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 | "bytes" 19 | "io" 20 | ) 21 | 22 | // patriciaTree is a simple patricia tree that handles []byte instead of string 23 | // and cannot be changed after instantiation. 24 | type patriciaTree struct { 25 | root *ptNode 26 | maxDepth int // max depth of the tree. 27 | } 28 | 29 | func newPatriciaTree(bs ...[]byte) *patriciaTree { 30 | max := 0 31 | for _, b := range bs { 32 | if max < len(b) { 33 | max = len(b) 34 | } 35 | } 36 | return &patriciaTree{ 37 | root: newNode(bs), 38 | maxDepth: max + 1, 39 | } 40 | } 41 | 42 | func newPatriciaTreeString(strs ...string) *patriciaTree { 43 | b := make([][]byte, len(strs)) 44 | for i, s := range strs { 45 | b[i] = []byte(s) 46 | } 47 | return newPatriciaTree(b...) 48 | } 49 | 50 | func (t *patriciaTree) matchPrefix(r io.Reader) bool { 51 | buf := make([]byte, t.maxDepth) 52 | n, _ := io.ReadFull(r, buf) 53 | return t.root.match(buf[:n], true) 54 | } 55 | 56 | func (t *patriciaTree) match(r io.Reader) bool { 57 | buf := make([]byte, t.maxDepth) 58 | n, _ := io.ReadFull(r, buf) 59 | return t.root.match(buf[:n], false) 60 | } 61 | 62 | type ptNode struct { 63 | prefix []byte 64 | next map[byte]*ptNode 65 | terminal bool 66 | } 67 | 68 | func newNode(strs [][]byte) *ptNode { 69 | if len(strs) == 0 { 70 | return &ptNode{ 71 | prefix: []byte{}, 72 | terminal: true, 73 | } 74 | } 75 | 76 | if len(strs) == 1 { 77 | return &ptNode{ 78 | prefix: strs[0], 79 | terminal: true, 80 | } 81 | } 82 | 83 | p, strs := splitPrefix(strs) 84 | n := &ptNode{ 85 | prefix: p, 86 | } 87 | 88 | nexts := make(map[byte][][]byte) 89 | for _, s := range strs { 90 | if len(s) == 0 { 91 | n.terminal = true 92 | continue 93 | } 94 | nexts[s[0]] = append(nexts[s[0]], s[1:]) 95 | } 96 | 97 | n.next = make(map[byte]*ptNode) 98 | for first, rests := range nexts { 99 | n.next[first] = newNode(rests) 100 | } 101 | 102 | return n 103 | } 104 | 105 | func splitPrefix(bss [][]byte) (prefix []byte, rest [][]byte) { 106 | if len(bss) == 0 || len(bss[0]) == 0 { 107 | return prefix, bss 108 | } 109 | 110 | if len(bss) == 1 { 111 | return bss[0], [][]byte{{}} 112 | } 113 | 114 | for i := 0; ; i++ { 115 | var cur byte 116 | eq := true 117 | for j, b := range bss { 118 | if len(b) <= i { 119 | eq = false 120 | break 121 | } 122 | 123 | if j == 0 { 124 | cur = b[i] 125 | continue 126 | } 127 | 128 | if cur != b[i] { 129 | eq = false 130 | break 131 | } 132 | } 133 | 134 | if !eq { 135 | break 136 | } 137 | 138 | prefix = append(prefix, cur) 139 | } 140 | 141 | rest = make([][]byte, 0, len(bss)) 142 | for _, b := range bss { 143 | rest = append(rest, b[len(prefix):]) 144 | } 145 | 146 | return prefix, rest 147 | } 148 | 149 | func (n *ptNode) match(b []byte, prefix bool) bool { 150 | l := len(n.prefix) 151 | if l > 0 { 152 | if l > len(b) { 153 | l = len(b) 154 | } 155 | if !bytes.Equal(b[:l], n.prefix) { 156 | return false 157 | } 158 | } 159 | 160 | if n.terminal && (prefix || len(n.prefix) == len(b)) { 161 | return true 162 | } 163 | 164 | if l >= len(b) { 165 | return false 166 | } 167 | 168 | nextN, ok := n.next[b[l]] 169 | if !ok { 170 | return false 171 | } 172 | 173 | if l == len(b) { 174 | b = b[l:l] 175 | } else { 176 | b = b[l+1:] 177 | } 178 | return nextN.match(b, prefix) 179 | } 180 | -------------------------------------------------------------------------------- /patricia_test.go: -------------------------------------------------------------------------------- 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 | "strings" 19 | "testing" 20 | ) 21 | 22 | func testPTree(t *testing.T, strs ...string) { 23 | pt := newPatriciaTreeString(strs...) 24 | for _, s := range strs { 25 | if !pt.match(strings.NewReader(s)) { 26 | t.Errorf("%s is not matched by %s", s, s) 27 | } 28 | 29 | if !pt.matchPrefix(strings.NewReader(s + s)) { 30 | t.Errorf("%s is not matched as a prefix by %s", s+s, s) 31 | } 32 | 33 | if pt.match(strings.NewReader(s + s)) { 34 | t.Errorf("%s matches %s", s+s, s) 35 | } 36 | 37 | // The following tests are just to catch index out of 38 | // range and off-by-one errors and not the functionality. 39 | pt.matchPrefix(strings.NewReader(s[:len(s)-1])) 40 | pt.match(strings.NewReader(s[:len(s)-1])) 41 | pt.matchPrefix(strings.NewReader(s + "$")) 42 | pt.match(strings.NewReader(s + "$")) 43 | } 44 | } 45 | 46 | func TestPatriciaOnePrefix(t *testing.T) { 47 | testPTree(t, "prefix") 48 | } 49 | 50 | func TestPatriciaNonOverlapping(t *testing.T) { 51 | testPTree(t, "foo", "bar", "dummy") 52 | } 53 | 54 | func TestPatriciaOverlapping(t *testing.T) { 55 | testPTree(t, "foo", "far", "farther", "boo", "ba", "bar") 56 | } 57 | --------------------------------------------------------------------------------