mirror of https://github.com/status-im/consul.git
Merge pull request #8184 from hashicorp/bugfix/goroutine-leaks
This commit is contained in:
commit
7041f69892
|
@ -4424,7 +4424,8 @@ func (a *Agent) LocalBlockingQuery(alwaysBlock bool, hash string, wait time.Dura
|
|||
// If we are not blocking we can skip tracking and allocating - nil WatchSet
|
||||
// is still valid to call Add on and will just be a no op.
|
||||
var ws memdb.WatchSet
|
||||
var timeout *time.Timer
|
||||
var ctx context.Context = &lib.StopChannelContext{StopCh: a.shutdownCh}
|
||||
shouldBlock := false
|
||||
|
||||
if alwaysBlock || hash != "" {
|
||||
if wait == 0 {
|
||||
|
@ -4435,7 +4436,11 @@ func (a *Agent) LocalBlockingQuery(alwaysBlock bool, hash string, wait time.Dura
|
|||
}
|
||||
// Apply a small amount of jitter to the request.
|
||||
wait += lib.RandomStagger(wait / 16)
|
||||
timeout = time.NewTimer(wait)
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithDeadline(ctx, time.Now().Add(wait))
|
||||
defer cancel()
|
||||
|
||||
shouldBlock = true
|
||||
}
|
||||
|
||||
for {
|
||||
|
@ -4453,7 +4458,7 @@ func (a *Agent) LocalBlockingQuery(alwaysBlock bool, hash string, wait time.Dura
|
|||
// WatchSet immediately returns false which would incorrectly cause this to
|
||||
// loop and repeat again, however we rely on the invariant that ws == nil
|
||||
// IFF timeout == nil in which case the Watch call is never invoked.
|
||||
if timeout == nil || hash != curHash || ws.Watch(timeout.C) {
|
||||
if !shouldBlock || hash != curHash || ws.WatchCtx(ctx) != nil {
|
||||
return curHash, curResp, err
|
||||
}
|
||||
// Watch returned false indicating a change was detected, loop and repeat
|
||||
|
@ -4465,7 +4470,7 @@ func (a *Agent) LocalBlockingQuery(alwaysBlock bool, hash string, wait time.Dura
|
|||
if syncPauseCh := a.SyncPausedCh(); syncPauseCh != nil {
|
||||
select {
|
||||
case <-syncPauseCh:
|
||||
case <-timeout.C:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"sort"
|
||||
|
@ -261,9 +262,9 @@ func NewGatewayLocator(
|
|||
|
||||
var errGatewayLocalStateNotInitialized = errors.New("local state not initialized")
|
||||
|
||||
func (g *GatewayLocator) Run(stopCh <-chan struct{}) {
|
||||
func (g *GatewayLocator) Run(ctx context.Context) {
|
||||
var lastFetchIndex uint64
|
||||
retryLoopBackoff(stopCh, func() error {
|
||||
retryLoopBackoff(ctx, func() error {
|
||||
idx, err := g.runOnce(lastFetchIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -651,7 +651,7 @@ func (s *Server) secondaryIntermediateCertRenewalWatch(ctx context.Context) erro
|
|||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-time.After(structs.IntermediateCertRenewInterval):
|
||||
retryLoopBackoff(ctx.Done(), func() error {
|
||||
retryLoopBackoff(ctx, func() error {
|
||||
s.caProviderReconfigurationLock.Lock()
|
||||
defer s.caProviderReconfigurationLock.Unlock()
|
||||
|
||||
|
@ -724,7 +724,7 @@ func (s *Server) secondaryCARootWatch(ctx context.Context) error {
|
|||
|
||||
connectLogger.Debug("starting Connect CA root replication from primary datacenter", "primary", s.config.PrimaryDatacenter)
|
||||
|
||||
retryLoopBackoff(ctx.Done(), func() error {
|
||||
retryLoopBackoff(ctx, func() error {
|
||||
var roots structs.IndexedCARoots
|
||||
if err := s.forwardDC("ConnectCA.Roots", s.config.PrimaryDatacenter, &args, &roots); err != nil {
|
||||
return fmt.Errorf("Error retrieving the primary datacenter's roots: %v", err)
|
||||
|
@ -780,7 +780,7 @@ func (s *Server) replicateIntentions(ctx context.Context) error {
|
|||
|
||||
connectLogger.Debug("starting Connect intention replication from primary datacenter", "primary", s.config.PrimaryDatacenter)
|
||||
|
||||
retryLoopBackoff(ctx.Done(), func() error {
|
||||
retryLoopBackoff(ctx, func() error {
|
||||
// Always use the latest replication token value in case it changed while looping.
|
||||
args.QueryOptions.Token = s.tokens.ReplicationToken()
|
||||
|
||||
|
@ -832,14 +832,14 @@ func (s *Server) replicateIntentions(ctx context.Context) error {
|
|||
|
||||
// retryLoopBackoff loops a given function indefinitely, backing off exponentially
|
||||
// upon errors up to a maximum of maxRetryBackoff seconds.
|
||||
func retryLoopBackoff(stopCh <-chan struct{}, loopFn func() error, errFn func(error)) {
|
||||
func retryLoopBackoff(ctx context.Context, loopFn func() error, errFn func(error)) {
|
||||
var failedAttempts uint
|
||||
limiter := rate.NewLimiter(loopRateLimit, retryBucketSize)
|
||||
for {
|
||||
// Rate limit how often we run the loop
|
||||
limiter.Wait(context.Background())
|
||||
limiter.Wait(ctx)
|
||||
select {
|
||||
case <-stopCh:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
@ -850,8 +850,15 @@ func retryLoopBackoff(stopCh <-chan struct{}, loopFn func() error, errFn func(er
|
|||
|
||||
if err := loopFn(); err != nil {
|
||||
errFn(err)
|
||||
time.Sleep(retryTime)
|
||||
continue
|
||||
|
||||
timer := time.NewTimer(retryTime)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return
|
||||
case <-timer.C:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the failed attempts after a successful run.
|
||||
|
|
|
@ -42,7 +42,7 @@ func (s *Server) stopFederationStateAntiEntropy() {
|
|||
func (s *Server) federationStateAntiEntropySync(ctx context.Context) error {
|
||||
var lastFetchIndex uint64
|
||||
|
||||
retryLoopBackoff(ctx.Done(), func() error {
|
||||
retryLoopBackoff(ctx, func() error {
|
||||
if !s.DatacenterSupportsFederationStates() {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
@ -752,7 +753,9 @@ type queryFn func(memdb.WatchSet, *state.Store) error
|
|||
|
||||
// blockingQuery is used to process a potentially blocking query operation.
|
||||
func (s *Server) blockingQuery(queryOpts structs.QueryOptionsCompat, queryMeta structs.QueryMetaCompat, fn queryFn) error {
|
||||
var timeout *time.Timer
|
||||
var cancel func()
|
||||
var ctx context.Context = &lib.StopChannelContext{StopCh: s.shutdownCh}
|
||||
|
||||
var queriesBlocking uint64
|
||||
var queryTimeout time.Duration
|
||||
|
||||
|
@ -776,9 +779,9 @@ func (s *Server) blockingQuery(queryOpts structs.QueryOptionsCompat, queryMeta s
|
|||
// Apply a small amount of jitter to the request.
|
||||
queryTimeout += lib.RandomStagger(queryTimeout / jitterFraction)
|
||||
|
||||
// Setup a query timeout.
|
||||
timeout = time.NewTimer(queryTimeout)
|
||||
defer timeout.Stop()
|
||||
// wrap the base context with a deadline
|
||||
ctx, cancel = context.WithDeadline(ctx, time.Now().Add(queryTimeout))
|
||||
defer cancel()
|
||||
|
||||
// instrument blockingQueries
|
||||
// atomic inc our server's count of in-flight blockingQueries and store the new value
|
||||
|
@ -833,7 +836,9 @@ RUN_QUERY:
|
|||
}
|
||||
// block up to the timeout if we don't see anything fresh.
|
||||
if err == nil && minQueryIndex > 0 && queryMeta.GetIndex() <= minQueryIndex {
|
||||
if expired := ws.Watch(timeout.C); !expired {
|
||||
if err := ws.WatchCtx(ctx); err == nil {
|
||||
// a non-nil error only occurs when the context is cancelled
|
||||
|
||||
// If a restore may have woken us up then bail out from
|
||||
// the query immediately. This is slightly race-ey since
|
||||
// this might have been interrupted for other reasons,
|
||||
|
|
|
@ -525,7 +525,7 @@ func NewServerWithOptions(config *Config, options ...ConsulOption) (*Server, err
|
|||
}
|
||||
|
||||
if s.gatewayLocator != nil {
|
||||
go s.gatewayLocator.Run(s.shutdownCh)
|
||||
go s.gatewayLocator.Run(&lib.StopChannelContext{StopCh: s.shutdownCh})
|
||||
}
|
||||
|
||||
// Serf and dynamic bind ports
|
||||
|
@ -640,6 +640,7 @@ func (s *Server) trackAutoEncryptCARoots() {
|
|||
ws := memdb.NewWatchSet()
|
||||
state := s.fsm.State()
|
||||
ws.Add(state.AbandonCh())
|
||||
ws.Add(s.shutdownCh)
|
||||
_, cas, err := state.CARoots(ws)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to watch AutoEncrypt CARoot", "error", err)
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
package leakcheck
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/agent"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
"github.com/hashicorp/consul/testrpc"
|
||||
"github.com/hashicorp/consul/tlsutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func testTLSCertificates(serverName string) (cert string, key string, cacert string, err error) {
|
||||
// generate CA
|
||||
serial, err := tlsutil.GenerateSerialNumber()
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
ca, err := tlsutil.GenerateCA(signer, serial, 365, nil)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
// generate leaf
|
||||
serial, err = tlsutil.GenerateSerialNumber()
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
cert, privateKey, err := tlsutil.GenerateCert(
|
||||
signer,
|
||||
ca,
|
||||
serial,
|
||||
"Test Cert Name",
|
||||
365,
|
||||
[]string{serverName},
|
||||
nil,
|
||||
[]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||
)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
return cert, privateKey, ca, nil
|
||||
}
|
||||
|
||||
func setupPrimaryServer(t *testing.T) *agent.TestAgent {
|
||||
d := testutil.TempDir(t, "leaks-primary-server")
|
||||
t.Cleanup(func() { os.RemoveAll(d) })
|
||||
|
||||
certPEM, keyPEM, caPEM, err := testTLSCertificates("server.primary.consul")
|
||||
require.NoError(t, err)
|
||||
|
||||
certPath := filepath.Join(d, "cert.pem")
|
||||
keyPath := filepath.Join(d, "key.pem")
|
||||
caPath := filepath.Join(d, "cacert.pem")
|
||||
|
||||
require.NoError(t, ioutil.WriteFile(certPath, []byte(certPEM), 0600))
|
||||
require.NoError(t, ioutil.WriteFile(keyPath, []byte(keyPEM), 0600))
|
||||
require.NoError(t, ioutil.WriteFile(caPath, []byte(caPEM), 0600))
|
||||
|
||||
aclParams := agent.DefaulTestACLConfigParams()
|
||||
aclParams.PrimaryDatacenter = "primary"
|
||||
aclParams.EnableTokenReplication = true
|
||||
|
||||
config := `
|
||||
server = true
|
||||
datacenter = "primary"
|
||||
primary_datacenter = "primary"
|
||||
enable_central_service_config = true
|
||||
|
||||
connect {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
auto_encrypt {
|
||||
allow_tls = true
|
||||
}
|
||||
` + agent.TestACLConfigWithParams(aclParams)
|
||||
|
||||
a := agent.NewTestAgent(t, config)
|
||||
t.Cleanup(func() { a.Shutdown() })
|
||||
|
||||
testrpc.WaitForTestAgent(t, a.RPC, "primary", testrpc.WithToken(agent.TestDefaultMasterToken))
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func TestTestAgentLeaks_Server(t *testing.T) {
|
||||
/*
|
||||
Eventually go routine leak checking should be moved into other packages such as the agent
|
||||
and agent/consul packages. However there are too many leaks for the test to run properly.
|
||||
|
||||
Many of the leaks are due to blocking queries from clients to servers being uncancellable.
|
||||
Until we can move away from net/rpc and fix some of the other issues we don't want a
|
||||
completely unbounded test which is guaranteed to fail 100% of the time. For now this
|
||||
test will do. When we do update it we should add this in a *_test.go file in the packages
|
||||
that we want to enable leak checking within:
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m,
|
||||
goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"),
|
||||
goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"),
|
||||
goleak.IgnoreTopFunction("github.com/hashicorp/consul/sdk/freeport.checkFreedPorts"),
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
defer goleak.VerifyNone(t,
|
||||
goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"),
|
||||
goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"),
|
||||
goleak.IgnoreTopFunction("github.com/hashicorp/consul/sdk/freeport.checkFreedPorts"),
|
||||
)
|
||||
|
||||
primaryServer := setupPrimaryServer(t)
|
||||
primaryServer.Shutdown()
|
||||
}
|
4
go.mod
4
go.mod
|
@ -78,12 +78,14 @@ require (
|
|||
github.com/sirupsen/logrus v1.4.2 // indirect
|
||||
github.com/stretchr/testify v1.5.1
|
||||
go.opencensus.io v0.22.0 // indirect
|
||||
go.uber.org/goleak v1.0.0
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
|
||||
golang.org/x/sys v0.0.0-20200316230553-a7d97aace0b0
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
|
||||
golang.org/x/tools v0.0.0-20200513154647-78b527d18275 // indirect
|
||||
google.golang.org/api v0.9.0 // indirect
|
||||
google.golang.org/appengine v1.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55
|
||||
|
|
20
go.sum
20
go.sum
|
@ -32,6 +32,7 @@ github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1Gn
|
|||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DataDog/datadog-go v2.2.0+incompatible h1:V5BKkxACZLjzHjSgBbr2gvLA2Ae49yhc6CSY7MLy5k4=
|
||||
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
|
@ -492,11 +493,14 @@ github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo
|
|||
github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=
|
||||
go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
@ -506,6 +510,7 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
@ -517,6 +522,10 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
|
|||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -536,6 +545,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
|
@ -546,6 +557,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -602,7 +614,15 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200513154647-78b527d18275 h1:e7nYe9s94RHunFJ7b+mmPxiQMOKMVSqYASToWb1EcHs=
|
||||
golang.org/x/tools v0.0.0-20200513154647-78b527d18275/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStopChannelContext(t *testing.T) {
|
||||
ch := make(chan struct{})
|
||||
|
||||
ctx := StopChannelContext{StopCh: ch}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
require.FailNow(t, "StopChannelContext should not be done yet")
|
||||
default:
|
||||
// do nothing things are good
|
||||
}
|
||||
|
||||
close(ch)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// things are good, as we are done
|
||||
default:
|
||||
require.FailNow(t, "StopChannelContext should be done")
|
||||
}
|
||||
|
||||
// issue it twice to ensure that we indefinitely return the
|
||||
// same value - this is what the context interface says is
|
||||
// the correct behavior.
|
||||
require.Equal(t, context.Canceled, ctx.Err())
|
||||
require.Equal(t, context.Canceled, ctx.Err())
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
vendor/
|
||||
/bin
|
||||
/lint.log
|
||||
/cover.out
|
||||
/cover.html
|
|
@ -0,0 +1,24 @@
|
|||
sudo: false
|
||||
language: go
|
||||
go_import_path: go.uber.org/goleak
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.12.x
|
||||
- go: 1.13.x
|
||||
env: LINT=1
|
||||
|
||||
install:
|
||||
- make install
|
||||
|
||||
script:
|
||||
- test -z "$LINT" || make lint
|
||||
- make test
|
||||
|
||||
after_success:
|
||||
- make cover
|
||||
- bash <(curl -s https://codecov.io/bash)
|
|
@ -0,0 +1,17 @@
|
|||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.0]
|
||||
### Changed
|
||||
- Migrate to Go modules.
|
||||
|
||||
### Fixed
|
||||
- Ignore trace related goroutines that cause false positives with -trace.
|
||||
|
||||
## 0.10.0
|
||||
- Initial release.
|
||||
|
||||
[1.0.0]: https://github.com/uber-go/goleak/compare/v0.10.0...v1.0.0
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Uber Technologies, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,41 @@
|
|||
export GOBIN ?= $(shell pwd)/bin
|
||||
|
||||
GOLINT = $(GOBIN)/golint
|
||||
|
||||
GO_FILES := $(shell \
|
||||
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||
-o -name '*.go' -print | cut -b3-)
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build ./...
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
go mod download
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v -race ./...
|
||||
go test -v -trace=/dev/null .
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -race -coverprofile=cover.out -coverpkg=./... ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
$(GOLINT):
|
||||
go install golang.org/x/lint/golint
|
||||
|
||||
.PHONY: lint
|
||||
lint: $(GOLINT)
|
||||
@rm -rf lint.log
|
||||
@echo "Checking formatting..."
|
||||
@gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log
|
||||
@echo "Checking vet..."
|
||||
@go vet ./... 2>&1 | tee -a lint.log
|
||||
@echo "Checking lint..."
|
||||
@$(GOLINT) ./... 2>&1 | tee -a lint.log
|
||||
@echo "Checking for unresolved FIXMEs..."
|
||||
@git grep -i fixme | grep -v -e '^vendor/' -e '^Makefile' | tee -a lint.log
|
||||
@[ ! -s lint.log ]
|
|
@ -0,0 +1,70 @@
|
|||
# goleak [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
Goroutine leak detector to help avoid Goroutine leaks.
|
||||
|
||||
## Development Status: Alpha
|
||||
|
||||
goleak is still in development, and APIs are still in flux.
|
||||
|
||||
## Installation
|
||||
|
||||
You can use `go get` to get the latest version:
|
||||
|
||||
`go get -u go.uber.org/goleak`
|
||||
|
||||
`goleak` also supports semver releases. It is compatible with Go 1.5+.
|
||||
|
||||
## Quick Start
|
||||
|
||||
To verify that there are no unexpected goroutines running at the end of a test:
|
||||
|
||||
```go
|
||||
func TestA(t *testing.T) {
|
||||
defer goleak.VerifyNone(t)
|
||||
|
||||
// test logic here.
|
||||
}
|
||||
```
|
||||
|
||||
Instead of checking for leaks at the end of every test, `goleak` can also be run
|
||||
at the end of every test package by creating a `TestMain` function for your
|
||||
package:
|
||||
|
||||
```go
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m)
|
||||
}
|
||||
```
|
||||
|
||||
## Determine Source of Package Leaks
|
||||
|
||||
When verifying leaks using `TestMain`, the leak test is only run once after all tests
|
||||
have been run. This is typically enough to ensure there's no goroutines leaked from
|
||||
tests, but when there are leaks, it's hard to determine which test is causing them.
|
||||
|
||||
You can use the following bash script to determine the source of the failing test:
|
||||
|
||||
```sh
|
||||
# Create a test binary which will be used to run each test individually
|
||||
$ go test -c -o tests
|
||||
|
||||
# Run each test individually, printing "." for successful tests, or the test name
|
||||
# for failing tests.
|
||||
$ for test in $(go test -list . | grep "^Test"); do ./tests -test.run "^$test\$" &>/dev/null && echo -n "." || echo "\n$test failed"; done
|
||||
```
|
||||
|
||||
This will only print names of failing tests which can be investigated individually. E.g.,
|
||||
|
||||
```
|
||||
.....
|
||||
TestLeakyTest failed
|
||||
.......
|
||||
```
|
||||
|
||||
|
||||
[doc-img]: https://godoc.org/go.uber.org/goleak?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/goleak
|
||||
[ci-img]: https://travis-ci.com/uber-go/goleak.svg?branch=master
|
||||
[ci]: https://travis-ci.com/uber-go/goleak
|
||||
[cov-img]: https://codecov.io/gh/uber-go/goleak/branch/master/graph/badge.svg
|
||||
[cov]: https://codecov.io/gh/uber-go/goleak
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2018 Uber Technologies, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// Package goleak is a Goroutine leak detector.
|
||||
package goleak // import "go.uber.org/goleak"
|
|
@ -0,0 +1,8 @@
|
|||
package: go.uber.org/goleak
|
||||
import: []
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
version: ^1.1.4
|
||||
subpackages:
|
||||
- assert
|
||||
- require
|
|
@ -0,0 +1,11 @@
|
|||
module go.uber.org/goleak
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
|
@ -0,0 +1,30 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright (c) 2017 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package stack
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const _defaultBufferSize = 64 * 1024 // 64 KiB
|
||||
|
||||
// Stack represents a single Goroutine's stack.
|
||||
type Stack struct {
|
||||
id int
|
||||
state string
|
||||
firstFunction string
|
||||
fullStack *bytes.Buffer
|
||||
}
|
||||
|
||||
// ID returns the goroutine ID.
|
||||
func (s Stack) ID() int {
|
||||
return s.id
|
||||
}
|
||||
|
||||
// State returns the Goroutine's state.
|
||||
func (s Stack) State() string {
|
||||
return s.state
|
||||
}
|
||||
|
||||
// Full returns the full stack trace for this goroutine.
|
||||
func (s Stack) Full() string {
|
||||
return s.fullStack.String()
|
||||
}
|
||||
|
||||
// FirstFunction returns the name of the first function on the stack.
|
||||
func (s Stack) FirstFunction() string {
|
||||
return s.firstFunction
|
||||
}
|
||||
|
||||
func (s Stack) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Goroutine %v in state %v, with %v on top of the stack:\n%s",
|
||||
s.id, s.state, s.firstFunction, s.Full())
|
||||
}
|
||||
|
||||
func getStacks(all bool) []Stack {
|
||||
var stacks []Stack
|
||||
|
||||
var curStack *Stack
|
||||
stackReader := bufio.NewReader(bytes.NewReader(getStackBuffer(all)))
|
||||
for {
|
||||
line, err := stackReader.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
// We're reading using bytes.NewReader which should never fail.
|
||||
panic("bufio.NewReader failed on a fixed string")
|
||||
}
|
||||
|
||||
// If we see the goroutine header, start a new stack.
|
||||
isFirstLine := false
|
||||
if strings.HasPrefix(line, "goroutine ") {
|
||||
// flush any previous stack
|
||||
if curStack != nil {
|
||||
stacks = append(stacks, *curStack)
|
||||
}
|
||||
id, goState := parseGoStackHeader(line)
|
||||
curStack = &Stack{
|
||||
id: id,
|
||||
state: goState,
|
||||
fullStack: &bytes.Buffer{},
|
||||
}
|
||||
isFirstLine = true
|
||||
}
|
||||
curStack.fullStack.WriteString(line)
|
||||
if !isFirstLine && curStack.firstFunction == "" {
|
||||
curStack.firstFunction = parseFirstFunc(line)
|
||||
}
|
||||
}
|
||||
|
||||
if curStack != nil {
|
||||
stacks = append(stacks, *curStack)
|
||||
}
|
||||
return stacks
|
||||
}
|
||||
|
||||
// All returns the stacks for all running goroutines.
|
||||
func All() []Stack {
|
||||
return getStacks(true)
|
||||
}
|
||||
|
||||
// Current returns the stack for the current goroutine.
|
||||
func Current() Stack {
|
||||
return getStacks(false)[0]
|
||||
}
|
||||
|
||||
func getStackBuffer(all bool) []byte {
|
||||
for i := _defaultBufferSize; ; i *= 2 {
|
||||
buf := make([]byte, i)
|
||||
if n := runtime.Stack(buf, all); n < i {
|
||||
return buf[:n]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseFirstFunc(line string) string {
|
||||
line = strings.TrimSpace(line)
|
||||
if idx := strings.LastIndex(line, "("); idx > 0 {
|
||||
return line[:idx]
|
||||
}
|
||||
panic(fmt.Sprintf("function calls missing parents: %q", line))
|
||||
}
|
||||
|
||||
// parseGoStackHeader parses a stack header that looks like:
|
||||
// goroutine 643 [runnable]:\n
|
||||
// And returns the goroutine ID, and the state.
|
||||
func parseGoStackHeader(line string) (goroutineID int, state string) {
|
||||
line = strings.TrimSuffix(line, ":\n")
|
||||
parts := strings.SplitN(line, " ", 3)
|
||||
if len(parts) != 3 {
|
||||
panic(fmt.Sprintf("unexpected stack header format: %q", line))
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse goroutine ID: %v in line %q", parts[1], line))
|
||||
}
|
||||
|
||||
state = strings.TrimSuffix(strings.TrimPrefix(parts[2], "["), "]")
|
||||
return id, state
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) 2017 Uber Technologies, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package goleak
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/goleak/internal/stack"
|
||||
)
|
||||
|
||||
// TestingT is the minimal subset of testing.TB that we use.
|
||||
type TestingT interface {
|
||||
Error(...interface{})
|
||||
}
|
||||
|
||||
// filterStacks will filter any stacks excluded by the given opts.
|
||||
// filterStacks modifies the passed in stacks slice.
|
||||
func filterStacks(stacks []stack.Stack, skipID int, opts *opts) []stack.Stack {
|
||||
filtered := stacks[:0]
|
||||
for _, stack := range stacks {
|
||||
// Always skip the running goroutine.
|
||||
if stack.ID() == skipID {
|
||||
continue
|
||||
}
|
||||
// Run any default or user-specified filters.
|
||||
if opts.filter(stack) {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, stack)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// Find looks for extra goroutines, and returns a descriptive error if
|
||||
// any are found.
|
||||
func Find(options ...Option) error {
|
||||
cur := stack.Current().ID()
|
||||
|
||||
opts := buildOpts(options...)
|
||||
var stacks []stack.Stack
|
||||
retry := true
|
||||
for i := 0; retry; i++ {
|
||||
stacks = filterStacks(stack.All(), cur, opts)
|
||||
|
||||
if len(stacks) == 0 {
|
||||
return nil
|
||||
}
|
||||
retry = opts.retry(i)
|
||||
}
|
||||
|
||||
return fmt.Errorf("found unexpected goroutines:\n%s", stacks)
|
||||
}
|
||||
|
||||
// VerifyNone marks the given TestingT as failed if any extra goroutines are
|
||||
// found by Find. This is a helper method to make it easier to integrate in
|
||||
// tests by doing:
|
||||
// defer VerifyNone(t)
|
||||
func VerifyNone(t TestingT, options ...Option) {
|
||||
if err := Find(options...); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright (c) 2017 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package goleak
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/goleak/internal/stack"
|
||||
)
|
||||
|
||||
// Option lets users specify custom verifications.
|
||||
type Option interface {
|
||||
apply(*opts)
|
||||
}
|
||||
|
||||
// We retry up to 20 times if we can't find the goroutine that
|
||||
// we are looking for. In between each attempt, we will sleep for
|
||||
// a short while to let any running goroutines complete.
|
||||
const _defaultRetries = 20
|
||||
|
||||
type opts struct {
|
||||
filters []func(stack.Stack) bool
|
||||
maxRetries int
|
||||
maxSleep time.Duration
|
||||
}
|
||||
|
||||
// optionFunc lets us easily write options without a custom type.
|
||||
type optionFunc func(*opts)
|
||||
|
||||
func (f optionFunc) apply(opts *opts) { f(opts) }
|
||||
|
||||
// IgnoreTopFunction ignores any goroutines where the specified function
|
||||
// is at the top of the stack. The function name should be fully qualified,
|
||||
// e.g., go.uber.org/goleak.IgnoreTopFunction
|
||||
func IgnoreTopFunction(f string) Option {
|
||||
return addFilter(func(s stack.Stack) bool {
|
||||
return s.FirstFunction() == f
|
||||
})
|
||||
}
|
||||
|
||||
func maxSleep(d time.Duration) Option {
|
||||
return optionFunc(func(opts *opts) {
|
||||
opts.maxSleep = d
|
||||
})
|
||||
}
|
||||
|
||||
func addFilter(f func(stack.Stack) bool) Option {
|
||||
return optionFunc(func(opts *opts) {
|
||||
opts.filters = append(opts.filters, f)
|
||||
})
|
||||
}
|
||||
|
||||
func buildOpts(options ...Option) *opts {
|
||||
opts := &opts{
|
||||
maxRetries: _defaultRetries,
|
||||
maxSleep: 100 * time.Millisecond,
|
||||
}
|
||||
opts.filters = append(opts.filters,
|
||||
isTestStack,
|
||||
isSyscallStack,
|
||||
isStdLibStack,
|
||||
isTraceStack,
|
||||
)
|
||||
for _, option := range options {
|
||||
option.apply(opts)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func (vo *opts) filter(s stack.Stack) bool {
|
||||
for _, filter := range vo.filters {
|
||||
if filter(s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (vo *opts) retry(i int) bool {
|
||||
if i >= vo.maxRetries {
|
||||
return false
|
||||
}
|
||||
|
||||
d := time.Duration(int(time.Microsecond) << uint(i))
|
||||
if d > vo.maxSleep {
|
||||
d = vo.maxSleep
|
||||
}
|
||||
time.Sleep(d)
|
||||
return true
|
||||
}
|
||||
|
||||
// isTestStack is a default filter installed to automatically skip goroutines
|
||||
// that the testing package runs while the user's tests are running.
|
||||
func isTestStack(s stack.Stack) bool {
|
||||
// Until go1.7, the main goroutine ran RunTests, which started
|
||||
// the test in a separate goroutine and waited for that test goroutine
|
||||
// to end by waiting on a channel.
|
||||
// Since go1.7, a separate goroutine is started to wait for signals.
|
||||
// T.Parallel is for parallel tests, which are blocked until all serial
|
||||
// tests have run with T.Parallel at the top of the stack.
|
||||
switch s.FirstFunction() {
|
||||
case "testing.RunTests", "testing.(*T).Run", "testing.(*T).Parallel":
|
||||
// In pre1.7 and post-1.7, background goroutines started by the testing
|
||||
// package are blocked waiting on a channel.
|
||||
return strings.HasPrefix(s.State(), "chan receive")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isSyscallStack(s stack.Stack) bool {
|
||||
// Typically runs in the background when code uses CGo:
|
||||
// https://github.com/golang/go/issues/16714
|
||||
return s.FirstFunction() == "runtime.goexit" && strings.HasPrefix(s.State(), "syscall")
|
||||
}
|
||||
|
||||
func isStdLibStack(s stack.Stack) bool {
|
||||
// Importing os/signal starts a background goroutine.
|
||||
// The name of the function at the top has changed between versions.
|
||||
if f := s.FirstFunction(); f == "os/signal.signal_recv" || f == "os/signal.loop" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Using signal.Notify will start a runtime goroutine.
|
||||
return strings.Contains(s.Full(), "runtime.ensureSigM")
|
||||
}
|
||||
|
||||
func isTraceStack(s stack.Stack) bool {
|
||||
if f := s.FirstFunction(); f != "runtime.goparkunlock" {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.Contains(s.Full(), "runtime.ReadTrace")
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2017 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package goleak
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Variables for stubbing in unit tests.
|
||||
var (
|
||||
_osExit = os.Exit
|
||||
_osStderr io.Writer = os.Stderr
|
||||
)
|
||||
|
||||
// TestingM is the minimal subset of testing.M that we use.
|
||||
type TestingM interface {
|
||||
Run() int
|
||||
}
|
||||
|
||||
// VerifyTestMain can be used in a TestMain function for package tests to
|
||||
// verify that there were no goroutine leaks.
|
||||
// To use it, your TestMain function should look like:
|
||||
//
|
||||
// func TestMain(m *testing.M) {
|
||||
// goleak.VerifyTestMain(m)
|
||||
// }
|
||||
//
|
||||
// See https://golang.org/pkg/testing/#hdr-Main for more details.
|
||||
//
|
||||
// This will run all tests as per normal, and if they were successful, look
|
||||
// for any goroutine leaks and fail the tests if any leaks were found.
|
||||
func VerifyTestMain(m TestingM, options ...Option) {
|
||||
exitCode := m.Run()
|
||||
|
||||
if exitCode == 0 {
|
||||
if err := Find(options...); err != nil {
|
||||
fmt.Fprintf(_osStderr, "goleak: Errors on successful test run: %v\n", err)
|
||||
exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
_osExit(exitCode)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// +build tools
|
||||
|
||||
package goleak
|
||||
|
||||
import (
|
||||
// Tools we use during development.
|
||||
_ "golang.org/x/lint/golint"
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- master
|
||||
|
||||
go_import_path: golang.org/x/lint
|
||||
|
||||
install:
|
||||
- go get -t -v ./...
|
||||
|
||||
script:
|
||||
- go test -v -race ./...
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
fast_finish: true
|
|
@ -0,0 +1,15 @@
|
|||
# Contributing to Golint
|
||||
|
||||
## Before filing an issue:
|
||||
|
||||
### Are you having trouble building golint?
|
||||
|
||||
Check you have the latest version of its dependencies. Run
|
||||
```
|
||||
go get -u golang.org/x/lint/golint
|
||||
```
|
||||
If you still have problems, consider searching for existing issues before filing a new issue.
|
||||
|
||||
## Before sending a pull request:
|
||||
|
||||
Have you understood the purpose of golint? Make sure to carefully read `README`.
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2013 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,88 @@
|
|||
Golint is a linter for Go source code.
|
||||
|
||||
[![Build Status](https://travis-ci.org/golang/lint.svg?branch=master)](https://travis-ci.org/golang/lint)
|
||||
|
||||
## Installation
|
||||
|
||||
Golint requires a
|
||||
[supported release of Go](https://golang.org/doc/devel/release.html#policy).
|
||||
|
||||
go get -u golang.org/x/lint/golint
|
||||
|
||||
To find out where `golint` was installed you can run `go list -f {{.Target}} golang.org/x/lint/golint`. For `golint` to be used globally add that directory to the `$PATH` environment setting.
|
||||
|
||||
## Usage
|
||||
|
||||
Invoke `golint` with one or more filenames, directories, or packages named
|
||||
by its import path. Golint uses the same
|
||||
[import path syntax](https://golang.org/cmd/go/#hdr-Import_path_syntax) as
|
||||
the `go` command and therefore
|
||||
also supports relative import paths like `./...`. Additionally the `...`
|
||||
wildcard can be used as suffix on relative and absolute file paths to recurse
|
||||
into them.
|
||||
|
||||
The output of this tool is a list of suggestions in Vim quickfix format,
|
||||
which is accepted by lots of different editors.
|
||||
|
||||
## Purpose
|
||||
|
||||
Golint differs from gofmt. Gofmt reformats Go source code, whereas
|
||||
golint prints out style mistakes.
|
||||
|
||||
Golint differs from govet. Govet is concerned with correctness, whereas
|
||||
golint is concerned with coding style. Golint is in use at Google, and it
|
||||
seeks to match the accepted style of the open source Go project.
|
||||
|
||||
The suggestions made by golint are exactly that: suggestions.
|
||||
Golint is not perfect, and has both false positives and false negatives.
|
||||
Do not treat its output as a gold standard. We will not be adding pragmas
|
||||
or other knobs to suppress specific warnings, so do not expect or require
|
||||
code to be completely "lint-free".
|
||||
In short, this tool is not, and will never be, trustworthy enough for its
|
||||
suggestions to be enforced automatically, for example as part of a build process.
|
||||
Golint makes suggestions for many of the mechanically checkable items listed in
|
||||
[Effective Go](https://golang.org/doc/effective_go.html) and the
|
||||
[CodeReviewComments wiki page](https://golang.org/wiki/CodeReviewComments).
|
||||
|
||||
## Scope
|
||||
|
||||
Golint is meant to carry out the stylistic conventions put forth in
|
||||
[Effective Go](https://golang.org/doc/effective_go.html) and
|
||||
[CodeReviewComments](https://golang.org/wiki/CodeReviewComments).
|
||||
Changes that are not aligned with those documents will not be considered.
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions to this project are welcome provided they are [in scope](#scope),
|
||||
though please send mail before starting work on anything major.
|
||||
Contributors retain their copyright, so we need you to fill out
|
||||
[a short form](https://developers.google.com/open-source/cla/individual)
|
||||
before we can accept your contribution.
|
||||
|
||||
## Vim
|
||||
|
||||
Add this to your ~/.vimrc:
|
||||
|
||||
set rtp+=$GOPATH/src/golang.org/x/lint/misc/vim
|
||||
|
||||
If you have multiple entries in your GOPATH, replace `$GOPATH` with the right value.
|
||||
|
||||
Running `:Lint` will run golint on the current file and populate the quickfix list.
|
||||
|
||||
Optionally, add this to your `~/.vimrc` to automatically run `golint` on `:w`
|
||||
|
||||
autocmd BufWritePost,FileWritePost *.go execute 'Lint' | cwindow
|
||||
|
||||
|
||||
## Emacs
|
||||
|
||||
Add this to your `.emacs` file:
|
||||
|
||||
(add-to-list 'load-path (concat (getenv "GOPATH") "/src/golang.org/x/lint/misc/emacs/"))
|
||||
(require 'golint)
|
||||
|
||||
If you have multiple entries in your GOPATH, replace `$GOPATH` with the right value.
|
||||
|
||||
Running M-x golint will run golint on the current file.
|
||||
|
||||
For more usage, see [Compilation-Mode](http://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html).
|
|
@ -0,0 +1,3 @@
|
|||
module golang.org/x/lint
|
||||
|
||||
require golang.org/x/tools v0.0.0-20190311212946-11955173bddd
|
|
@ -0,0 +1,6 @@
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright (c) 2013 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// golint lints the Go source files named on its command line.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/lint"
|
||||
)
|
||||
|
||||
var (
|
||||
minConfidence = flag.Float64("min_confidence", 0.8, "minimum confidence of a problem to print it")
|
||||
setExitStatus = flag.Bool("set_exit_status", false, "set exit status to 1 if any issues are found")
|
||||
suggestions int
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "\tgolint [flags] # runs on package in current directory\n")
|
||||
fmt.Fprintf(os.Stderr, "\tgolint [flags] [packages]\n")
|
||||
fmt.Fprintf(os.Stderr, "\tgolint [flags] [directories] # where a '/...' suffix includes all sub-directories\n")
|
||||
fmt.Fprintf(os.Stderr, "\tgolint [flags] [files] # all must belong to a single package\n")
|
||||
fmt.Fprintf(os.Stderr, "Flags:\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
lintDir(".")
|
||||
} else {
|
||||
// dirsRun, filesRun, and pkgsRun indicate whether golint is applied to
|
||||
// directory, file or package targets. The distinction affects which
|
||||
// checks are run. It is no valid to mix target types.
|
||||
var dirsRun, filesRun, pkgsRun int
|
||||
var args []string
|
||||
for _, arg := range flag.Args() {
|
||||
if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-len("/...")]) {
|
||||
dirsRun = 1
|
||||
for _, dirname := range allPackagesInFS(arg) {
|
||||
args = append(args, dirname)
|
||||
}
|
||||
} else if isDir(arg) {
|
||||
dirsRun = 1
|
||||
args = append(args, arg)
|
||||
} else if exists(arg) {
|
||||
filesRun = 1
|
||||
args = append(args, arg)
|
||||
} else {
|
||||
pkgsRun = 1
|
||||
args = append(args, arg)
|
||||
}
|
||||
}
|
||||
|
||||
if dirsRun+filesRun+pkgsRun != 1 {
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
switch {
|
||||
case dirsRun == 1:
|
||||
for _, dir := range args {
|
||||
lintDir(dir)
|
||||
}
|
||||
case filesRun == 1:
|
||||
lintFiles(args...)
|
||||
case pkgsRun == 1:
|
||||
for _, pkg := range importPaths(args) {
|
||||
lintPackage(pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *setExitStatus && suggestions > 0 {
|
||||
fmt.Fprintf(os.Stderr, "Found %d lint suggestions; failing.\n", suggestions)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func isDir(filename string) bool {
|
||||
fi, err := os.Stat(filename)
|
||||
return err == nil && fi.IsDir()
|
||||
}
|
||||
|
||||
func exists(filename string) bool {
|
||||
_, err := os.Stat(filename)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func lintFiles(filenames ...string) {
|
||||
files := make(map[string][]byte)
|
||||
for _, filename := range filenames {
|
||||
src, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
continue
|
||||
}
|
||||
files[filename] = src
|
||||
}
|
||||
|
||||
l := new(lint.Linter)
|
||||
ps, err := l.LintFiles(files)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return
|
||||
}
|
||||
for _, p := range ps {
|
||||
if p.Confidence >= *minConfidence {
|
||||
fmt.Printf("%v: %s\n", p.Position, p.Text)
|
||||
suggestions++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lintDir(dirname string) {
|
||||
pkg, err := build.ImportDir(dirname, 0)
|
||||
lintImportedPackage(pkg, err)
|
||||
}
|
||||
|
||||
func lintPackage(pkgname string) {
|
||||
pkg, err := build.Import(pkgname, ".", 0)
|
||||
lintImportedPackage(pkg, err)
|
||||
}
|
||||
|
||||
func lintImportedPackage(pkg *build.Package, err error) {
|
||||
if err != nil {
|
||||
if _, nogo := err.(*build.NoGoError); nogo {
|
||||
// Don't complain if the failure is due to no Go source files.
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
|
||||
var files []string
|
||||
files = append(files, pkg.GoFiles...)
|
||||
files = append(files, pkg.CgoFiles...)
|
||||
files = append(files, pkg.TestGoFiles...)
|
||||
if pkg.Dir != "." {
|
||||
for i, f := range files {
|
||||
files[i] = filepath.Join(pkg.Dir, f)
|
||||
}
|
||||
}
|
||||
// TODO(dsymonds): Do foo_test too (pkg.XTestGoFiles)
|
||||
|
||||
lintFiles(files...)
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
|
||||
This file holds a direct copy of the import path matching code of
|
||||
https://github.com/golang/go/blob/master/src/cmd/go/main.go. It can be
|
||||
replaced when https://golang.org/issue/8768 is resolved.
|
||||
|
||||
It has been updated to follow upstream changes in a few ways.
|
||||
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
buildContext = build.Default
|
||||
goroot = filepath.Clean(runtime.GOROOT())
|
||||
gorootSrc = filepath.Join(goroot, "src")
|
||||
)
|
||||
|
||||
// importPathsNoDotExpansion returns the import paths to use for the given
|
||||
// command line, but it does no ... expansion.
|
||||
func importPathsNoDotExpansion(args []string) []string {
|
||||
if len(args) == 0 {
|
||||
return []string{"."}
|
||||
}
|
||||
var out []string
|
||||
for _, a := range args {
|
||||
// Arguments are supposed to be import paths, but
|
||||
// as a courtesy to Windows developers, rewrite \ to /
|
||||
// in command-line arguments. Handles .\... and so on.
|
||||
if filepath.Separator == '\\' {
|
||||
a = strings.Replace(a, `\`, `/`, -1)
|
||||
}
|
||||
|
||||
// Put argument in canonical form, but preserve leading ./.
|
||||
if strings.HasPrefix(a, "./") {
|
||||
a = "./" + path.Clean(a)
|
||||
if a == "./." {
|
||||
a = "."
|
||||
}
|
||||
} else {
|
||||
a = path.Clean(a)
|
||||
}
|
||||
if a == "all" || a == "std" {
|
||||
out = append(out, allPackages(a)...)
|
||||
continue
|
||||
}
|
||||
out = append(out, a)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// importPaths returns the import paths to use for the given command line.
|
||||
func importPaths(args []string) []string {
|
||||
args = importPathsNoDotExpansion(args)
|
||||
var out []string
|
||||
for _, a := range args {
|
||||
if strings.Contains(a, "...") {
|
||||
if build.IsLocalImport(a) {
|
||||
out = append(out, allPackagesInFS(a)...)
|
||||
} else {
|
||||
out = append(out, allPackages(a)...)
|
||||
}
|
||||
continue
|
||||
}
|
||||
out = append(out, a)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// matchPattern(pattern)(name) reports whether
|
||||
// name matches pattern. Pattern is a limited glob
|
||||
// pattern in which '...' means 'any string' and there
|
||||
// is no other special syntax.
|
||||
func matchPattern(pattern string) func(name string) bool {
|
||||
re := regexp.QuoteMeta(pattern)
|
||||
re = strings.Replace(re, `\.\.\.`, `.*`, -1)
|
||||
// Special case: foo/... matches foo too.
|
||||
if strings.HasSuffix(re, `/.*`) {
|
||||
re = re[:len(re)-len(`/.*`)] + `(/.*)?`
|
||||
}
|
||||
reg := regexp.MustCompile(`^` + re + `$`)
|
||||
return func(name string) bool {
|
||||
return reg.MatchString(name)
|
||||
}
|
||||
}
|
||||
|
||||
// hasPathPrefix reports whether the path s begins with the
|
||||
// elements in prefix.
|
||||
func hasPathPrefix(s, prefix string) bool {
|
||||
switch {
|
||||
default:
|
||||
return false
|
||||
case len(s) == len(prefix):
|
||||
return s == prefix
|
||||
case len(s) > len(prefix):
|
||||
if prefix != "" && prefix[len(prefix)-1] == '/' {
|
||||
return strings.HasPrefix(s, prefix)
|
||||
}
|
||||
return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
|
||||
}
|
||||
}
|
||||
|
||||
// treeCanMatchPattern(pattern)(name) reports whether
|
||||
// name or children of name can possibly match pattern.
|
||||
// Pattern is the same limited glob accepted by matchPattern.
|
||||
func treeCanMatchPattern(pattern string) func(name string) bool {
|
||||
wildCard := false
|
||||
if i := strings.Index(pattern, "..."); i >= 0 {
|
||||
wildCard = true
|
||||
pattern = pattern[:i]
|
||||
}
|
||||
return func(name string) bool {
|
||||
return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
|
||||
wildCard && strings.HasPrefix(name, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// allPackages returns all the packages that can be found
|
||||
// under the $GOPATH directories and $GOROOT matching pattern.
|
||||
// The pattern is either "all" (all packages), "std" (standard packages)
|
||||
// or a path including "...".
|
||||
func allPackages(pattern string) []string {
|
||||
pkgs := matchPackages(pattern)
|
||||
if len(pkgs) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
func matchPackages(pattern string) []string {
|
||||
match := func(string) bool { return true }
|
||||
treeCanMatch := func(string) bool { return true }
|
||||
if pattern != "all" && pattern != "std" {
|
||||
match = matchPattern(pattern)
|
||||
treeCanMatch = treeCanMatchPattern(pattern)
|
||||
}
|
||||
|
||||
have := map[string]bool{
|
||||
"builtin": true, // ignore pseudo-package that exists only for documentation
|
||||
}
|
||||
if !buildContext.CgoEnabled {
|
||||
have["runtime/cgo"] = true // ignore during walk
|
||||
}
|
||||
var pkgs []string
|
||||
|
||||
// Commands
|
||||
cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator)
|
||||
filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || !fi.IsDir() || path == cmd {
|
||||
return nil
|
||||
}
|
||||
name := path[len(cmd):]
|
||||
if !treeCanMatch(name) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
// Commands are all in cmd/, not in subdirectories.
|
||||
if strings.Contains(name, string(filepath.Separator)) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
// We use, e.g., cmd/gofmt as the pseudo import path for gofmt.
|
||||
name = "cmd/" + name
|
||||
if have[name] {
|
||||
return nil
|
||||
}
|
||||
have[name] = true
|
||||
if !match(name) {
|
||||
return nil
|
||||
}
|
||||
_, err = buildContext.ImportDir(path, 0)
|
||||
if err != nil {
|
||||
if _, noGo := err.(*build.NoGoError); !noGo {
|
||||
log.Print(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
pkgs = append(pkgs, name)
|
||||
return nil
|
||||
})
|
||||
|
||||
for _, src := range buildContext.SrcDirs() {
|
||||
if (pattern == "std" || pattern == "cmd") && src != gorootSrc {
|
||||
continue
|
||||
}
|
||||
src = filepath.Clean(src) + string(filepath.Separator)
|
||||
root := src
|
||||
if pattern == "cmd" {
|
||||
root += "cmd" + string(filepath.Separator)
|
||||
}
|
||||
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || !fi.IsDir() || path == src {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Avoid .foo, _foo, and testdata directory trees.
|
||||
_, elem := filepath.Split(path)
|
||||
if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
name := filepath.ToSlash(path[len(src):])
|
||||
if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") {
|
||||
// The name "std" is only the standard library.
|
||||
// If the name is cmd, it's the root of the command tree.
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if !treeCanMatch(name) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if have[name] {
|
||||
return nil
|
||||
}
|
||||
have[name] = true
|
||||
if !match(name) {
|
||||
return nil
|
||||
}
|
||||
_, err = buildContext.ImportDir(path, 0)
|
||||
if err != nil {
|
||||
if _, noGo := err.(*build.NoGoError); noGo {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
pkgs = append(pkgs, name)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// allPackagesInFS is like allPackages but is passed a pattern
|
||||
// beginning ./ or ../, meaning it should scan the tree rooted
|
||||
// at the given directory. There are ... in the pattern too.
|
||||
func allPackagesInFS(pattern string) []string {
|
||||
pkgs := matchPackagesInFS(pattern)
|
||||
if len(pkgs) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
func matchPackagesInFS(pattern string) []string {
|
||||
// Find directory to begin the scan.
|
||||
// Could be smarter but this one optimization
|
||||
// is enough for now, since ... is usually at the
|
||||
// end of a path.
|
||||
i := strings.Index(pattern, "...")
|
||||
dir, _ := path.Split(pattern[:i])
|
||||
|
||||
// pattern begins with ./ or ../.
|
||||
// path.Clean will discard the ./ but not the ../.
|
||||
// We need to preserve the ./ for pattern matching
|
||||
// and in the returned import paths.
|
||||
prefix := ""
|
||||
if strings.HasPrefix(pattern, "./") {
|
||||
prefix = "./"
|
||||
}
|
||||
match := matchPattern(pattern)
|
||||
|
||||
var pkgs []string
|
||||
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || !fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if path == dir {
|
||||
// filepath.Walk starts at dir and recurses. For the recursive case,
|
||||
// the path is the result of filepath.Join, which calls filepath.Clean.
|
||||
// The initial case is not Cleaned, though, so we do this explicitly.
|
||||
//
|
||||
// This converts a path like "./io/" to "io". Without this step, running
|
||||
// "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io
|
||||
// package, because prepending the prefix "./" to the unclean path would
|
||||
// result in "././io", and match("././io") returns false.
|
||||
path = filepath.Clean(path)
|
||||
}
|
||||
|
||||
// Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
|
||||
_, elem := filepath.Split(path)
|
||||
dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
|
||||
if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
name := prefix + filepath.ToSlash(path)
|
||||
if !match(name) {
|
||||
return nil
|
||||
}
|
||||
if _, err = build.ImportDir(path, 0); err != nil {
|
||||
if _, noGo := err.(*build.NoGoError); !noGo {
|
||||
log.Print(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
pkgs = append(pkgs, name)
|
||||
return nil
|
||||
})
|
||||
return pkgs
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2018 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// +build go1.12
|
||||
|
||||
// Require use of the correct import path only for Go 1.12+ users, so
|
||||
// any breakages coincide with people updating their CI configs or
|
||||
// whatnot.
|
||||
|
||||
package main // import "golang.org/x/lint/golint"
|
File diff suppressed because it is too large
Load Diff
|
@ -150,7 +150,7 @@ func appendIndexed(dst []byte, i uint64) []byte {
|
|||
// extended buffer.
|
||||
//
|
||||
// If f.Sensitive is true, "Never Indexed" representation is used. If
|
||||
// f.Sensitive is false and indexing is true, "Inremental Indexing"
|
||||
// f.Sensitive is false and indexing is true, "Incremental Indexing"
|
||||
// representation is used.
|
||||
func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {
|
||||
dst = append(dst, encodeTypeByte(indexing, f.Sensitive))
|
||||
|
|
|
@ -19,7 +19,6 @@ package http2 // import "golang.org/x/net/http2"
|
|||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -173,11 +172,6 @@ func (s SettingID) String() string {
|
|||
return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidHeaderFieldName = errors.New("http2: invalid header field name")
|
||||
errInvalidHeaderFieldValue = errors.New("http2: invalid header field value")
|
||||
)
|
||||
|
||||
// validWireHeaderFieldName reports whether v is a valid header field
|
||||
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||
//
|
||||
|
|
|
@ -17,6 +17,7 @@ type pipe struct {
|
|||
mu sync.Mutex
|
||||
c sync.Cond // c.L lazily initialized to &p.mu
|
||||
b pipeBuffer // nil when done reading
|
||||
unread int // bytes unread when done
|
||||
err error // read error once empty. non-nil means closed.
|
||||
breakErr error // immediate read error (caller doesn't see rest of b)
|
||||
donec chan struct{} // closed on error
|
||||
|
@ -33,7 +34,7 @@ func (p *pipe) Len() int {
|
|||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if p.b == nil {
|
||||
return 0
|
||||
return p.unread
|
||||
}
|
||||
return p.b.Len()
|
||||
}
|
||||
|
@ -80,6 +81,7 @@ func (p *pipe) Write(d []byte) (n int, err error) {
|
|||
return 0, errClosedPipeWrite
|
||||
}
|
||||
if p.breakErr != nil {
|
||||
p.unread += len(d)
|
||||
return len(d), nil // discard when there is no reader
|
||||
}
|
||||
return p.b.Write(d)
|
||||
|
@ -117,6 +119,9 @@ func (p *pipe) closeWithError(dst *error, err error, fn func()) {
|
|||
}
|
||||
p.readFn = fn
|
||||
if dst == &p.breakErr {
|
||||
if p.b != nil {
|
||||
p.unread += p.b.Len()
|
||||
}
|
||||
p.b = nil
|
||||
}
|
||||
*dst = err
|
||||
|
|
|
@ -252,7 +252,7 @@ func ConfigureServer(s *http.Server, conf *Server) error {
|
|||
}
|
||||
}
|
||||
if !haveRequired {
|
||||
return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher.")
|
||||
return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 or TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,7 +322,7 @@ type ServeConnOpts struct {
|
|||
}
|
||||
|
||||
func (o *ServeConnOpts) context() context.Context {
|
||||
if o.Context != nil {
|
||||
if o != nil && o.Context != nil {
|
||||
return o.Context
|
||||
}
|
||||
return context.Background()
|
||||
|
@ -581,13 +581,10 @@ type stream struct {
|
|||
cancelCtx func()
|
||||
|
||||
// owned by serverConn's serve loop:
|
||||
bodyBytes int64 // body bytes seen so far
|
||||
declBodyBytes int64 // or -1 if undeclared
|
||||
flow flow // limits writing from Handler to client
|
||||
inflow flow // what the client is allowed to POST/etc to us
|
||||
parent *stream // or nil
|
||||
numTrailerValues int64
|
||||
weight uint8
|
||||
bodyBytes int64 // body bytes seen so far
|
||||
declBodyBytes int64 // or -1 if undeclared
|
||||
flow flow // limits writing from Handler to client
|
||||
inflow flow // what the client is allowed to POST/etc to us
|
||||
state streamState
|
||||
resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
|
||||
gotTrailerHeader bool // HEADER frame for trailers was seen
|
||||
|
@ -2415,7 +2412,11 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|||
clen = strconv.Itoa(len(p))
|
||||
}
|
||||
_, hasContentType := rws.snapHeader["Content-Type"]
|
||||
if !hasContentType && bodyAllowedForStatus(rws.status) && len(p) > 0 {
|
||||
// If the Content-Encoding is non-blank, we shouldn't
|
||||
// sniff the body. See Issue golang.org/issue/31753.
|
||||
ce := rws.snapHeader.Get("Content-Encoding")
|
||||
hasCE := len(ce) > 0
|
||||
if !hasCE && !hasContentType && bodyAllowedForStatus(rws.status) && len(p) > 0 {
|
||||
ctype = http.DetectContentType(p)
|
||||
}
|
||||
var date string
|
||||
|
@ -2524,7 +2525,7 @@ const TrailerPrefix = "Trailer:"
|
|||
// trailers. That worked for a while, until we found the first major
|
||||
// user of Trailers in the wild: gRPC (using them only over http2),
|
||||
// and gRPC libraries permit setting trailers mid-stream without
|
||||
// predeclarnig them. So: change of plans. We still permit the old
|
||||
// predeclaring them. So: change of plans. We still permit the old
|
||||
// way, but we also permit this hack: if a Header() key begins with
|
||||
// "Trailer:", the suffix of that key is a Trailer. Because ':' is an
|
||||
// invalid token byte anyway, there is no ambiguity. (And it's already
|
||||
|
@ -2824,7 +2825,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) {
|
|||
// PUSH_PROMISE frames MUST only be sent on a peer-initiated stream that
|
||||
// is in either the "open" or "half-closed (remote)" state.
|
||||
if msg.parent.state != stateOpen && msg.parent.state != stateHalfClosedRemote {
|
||||
// responseWriter.Push checks that the stream is peer-initiaed.
|
||||
// responseWriter.Push checks that the stream is peer-initiated.
|
||||
msg.done <- errStreamClosed
|
||||
return
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ type Transport struct {
|
|||
// send in the initial settings frame. It is how many bytes
|
||||
// of response headers are allowed. Unlike the http2 spec, zero here
|
||||
// means to use a default limit (currently 10MB). If you actually
|
||||
// want to advertise an ulimited value to the peer, Transport
|
||||
// want to advertise an unlimited value to the peer, Transport
|
||||
// interprets the highest possible value here (0xffffffff or 1<<32-1)
|
||||
// to mean no limit.
|
||||
MaxHeaderListSize uint32
|
||||
|
@ -227,6 +227,7 @@ type ClientConn struct {
|
|||
br *bufio.Reader
|
||||
fr *Framer
|
||||
lastActive time.Time
|
||||
lastIdle time.Time // time last idle
|
||||
// Settings from peer: (also guarded by mu)
|
||||
maxFrameSize uint32
|
||||
maxConcurrentStreams uint32
|
||||
|
@ -603,7 +604,7 @@ func (t *Transport) expectContinueTimeout() time.Duration {
|
|||
}
|
||||
|
||||
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
|
||||
return t.newClientConn(c, false)
|
||||
return t.newClientConn(c, t.disableKeepAlives())
|
||||
}
|
||||
|
||||
func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) {
|
||||
|
@ -736,7 +737,8 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {
|
|||
}
|
||||
|
||||
st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay &&
|
||||
int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32
|
||||
int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 &&
|
||||
!cc.tooIdleLocked()
|
||||
st.freshConn = cc.nextStreamID == 1 && st.canTakeNewRequest
|
||||
return
|
||||
}
|
||||
|
@ -746,6 +748,16 @@ func (cc *ClientConn) canTakeNewRequestLocked() bool {
|
|||
return st.canTakeNewRequest
|
||||
}
|
||||
|
||||
// tooIdleLocked reports whether this connection has been been sitting idle
|
||||
// for too much wall time.
|
||||
func (cc *ClientConn) tooIdleLocked() bool {
|
||||
// The Round(0) strips the monontonic clock reading so the
|
||||
// times are compared based on their wall time. We don't want
|
||||
// to reuse a connection that's been sitting idle during
|
||||
// VM/laptop suspend if monotonic time was also frozen.
|
||||
return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout
|
||||
}
|
||||
|
||||
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
|
||||
// only be called when we're idle, but because we're coming from a new
|
||||
// goroutine, there could be a new request coming in at the same time,
|
||||
|
@ -1150,6 +1162,7 @@ func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
|
|||
}
|
||||
return errClientConnUnusable
|
||||
}
|
||||
cc.lastIdle = time.Time{}
|
||||
if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) {
|
||||
if waitingForConn != nil {
|
||||
close(waitingForConn)
|
||||
|
@ -1216,6 +1229,8 @@ var (
|
|||
|
||||
// abort request body write, but send stream reset of cancel.
|
||||
errStopReqBodyWriteAndCancel = errors.New("http2: canceling request")
|
||||
|
||||
errReqBodyTooLong = errors.New("http2: request body larger than specified content length")
|
||||
)
|
||||
|
||||
func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (err error) {
|
||||
|
@ -1238,10 +1253,32 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
|
|||
|
||||
req := cs.req
|
||||
hasTrailers := req.Trailer != nil
|
||||
remainLen := actualContentLength(req)
|
||||
hasContentLen := remainLen != -1
|
||||
|
||||
var sawEOF bool
|
||||
for !sawEOF {
|
||||
n, err := body.Read(buf)
|
||||
n, err := body.Read(buf[:len(buf)-1])
|
||||
if hasContentLen {
|
||||
remainLen -= int64(n)
|
||||
if remainLen == 0 && err == nil {
|
||||
// The request body's Content-Length was predeclared and
|
||||
// we just finished reading it all, but the underlying io.Reader
|
||||
// returned the final chunk with a nil error (which is one of
|
||||
// the two valid things a Reader can do at EOF). Because we'd prefer
|
||||
// to send the END_STREAM bit early, double-check that we're actually
|
||||
// at EOF. Subsequent reads should return (0, EOF) at this point.
|
||||
// If either value is different, we return an error in one of two ways below.
|
||||
var n1 int
|
||||
n1, err = body.Read(buf[n:])
|
||||
remainLen -= int64(n1)
|
||||
}
|
||||
if remainLen < 0 {
|
||||
err = errReqBodyTooLong
|
||||
cc.writeStreamReset(cs.ID, ErrCodeCancel, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
sawEOF = true
|
||||
err = nil
|
||||
|
@ -1454,7 +1491,29 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
|||
if vv[0] == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
} else if strings.EqualFold(k, "cookie") {
|
||||
// Per 8.1.2.5 To allow for better compression efficiency, the
|
||||
// Cookie header field MAY be split into separate header fields,
|
||||
// each with one or more cookie-pairs.
|
||||
for _, v := range vv {
|
||||
for {
|
||||
p := strings.IndexByte(v, ';')
|
||||
if p < 0 {
|
||||
break
|
||||
}
|
||||
f("cookie", v[:p])
|
||||
p++
|
||||
// strip space after semicolon if any.
|
||||
for p+1 <= len(v) && v[p] == ' ' {
|
||||
p++
|
||||
}
|
||||
v = v[p:]
|
||||
}
|
||||
if len(v) > 0 {
|
||||
f("cookie", v)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for _, v := range vv {
|
||||
|
@ -1592,6 +1651,7 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
|
|||
delete(cc.streams, id)
|
||||
if len(cc.streams) == 0 && cc.idleTimer != nil {
|
||||
cc.idleTimer.Reset(cc.idleTimeout)
|
||||
cc.lastIdle = time.Now()
|
||||
}
|
||||
close(cs.done)
|
||||
// Wake up checkResetOrDone via clientStream.awaitFlowControl and
|
||||
|
@ -2138,8 +2198,6 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var errInvalidTrailers = errors.New("http2: invalid trailers")
|
||||
|
||||
func (rl *clientConnReadLoop) endStream(cs *clientStream) {
|
||||
// TODO: check that any declared content-length matches, like
|
||||
// server.go's (*stream).endStream method.
|
||||
|
@ -2370,7 +2428,6 @@ func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, err error)
|
|||
var (
|
||||
errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
|
||||
errRequestHeaderListSize = errors.New("http2: request header list larger than peer's advertised limit")
|
||||
errPseudoTrailers = errors.New("http2: invalid pseudo header in trailers")
|
||||
)
|
||||
|
||||
func (cc *ClientConn) logf(format string, args ...interface{}) {
|
||||
|
|
|
@ -149,7 +149,7 @@ func (n *priorityNode) addBytes(b int64) {
|
|||
}
|
||||
|
||||
// walkReadyInOrder iterates over the tree in priority order, calling f for each node
|
||||
// with a non-empty write queue. When f returns true, this funcion returns true and the
|
||||
// with a non-empty write queue. When f returns true, this function returns true and the
|
||||
// walk halts. tmp is used as scratch space for sorting.
|
||||
//
|
||||
// f(n, openParent) takes two arguments: the node to visit, n, and a bool that is true
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// +build go1.13
|
||||
// +build go1.13,!go1.14
|
||||
|
||||
package idna
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,12 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !race
|
||||
|
||||
package socket
|
||||
|
||||
func (m *Message) raceRead() {
|
||||
}
|
||||
func (m *Message) raceWrite() {
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build race
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// This package reads and writes the Message buffers using a
|
||||
// direct system call, which the race detector can't see.
|
||||
// These functions tell the race detector what is going on during the syscall.
|
||||
|
||||
func (m *Message) raceRead() {
|
||||
for _, b := range m.Buffers {
|
||||
if len(b) > 0 {
|
||||
runtime.RaceReadRange(unsafe.Pointer(&b[0]), len(b))
|
||||
}
|
||||
}
|
||||
if b := m.OOB; len(b) > 0 {
|
||||
runtime.RaceReadRange(unsafe.Pointer(&b[0]), len(b))
|
||||
}
|
||||
}
|
||||
func (m *Message) raceWrite() {
|
||||
for _, b := range m.Buffers {
|
||||
if len(b) > 0 {
|
||||
runtime.RaceWriteRange(unsafe.Pointer(&b[0]), len(b))
|
||||
}
|
||||
}
|
||||
if b := m.OOB; len(b) > 0 {
|
||||
runtime.RaceWriteRange(unsafe.Pointer(&b[0]), len(b))
|
||||
}
|
||||
}
|
|
@ -13,6 +13,9 @@ import (
|
|||
)
|
||||
|
||||
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
||||
for i := range ms {
|
||||
ms[i].raceWrite()
|
||||
}
|
||||
hs := make(mmsghdrs, len(ms))
|
||||
var parseFn func([]byte, string) (net.Addr, error)
|
||||
if c.network != "tcp" {
|
||||
|
@ -43,6 +46,9 @@ func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
|||
}
|
||||
|
||||
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
|
||||
for i := range ms {
|
||||
ms[i].raceRead()
|
||||
}
|
||||
hs := make(mmsghdrs, len(ms))
|
||||
var marshalFn func(net.Addr) []byte
|
||||
if c.network != "tcp" {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
)
|
||||
|
||||
func (c *Conn) recvMsg(m *Message, flags int) error {
|
||||
m.raceWrite()
|
||||
var h msghdr
|
||||
vs := make([]iovec, len(m.Buffers))
|
||||
var sa []byte
|
||||
|
@ -48,6 +49,7 @@ func (c *Conn) recvMsg(m *Message, flags int) error {
|
|||
}
|
||||
|
||||
func (c *Conn) sendMsg(m *Message, flags int) error {
|
||||
m.raceRead()
|
||||
var h msghdr
|
||||
vs := make([]iovec, len(m.Buffers))
|
||||
var sa []byte
|
||||
|
|
|
@ -4,4 +4,29 @@
|
|||
|
||||
package socket
|
||||
|
||||
func probeProtocolStack() int { return 4 }
|
||||
import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// See version list in https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/param.h
|
||||
var (
|
||||
osreldateOnce sync.Once
|
||||
osreldate uint32
|
||||
)
|
||||
|
||||
// First __DragonFly_version after September 2019 ABI changes
|
||||
// http://lists.dragonflybsd.org/pipermail/users/2019-September/358280.html
|
||||
const _dragonflyABIChangeVersion = 500705
|
||||
|
||||
func probeProtocolStack() int {
|
||||
osreldateOnce.Do(func() { osreldate, _ = syscall.SysctlUint32("kern.osreldate") })
|
||||
var p uintptr
|
||||
if int(unsafe.Sizeof(p)) == 8 && osreldate >= _dragonflyABIChangeVersion {
|
||||
return int(unsafe.Sizeof(p))
|
||||
}
|
||||
// 64-bit Dragonfly before the September 2019 ABI changes still requires
|
||||
// 32-bit aligned access to network subsystem.
|
||||
return 4
|
||||
}
|
||||
|
|
|
@ -53,7 +53,6 @@ type sockaddrInet6 struct {
|
|||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x30
|
||||
sizeofMmsghdr = 0x38
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -47,7 +47,6 @@ type sockaddrInet6 struct {
|
|||
const (
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
sizeofMmsghdr = 0x20
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -50,7 +50,6 @@ type sockaddrInet6 struct {
|
|||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofCmsghdr = 0x10
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -45,9 +45,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
sizeofMmsghdr = 0x20
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -48,9 +48,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
|
||||
sizeofCmsghdr = 0x10
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -45,9 +45,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
sizeofMmsghdr = 0x20
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -48,9 +48,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
|
||||
sizeofCmsghdr = 0x10
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -48,9 +48,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
|
||||
sizeofCmsghdr = 0x10
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -45,9 +45,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
sizeofMmsghdr = 0x20
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -48,9 +48,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
|
||||
sizeofCmsghdr = 0x10
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -48,9 +48,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
|
||||
sizeofCmsghdr = 0x10
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -49,9 +49,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
|
||||
sizeofCmsghdr = 0x10
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -48,9 +48,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x38
|
||||
|
||||
sizeofCmsghdr = 0x10
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -47,9 +47,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
sizeofMmsghdr = 0x20
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -50,9 +50,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x30
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x30
|
||||
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -47,9 +47,9 @@ type sockaddrInet6 struct {
|
|||
}
|
||||
|
||||
const (
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
sizeofMmsghdr = 0x20
|
||||
sizeofIovec = 0x8
|
||||
sizeofMsghdr = 0x1c
|
||||
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -52,7 +52,6 @@ type sockaddrInet6 struct {
|
|||
const (
|
||||
sizeofIovec = 0x10
|
||||
sizeofMsghdr = 0x30
|
||||
sizeofMmsghdr = 0x40
|
||||
sizeofCmsghdr = 0xc
|
||||
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
|
|
@ -127,7 +127,7 @@ type Dialer struct {
|
|||
// establishing the transport connection.
|
||||
ProxyDial func(context.Context, string, string) (net.Conn, error)
|
||||
|
||||
// AuthMethods specifies the list of request authention
|
||||
// AuthMethods specifies the list of request authentication
|
||||
// methods.
|
||||
// If empty, SOCKS client requests only AuthMethodNotRequired.
|
||||
AuthMethods []AuthMethod
|
||||
|
|
|
@ -403,9 +403,9 @@ func (ts *timeSeries) extract(l *tsLevel, start, finish time.Time, num int, resu
|
|||
|
||||
// Where should scanning start?
|
||||
if dstStart.After(srcStart) {
|
||||
advance := dstStart.Sub(srcStart) / srcInterval
|
||||
srcIndex += int(advance)
|
||||
srcStart = srcStart.Add(advance * srcInterval)
|
||||
advance := int(dstStart.Sub(srcStart) / srcInterval)
|
||||
srcIndex += advance
|
||||
srcStart = srcStart.Add(time.Duration(advance) * srcInterval)
|
||||
}
|
||||
|
||||
// The i'th value is computed as show below.
|
||||
|
|
|
@ -35,6 +35,7 @@ func marshalInterface(b []byte, cm *ControlMessage) []byte {
|
|||
}
|
||||
|
||||
func parseInterface(cm *ControlMessage, b []byte) {
|
||||
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&b[0]))
|
||||
var sadl syscall.SockaddrDatalink
|
||||
copy((*[unsafe.Sizeof(sadl)]byte)(unsafe.Pointer(&sadl))[:], b)
|
||||
cm.IfIndex = int(sadl.Index)
|
||||
}
|
||||
|
|
|
@ -13,16 +13,13 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
errInvalidConn = errors.New("invalid connection")
|
||||
errMissingAddress = errors.New("missing address")
|
||||
errMissingHeader = errors.New("missing header")
|
||||
errNilHeader = errors.New("nil header")
|
||||
errHeaderTooShort = errors.New("header too short")
|
||||
errExtHeaderTooShort = errors.New("extension header too short")
|
||||
errInvalidConnType = errors.New("invalid conn type")
|
||||
errNoSuchInterface = errors.New("no such interface")
|
||||
errNoSuchMulticastInterface = errors.New("no such multicast interface")
|
||||
errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
|
||||
errInvalidConn = errors.New("invalid connection")
|
||||
errMissingAddress = errors.New("missing address")
|
||||
errNilHeader = errors.New("nil header")
|
||||
errHeaderTooShort = errors.New("header too short")
|
||||
errExtHeaderTooShort = errors.New("extension header too short")
|
||||
errInvalidConnType = errors.New("invalid conn type")
|
||||
errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
|
||||
|
||||
// See https://www.freebsd.org/doc/en/books/porters-handbook/versions.html.
|
||||
freebsdVersion uint32
|
||||
|
|
|
@ -7,12 +7,15 @@
|
|||
package ipv4
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/internal/socket"
|
||||
)
|
||||
|
||||
var errNoSuchInterface = errors.New("no such interface")
|
||||
|
||||
func (so *sockOpt) setIPMreq(c *socket.Conn, ifi *net.Interface, grp net.IP) error {
|
||||
mreq := ipMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
|
||||
if err := setIPMreqInterface(&mreq, ifi); err != nil {
|
||||
|
|
|
@ -11,13 +11,14 @@ import (
|
|||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/net/internal/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (so *sockOpt) setAttachFilter(c *socket.Conn, f []bpf.RawInstruction) error {
|
||||
prog := sockFProg{
|
||||
prog := unix.SockFprog{
|
||||
Len: uint16(len(f)),
|
||||
Filter: (*sockFilter)(unsafe.Pointer(&f[0])),
|
||||
Filter: (*unix.SockFilter)(unsafe.Pointer(&f[0])),
|
||||
}
|
||||
b := (*[sizeofSockFprog]byte)(unsafe.Pointer(&prog))[:sizeofSockFprog]
|
||||
b := (*[unix.SizeofSockFprog]byte)(unsafe.Pointer(&prog))[:unix.SizeofSockFprog]
|
||||
return so.Set(c, b)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"golang.org/x/net/internal/iana"
|
||||
"golang.org/x/net/internal/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -35,7 +36,7 @@ var (
|
|||
ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoAttachFilter: {Option: socket.Option{Level: sysSOL_SOCKET, Name: sysSO_ATTACH_FILTER, Len: sizeofSockFprog}},
|
||||
ssoAttachFilter: {Option: socket.Option{Level: unix.SOL_SOCKET, Name: unix.SO_ATTACH_FILTER, Len: unix.SizeofSockFprog}},
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs defs_freebsd.go
|
||||
|
||||
package ipv4
|
||||
|
||||
const (
|
||||
sysIP_OPTIONS = 0x1
|
||||
sysIP_HDRINCL = 0x2
|
||||
sysIP_TOS = 0x3
|
||||
sysIP_TTL = 0x4
|
||||
sysIP_RECVOPTS = 0x5
|
||||
sysIP_RECVRETOPTS = 0x6
|
||||
sysIP_RECVDSTADDR = 0x7
|
||||
sysIP_SENDSRCADDR = 0x7
|
||||
sysIP_RETOPTS = 0x8
|
||||
sysIP_RECVIF = 0x14
|
||||
sysIP_ONESBCAST = 0x17
|
||||
sysIP_BINDANY = 0x18
|
||||
sysIP_RECVTTL = 0x41
|
||||
sysIP_MINTTL = 0x42
|
||||
sysIP_DONTFRAG = 0x43
|
||||
sysIP_RECVTOS = 0x44
|
||||
|
||||
sysIP_MULTICAST_IF = 0x9
|
||||
sysIP_MULTICAST_TTL = 0xa
|
||||
sysIP_MULTICAST_LOOP = 0xb
|
||||
sysIP_ADD_MEMBERSHIP = 0xc
|
||||
sysIP_DROP_MEMBERSHIP = 0xd
|
||||
sysIP_MULTICAST_VIF = 0xe
|
||||
sysIP_ADD_SOURCE_MEMBERSHIP = 0x46
|
||||
sysIP_DROP_SOURCE_MEMBERSHIP = 0x47
|
||||
sysIP_BLOCK_SOURCE = 0x48
|
||||
sysIP_UNBLOCK_SOURCE = 0x49
|
||||
sysMCAST_JOIN_GROUP = 0x50
|
||||
sysMCAST_LEAVE_GROUP = 0x51
|
||||
sysMCAST_JOIN_SOURCE_GROUP = 0x52
|
||||
sysMCAST_LEAVE_SOURCE_GROUP = 0x53
|
||||
sysMCAST_BLOCK_SOURCE = 0x54
|
||||
sysMCAST_UNBLOCK_SOURCE = 0x55
|
||||
|
||||
sizeofSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
||||
sizeofIPMreq = 0x8
|
||||
sizeofIPMreqn = 0xc
|
||||
sizeofIPMreqSource = 0xc
|
||||
sizeofGroupReq = 0x88
|
||||
sizeofGroupSourceReq = 0x108
|
||||
)
|
||||
|
||||
type sockaddrStorage struct {
|
||||
Len uint8
|
||||
Family uint8
|
||||
X__ss_pad1 [6]uint8
|
||||
X__ss_align int64
|
||||
X__ss_pad2 [112]uint8
|
||||
}
|
||||
|
||||
type sockaddrInet struct {
|
||||
Len uint8
|
||||
Family uint8
|
||||
Port uint16
|
||||
Addr [4]byte /* in_addr */
|
||||
Zero [8]uint8
|
||||
}
|
||||
|
||||
type ipMreq struct {
|
||||
Multiaddr [4]byte /* in_addr */
|
||||
Interface [4]byte /* in_addr */
|
||||
}
|
||||
|
||||
type ipMreqn struct {
|
||||
Multiaddr [4]byte /* in_addr */
|
||||
Address [4]byte /* in_addr */
|
||||
Ifindex int32
|
||||
}
|
||||
|
||||
type ipMreqSource struct {
|
||||
Multiaddr [4]byte /* in_addr */
|
||||
Sourceaddr [4]byte /* in_addr */
|
||||
Interface [4]byte /* in_addr */
|
||||
}
|
||||
|
||||
type groupReq struct {
|
||||
Interface uint32
|
||||
Group sockaddrStorage
|
||||
}
|
||||
|
||||
type groupSourceReq struct {
|
||||
Interface uint32
|
||||
Group sockaddrStorage
|
||||
Source sockaddrStorage
|
||||
}
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -133,16 +128,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -135,16 +130,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -133,16 +128,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -135,16 +130,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -133,16 +128,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -135,16 +130,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -135,16 +130,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -133,16 +128,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -133,16 +128,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -135,16 +130,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -135,16 +130,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -57,9 +57,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -72,8 +69,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -137,15 +132,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -55,9 +55,6 @@ const (
|
|||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
|
@ -70,8 +67,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -135,16 +130,3 @@ type groupSourceReq struct {
|
|||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ var (
|
|||
errMissingAddress = errors.New("missing address")
|
||||
errHeaderTooShort = errors.New("header too short")
|
||||
errInvalidConnType = errors.New("invalid conn type")
|
||||
errNoSuchInterface = errors.New("no such interface")
|
||||
errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
|
||||
)
|
||||
|
||||
|
|
|
@ -11,13 +11,14 @@ import (
|
|||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/net/internal/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (so *sockOpt) setAttachFilter(c *socket.Conn, f []bpf.RawInstruction) error {
|
||||
prog := sockFProg{
|
||||
prog := unix.SockFprog{
|
||||
Len: uint16(len(f)),
|
||||
Filter: (*sockFilter)(unsafe.Pointer(&f[0])),
|
||||
Filter: (*unix.SockFilter)(unsafe.Pointer(&f[0])),
|
||||
}
|
||||
b := (*[sizeofSockFprog]byte)(unsafe.Pointer(&prog))[:sizeofSockFprog]
|
||||
b := (*[unix.SizeofSockFprog]byte)(unsafe.Pointer(&prog))[:unix.SizeofSockFprog]
|
||||
return so.Set(c, b)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"golang.org/x/net/internal/iana"
|
||||
"golang.org/x/net/internal/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -40,7 +41,7 @@ var (
|
|||
ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoAttachFilter: {Option: socket.Option{Level: sysSOL_SOCKET, Name: sysSO_ATTACH_FILTER, Len: sizeofSockFprog}},
|
||||
ssoAttachFilter: {Option: socket.Option{Level: unix.SOL_SOCKET, Name: unix.SO_ATTACH_FILTER, Len: unix.SizeofSockFprog}},
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs defs_freebsd.go
|
||||
|
||||
package ipv6
|
||||
|
||||
const (
|
||||
sysIPV6_UNICAST_HOPS = 0x4
|
||||
sysIPV6_MULTICAST_IF = 0x9
|
||||
sysIPV6_MULTICAST_HOPS = 0xa
|
||||
sysIPV6_MULTICAST_LOOP = 0xb
|
||||
sysIPV6_JOIN_GROUP = 0xc
|
||||
sysIPV6_LEAVE_GROUP = 0xd
|
||||
sysIPV6_PORTRANGE = 0xe
|
||||
sysICMP6_FILTER = 0x12
|
||||
|
||||
sysIPV6_CHECKSUM = 0x1a
|
||||
sysIPV6_V6ONLY = 0x1b
|
||||
|
||||
sysIPV6_IPSEC_POLICY = 0x1c
|
||||
|
||||
sysIPV6_RTHDRDSTOPTS = 0x23
|
||||
|
||||
sysIPV6_RECVPKTINFO = 0x24
|
||||
sysIPV6_RECVHOPLIMIT = 0x25
|
||||
sysIPV6_RECVRTHDR = 0x26
|
||||
sysIPV6_RECVHOPOPTS = 0x27
|
||||
sysIPV6_RECVDSTOPTS = 0x28
|
||||
|
||||
sysIPV6_USE_MIN_MTU = 0x2a
|
||||
sysIPV6_RECVPATHMTU = 0x2b
|
||||
|
||||
sysIPV6_PATHMTU = 0x2c
|
||||
|
||||
sysIPV6_PKTINFO = 0x2e
|
||||
sysIPV6_HOPLIMIT = 0x2f
|
||||
sysIPV6_NEXTHOP = 0x30
|
||||
sysIPV6_HOPOPTS = 0x31
|
||||
sysIPV6_DSTOPTS = 0x32
|
||||
sysIPV6_RTHDR = 0x33
|
||||
|
||||
sysIPV6_RECVTCLASS = 0x39
|
||||
|
||||
sysIPV6_AUTOFLOWLABEL = 0x3b
|
||||
|
||||
sysIPV6_TCLASS = 0x3d
|
||||
sysIPV6_DONTFRAG = 0x3e
|
||||
|
||||
sysIPV6_PREFER_TEMPADDR = 0x3f
|
||||
|
||||
sysIPV6_BINDANY = 0x40
|
||||
|
||||
sysIPV6_MSFILTER = 0x4a
|
||||
|
||||
sysMCAST_JOIN_GROUP = 0x50
|
||||
sysMCAST_LEAVE_GROUP = 0x51
|
||||
sysMCAST_JOIN_SOURCE_GROUP = 0x52
|
||||
sysMCAST_LEAVE_SOURCE_GROUP = 0x53
|
||||
sysMCAST_BLOCK_SOURCE = 0x54
|
||||
sysMCAST_UNBLOCK_SOURCE = 0x55
|
||||
|
||||
sysIPV6_PORTRANGE_DEFAULT = 0x0
|
||||
sysIPV6_PORTRANGE_HIGH = 0x1
|
||||
sysIPV6_PORTRANGE_LOW = 0x2
|
||||
|
||||
sizeofSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
sizeofIPv6Mtuinfo = 0x20
|
||||
|
||||
sizeofIPv6Mreq = 0x14
|
||||
sizeofGroupReq = 0x88
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
)
|
||||
|
||||
type sockaddrStorage struct {
|
||||
Len uint8
|
||||
Family uint8
|
||||
X__ss_pad1 [6]uint8
|
||||
X__ss_align int64
|
||||
X__ss_pad2 [112]uint8
|
||||
}
|
||||
|
||||
type sockaddrInet6 struct {
|
||||
Len uint8
|
||||
Family uint8
|
||||
Port uint16
|
||||
Flowinfo uint32
|
||||
Addr [16]byte /* in6_addr */
|
||||
Scope_id uint32
|
||||
}
|
||||
|
||||
type inet6Pktinfo struct {
|
||||
Addr [16]byte /* in6_addr */
|
||||
Ifindex uint32
|
||||
}
|
||||
|
||||
type ipv6Mtuinfo struct {
|
||||
Addr sockaddrInet6
|
||||
Mtu uint32
|
||||
}
|
||||
|
||||
type ipv6Mreq struct {
|
||||
Multiaddr [16]byte /* in6_addr */
|
||||
Interface uint32
|
||||
}
|
||||
|
||||
type groupReq struct {
|
||||
Interface uint32
|
||||
Group sockaddrStorage
|
||||
}
|
||||
|
||||
type groupSourceReq struct {
|
||||
Interface uint32
|
||||
Group sockaddrStorage
|
||||
Source sockaddrStorage
|
||||
}
|
||||
|
||||
type icmpv6Filter struct {
|
||||
Filt [8]uint32
|
||||
}
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -155,16 +150,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -157,16 +152,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -155,16 +150,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -157,16 +152,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -155,16 +150,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -157,16 +152,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -157,16 +152,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -155,16 +150,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -155,16 +150,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
|
@ -98,8 +95,6 @@ const (
|
|||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
|
@ -157,16 +152,3 @@ type groupSourceReq struct {
|
|||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue