2
0
mirror of https://github.com/soheilhy/cmux.git synced 2026-08-29 14:54:30 +08:00

16 Commits

Author SHA1 Message Date
Soheil Hassas Yeganeh
7b740c1fa6 Modernize for current Go toolchains
The library already compiled and passed tests, but tooling and
dependencies were stale. This brings everything up to current standards
without changing public behavior.

Library (go 1.23.0):
- cmux_test.go: fix 8 `go vet` failures by sending writer-goroutine
  errors to the existing errCh (now buffered) instead of t.Fatal;
  ioutil.ReadAll -> io.ReadAll
- matchers.go: io/ioutil -> io (io.Discard); gofmt -s doc comment
- go.mod: go 1.11 -> 1.23.0; golang.org/x/net -> v0.42.0 (newest
  release that still permits a 1.23 directive)

Example submodule:
- golang.org/x/net/context -> stdlib context
- grpc v1.27.0 -> v1.80, modern protobuf; helloworld now comes from
  the separate google.golang.org/grpc/examples module (requires go 1.25,
  isolated to this demo-only module)

CI:
- remove ancient .travis.yml (Go 1.6-1.8)
- add .github/workflows/ci.yml: gofmt/vet/build/test -race for the
  library (Go 1.23 + 1.24 matrix) and the example

Temporary() methods and TLS version defaults are intentionally
unchanged: net.Error still requires Temporary(), and the TLS defaults
are public-API behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 23:38:42 -04:00
Ryan Fitzpatrick
5ec6847320 Adopt go modules 2021-02-05 14:11:34 -05:00
Abhilash Gnan
cdd3331e3e Add TestClose
Signed-off-by: Abhilash Gnan <abhilashgnan@gmail.com>
2021-01-14 18:06:57 -05:00
Abhilash Gnan
ce11cfdf3a Add mutex around channel close
Signed-off-by: Abhilash Gnan <abhilashgnan@gmail.com>
2021-01-14 18:06:57 -05:00
Abhilash Gnan
ae889c5259 fix blocking select on donec
Signed-off-by: Abhilash Gnan <abhilashgnan@gmail.com>
2021-01-14 18:06:57 -05:00
Abhilash Gnan
3c0ee784ab use param for recieve only chan
Signed-off-by: Abhilash Gnan <abhilashgnan@gmail.com>
2021-01-14 18:06:57 -05:00
Abhilash Gnan
a192073df5 Remove use of mutex around done chan 2021-01-14 18:06:57 -05:00
Abhilash Gnan
e13d1cbf02 add cmux.Close() function 2021-01-14 18:06:57 -05:00
golint fixer
8a8ea3c539 Fix golint import path 2018-10-25 10:41:06 -04:00
Alan D. Cabrera
f7603f4e1c Fix README.md example code 2018-05-19 12:37:04 -04:00
Soheil Hassas Yeganeh
e09e9389d8 Do not write SETTINGS in response to ACKs.
Reported-by talgendler in Issue #42
2018-01-29 10:50:01 -05:00
Yuki Ito
444ce56efe Add test for multiple matchers 2018-01-23 12:52:48 -05:00
Yuki Ito
cfc68f9888 Fix ugly variable name 2018-01-23 12:52:48 -05:00
Yuki Ito
e96bd75f84 Fix bug matchers are ignored except last one 2018-01-23 12:52:48 -05:00
Soheil Hassas Yeganeh
be5b383fd5 Use IPv4 for the listener to avoid v6 failures on Travis. 2017-12-04 12:53:26 -05:00
Soheil Hassas Yeganeh
b9e684ba4e Fix TestClose for Go10.
Depending on the Go version used, reading from a closed pipe can return
net.OpError or io.ErrClosedPipe. Simply check the string content of the
error.
2017-12-04 12:53:26 -05:00
13 changed files with 264 additions and 67 deletions

