Documentation ¶
Overview ¶
Package cmux is a library to multiplex network connections based on their payload. Using cmux, you can serve different protocols from the same listener.
Example ¶
package main import ( "fmt" "io" "log" "net" "net/http" "net/rpc" "strings" "google.golang.org/grpc" "golang.org/x/net/context" "golang.org/x/net/websocket" "github.com/soheilhy/cmux" grpchello "google.golang.org/grpc/examples/helloworld/helloworld" ) type exampleHTTPHandler struct{} func (h *exampleHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "example http response") } func serveHTTP(l net.Listener) { s := &http.Server{ Handler: &exampleHTTPHandler{}, } if err := s.Serve(l); err != cmux.ErrListenerClosed { panic(err) } } func EchoServer(ws *websocket.Conn) { if _, err := io.Copy(ws, ws); err != nil { panic(err) } } func serveWS(l net.Listener) { s := &http.Server{ Handler: websocket.Handler(EchoServer), } if err := s.Serve(l); err != cmux.ErrListenerClosed { panic(err) } } type ExampleRPCRcvr struct{} func (r *ExampleRPCRcvr) Cube(i int, j *int) error { *j = i * i return nil } func serveRPC(l net.Listener) { s := rpc.NewServer() if err := s.Register(&ExampleRPCRcvr{}); err != nil { panic(err) } for { conn, err := l.Accept() if err != nil { if err != cmux.ErrListenerClosed { panic(err) } return } go s.ServeConn(conn) } } type grpcServer struct{} func (s *grpcServer) SayHello(ctx context.Context, in *grpchello.HelloRequest) ( *grpchello.HelloReply, error) { return &grpchello.HelloReply{Message: "Hello " + in.Name + " from cmux"}, nil } func serveGRPC(l net.Listener) { grpcs := grpc.NewServer() grpchello.RegisterGreeterServer(grpcs, &grpcServer{}) if err := grpcs.Serve(l); err != cmux.ErrListenerClosed { panic(err) } } func main() { l, err := net.Listen("tcp", "127.0.0.1:50051") if err != nil { log.Panic(err) } m := cmux.New(l) // We first match the connection against HTTP2 fields. If matched, the // connection will be sent through the "grpcl" listener. grpcl := m.Match(cmux.HTTP2HeaderField("content-type", "application/grpc")) //Otherwise, we match it againts a websocket upgrade request. wsl := m.Match(cmux.HTTP1HeaderField("Upgrade", "websocket")) // Otherwise, we match it againts HTTP1 methods. If matched, // it is sent through the "httpl" listener. httpl := m.Match(cmux.HTTP1Fast()) // If not matched by HTTP, we assume it is an RPC connection. rpcl := m.Match(cmux.Any()) // Then we used the muxed listeners. go serveGRPC(grpcl) go serveWS(wsl) go serveHTTP(httpl) go serveRPC(rpcl) if err := m.Serve(); !strings.Contains(err.Error(), "use of closed network connection") { panic(err) } }
Output:
Example (BothHTTPAndHTTPS) ¶
This is an example for serving HTTP and HTTPS on the same port.
package main import ( "crypto/rand" "crypto/tls" "fmt" "log" "net" "net/http" "strings" "github.com/soheilhy/cmux" ) type anotherHTTPHandler struct{} func (h *anotherHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "example http response") } func serveHTTP1(l net.Listener) { s := &http.Server{ Handler: &anotherHTTPHandler{}, } if err := s.Serve(l); err != cmux.ErrListenerClosed { panic(err) } } func serveHTTPS(l net.Listener) { // Load certificates. certificate, err := tls.LoadX509KeyPair("cert.pem", "key.pem") if err != nil { log.Panic(err) } config := &tls.Config{ Certificates: []tls.Certificate{certificate}, Rand: rand.Reader, } // Create TLS listener. tlsl := tls.NewListener(l, config) // Serve HTTP over TLS. serveHTTP1(tlsl) } // This is an example for serving HTTP and HTTPS on the same port. func main() { // Create the TCP listener. l, err := net.Listen("tcp", "127.0.0.1:50051") if err != nil { log.Panic(err) } // Create a mux. m := cmux.New(l) // We first match on HTTP 1.1 methods. httpl := m.Match(cmux.HTTP1Fast()) // If not matched, we assume that its TLS. // // Note that you can take this listener, do TLS handshake and // create another mux to multiplex the connections over TLS. tlsl := m.Match(cmux.Any()) go serveHTTP1(httpl) go serveHTTPS(tlsl) if err := m.Serve(); !strings.Contains(err.Error(), "use of closed network connection") { panic(err) } }
Output:
Example (RecursiveCmux) ¶
This is an example for serving HTTP, HTTPS, and GoRPC/TLS on the same port.
package main import ( "crypto/rand" "crypto/tls" "fmt" "log" "net" "net/http" "net/rpc" "strings" "github.com/soheilhy/cmux" ) type recursiveHTTPHandler struct{} func (h *recursiveHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "example http response") } func recursiveServeHTTP(l net.Listener) { s := &http.Server{ Handler: &recursiveHTTPHandler{}, } if err := s.Serve(l); err != cmux.ErrListenerClosed { panic(err) } } func tlsListener(l net.Listener) net.Listener { // Load certificates. certificate, err := tls.LoadX509KeyPair("cert.pem", "key.pem") if err != nil { log.Panic(err) } config := &tls.Config{ Certificates: []tls.Certificate{certificate}, Rand: rand.Reader, } // Create TLS listener. tlsl := tls.NewListener(l, config) return tlsl } type RecursiveRPCRcvr struct{} func (r *RecursiveRPCRcvr) Cube(i int, j *int) error { *j = i * i return nil } func recursiveServeRPC(l net.Listener) { s := rpc.NewServer() if err := s.Register(&RecursiveRPCRcvr{}); err != nil { panic(err) } for { conn, err := l.Accept() if err != nil { if err != cmux.ErrListenerClosed { panic(err) } return } go s.ServeConn(conn) } } // This is an example for serving HTTP, HTTPS, and GoRPC/TLS on the same port. func main() { // Create the TCP listener. l, err := net.Listen("tcp", "127.0.0.1:50051") if err != nil { log.Panic(err) } // Create a mux. tcpm := cmux.New(l) // We first match on HTTP 1.1 methods. httpl := tcpm.Match(cmux.HTTP1Fast()) // If not matched, we assume that its TLS. tlsl := tcpm.Match(cmux.Any()) tlsl = tlsListener(tlsl) // Now, we build another mux recursively to match HTTPS and GoRPC. // You can use the same trick for SSH. tlsm := cmux.New(tlsl) httpsl := tlsm.Match(cmux.HTTP1Fast()) gorpcl := tlsm.Match(cmux.Any()) go recursiveServeHTTP(httpl) go recursiveServeHTTP(httpsl) go recursiveServeRPC(gorpcl) go func() { if err := tlsm.Serve(); err != cmux.ErrListenerClosed { panic(err) } }() if err := tcpm.Serve(); !strings.Contains(err.Error(), "use of closed network connection") { panic(err) } }
Output:
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrListenerClosed = errListenerClosed("mux: listener closed")
ErrListenerClosed is returned from muxListener.Accept when the underlying listener is closed.
Functions ¶
This section is empty.
Types ¶
type CMux ¶
type CMux interface { // Match returns a net.Listener that sees (i.e., accepts) only // the connections matched by at least one of the matcher. // // 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) }
CMux is a multiplexer for network connections.
type ErrNotMatched ¶
type ErrNotMatched struct {
// contains filtered or unexported fields
}
ErrNotMatched is returned whenever a connection is not matched by any of the matchers registered in the multiplexer.
func (ErrNotMatched) Error ¶
func (e ErrNotMatched) Error() string
func (ErrNotMatched) Temporary ¶
func (e ErrNotMatched) Temporary() bool
Temporary implements the net.Error interface.
func (ErrNotMatched) Timeout ¶
func (e ErrNotMatched) Timeout() bool
Timeout implements the net.Error interface.
type ErrorHandler ¶
ErrorHandler handles an error and returns whether the mux should continue serving the listener.
type MatchWriter ¶
MatchWriter is a match that can also write response (say to do handshake).
func HTTP2MatchHeaderFieldSendSettings ¶
func HTTP2MatchHeaderFieldSendSettings(name, value string) MatchWriter
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.
type Matcher ¶
Matcher matches a connection based on its content.
func HTTP1 ¶
func HTTP1() Matcher
HTTP1 parses the first line or upto 4096 bytes of the request to see if the conection contains an HTTP request.
func HTTP1Fast ¶
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 HTTP1HeaderField ¶
HTTP1HeaderField returns a matcher matching the header fields of the first request of an HTTP 1 connection.
func HTTP2 ¶
func HTTP2() Matcher
HTTP2 parses the frame header of the first frame to detect whether the connection is an HTTP2 connection.
func HTTP2HeaderField ¶
HTTP2HeaderField resturns a matcher matching the header fields of the first headers frame.
func PrefixMatcher ¶
PrefixMatcher returns a matcher that matches a connection if it starts with any of the strings in strs.
type MuxConn ¶
MuxConn wraps a net.Conn and provides transparent sniffing of connection data.
func (*MuxConn) Read ¶
From the io.Reader documentation:
When Read encounters an error or end-of-file condition after successfully reading n > 0 bytes, it returns the number of bytes read. It may return the (non-nil) error from the same call or return the error (and n == 0) from a subsequent call. An instance of this general case is that a Reader returning a non-zero number of bytes at the end of the input stream may return either err == EOF or err == nil. The next Read should return 0, EOF.