mux

package
v1.2.101 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 12, 2023 License: MIT Imports: 29 Imported by: 0

README

Build Status GoDoc Report card Sourcegraph

mux: Connection Mux

mux is a generic Go library to multiplex connections based on their payload. Using mux, you can serve gRPC, SSH, HTTPS, HTTP, Go RPC, and pretty much any other protocol on the same TCP listener.

How-To

Simply create your main listener, create a mux for that listener, and then match connections:

package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"

	"github.com/searKing/golang/go/net/mux"
	"golang.org/x/net/http2/hpack"
)

func main() {
	srv := mux.NewServer()
	defer srv.Close()

	// We first match the connection against HTTP2 fields. If matched, the
	// connection will be sent through the "grpcl" listener.
	grpcl := mux.HandleListener(mux.HTTP2HeaderFieldValue(false, strings.EqualFold, hpack.HeaderField{
		Name:  "Content-Type",
		Value: "application/grpc",
	}))
	//Otherwise, we match it againts a websocket upgrade request.
	var wslH = http.Header{}
	wslH.Set("Upgrade", "websocket")
	wsl := mux.HandleListener(mux.HTTP1HeaderEqual(wslH))

	// Otherwise, we match it againts HTTP1 methods. If matched,
	// it is sent through the "httpl" listener.
	httpl := mux.HandleListener(mux.HTTP1Fast())
	// If not matched by HTTP, we assume it is an RPC connection.
	rpcl := mux.HandleListener(mux.Any())

	// Then we used the muxed listeners.
	// See safeServe in github.com/searKing/golang/go/net/mux/mux_helper_test.go
	go serveGRPC(grpcl)
	go serveWS(wsl)
	go serveHTTP(httpl)
	go serveRPC(rpcl)

	idleConnsClosed := make(chan struct{})
	go func() {
		sigint := make(chan os.Signal, 1)
		signal.Notify(sigint, os.Interrupt)
		<-sigint

		// We received an interrupt signal, shut down.
		if err := srv.Shutdown(context.Background()); err != nil {
			// Error from closing listeners, or context timeout:
			log.Printf("mux server Shutdown: %v", err)
		}
		close(idleConnsClosed)
	}()
	if err := srv.ListenAndServe("localhost:0"); err != mux.ErrServerClosed {
		// Error starting or closing listener:
		log.Printf("mux server ListenAndServe: %v", err)
	}
	<-idleConnsClosed
}

Take a look at other examples in the GoDoc.

Docs

Performance

There is room for improvment but, since we are only matching the very first bytes of a connection, the performance overheads on long-lived connections (i.e., RPCs and pipelined HTTP streams) is negligible.

Limitations

  • TLS: net/http uses a type assertion to identify TLS connections; since mux's lookahead-implementing connection wraps the underlying TLS connection, this type assertion fails. Because of that, you can serve HTTPS using mux but http.Request.TLS would not be set in your handlers.

  • Different Protocols on The Same Connection: mux matches the connection when it's accepted. For example, one connection can be either gRPC or REST, but not both. That is, we assume that a client connection is either used for gRPC or REST.

  • Java gRPC Clients: Java gRPC client blocks until it receives a SETTINGS frame from the server. If you are using the Java client to connect to a mux'ed gRPC server please match with writers:

package main

import (
	"strings"

	"github.com/searKing/golang/go/net/mux"
	"golang.org/x/net/http2/hpack"
)

func main() {
	grpcl := mux.HandleListener(mux.HTTP2HeaderFieldValue(false, strings.EqualFold, hpack.HeaderField{
		Name:  "Content-Type",
		Value: "application/grpc",
	}))
	_ = grpcl
}

Thanks to

Copyright 2019 The searKing Authors. All rights reserved.

Code is released under the MIT license.

Documentation

Overview

Package mux is a library to multiplex network connections based on their payload. Using mux, you can serve different protocols from the same listener.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ServerContextKey is a context key. It can be used in cmux
	// handlers with context.WithValue to access the server that
	// started the handler. The associated value will be of
	// type CMuxer.
	ServerContextKey = &contextKey{"cmux-server"}

	// LocalAddrContextKey is a context key. It can be used in
	// cmux handlers with context.WithValue to access the local
	// address the connection arrived on.
	// The associated value will be of type net.Addr.
	LocalAddrContextKey = &contextKey{"local-addr"}
)
View Source
var DefaultServeMux = &defaultServeMux