50
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: CI
on:
push:
branches: [master]
pull_request:
permissions:
contents: read
jobs:
library:
name: library (go ${{ matrix.go }})
runs-on: ubuntu-latest
strategy:
matrix:
go: ["1.23", "1.24"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: gofmt
run: test -z "$(gofmt -s -l .)" || (gofmt -s -l . && exit 1)
- name: vet
run: go vet ./...
- name: build
run: go build ./...
- name: test
run: go test -race ./...
example:
name: example
runs-on: ubuntu-latest
defaults:
run:
working-directory: example
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.25"
- name: gofmt
run: test -z "$(gofmt -s -l .)" || (gofmt -s -l . && exit 1)
- name: vet
run: go vet ./...
- name: build
run: go build ./...
- name: test
run: go test -race ./...

View File

@@ -1,29 +0,0 @@
language: go
go:
- 1.6
- 1.7
- 1.8
- tip
matrix:
allow_failures:
- go: tip
gobuild_args: -race
before_install:
- if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then go get -u github.com/kisielk/errcheck; fi
- if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then go get -u github.com/golang/lint/golint; fi
before_script:
- '! gofmt -s -l . | read'
- echo $TRAVIS_GO_VERSION
- if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then golint ./...; fi
- if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then errcheck ./...; fi
- if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then go tool vet .; fi
- if [[ $TRAVIS_GO_VERSION == 1.6* ]]; then go tool vet --shadow .; fi
script:
- go test -bench . -v ./...
- go test -race -bench . -v ./...

View File

@@ -25,7 +25,7 @@ trpcL := m.Match(cmux.Any()) // Any means anything that is not yet matched.
// Create your protocol servers.
grpcS := grpc.NewServer()
grpchello.RegisterGreeterServer(grpcs, &server{})
grpchello.RegisterGreeterServer(grpcS, &server{})
httpS := &http.Server{
Handler: &helloHTTP1Handler{},

52
cmux.go
View File

@@ -15,6 +15,7 @@
package cmux
import (
"errors"
"fmt"
"io"
"net"
@@ -61,6 +62,9 @@ func (e errListenerClosed) Timeout() bool { return false }
// listener is closed.
var ErrListenerClosed = errListenerClosed("mux: listener closed")
// ErrServerClosed is returned from muxListener.Accept when mux server is closed.
var ErrServerClosed = errors.New("mux: server closed")
// for readability of readTimeout
var noTimeout time.Duration
@@ -93,6 +97,8 @@ type CMux interface {
// Serve starts multiplexing the listener. Serve blocks and perhaps
// should be invoked concurrently within a go routine.
Serve() error
// Closes cmux server and stops accepting any connections on listener
Close()
// HandleError registers an error handler that handles listener errors.
HandleError(ErrorHandler)
// sets a timeout for the read of matchers
@@ -108,16 +114,18 @@ type cMux struct {
root net.Listener
bufLen int
errh ErrorHandler
donec chan struct{}
sls []matchersListener
readTimeout time.Duration
donec chan struct{}
mu sync.Mutex
}
func matchersToMatchWriters(matchers []Matcher) []MatchWriter {
mws := make([]MatchWriter, 0, len(matchers))
for _, m := range matchers {
cm := m
mws = append(mws, func(w io.Writer, r io.Reader) bool {
return m(r)
return cm(r)
})
}
return mws
@@ -132,6 +140,7 @@ func (m *cMux) MatchWithWriters(matchers ...MatchWriter) net.Listener {
ml := muxListener{
Listener: m.root,
connc: make(chan net.Conn, m.bufLen),
donec: make(chan struct{}),
}
m.sls = append(m.sls, matchersListener{ss: matchers, l: ml})
return ml
@@ -145,7 +154,7 @@ func (m *cMux) Serve() error {
var wg sync.WaitGroup
defer func() {
close(m.donec)
m.closeDoneChans()
wg.Wait()
for _, sl := range m.sls {
@@ -203,6 +212,30 @@ func (m *cMux) serve(c net.Conn, donec <-chan struct{}, wg *sync.WaitGroup) {
}
}
func (m *cMux) Close() {
m.closeDoneChans()
}
func (m *cMux) closeDoneChans() {
m.mu.Lock()
defer m.mu.Unlock()
select {
case <-m.donec:
// Already closed. Don't close again
default:
close(m.donec)
}
for _, sl := range m.sls {
select {
case <-sl.l.donec:
// Already closed. Don't close again
default:
close(sl.l.donec)
}
}
}
func (m *cMux) HandleError(h ErrorHandler) {
m.errh = h
}
@@ -222,14 +255,19 @@ func (m *cMux) handleErr(err error) bool {
type muxListener struct {
net.Listener
connc chan net.Conn
donec chan struct{}
}
func (l muxListener) Accept() (net.Conn, error) {
c, ok := <-l.connc
if !ok {
return nil, ErrListenerClosed
select {
case c, ok := <-l.connc:
if !ok {
return nil, ErrListenerClosed
}
return c, nil
case <-l.donec:
return nil, ErrServerClosed
}
return c, nil
}
// MuxConn wraps a net.Conn and provides transparent sniffing of connection data.

View File

@@ -22,7 +22,6 @@ import (
"fmt"
"go/build"
"io"
"io/ioutil"
"log"
"net"
"net/http"
@@ -81,7 +80,7 @@ func (l *chanListener) Accept() (net.Conn, error) {
}
func testListener(t *testing.T) (net.Listener, func()) {
l, err := net.Listen("tcp", ":0")
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
@@ -128,7 +127,7 @@ func runTestHTTPServer(errCh chan<- error, l net.Listener) {
mu.Unlock()
},
}
if err := s.Serve(l); err != ErrListenerClosed {
if err := s.Serve(l); err != ErrListenerClosed && err != ErrServerClosed {
errCh <- err
}
}
@@ -194,7 +193,7 @@ func runTestHTTPClient(t *testing.T, proto string, addr net.Addr) {
}
}()
b, err := ioutil.ReadAll(r.Body)
b, err := io.ReadAll(r.Body)
if err != nil {
t.Fatal(err)
}
@@ -218,7 +217,7 @@ func runTestRPCServer(errCh chan<- error, l net.Listener) {
for {
c, err := l.Accept()
if err != nil {
if err != ErrListenerClosed {
if err != ErrListenerClosed && err != ErrServerClosed {
errCh <- err
}
return
@@ -321,7 +320,7 @@ func TestTimeout(t *testing.T) {
func TestRead(t *testing.T) {
defer leakCheck(t)()
errCh := make(chan error)
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
@@ -335,10 +334,11 @@ func TestRead(t *testing.T) {
writer, reader := net.Pipe()
go func() {
if _, err := io.WriteString(writer, strings.Repeat(payload, mult)); err != nil {
t.Fatal(err)
errCh <- err
return
}
if err := writer.Close(); err != nil {
t.Fatal(err)
errCh <- err
}
}()
@@ -379,7 +379,7 @@ func TestRead(t *testing.T) {
func TestAny(t *testing.T) {
defer leakCheck(t)()
errCh := make(chan error)
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
@@ -403,7 +403,7 @@ func TestTLS(t *testing.T) {
generateTLSCert(t)
defer cleanupTLSCert(t)
defer leakCheck(t)()
errCh := make(chan error)
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
@@ -428,7 +428,7 @@ func TestTLS(t *testing.T) {
func TestHTTP2(t *testing.T) {
defer leakCheck(t)()
errCh := make(chan error)
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
@@ -439,10 +439,11 @@ func TestHTTP2(t *testing.T) {
writer, reader := net.Pipe()
go func() {
if _, err := io.WriteString(writer, http2.ClientPreface); err != nil {
t.Fatal(err)
errCh <- err
return
}
if err := writer.Close(); err != nil {
t.Fatal(err)
errCh <- err
}
}()
@@ -493,7 +494,7 @@ func testHTTP2MatchHeaderField(
notMatchValue string,
) {
defer leakCheck(t)()
errCh := make(chan error)
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
@@ -505,12 +506,14 @@ func testHTTP2MatchHeaderField(
writer, reader := net.Pipe()
go func() {
if _, err := io.WriteString(writer, http2.ClientPreface); err != nil {
t.Fatal(err)
errCh <- err
return
}
var buf bytes.Buffer
enc := hpack.NewEncoder(&buf)
if err := enc.WriteField(hpack.HeaderField{Name: name, Value: headerValue}); err != nil {
t.Fatal(err)
errCh <- err
return
}
framer := http2.NewFramer(writer, nil)
err := framer.WriteHeaders(http2.HeadersFrameParam{
@@ -520,10 +523,11 @@ func testHTTP2MatchHeaderField(
EndHeaders: true,
})
if err != nil {
t.Fatal(err)
errCh <- err
return
}
if err := writer.Close(); err != nil {
t.Fatal(err)
errCh <- err
}
}()
@@ -558,7 +562,7 @@ func testHTTP2MatchHeaderField(
func TestHTTPGoRPC(t *testing.T) {
defer leakCheck(t)()
errCh := make(chan error)
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
@@ -583,7 +587,7 @@ func TestHTTPGoRPC(t *testing.T) {
func TestErrorHandler(t *testing.T) {
defer leakCheck(t)()
errCh := make(chan error)
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
@@ -622,9 +626,38 @@ func TestErrorHandler(t *testing.T) {
}
}
func TestClose(t *testing.T) {
func TestMultipleMatchers(t *testing.T) {
defer leakCheck(t)()
errCh := make(chan error)
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
t.Fatal(err)
default:
}
}()
l, cleanup := testListener(t)
defer cleanup()
matcher := func(r io.Reader) bool {
return true
}
unmatcher := func(r io.Reader) bool {
return false
}
muxl := New(l)
lis := muxl.Match(unmatcher, matcher, unmatcher)
go runTestHTTPServer(errCh, lis)
go safeServe(errCh, muxl)
runTestHTTP1Client(t, l.Addr())
}
func TestListenerClose(t *testing.T) {
defer leakCheck(t)()
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
@@ -656,15 +689,42 @@ func TestClose(t *testing.T) {
// Second connection either goes through or it is closed.
if _, err := anyl.Accept(); err != nil {
if err != ErrListenerClosed {
if err != ErrListenerClosed && err != ErrServerClosed {
t.Fatal(err)
}
if _, err := c2.Read([]byte{}); err != io.ErrClosedPipe {
// The error is either io.ErrClosedPipe or net.OpError wrapping
// a net.pipeError depending on the go version.
if _, err := c2.Read([]byte{}); !strings.Contains(err.Error(), "closed") {
t.Fatalf("connection is not closed and is leaked: %v", err)
}
}
}
func TestClose(t *testing.T) {
defer leakCheck(t)()
errCh := make(chan error, 1)
defer func() {
select {
case err := <-errCh:
t.Fatal(err)
default:
}
}()
l, cleanup := testListener(t)
defer cleanup()
muxl := New(l)
anyl := muxl.Match(Any())
go safeServe(errCh, muxl)
muxl.Close()
if _, err := anyl.Accept(); err != ErrServerClosed {
t.Fatal(err)
}
}
// Cribbed from google.golang.org/grpc/test/end2end_test.go.
// interestingGoroutines returns all goroutines we care about for the purpose

View File

@@ -15,6 +15,7 @@
package cmux_test
import (
"context"
"fmt"
"io"
"log"
@@ -25,10 +26,10 @@ import (
"google.golang.org/grpc"
"golang.org/x/net/context"
"golang.org/x/net/websocket"
"github.com/soheilhy/cmux"
"google.golang.org/grpc/examples/helloworld/helloworld"
grpchello "google.golang.org/grpc/examples/helloworld/helloworld"
)
@@ -86,7 +87,9 @@ func serveRPC(l net.Listener) {
}
}
type grpcServer struct{}
type grpcServer struct {
helloworld.UnimplementedGreeterServer
}
func (s *grpcServer) SayHello(ctx context.Context, in *grpchello.HelloRequest) (
*grpchello.HelloReply, error) {

19
example/go.mod Normal file
View File

@@ -0,0 +1,19 @@
module github.com/soheilhy/cmux/example
go 1.25.0
require (
github.com/soheilhy/cmux v0.0.0-00010101000000-000000000000
golang.org/x/net v0.53.0
google.golang.org/grpc v1.80.0
google.golang.org/grpc/examples v0.0.0-20260605180800-0f3086db7a75
)
require (
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.36.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
google.golang.org/protobuf v1.36.11 // indirect
)
replace github.com/soheilhy/cmux => ../

40
example/go.sum Normal file
View File

@@ -0,0 +1,40 @@
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/grpc/examples v0.0.0-20260605180800-0f3086db7a75 h1:AEIy9rWQCaqGLD5thytE2ox41wjT4vOe43/yh2enp+A=
google.golang.org/grpc/examples v0.0.0-20260605180800-0f3086db7a75/go.mod h1:Cj6Qdy58KTzw400M0aa1PA3rNLrhQgPFdqXxShtOXmM=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=

7
go.mod Normal file
View File

@@ -0,0 +1,7 @@
module github.com/soheilhy/cmux
go 1.23.0
require golang.org/x/net v0.42.0
require golang.org/x/text v0.27.0 // indirect

4
go.sum Normal file
View File

@@ -0,0 +1,4 @@
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=

View File

@@ -18,7 +18,6 @@ import (
"bufio"
"crypto/tls"
"io"
"io/ioutil"
"net/http"
"strings"
@@ -67,7 +66,8 @@ func HTTP1Fast(extMethods ...string) Matcher {
//
// 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)
//
// TLS(tls.VersionTLS11, tls.VersionTLS12)
func TLS(versions ...int) Matcher {
if len(versions) == 0 {
versions = []int{
@@ -148,7 +148,7 @@ func HTTP1HeaderFieldPrefix(name, valuePrefix string) Matcher {
// headers frame.
func HTTP2HeaderField(name, value string) Matcher {
return func(r io.Reader) bool {
return matchHTTP2Field(ioutil.Discard, r, name, func(gotValue string) bool {
return matchHTTP2Field(io.Discard, r, name, func(gotValue string) bool {
return gotValue == value
})
}
@@ -159,7 +159,7 @@ func HTTP2HeaderField(name, value string) Matcher {
// valuePrefix, this will match.
func HTTP2HeaderFieldPrefix(name, valuePrefix string) Matcher {
return func(r io.Reader) bool {
return matchHTTP2Field(ioutil.Discard, r, name, func(gotValue string) bool {
return matchHTTP2Field(io.Discard, r, name, func(gotValue string) bool {
return strings.HasPrefix(gotValue, valuePrefix)
})
}
@@ -240,6 +240,11 @@ func matchHTTP2Field(w io.Writer, r io.Reader, name string, matches func(string)
switch f := f.(type) {
case *http2.SettingsFrame:
// Sender acknoweldged the SETTINGS frame. No need to write
// SETTINGS again.
if f.IsAck() {
break
}
if err := framer.WriteSettings(); err != nil {
return false
}