Documentation
¶
Overview ¶
Package connmux multiplexes network connections based on their initial bytes.
It lets you serve multiple protocols (gRPC/HTTP/1/HTTP/2/raw TCP, etc.) on the same listening port by sniffing the beginning of each accepted connection and dispatching it to a protocol-specific net.Listener.
This package is conceptually similar to github.com/soheilhy/cmux (Apache-2.0) but is an independent implementation tailored for this repository.
## Basics
root, _ := net.Listen("tcp", ":8080")
m := connmux.New(root,
connmux.WithReadTimeout(2*time.Second),
connmux.WithMaxSniffBytes(1<<20),
)
// Match order defines priority.
grpcL := m.Match(connmux.HTTP2HeaderField("content-type", "application/grpc"))
httpL := m.Match(connmux.HTTP1Fast())
other := m.Match(connmux.Any())
go grpcServer.Serve(grpcL)
go httpServer.Serve(httpL)
go serveRaw(other)
_ = m.Serve()
## MatchWithWriters (Java gRPC)
Some clients (notably Java gRPC) may wait for the server SETTINGS frame before sending request headers. In those cases use MatchWithWriters with HTTP2HeaderFieldSendSettings:
grpcL := m.MatchWithWriters(
connmux.HTTP2HeaderFieldSendSettings("content-type", "application/grpc"),
)
## Notes / limitations
- The match decision is made when a connection is accepted. A single connection cannot switch protocols later.
- Matching is based on reading and buffering initial bytes. Use WithMaxSniffBytes to cap memory per connection and WithReadTimeout to avoid slowloris-style hangs during sniffing.
- If you terminate TLS after connmux, some stdlib components may not detect the underlying *tls.Conn due to type assertions on net.Conn wrappers. If your handler relies on TLS-specific state, prefer terminating TLS before multiplexing.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNotMatched = errors.New("connmux: connection not matched")
ErrNotMatched indicates that an accepted connection did not match any rule.
var ErrServerClosed = errors.New("connmux: server closed")
ErrServerClosed is returned by (*Mux).Serve after Close is called.
Functions ¶
This section is empty.
Types ¶
type MatchWriter ¶
MatchWriter is like Matcher but can also write back to the connection during sniffing (e.g. sending HTTP/2 SETTINGS to unblock certain clients).
func HTTP2HeaderFieldSendSettings ¶
func HTTP2HeaderFieldSendSettings(name, value string) MatchWriter
HTTP2HeaderFieldSendSettings is a MatchWriter that writes an initial SETTINGS frame (when appropriate) while sniffing, then matches based on a header field.
This is useful for clients that wait for server SETTINGS before sending request HEADERS (e.g. Java gRPC).
type Matcher ¶
Matcher matches a connection by reading from r.
Notes:
- r is a sniffing reader: reads are buffered and will be replayed to the final protocol server once the connection is dispatched.
- Each matcher is evaluated against a fresh reader starting from the beginning of the buffered stream (OR semantics).
func HTTP1Fast ¶
func HTTP1Fast() Matcher
HTTP1Fast matches common HTTP/1.x methods using a small prefix check. It is faster but less accurate than HTTP1().
func HTTP2 ¶
func HTTP2() Matcher
HTTP2 matches an HTTP/2 connection by verifying the client preface.
func HTTP2HeaderField ¶
HTTP2HeaderField matches an HTTP/2 connection by looking for an exact header field value (case-insensitive name match).
Note: some clients (notably Java gRPC) will not send HEADERS until they receive a SETTINGS frame from the server; for those, use HTTP2HeaderFieldSendSettings with MatchWithWriters.
func HTTP2HeaderFieldPrefix ¶
HTTP2HeaderFieldPrefix matches an HTTP/2 connection by looking for a header field value prefix (case-insensitive name match).
type Mux ¶
type Mux struct {
// contains filtered or unexported fields
}
Mux multiplexes a single net.Listener into multiple protocol-specific listeners.
Example ¶
package main
import (
"fmt"
"io"
"net"
"time"
"github.com/pubgo/funk/v2/closer"
"github.com/pubgo/funk/v2/connmux"
)
func main() {
root, _ := net.Listen("tcp", "127.0.0.1:0")
defer closer.SafeClose(root)
m := connmux.New(root, connmux.WithReadTimeout(2*time.Second))
httpL := m.Match(connmux.HTTP1Fast())
_ = m.Match(connmux.Any())
go func() { _ = m.Serve() }()
defer closer.SafeClose(m)
c, _ := net.Dial("tcp", root.Addr().String())
defer closer.SafeClose(c)
_, _ = c.Write([]byte("GET / HTTP/1.1\r\nHost: example\r\n\r\n"))
s, _ := httpL.Accept()
defer closer.SafeClose(s)
b := make([]byte, 3)
_, _ = io.ReadFull(s, b)
fmt.Println(string(b))
}
Output: GET
func (*Mux) Match ¶
Match registers a rule and returns a listener that only accepts matching connections. Match order defines priority.
func (*Mux) MatchWithWriters ¶
func (m *Mux) MatchWithWriters(writers ...MatchWriter) net.Listener
MatchWithWriters registers a rule composed of match-writers. Match order defines priority.
type Option ¶
type Option func(*Mux)
Option configures a Mux.
func WithConnBacklog ¶
WithConnBacklog sets the per-matched-listener connection backlog. Defaults to 128.
func WithErrorHandler ¶
WithErrorHandler sets a handler for accept/match errors. If h returns true, Serve continues; otherwise Serve returns the error.
func WithMaxSniffBytes ¶
WithMaxSniffBytes caps how many bytes can be buffered while matching. Defaults to 1 MiB.
func WithReadTimeout ¶
WithReadTimeout sets a per-read deadline while sniffing. A zero duration disables deadlines (default).