DefaultServeMux is the default ServeMux used by Serve.

View Source
var ErrAbortHandler = errors.New("net/mux: abort HandlerConn")

ErrAbortHandler is a sentinel panic value to abort a handler. While any panic from ServeHTTP aborts the response to the client, panicking with ErrAbortHandler also suppresses logging of a stack trace to the server's error log.

View Source
var (
	// ErrHijacked is returned by ResponseWriter.Write calls when
	// the underlying connection has been hijacked using the
	// Hijacker interface. A zero-byte write on a hijacked
	// connection will return ErrHijacked without any other side
	// effects.
	ErrHijacked = errors.New("cmux: connection has been hijacked")
)
View Source
var ErrListenerClosed = net_.ErrListenerClosed

ErrListenerClosed is returned from MuxListener.Accept when the underlying listener is closed.

View Source
var ErrServerClosed = errors.New("net/mux: Server closed")

ErrServerClosed is returned by the Server's Serve, ServeTLS, ListenAndServe, and ListenAndServeTLS methods after a call to Shutdown or Close.

Functions

func ConnStateSliceContains

func ConnStateSliceContains(enums []ConnState, sunEnums ...ConnState) bool

ConnStateSliceContains reports whether sunEnums is within enums.

func ConnStateSliceContainsAny

func ConnStateSliceContainsAny(enums []ConnState, sunEnums ...ConnState) bool

ConnStateSliceContainsAny reports whether any sunEnum is within enums.

func Handle

func Handle(pattern Matcher, handler HandlerConn)

func HandleFunc

func HandleFunc(pattern Matcher, handler func(net.Conn))

HandleFunc registers the handler function for the given pattern.

func HandleListener

func HandleListener(pattern Matcher) net.Listener

func ListenAndServe

func ListenAndServe(addr string, handler HandlerConn) error

ListenAndServe listens on the TCP network address addr and then calls Serve with handler to handle requests on incoming connections. Accepted connections are configured to enable TCP keep-alives.

The handler is typically nil, in which case the DefaultServeMux is used.

ListenAndServe always returns a non-nil error.

func ListenAndServeTLS

func ListenAndServeTLS(addr string, handler HandlerConn, tlsConfig *tls.Config, certFile, keyFile string) error

ListenAndServeTLS acts identically to ListenAndServe, except that it expects HTTPS connections. Additionally, files containing a certificate and matching private key for the server must be provided. If the certificate is signed by a certificate authority, the certFile should be the concatenation of the server's certificate, any intermediates, and the CA's certificate.

func NotFound

func NotFound(c net.Conn)

NotFound replies to the request with an HTTP 404 not found error.

func SetReadTimeout

func SetReadTimeout(t time.Duration)

Types

type ConnState

type ConnState int

A ConnState represents the state of a client connection to a server. It's used by the optional Server.ConnStateHook hook.

const (
	// ConnStateNew represents a new connection that is expected to
	// send a request immediately. Connections begin at this
	// state and then transition to either ConnStateActive or
	// ConnStateClosed.
	ConnStateNew ConnState = iota

	// ConnStateActive represents a connection that has read 1 or more
	// bytes of a request. The Server.ConnStateHook hook for
	// ConnStateActive fires before the request has entered a handler
	// and doesn't fire again until the request has been
	// handled. After the request is handled, the state
	// transitions to ConnStateClosed, ConnStateHijacked, or ConnStateIdle.
	// For HTTP/2, ConnStateActive fires on the transition from zero
	// to one active request, and only transitions away once all
	// active requests are complete. That means that ConnStateHook
	// cannot be used to do per-request work; ConnStateHook only notes
	// the overall state of the connection.
	ConnStateActive

	// ConnStateIdle represents a connection that has finished
	// handling a request and is in the keep-alive state, waiting
	// for a new request. Connections transition from ConnStateIdle
	// to either ConnStateActive or ConnStateClosed.
	ConnStateIdle

	// ConnStateHijacked represents a hijacked connection.
	// This is a terminal state. It does not transition to ConnStateClosed.
	ConnStateHijacked

	// ConnStateClosed represents a closed connection.
	// This is a terminal state. Hijacked connections do not
	// transition to ConnStateClosed.
	ConnStateClosed
)

func ConnStateValues

func ConnStateValues() []ConnState

ConnStateValues returns all values of the enum

func ParseConnStateString

func ParseConnStateString(s string) (ConnState, error)

ParseConnStateString retrieves an enum value from the enum constants string name. Throws an error if the param is not part of the enum.

func (ConnState) MarshalBinary

func (i ConnState) MarshalBinary() (data []byte, err error)

MarshalBinary implements the encoding.BinaryMarshaler interface for ConnState

func (ConnState) MarshalJSON

func (i ConnState) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface for ConnState

func (ConnState) MarshalText

func (i ConnState) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface for ConnState

func (ConnState) MarshalYAML

func (i ConnState) MarshalYAML() (interface{}, error)

MarshalYAML implements a YAML Marshaler for ConnState

func (ConnState) Registered

func (i ConnState) Registered() bool

IsAConnState returns "true" if the value is listed in the enum definition. "false" otherwise

func (*ConnState) Scan

func (i *ConnState) Scan(value interface{}) error

func (ConnState) String

func (i ConnState) String() string

func (*ConnState) UnmarshalBinary

func (i *ConnState) UnmarshalBinary(data []byte) error

UnmarshalBinary implements the encoding.BinaryUnmarshaler interface for ConnState

func (*ConnState) UnmarshalJSON

func (i *ConnState) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface for ConnState

func (*ConnState) UnmarshalText

func (i *ConnState) UnmarshalText(text []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface for ConnState

func (*ConnState) UnmarshalYAML

func (i *ConnState) UnmarshalYAML(unmarshal func(interface{}) error) error

UnmarshalYAML implements a YAML Unmarshaler for ConnState

func (ConnState) Value

func (i ConnState) Value() (driver.Value, error)

type EmptyServerOption

type EmptyServerOption struct{}

EmptyServerOption does not alter the configuration. It can be embedded in another structure to build custom options.

This API is EXPERIMENTAL.

type ErrorHandler

type ErrorHandler interface {
	Continue(error) bool
}

type ErrorHandlerFunc

type ErrorHandlerFunc func(error) bool

ErrorHandler handles an error and returns whether the mux should continue serving the listener.

func (ErrorHandlerFunc) Continue

func (f ErrorHandlerFunc) Continue(err error) bool

type HandlerConn

type HandlerConn interface {
	Serve(net.Conn)
}

func NotFoundHandler

func NotFoundHandler() HandlerConn

NotFoundHandler returns a simple request handler that replies to each request with a “404 page not found” reply.

type HandlerConnFunc

type HandlerConnFunc func(net.Conn)

HandlerConnFunc is a match that can also write response (say to do handshake).

func (HandlerConnFunc) Serve

func (f HandlerConnFunc) Serve(c net.Conn)

type Matcher

type Matcher interface {
	Match(io.Writer, io.Reader) bool
}

func MatcherAny

func MatcherAny(matchers ...Matcher) Matcher

type MatcherFunc

type MatcherFunc func(io.Writer, io.Reader) bool

MatcherFunc is a match that can also write response (say to do handshake).

func Any

func Any() MatcherFunc

Any is a Matcher that matches any connection.

func AnyPrefixByteMatcher

func AnyPrefixByteMatcher(list ...[]byte) MatcherFunc

func AnyPrefixMatcher

func AnyPrefixMatcher(strs ...string) MatcherFunc

AnyPrefixMatcher returns a matcher that matches a connection if it starts with any of the strings in strs.

func GRPC

func GRPC() MatcherFunc

GRPC parses the frame header of the first frame to detect whether the connection is an HTTP2 connection.

func HTTP

func HTTP() MatcherFunc

PRI * HTTP/2.0\r\n\r\n HTTP parses the first line or upto 4096 bytes of the request to see if the connection contains an HTTP request.

func HTTP1

func HTTP1() MatcherFunc

HTTP1 parses the first line or upto 4096 bytes of the request to see if the conection contains an HTTP request.

func HTTP1Fast

func HTTP1Fast(extMethods ...string) MatcherFunc

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 HTTP1Header

func HTTP1Header(match func(actual, expect http.Header) bool, expect http.Header) MatcherFunc

HTTP1Header returns true if all headers are expected

func HTTP1HeaderEqual

func HTTP1HeaderEqual(header http.Header) MatcherFunc

HTTP1HeaderEqual returns a matcher matching the header fields of the first request of an HTTP 1 connection. strings.Equal for all value in expects

func HTTP1HeaderPrefix

func HTTP1HeaderPrefix(header http.Header) MatcherFunc

HTTP1HeaderPrefix returns a matcher matching the header fields of the first request of an HTTP 1 connection. If the header with key name has a value prefixed with valuePrefix, this will match. strings.HasPrefix for all value in expects

func HTTP1HeaderValue

func HTTP1HeaderValue(match func(actualVal, expectVal string) bool, expect http.Header) MatcherFunc

HTTP1HeaderValue returns true if all headers are expected, shorthand for HTTP1Header strings.Match for all value in expects

func HTTP2

func HTTP2() MatcherFunc

HTTP2 parses the frame header of the first frame to detect whether the connection is an HTTP2 connection.

func HTTP2HeaderField

func HTTP2HeaderField(sendSetting bool,
	match func(actual, expect map[string]hpack.HeaderField) bool,
	expects ...hpack.HeaderField) MatcherFunc

HTTP2HeaderField returns a matcher matching the header fields of the first headers frame. writes the settings to the server if sendSetting is true. Prefer HTTP2HeaderField over this one, if the client does not block on receiving a SETTING frame.

func HTTP2HeaderFieldEqual

func HTTP2HeaderFieldEqual(sendSetting bool, headers ...hpack.HeaderField) MatcherFunc

HTTP2HeaderFieldEqual returns a matcher matching the header fields.

func HTTP2HeaderFieldPrefix

func HTTP2HeaderFieldPrefix(sendSetting bool, headers ...hpack.HeaderField) MatcherFunc

HTTP2HeaderFieldPrefix returns a matcher matching the header fields. If the header with key name has a value prefixed with valuePrefix, this will match.

func HTTP2HeaderFieldValue

func HTTP2HeaderFieldValue(sendSetting bool, match func(actualVal, expectVal string) bool, expects ...hpack.HeaderField) MatcherFunc

helper functions

func TLS

func TLS(versions ...int) MatcherFunc

TLS matches HTTPS requests.

By default, any TLS handshake packet is matched. An optional whitelist of versions can be passed in to restrict the matcher, for example:

TLS(tls.VersionTLS11, tls.VersionTLS12)

reverse of crypto/tls/conn.go func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { HandlerShake of TLS type byte // recordTypeHandshake versions [2]byte

func (MatcherFunc) Match

func (f MatcherFunc) Match(w io.Writer, r io.Reader) bool

type ServeMux

type ServeMux struct {
	// NotFound replies to the listener with a not found error.
	NotFoundHandler HandlerConn
	// contains filtered or unexported fields
}

ServeMux is a net.ServeMux that accepts only the connections that matched. goroutine unsafe

func NewServeMux

func NewServeMux() *ServeMux

NewServeMux allocates and returns a new ServeMux.

func (*ServeMux) Close

func (mux *ServeMux) Close() error

func (*ServeMux) Handle

func (mux *ServeMux) Handle(pattern Matcher, handler HandlerConn)

func (*ServeMux) HandleFunc

func (mux *ServeMux) HandleFunc(pattern Matcher, handler func(net.Conn))

HandleFunc registers the handler function for the given pattern.

func (*ServeMux) HandleListener

func (mux *ServeMux) HandleListener(pattern Matcher) net.Listener

func (*ServeMux) Handler

func (mux *ServeMux) Handler(c *sniffConn) (h HandlerConn)

Handler is the main implementation of HandlerConn.

func (*ServeMux) Serve

func (mux *ServeMux) Serve(c net.Conn)

func (*ServeMux) SetReadTimeout

func (mux *ServeMux) SetReadTimeout(t time.Duration)

SetReadTimeout sets a timeout for the read of matchers

type Server

type Server struct {
	Handler HandlerConn // handler to invoke, mux.DefaultServeMux if nil

	// ConnStateHook specifies an optional callback function that is
	// called when a client connection changes state. See the
	// ConnStateHook type and associated constants for details.
	ConnStateHook func(net.Conn, ConnState)
	// contains filtered or unexported fields
}

Server is a multiplexer for network connections.

func NewServer

func NewServer() *Server

NewServer wraps NewServerWithContext using the background context.

func NewServerWithContext

func NewServerWithContext(ctx context.Context) *Server

NewServerWithContext returns a new Server.

func (*Server) ApplyOptions

func (srv *Server) ApplyOptions(options ...ServerOption) *Server

func (*Server) Close

func (srv *Server) Close() error

Close immediately closes all active net.Listeners and any connections in state StateNew, StateActive, or StateIdle. For a graceful shutdown, use Shutdown.

Close does not attempt to close (and does not even know about) any hijacked connections, such as WebSockets.

Close returns any error returned from closing the ServeMux's underlying ServeMux(s).

func (*Server) Context

func (srv *Server) Context() context.Context

Context returns the request's context. To change the context, use WithContext.

The returned context is always non-nil; it defaults to the background context.

func (*Server) HandleError

func (srv *Server) HandleError(h ErrorHandler)

HandleError registers an error handler that handles listener errors.

func (*Server) ListenAndServe

func (srv *Server) ListenAndServe(addr string) error

ListenAndServe listens on the TCP network address srv.Addr and then calls Serve to handle requests on incoming connections. Accepted connections are configured to enable TCP keep-alives.

If srv.Addr is blank, ":http" is used.

ListenAndServe always returns a non-nil error. After Shutdown or Close, the returned error is ErrServerClosed.

func (*Server) ListenAndServeTLS

func (srv *Server) ListenAndServeTLS(addr string, tlsConfig *tls.Config, certFile, keyFile string) error

ListenAndServeTLS listens on the TCP network address srv.Addr and then calls ServeTLS to handle requests on incoming TLS connections. Accepted connections are configured to enable TCP keep-alives.

Filenames containing a certificate and matching private key for the server must be provided if neither the ServeMux's TLSConfig.Certificates nor TLSConfig.GetCertificate are populated. If the certificate is signed by a certificate authority, the certFile should be the concatenation of the server's certificate, any intermediates, and the CA's certificate.

If srv.Addr is blank, ":https" is used.

ListenAndServeTLS always returns a non-nil error. After Shutdown or Close, the returned error is ErrServerClosed.

func (*Server) RegisterOnShutdown

func (srv *Server) RegisterOnShutdown(f func())

RegisterOnShutdown registers a function to call on Shutdown. This can be used to gracefully shutdown connections that have undergone NPN/ALPN protocol upgrade or that have been hijacked. This function should start protocol-specific graceful shutdown, but should not wait for shutdown to complete.

func (*Server) Serve

func (srv *Server) Serve(l net.Listener) error

Serve starts multiplexing the listener. Serve blocks and perhaps should be invoked concurrently within a go routine. Serve accepts incoming connections on the ServeMux l, creating a new service goroutine for each. The service goroutines read requests and then call srv.HandlerConn to reply to them.

func (*Server) ServeTLS

func (srv *Server) ServeTLS(l net.Listener, tLSConfig *tls.Config, certFile, keyFile string) error

ServeTLS accepts incoming connections on the ServeMux l, creating a new service goroutine for each. The service goroutines perform TLS setup and then read requests, calling srv.HandlerConn to reply to them.

Files containing a certificate and matching private key for the server must be provided if neither the ServeMux's TLSConfig.Certificates nor TLSConfig.GetCertificate are populated. If the certificate is signed by a certificate authority, the certFile should be the concatenation of the server's certificate, any intermediates, and the CA's certificate.

ServeTLS always returns a non-nil error. After Shutdown or Close, the returned error is ErrServerClosed.

func (*Server) Shutdown

func (srv *Server) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners, then closing all idle connections, and then waiting indefinitely for connections to return to idle and then shut down. If the provided context expires before the shutdown is complete, Shutdown returns the context's error, otherwise it returns any error returned from closing the ServeMux's underlying ServeMux(s).

When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return ErrServerClosed. Make sure the program doesn't exit and waits instead for Shutdown to return.

Shutdown does not attempt to close nor wait for hijacked connections such as WebSockets. The caller of Shutdown should separately notify such long-lived connections of shutdown and wait for them to close, if desired. See RegisterOnShutdown for a way to register shutdown notification functions.

Once Shutdown has been called on a server, it may not be reused; future calls to methods such as Serve will return ErrServerClosed.

type ServerOption

type ServerOption interface {
	// contains filtered or unexported methods
}

A ServerOption sets options.

func WithErrorLog

func WithErrorLog(errorLog *log.Logger) ServerOption

WithErrorLog specifies an optional logger for errors

func WithMaxIdleConns

func WithMaxIdleConns(maxIdleConns int) ServerOption

WithMaxIdleConns controls the maximum number of idle (keep-alive) connections across all hosts. Zero means no limit.

type ServerOptionFunc

type ServerOptionFunc func(*Server)

ServerOptionFunc wraps a function that modifies Server into an implementation of the ServerOption interface.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL