fea: access for ceci.

This commit is contained in:
Daniel Ding
2025-04-13 15:33:35 +08:00
parent d7622bbc05
commit 754d933e68
859 changed files with 120670 additions and 145098 deletions

View File

@@ -106,20 +106,20 @@ docker-compose: ## create a compose files
ceci: linux-ceci darwin-ceci windows-ceci ## build all platform ceci
linux: env linux-access linux-switch linux-ceci linux-proxy ## build linux binary
go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openlan ./cmd/main.go
linux: linux-access linux-switch linux-ceci linux-proxy ## build linux binary
GOOS=linux go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openlan ./cmd/main.go
linux-access:
go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openlan-access ./cmd/access
GOOS=linux go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openlan-access ./cmd/access
linux-switch:
go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openlan-switch ./cmd/switch
GOOS=linux go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openlan-switch ./cmd/switch
linux-ceci:
go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openceci ./cmd/ceci
GOOS=linux go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openceci ./cmd/ceci
linux-proxy:
go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openlan-proxy ./cmd/proxy
GOOS=linux go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openlan-proxy ./cmd/proxy
linux-gzip: install ## build linux packages
@rm -rf $(LIN_DIR).tar.gz
@@ -148,7 +148,7 @@ install: env linux ## install packages
@echo "Installed to $(LIN_DIR)"
## cross build for windows
windows: env windows-ceci ## build windows binary
windows: windows-ceci ## build windows binary
windows-ceci:
GOOS=windows GOARCH=amd64 go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openceci.exe ./cmd/ceci
@@ -163,7 +163,7 @@ windows-gzip: env windows ## build windows packages
## cross build for osx
osx: darwin
darwin: env darwin-ceci ## build darwin binary
darwin: darwin-ceci ## build darwin binary
darwin-ceci:
GOOS=darwin GOARCH=amd64 go build -mod=vendor -ldflags "$(LDFLAGS)" -o $(BD)/openceci.dar ./cmd/ceci

View File

@@ -1,4 +1,4 @@
// +build linux
//go:build linux
package main
@@ -16,8 +16,10 @@ func main() {
if c.Terminal == "off" {
libol.PreNotify()
}
p.Initialize()
libol.Go(p.Start)
if c.Terminal == "on" {
t := access.NewTerminal(p)
t.Start()

View File

@@ -3,6 +3,7 @@ package main
import (
"flag"
"github.com/luscis/openlan/pkg/access"
"github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/proxy"
@@ -11,7 +12,7 @@ import (
func main() {
mode := "http"
conf := ""
flag.StringVar(&mode, "mode", "http", "Proxy mode for http, socks or tcp")
flag.StringVar(&mode, "mode", "http", "Proxy mode for http, socks, tcp and access")
flag.StringVar(&conf, "conf", "ceci.yaml", "The configuration file")
flag.Parse()
@@ -30,13 +31,25 @@ func main() {
}
p := proxy.NewSocksProxy(c)
libol.Go(p.Start)
} else {
} else if mode == "tcp" {
c := &config.TcpProxy{Conf: conf}
if err := c.Initialize(); err != nil {
return
}
p := proxy.NewTcpProxy(c)
libol.Go(p.Start)
} else if mode == "access" {
c := &config.Point{
RequestAddr: true,
Terminal: "off",
Conf: conf,
}
if err := c.Initialize(); err != nil {
return
}
p := access.NewPoint(c)
p.Initialize()
libol.Go(p.Start)
}
libol.SdNotify()
libol.Wait()

35
go.mod
View File

@@ -1,16 +1,8 @@
module github.com/luscis/openlan
go 1.22.0
go 1.23.0
toolchain go1.23.0
replace (
github.com/Sirupsen/logrus => github.com/sirupsen/logrus v1.8.1
golang.org/x/crypto => github.com/golang/crypto v0.0.0-20200604202706-70a84ac30bf9
golang.org/x/net => github.com/golang/net v0.0.0-20190812203447-cdfb69ac37fc
golang.org/x/sys => github.com/golang/sys v0.0.0-20190209173611-3b5209105503
golang.org/x/time => github.com/golang/time v0.0.0-20210220033141-f8bda1e9f3ba
)
replace github.com/Sirupsen/logrus => github.com/sirupsen/logrus v1.8.1
exclude github.com/sirupsen/logrus v1.8.1
@@ -19,38 +11,29 @@ require (
github.com/coreos/go-systemd/v22 v22.3.2
github.com/ghodss/yaml v1.0.0
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-logr/logr v1.1.0
github.com/go-logr/stdr v1.1.0
github.com/gorilla/mux v1.8.0
github.com/miekg/dns v1.1.65
github.com/moby/libnetwork v0.5.6
github.com/ovn-org/libovsdb v0.6.1-0.20220127023511-a619f0fd93be
github.com/prometheus/client_golang v1.11.0
github.com/shadowsocks/go-shadowsocks2 v0.1.5
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/stretchr/testify v1.7.0
github.com/urfave/cli/v2 v2.3.0
github.com/vishvananda/netlink v1.1.0
github.com/xtaci/kcp-go/v5 v5.6.1
golang.org/x/net v0.35.0
golang.org/x/net v0.39.0
golang.org/x/sys v0.32.0
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/Sirupsen/logrus v0.0.0-00010101000000-000000000000 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/cenkalti/hub v1.0.1 // indirect
github.com/cenkalti/rpc2 v0.0.0-20210604223624-c1acbc6ec984 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/chzyer/logex v1.2.0 // indirect
github.com/chzyer/test v0.0.0-20210722231415-061457976a23 // indirect
github.com/chzyer/test v1.0.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/libnetwork v0.5.6 // indirect
github.com/godbus/dbus v4.1.0+incompatible // indirect
github.com/golang/protobuf v1.5.0 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/klauspost/reedsolomon v1.9.9 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
@@ -60,19 +43,17 @@ require (
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/templexxx/cpu v0.0.7 // indirect
github.com/templexxx/xorsimd v0.4.1 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/tools v0.30.0 // indirect
google.golang.org/protobuf v1.26.0 // indirect
google.golang.org/protobuf v1.26.0-rc.1 // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)

129
go.sum
View File

@@ -1,8 +1,5 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -12,43 +9,23 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/cenk/hub v1.0.1 h1:RBwXNOF4a8KjD8BJ08XqN8KbrqaGiQLDrgvUGJSHuPA=
github.com/cenk/hub v1.0.1/go.mod h1:rJM1LNAW0ppT8FMMuPK6c2NP/R2nH/UthtuRySSaf6Y=
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/hub v1.0.1 h1:UMtjc6dHSaOQTO15SVA50MBIR9zQwvsukQupDrkIRtg=
github.com/cenkalti/hub v1.0.1/go.mod h1:tcYwtS3a2d9NO/0xDXVJWx3IedurUjYCqFCmpi0lpHs=
github.com/cenkalti/rpc2 v0.0.0-20210604223624-c1acbc6ec984 h1:CNwZyGS6KpfaOWbh2yLkSy3rSTUh3jub9CzpFpP6PVQ=
github.com/cenkalti/rpc2 v0.0.0-20210604223624-c1acbc6ec984/go.mod h1:v2npkhrXyk5BCnkNIiPdRI23Uq6uWPUQGL2hnRcRr/M=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
github.com/chzyer/logex v1.2.0 h1:+eqR0HfOetur4tgnC8ftU5imRnhi4te+BadWS95c5AM=
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23 h1:dZ0/VyGgQdVGAss6Ju0dt5P0QltE0SFY5Woh6hbIfiQ=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/cli v20.10.8+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libnetwork v0.5.6 h1:hnGiypBsZR6PW1I8lqaBHh06U6LCJbI3IhOvfsZiymY=
github.com/docker/libnetwork v0.5.6/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -59,20 +36,11 @@ github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.1.0 h1:nAbevmWlS2Ic4m4+/An5NXkaGqlqpbBgdcuThZxnZyI=
github.com/go-logr/logr v1.1.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.1.0 h1:WSypkOvL7AfqHep42aGGLagyxUjJCQFFs/2nIitlVTQ=
github.com/go-logr/stdr v1.1.0/go.mod h1:Xff/PTSzmJ+zDsu/KDy4l6Axizfso1w7QcxLnWTdto4=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:lIQUCEYYutFXVbIXlWWJUOtinadHOl9N6P84tMjXLQU=
github.com/golang/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
github.com/golang/net v0.0.0-20190812203447-cdfb69ac37fc h1:ANlSYw21rkVMLEZGosY7p2hFSi/JitYbABaYmw7Tv4A=
github.com/golang/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:98y8FxUyMjTdJ5eOj/8vzuiVO14/dkJ98NYhEPG8QGY=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -82,11 +50,8 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/sys v0.0.0-20190209173611-3b5209105503 h1:KkzKRheGyjoZo7gN0XfHPvHPiigTohQEshRskFvbq6s=
github.com/golang/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:5JyrLPvD/ZdaYkT7IqKhsP5xt7aLjA99KXRtk4EIYDk=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -95,61 +60,42 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
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/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
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/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
github.com/moby/libnetwork v0.5.6 h1:pDuZwsc3ViMrIi0i4vrTtsgBN1+8ReJY6vAGnDCZ8AI=
github.com/moby/libnetwork v0.5.6/go.mod h1:RQTqDxGZChsPHosY8R3ZL2THYWUuW8X5SRhiBNoTY5I=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
github.com/ory/dockertest/v3 v3.8.0/go.mod h1:9zPATATlWQru+ynXP+DytBQrsXV7Tmlx7K86H6fQaDo=
github.com/ovn-org/libovsdb v0.6.1-0.20220127023511-a619f0fd93be h1:U8WVtNNTfBKj/5OE3uBe57oNJ+x5KSMl+3hM7iLSbdk=
github.com/ovn-org/libovsdb v0.6.1-0.20220127023511-a619f0fd93be/go.mod h1:aLvY7gPs/vLyJXF+PpZzvWlR5LB4QNJvBYIQMskJLZk=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -174,25 +120,16 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1HakRm86CUbr28=
github.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -201,7 +138,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.0.7 h1:pUEZn8JBy/w5yzdYWgx+0m0xL9uk6j4K91C5kOViAzo=
github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
@@ -209,47 +145,73 @@ github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORk
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -263,9 +225,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
@@ -282,6 +243,4 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@@ -0,0 +1,20 @@
//go:build !linux && !windows && !darwin
package access
import "github.com/luscis/openlan/pkg/config"
type Point struct {
}
func NewPoint(config *config.Point) *Point {
return nil
}
func (p *Point) Initialize() {
}
func (p *Point) Start() {
}

View File

@@ -1,9 +1,10 @@
package access
import (
"github.com/luscis/openlan/pkg/libol"
"github.com/songgao/water"
"testing"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/water"
)
func TestTapWrite(t *testing.T) {

View File

@@ -34,7 +34,7 @@ type Point struct {
PProf string `json:"pprof,omitempty"`
RequestAddr bool `json:"requestAddr"`
ByPass bool `json:"bypass,omitempty"`
SaveFile string `json:"-"`
Conf string `json:"-"`
Queue *Queue `json:"queue,omitempty"`
Terminal string `json:"-"`
Cert *Cert `json:"cert,omitempty"`
@@ -69,7 +69,7 @@ func (ap *Point) Parse() {
flag.StringVar(&ap.Alias, "alias", "", "Alias for this point")
flag.StringVar(&ap.Log.File, "log:file", "", "File log saved to")
flag.StringVar(&ap.Terminal, "terminal", "", "Run interactive terminal")
flag.StringVar(&ap.SaveFile, "conf", "", "The configuration file")
flag.StringVar(&ap.Conf, "conf", "", "The configuration file")
flag.Parse()
}
@@ -77,12 +77,14 @@ func (ap *Point) Id() string {
return ap.Connection + ":" + ap.Network
}
func (ap *Point) Initialize() {
func (ap *Point) Initialize() error {
if err := ap.Load(); err != nil {
libol.Warn("NewPoint.Initialize %s", err)
return err
}
ap.Correct()
libol.SetLogger(ap.Log.File, ap.Log.Verbose)
return nil
}
func (ap *Point) Correct() {
@@ -128,8 +130,8 @@ func (ap *Point) Correct() {
}
func (ap *Point) Load() error {
if err := libol.FileExist(ap.SaveFile); err == nil {
return libol.UnmarshalLoad(ap, ap.SaveFile)
if err := libol.FileExist(ap.Conf); err == nil {
return libol.UnmarshalLoad(ap, ap.Conf)
}
return nil
}

View File

@@ -2,10 +2,11 @@ package config
import (
"fmt"
"github.com/luscis/openlan/pkg/libol"
"github.com/stretchr/testify/assert"
"os"
"testing"
"github.com/luscis/openlan/pkg/libol"
"github.com/stretchr/testify/assert"
)
func TestPointFlags(t *testing.T) {
@@ -19,7 +20,7 @@ func TestPointFlags(t *testing.T) {
ap.Parse()
fmt.Println(ap)
assert.Equal(t, "fake", ap.Alias, "be the same.")
assert.Equal(t, "/etc/openlan/fake.json", ap.SaveFile, "be the same.")
assert.Equal(t, "/etc/openlan/fake.json", ap.Conf, "be the same.")
assert.Equal(t, "off", ap.Terminal, "be the same.")
}

View File

@@ -8,16 +8,6 @@ import (
"github.com/luscis/openlan/pkg/libol"
)
type ShadowProxy struct {
Server string `json:"server,omitempty"`
Key string `json:"key,omitempty"`
Cipher string `json:"cipher,omitempty"`
Secret string `json:"secret,omitempty"`
Plugin string `json:"plugin,omitempty"`
PluginOpts string `json:"pluginOpts,omitempty"`
Protocol string `json:"protocol,omitempty"`
}
type SocksProxy struct {
Conf string `json:"-" yaml:"-"`
ConfDir string `json:"-" yaml:"-"`
@@ -185,14 +175,13 @@ func (t *TcpProxy) Load() error {
}
type Proxy struct {
Conf string `json:"-" yaml:"-"`
ConfDir string `json:"-" yaml:"-"`
Log Log `json:"log"`
Socks []*SocksProxy `json:"socks,omitempty" yaml:"socks,omitempty"`
Http []*HttpProxy `json:"http,omitempty" yaml:"http,omitempty"`
Tcp []*TcpProxy `json:"tcp,omitempty" yaml:"tcp,omitempty"`
Shadow []*ShadowProxy `json:"shadow,omitempty" yaml:"shadow,omitempty"`
PProf string `json:"pprof,omitempty" yaml:"pprof,omitempty"`
Conf string `json:"-" yaml:"-"`
ConfDir string `json:"-" yaml:"-"`
Log Log `json:"log"`
Socks []*SocksProxy `json:"socks,omitempty" yaml:"socks,omitempty"`
Http []*HttpProxy `json:"http,omitempty" yaml:"http,omitempty"`
Tcp []*TcpProxy `json:"tcp,omitempty" yaml:"tcp,omitempty"`
PProf string `json:"pprof,omitempty" yaml:"pprof,omitempty"`
}
func NewProxy() *Proxy {

View File

@@ -1,151 +0,0 @@
package database
import (
"context"
"github.com/go-logr/logr"
"github.com/go-logr/stdr"
"github.com/luscis/openlan/cmd/api"
"github.com/luscis/openlan/pkg/libol"
"github.com/ovn-org/libovsdb/cache"
"github.com/ovn-org/libovsdb/client"
"github.com/ovn-org/libovsdb/model"
"github.com/ovn-org/libovsdb/ovsdb"
"log"
"os"
)
type OvSDB struct {
client.Client
Ops []ovsdb.Operation
}
func (o *OvSDB) Context() context.Context {
return context.Background()
}
func (o *OvSDB) Switch() (*Switch, error) {
var listSw []Switch
if err := o.List(&listSw); err != nil {
return nil, err
}
if len(listSw) == 0 {
return nil, libol.NewErr("hasn't switch")
}
return &listSw[0], nil
}
func (o *OvSDB) Execute(ops []ovsdb.Operation) {
o.Ops = append(o.Ops, ops...)
}
func (o *OvSDB) Commit() ([]ovsdb.OperationResult, error) {
ops := o.Ops
o.Ops = nil
return o.Client.Transact(o.Context(), ops...)
}
func (o *OvSDB) Get(m model.Model) error {
return o.Client.Get(o.Context(), m)
}
func (o *OvSDB) Transact(ops ...ovsdb.Operation) ([]ovsdb.OperationResult, error) {
return o.Client.Transact(o.Context(), ops...)
}
func (o *OvSDB) List(result interface{}) error {
return o.Client.List(o.Context(), result)
}
func (o *OvSDB) WhereList(predict interface{}, result interface{}) error {
cache := o.Client.WhereCache(predict)
if cache == nil {
return libol.NewErr("not cache list")
}
return cache.List(o.Context(), result)
}
type DBClient struct {
Server string
Database string
Verbose bool
Client *OvSDB
}
func (c *DBClient) Context() context.Context {
return context.Background()
}
func (c *DBClient) NilLog() *logr.Logger {
// create a new logger to log to /dev/null
writer, err := libol.OpenWrite(os.DevNull)
if err != nil {
writer = os.Stderr
}
l := stdr.NewWithOptions(log.New(writer, "", log.LstdFlags), stdr.Options{LogCaller: stdr.All})
return &l
}
func (c *DBClient) Open(handler *cache.EventHandlerFuncs, block func(val *DBClient)) error {
server := c.Server
database := c.Database
dbModel, err := model.NewClientDBModel(database, models)
if err != nil {
return err
}
ops := []client.Option{
client.WithEndpoint(server),
}
if !c.Verbose {
ops = append(ops, client.WithLogger(c.NilLog()))
}
ovs, err := client.NewOVSDBClient(dbModel, ops...)
if err != nil {
return err
}
if err := ovs.Connect(c.Context()); err != nil {
return err
}
c.Client = &OvSDB{Client: ovs}
if block != nil {
block(c)
}
if handler != nil {
processor := ovs.Cache()
if processor == nil {
return libol.NewErr("can't get cache.")
}
processor.AddEventHandler(handler)
}
if _, err := ovs.MonitorAll(c.Context()); err != nil {
return err
}
return nil
}
var Conf *DBClient
var Client *OvSDB
func NewConfClient(handler *cache.EventHandlerFuncs) (*DBClient, error) {
var err error
if Conf == nil {
obj := &DBClient{
Server: api.Server,
Database: api.Database,
Verbose: api.Verbose,
}
err = obj.Open(handler, func(val *DBClient) {
Conf = val
Client = val.Client
})
}
return Conf, err
}
func NewClient(handler *cache.EventHandlerFuncs) (*DBClient, error) {
obj := &DBClient{
Server: api.Server,
Database: api.Database,
Verbose: api.Verbose,
}
return obj, obj.Open(handler, nil)
}

View File

@@ -1,14 +0,0 @@
package database
import (
"github.com/ovn-org/libovsdb/model"
)
var models = map[string]model.Model{
"Global_Switch": &Switch{},
"Virtual_Network": &VirtualNetwork{},
"Virtual_Link": &VirtualLink{},
"Open_VPN": &OpenVPN{},
"Name_Cache": &NameCache{},
"Prefix_Route": &PrefixRoute{},
}

View File

@@ -1,72 +0,0 @@
package database
import (
"strconv"
)
type Switch struct {
UUID string `ovsdb:"_uuid" json:"uuid"`
Protocol string `ovsdb:"protocol" json:"protocol"`
Listen int `ovsdb:"listen" json:"listen"`
OtherConfig map[string]string `ovsdb:"other_config" json:"other_config"`
VirtualNetworks []string `ovsdb:"virtual_networks" json:"virtual_networks"`
}
type VirtualNetwork struct {
UUID string `ovsdb:"_uuid" json:"uuid"`
Name string `ovsdb:"name" json:"name"`
Provider string `ovsdb:"provider" json:"provider"`
Bridge string `ovsdb:"bridge" json:"bridge"`
Address string `ovsdb:"address" json:"address"`
OtherConfig map[string]string `ovsdb:"other_config" json:"other_config"`
RemoteLinks []string `ovsdb:"remote_links" json:"remote_links"`
LocalLinks []string `ovsdb:"local_links" json:"local_links"`
OpenVPN *string `ovsdb:"open_vpn" json:"open_vpn"`
PrefixRoutes []string `ovsdb:"prefix_routes" json:"prefix_routes"`
}
type VirtualLink struct {
UUID string `ovsdb:"_uuid" json:"uuid"`
Network string `ovsdb:"network" json:"network"`
Connection string `ovsdb:"connection" json:"connection"`
Device string `ovsdb:"device" json:"device"`
OtherConfig map[string]string `ovsdb:"other_config" json:"other_config"`
Authentication map[string]string `ovsdb:"authentication" json:"authentication"`
LinkState string `ovsdb:"link_state" json:"link_state"`
Status map[string]string `ovsdb:"status" json:"status"`
}
func (l *VirtualLink) IsUdpIn() bool {
if HasPrefix(l.Device, 4, "spi:") &&
HasPrefix(l.Connection, 4, "udp:") {
return true
}
return false
}
func (l *VirtualLink) Spi() uint32 {
spi, _ := strconv.Atoi(l.Device[4:])
return uint32(spi)
}
type OpenVPN struct {
UUID string `ovsdb:"_uuid" json:"uuid"`
Protocol string `ovsdb:"protocol" json:"protocol"`
Listen int `ovsdb:"listen" json:"listen"`
}
type NameCache struct {
UUID string `ovsdb:"_uuid" json:"uuid"`
Name string `ovsdb:"name" json:"name"`
Address string `ovsdb:"address" json:"address"`
UpdateAt string `ovsdb:"update_at" json:"update_at"`
}
type PrefixRoute struct {
UUID string `ovsdb:"_uuid" json:"uuid"`
Network string `ovsdb:"network" json:"network"`
Prefix string `ovsdb:"prefix" json:"prefix"`
Source string `ovsdb:"source" json:"source"`
Gateway string `ovsdb:"gateway" json:"gateway"`
Mode string `ovsdb:"mode" json:"mode"`
}

View File

@@ -1,37 +0,0 @@
package database
import (
"github.com/luscis/openlan/pkg/libol"
"github.com/ovn-org/libovsdb/ovsdb"
"strconv"
"strings"
)
func PrintError(result []ovsdb.OperationResult) {
for _, ret := range result {
if len(ret.Error) == 0 {
continue
}
libol.Info("%s", ret.Details)
}
}
func GenUUID() string {
return libol.GenString(32)
}
func HasPrefix(value string, index int, dest string) bool {
if len(value) >= index {
return value[:index] == dest
}
return false
}
func GetAddrPort(conn string) (string, int) {
values := strings.SplitN(conn, ":", 2)
if len(values) == 2 {
port, _ := strconv.Atoi(values[1])
return values[0], port
}
return values[0], 0
}

View File

@@ -8,6 +8,10 @@ import (
nl "github.com/vishvananda/netlink"
)
func NewBridger(provider, name string, ifMtu int) Bridger {
return NewLinuxBridge(name, ifMtu)
}
type LinuxBridge struct {
sts DeviceStats
address *nl.Addr

View File

@@ -0,0 +1,81 @@
//go:build !linux
package network
type OtherBridge struct {
name string
mtu int
}
func NewBridger(provider, name string, ifMtu int) Bridger {
return &OtherBridge{
name: name,
mtu: ifMtu,
}
}
func (b *OtherBridge) Type() string {
return "NAN"
}
func (b *OtherBridge) Name() string {
return b.name
}
func (b *OtherBridge) Open(addr string) {
}
func (b *OtherBridge) Close() error {
return nil
}
func (b *OtherBridge) AddSlave(name string) error {
return nil
}
func (b *OtherBridge) DelSlave(name string) error {
return nil
}
func (b *OtherBridge) ListSlave() <-chan Taper {
return nil
}
func (b *OtherBridge) Mtu() int {
return b.mtu
}
func (b *OtherBridge) Stp(enable bool) error {
return nil
}
func (b *OtherBridge) Delay(value int) error {
return nil
}
func (b *OtherBridge) Kernel() string {
return "NAN"
}
func (b *OtherBridge) ListMac() <-chan *MacFdb {
return nil
}
func (b *OtherBridge) String() string {
return "NAN"
}
func (b *OtherBridge) Stats() DeviceStats {
return DeviceStats{}
}
func (b *OtherBridge) CallIptables(value int) error {
return nil
}
func (b *OtherBridge) L3Name() string {
return "NAN"
}
func (b *OtherBridge) SetMtu(mtu int) error {
return nil
}

View File

@@ -1,5 +0,0 @@
package network
func NewBridger(provider, name string, ifMtu int) Bridger {
return NewLinuxBridge(name, ifMtu)
}

View File

@@ -1,9 +1,10 @@
package network
import (
"github.com/luscis/openlan/pkg/libol"
"github.com/songgao/water"
"sync"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/water"
)
type KernelTap struct {

View File

@@ -1,200 +0,0 @@
package network
import (
"github.com/luscis/openlan/pkg/libol"
"sync"
)
type VirtualTap struct {
lock sync.RWMutex
kernC int
kernQ chan []byte
virtC int
virtQ chan []byte
master Bridger
tenant string
flags uint
cfg TapConfig
name string
ifMtu int
sts DeviceStats
}
func NewVirtualTap(tenant string, c TapConfig) (*VirtualTap, error) {
name := c.Name
if name == "" {
name = Taps.GenName()
}
tap := &VirtualTap{
cfg: c,
tenant: tenant,
name: name,
ifMtu: 1514,
}
Taps.Add(tap)
return tap, nil
}
func (t *VirtualTap) Type() string {
return ProviderVir
}
func (t *VirtualTap) Tenant() string {
return t.tenant
}
func (t *VirtualTap) IsTun() bool {
return t.cfg.Type == TUN
}
func (t *VirtualTap) Name() string {
return t.name
}
func (t *VirtualTap) hasFlags(v uint) bool {
return t.flags&v == v
}
func (t *VirtualTap) setFlags(v uint) {
t.flags |= v
}
func (t *VirtualTap) clearFlags(v uint) {
t.flags &= ^v
}
func (t *VirtualTap) Has(v uint) bool {
t.lock.RLock()
defer t.lock.RUnlock()
return t.hasFlags(v)
}
func (t *VirtualTap) Write(p []byte) (int, error) {
if libol.HasLog(libol.DEBUG) {
libol.Debug("VirtualTap.Write: %s % x", t, p[:20])
}
t.lock.Lock()
defer t.lock.Unlock()
if !t.hasFlags(UsUp) {
return 0, libol.NewErr("notUp")
}
if t.virtC >= t.cfg.VirBuf {
libol.Warn("VirtualTap.Write: buffer fully")
t.sts.Drop++
return 0, nil
}
t.virtC++
t.virtQ <- p
return len(p), nil
}
func (t *VirtualTap) Read(p []byte) (int, error) {
t.lock.Lock()
if !t.hasFlags(UsUp) {
t.lock.Unlock()
return 0, libol.NewErr("notUp")
}
t.lock.Unlock()
data := <-t.kernQ
t.lock.Lock()
t.kernC--
t.lock.Unlock()
return copy(p, data), nil
}
func (t *VirtualTap) Recv(p []byte) (int, error) {
t.lock.Lock()
if !t.hasFlags(UsUp) {
t.lock.Unlock()
return 0, libol.NewErr("notUp")
}
t.lock.Unlock()
data := <-t.virtQ
t.lock.Lock()
t.virtC--
t.lock.Unlock()
return copy(p, data), nil
}
func (t *VirtualTap) Send(p []byte) (int, error) {
if libol.HasLog(libol.DEBUG) {
libol.Debug("VirtualTap.Send: %s % x", t, p[:20])
}
t.lock.Lock()
defer t.lock.Unlock()
if !t.hasFlags(UsUp) {
return 0, libol.NewErr("notUp")
}
if t.kernC >= t.cfg.KernBuf {
t.sts.Drop++
libol.Warn("VirtualTap.Send: buffer fully")
return 0, nil
}
t.kernC++
t.kernQ <- p
return len(p), nil
}
func (t *VirtualTap) Close() error {
t.Down()
t.lock.Lock()
defer t.lock.Unlock()
if t.hasFlags(UsClose) {
return nil
}
t.setFlags(UsClose)
t.clearFlags(UsUp)
if t.master != nil {
_ = t.master.DelSlave(t.name)
t.master = nil
}
Taps.Del(t.name)
return nil
}
func (t *VirtualTap) Master() Bridger {
t.lock.RLock()
defer t.lock.RUnlock()
return t.master
}
func (t *VirtualTap) SetMaster(dev Bridger) error {
t.lock.Lock()
defer t.lock.Unlock()
if t.master == nil {
t.master = dev
}
return libol.NewErr("already to %s", t.master)
}
func (t *VirtualTap) Up() {
t.lock.Lock()
defer t.lock.Unlock()
if !t.hasFlags(UsUp) {
t.kernC = 0
t.kernQ = make(chan []byte, t.cfg.KernBuf)
t.virtC = 0
t.virtQ = make(chan []byte, t.cfg.VirBuf)
t.setFlags(UsUp)
}
}
func (t *VirtualTap) Down() {
t.lock.Lock()
defer t.lock.Unlock()
if t.hasFlags(UsUp) {
t.clearFlags(UsUp)
close(t.kernQ)
t.kernQ = nil
close(t.virtQ)
t.virtQ = nil
}
}
func (t *VirtualTap) String() string {
return t.name
}
func (t *VirtualTap) Mtu() int {
return t.ifMtu
}

View File

@@ -38,9 +38,6 @@ type Taper interface {
}
func NewTaper(tenant string, c TapConfig) (Taper, error) {
if c.Provider == ProviderVir {
return NewVirtualTap(tenant, c)
}
return NewKernelTap(tenant, c)
}

20
pkg/network/vrf_others.go Normal file
View File

@@ -0,0 +1,20 @@
//go:build !linux
package network
type VRF struct {
name string
table int
}
func NewVRF(name string, table int) *VRF {
return &VRF{}
}
func (v *VRF) Table() int {
return v.table
}
func (v *VRF) Name() string {
return v.name
}

View File

@@ -0,0 +1,19 @@
package network
import (
"github.com/luscis/openlan/pkg/water"
)
func WaterNew(c TapConfig) (*water.Interface, error) {
deviceType := water.DeviceType(water.TAP)
if c.Type == TUN {
deviceType = water.TUN
}
cfg := water.Config{DeviceType: deviceType}
if c.Name != "" {
cfg.PlatformSpecificParams = water.PlatformSpecificParams{
Name: c.Name,
}
}
return water.New(cfg)
}

View File

@@ -0,0 +1,19 @@
package network
import (
"github.com/luscis/openlan/pkg/water"
)
func WaterNew(c TapConfig) (*water.Interface, error) {
deviceType := water.DeviceType(water.TAP)
if c.Type == TUN {
deviceType = water.TUN
}
cfg := water.Config{DeviceType: deviceType}
if c.Name != "" {
cfg.PlatformSpecificParams = water.PlatformSpecificParams{
Name: c.Name,
}
}
return water.New(cfg)
}

View File

@@ -1,21 +1,9 @@
// +build !windows
//go:build !linux && !darwin && !windows
package network
import (
"github.com/songgao/water"
)
import "github.com/luscis/openlan/pkg/water"
func WaterNew(c TapConfig) (*water.Interface, error) {
deviceType := water.DeviceType(water.TAP)
if c.Type == TUN {
deviceType = water.TUN
}
cfg := water.Config{DeviceType: deviceType}
if c.Name != "" {
cfg.PlatformSpecificParams = water.PlatformSpecificParams{
Name: c.Name,
}
}
return water.New(cfg)
return nil, nil
}

View File

@@ -1,7 +1,7 @@
package network
import (
"github.com/songgao/water"
"github.com/luscis/openlan/pkg/water"
)
func WaterNew(c TapConfig) (dev *water.Interface, err error) {

View File

@@ -5,7 +5,6 @@ import (
"github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/proxy/ss"
)
type Proxyer interface {
@@ -16,20 +15,18 @@ type Proxyer interface {
}
type Proxy struct {
cfg *config.Proxy
tcp map[string]*TcpProxy
socks map[string]*SocksProxy
http map[string]*HttpProxy
shadow map[string]*ss.ShadowSocks
cfg *config.Proxy
tcp map[string]*TcpProxy
socks map[string]*SocksProxy
http map[string]*HttpProxy
}
func NewProxy(cfg *config.Proxy) *Proxy {
return &Proxy{
cfg: cfg,
socks: make(map[string]*SocksProxy, 32),
tcp: make(map[string]*TcpProxy, 32),
http: make(map[string]*HttpProxy, 32),
shadow: make(map[string]*ss.ShadowSocks, 32),
cfg: cfg,
socks: make(map[string]*SocksProxy, 32),
tcp: make(map[string]*TcpProxy, 32),
http: make(map[string]*HttpProxy, 32),
}
}
@@ -54,13 +51,6 @@ func (p *Proxy) Initialize() {
h := NewHttpProxy(c, p)
p.http[c.Listen] = h
}
for _, c := range p.cfg.Shadow {
if c == nil || c.Server == "" {
continue
}
h := ss.NewShadowSocks(c)
p.shadow[c.Server] = h
}
}
func (p *Proxy) Start() {
@@ -77,9 +67,6 @@ func (p *Proxy) Start() {
for _, h := range p.http {
h.Start()
}
for _, s := range p.shadow {
s.Start()
}
}
func (p *Proxy) Stop() {
@@ -90,9 +77,6 @@ func (p *Proxy) Stop() {
for _, t := range p.tcp {
t.Stop()
}
for _, s := range p.shadow {
s.Stop()
}
}
func (p *Proxy) Save() {

View File

@@ -1,9 +0,0 @@
package ss
import "time"
var config struct {
Verbose bool
UDPTimeout time.Duration
TCPCork bool
}

View File

@@ -1,31 +0,0 @@
package ss
import (
"fmt"
"log"
"os"
)
var logger = log.New(os.Stderr, "", log.Lshortfile|log.LstdFlags)
func logf(f string, v ...interface{}) {
if config.Verbose {
_ = logger.Output(2, fmt.Sprintf(f, v...))
}
}
type logHelper struct {
prefix string
}
func (l *logHelper) Write(p []byte) (n int, err error) {
if config.Verbose {
logger.Printf("%s%s\n", l.prefix, p)
return len(p), nil
}
return len(p), nil
}
func newLogHelper(prefix string) *logHelper {
return &logHelper{prefix}
}

View File

@@ -1,118 +0,0 @@
package ss
import (
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"syscall"
"time"
)
var pluginCmd *exec.Cmd
func startPlugin(plugin, pluginOpts, ssAddr string, isServer bool) (newAddr string, err error) {
logf("starting plugin (%s) with option (%s)....", plugin, pluginOpts)
freePort, err := getFreePort()
if err != nil {
return "", fmt.Errorf("failed to fetch an unused port for plugin (%v)", err)
}
localHost := "127.0.0.1"
ssHost, ssPort, err := net.SplitHostPort(ssAddr)
if err != nil {
return "", err
}
newAddr = localHost + ":" + freePort
if isServer {
if ssHost == "" {
ssHost = "0.0.0.0"
}
logf("plugin (%s) will listen on %s:%s", plugin, ssHost, ssPort)
} else {
logf("plugin (%s) will listen on %s:%s", plugin, localHost, freePort)
}
err = execPlugin(plugin, pluginOpts, ssHost, ssPort, localHost, freePort)
return
}
func killPlugin() {
if pluginCmd != nil {
pluginCmd.Process.Signal(syscall.SIGTERM)
waitCh := make(chan struct{})
go func() {
pluginCmd.Wait()
close(waitCh)
}()
timeout := time.After(3 * time.Second)
select {
case <-waitCh:
case <-timeout:
pluginCmd.Process.Kill()
}
}
}
func execPlugin(plugin, pluginOpts, remoteHost, remotePort, localHost, localPort string) (err error) {
pluginFile := plugin
if fileExists(plugin) {
if !filepath.IsAbs(plugin) {
pluginFile = "./" + plugin
}
} else {
pluginFile, err = exec.LookPath(plugin)
if err != nil {
return err
}
}
logH := newLogHelper("[" + plugin + "]: ")
env := append(os.Environ(),
"SS_REMOTE_HOST="+remoteHost,
"SS_REMOTE_PORT="+remotePort,
"SS_LOCAL_HOST="+localHost,
"SS_LOCAL_PORT="+localPort,
"SS_PLUGIN_OPTIONS="+pluginOpts,
)
cmd := &exec.Cmd{
Path: pluginFile,
Env: env,
Stdout: logH,
Stderr: logH,
}
if err = cmd.Start(); err != nil {
return err
}
pluginCmd = cmd
go func() {
if err := cmd.Wait(); err != nil {
logf("plugin exited (%v)\n", err)
os.Exit(2)
}
logf("plugin exited\n")
os.Exit(0)
}()
return nil
}
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
func getFreePort() (string, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
return "", err
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return "", err
}
port := fmt.Sprintf("%d", l.Addr().(*net.TCPAddr).Port)
l.Close()
return port, nil
}

View File

@@ -1,76 +0,0 @@
package ss
import (
"encoding/base64"
"log"
c "github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
"github.com/shadowsocks/go-shadowsocks2/core"
)
type ShadowSocks struct {
Server string
Key string
Cipher string
Password string
Plugin string
PluginOpts string
Protocol string
out *libol.SubLogger
}
func NewShadowSocks(cfg *c.ShadowProxy) *ShadowSocks {
proto := cfg.Protocol
if proto == "" {
proto = "tcp"
}
return &ShadowSocks{
Server: cfg.Server,
Key: cfg.Key,
Cipher: cfg.Cipher,
Password: cfg.Secret,
Plugin: cfg.Plugin,
PluginOpts: cfg.PluginOpts,
Protocol: proto,
out: libol.NewSubLogger(cfg.Server),
}
}
func (s *ShadowSocks) Start() {
var key []byte
if s.Key != "" {
k, err := base64.URLEncoding.DecodeString(s.Key)
if err != nil {
log.Fatal(err)
}
key = k
}
addr := s.Server
cipher := s.Cipher
password := s.Password
var err error
udpAddr := addr
if s.Plugin != "" {
addr, err = startPlugin(s.Plugin, s.PluginOpts, addr, true)
if err != nil {
log.Fatal(err)
}
}
ciph, err := core.PickCipher(cipher, key, password)
if err != nil {
log.Fatal(err)
}
if s.Protocol == "udp" {
go udpRemote(udpAddr, ciph.PacketConn)
} else {
go tcpRemote(addr, ciph.StreamConn)
}
s.out.Info("ShadowSocks.start %s:%s", s.Protocol, s.Server)
}
func (s *ShadowSocks) Stop() {
killPlugin()
}

View File

@@ -1,204 +0,0 @@
package ss
import (
"bufio"
"errors"
"io"
"net"
"os"
"sync"
"time"
"github.com/shadowsocks/go-shadowsocks2/socks"
)
// Create a SOCKS server listening on addr and proxy to server.
func socksLocal(addr, server string, shadow func(net.Conn) net.Conn) {
logf("SOCKS proxy %s <-> %s", addr, server)
tcpLocal(addr, server, shadow, func(c net.Conn) (socks.Addr, error) { return socks.Handshake(c) })
}
// Create a TCP tunnel from addr to target via server.
func tcpTun(addr, server, target string, shadow func(net.Conn) net.Conn) {
tgt := socks.ParseAddr(target)
if tgt == nil {
logf("invalid target address %q", target)
return
}
logf("TCP tunnel %s <-> %s <-> %s", addr, server, target)
tcpLocal(addr, server, shadow, func(net.Conn) (socks.Addr, error) { return tgt, nil })
}
// Listen on addr and proxy to server to reach target from getAddr.
func tcpLocal(addr, server string, shadow func(net.Conn) net.Conn, getAddr func(net.Conn) (socks.Addr, error)) {
l, err := net.Listen("tcp", addr)
if err != nil {
logf("failed to listen on %s: %v", addr, err)
return
}
for {
c, err := l.Accept()
if err != nil {
logf("failed to accept: %s", err)
continue
}
go func() {
defer c.Close()
tgt, err := getAddr(c)
if err != nil {
// UDP: keep the connection until disconnect then free the UDP socket
if err == socks.InfoUDPAssociate {
buf := make([]byte, 1)
// block here
for {
_, err := c.Read(buf)
if err, ok := err.(net.Error); ok && err.Timeout() {
continue
}
logf("UDP Associate End.")
return
}
}
logf("failed to get target address: %v", err)
return
}
rc, err := net.Dial("tcp", server)
if err != nil {
logf("failed to connect to server %v: %v", server, err)
return
}
defer rc.Close()
if config.TCPCork {
rc = timedCork(rc, 10*time.Millisecond, 1280)
}
rc = shadow(rc)
if _, err = rc.Write(tgt); err != nil {
logf("failed to send target address: %v", err)
return
}
logf("proxy %s <-> %s <-> %s", c.RemoteAddr(), server, tgt)
if err = relay(rc, c); err != nil {
logf("relay error: %v", err)
}
}()
}
}
// Listen on addr for incoming connections.
func tcpRemote(addr string, shadow func(net.Conn) net.Conn) {
l, err := net.Listen("tcp", addr)
if err != nil {
logf("failed to listen on %s: %v", addr, err)
return
}
logf("listening TCP on %s", addr)
for {
c, err := l.Accept()
if err != nil {
logf("failed to accept: %v", err)
continue
}
go func() {
defer c.Close()
if config.TCPCork {
c = timedCork(c, 10*time.Millisecond, 1280)
}
sc := shadow(c)
tgt, err := socks.ReadAddr(sc)
if err != nil {
logf("failed to get target address from %v: %v", c.RemoteAddr(), err)
// drain c to avoid leaking server behavioral features
// see https://www.ndss-symposium.org/ndss-paper/detecting-probe-resistant-proxies/
_, err = io.Copy(io.Discard, c)
if err != nil {
logf("discard error: %v", err)
}
return
}
rc, err := net.Dial("tcp", tgt.String())
if err != nil {
logf("failed to connect to target: %v", err)
return
}
defer rc.Close()
logf("proxy %s <-> %s", c.RemoteAddr(), tgt)
if err = relay(sc, rc); err != nil {
logf("relay error: %v", err)
}
}()
}
}
// relay copies between left and right bidirectionally
func relay(left, right net.Conn) error {
var err, err1 error
var wg sync.WaitGroup
var wait = 5 * time.Second
wg.Add(1)
go func() {
defer wg.Done()
_, err1 = io.Copy(right, left)
right.SetReadDeadline(time.Now().Add(wait)) // unblock read on right
}()
_, err = io.Copy(left, right)
left.SetReadDeadline(time.Now().Add(wait)) // unblock read on left
wg.Wait()
if err1 != nil && !errors.Is(err1, os.ErrDeadlineExceeded) { // requires Go 1.15+
return err1
}
if err != nil && !errors.Is(err, os.ErrDeadlineExceeded) {
return err
}
return nil
}
type corkedConn struct {
net.Conn
bufw *bufio.Writer
corked bool
delay time.Duration
err error
lock sync.Mutex
once sync.Once
}
func timedCork(c net.Conn, d time.Duration, bufSize int) net.Conn {
return &corkedConn{
Conn: c,
bufw: bufio.NewWriterSize(c, bufSize),
corked: true,
delay: d,
}
}
func (w *corkedConn) Write(p []byte) (int, error) {
w.lock.Lock()
defer w.lock.Unlock()
if w.err != nil {
return 0, w.err
}
if w.corked {
w.once.Do(func() {
time.AfterFunc(w.delay, func() {
w.lock.Lock()
defer w.lock.Unlock()
w.corked = false
w.err = w.bufw.Flush()
})
})
return w.bufw.Write(p)
}
return w.Conn.Write(p)
}

View File

@@ -1,24 +0,0 @@
package ss
import (
"net"
"github.com/shadowsocks/go-shadowsocks2/pfutil"
"github.com/shadowsocks/go-shadowsocks2/socks"
)
func redirLocal(addr, server string, shadow func(net.Conn) net.Conn) {
tcpLocal(addr, server, shadow, natLookup)
}
func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
panic("TCP6 redirect not supported")
}
func natLookup(c net.Conn) (socks.Addr, error) {
if tc, ok := c.(*net.TCPConn); ok {
addr, err := pfutil.NatLookup(tc)
return socks.ParseAddr(addr.String()), err
}
panic("not TCP connection")
}

View File

@@ -1,28 +0,0 @@
package ss
import (
"net"
"github.com/shadowsocks/go-shadowsocks2/nfutil"
"github.com/shadowsocks/go-shadowsocks2/socks"
)
func getOrigDst(c net.Conn, ipv6 bool) (socks.Addr, error) {
if tc, ok := c.(*net.TCPConn); ok {
addr, err := nfutil.GetOrigDst(tc, ipv6)
return socks.ParseAddr(addr.String()), err
}
panic("not a TCP connection")
}
// Listen on addr for netfilter redirected TCP connections
func redirLocal(addr, server string, shadow func(net.Conn) net.Conn) {
logf("TCP redirect %s <-> %s", addr, server)
tcpLocal(addr, server, shadow, func(c net.Conn) (socks.Addr, error) { return getOrigDst(c, false) })
}
// Listen on addr for netfilter redirected TCP IPv6 connections.
func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
logf("TCP6 redirect %s <-> %s", addr, server)
tcpLocal(addr, server, shadow, func(c net.Conn) (socks.Addr, error) { return getOrigDst(c, true) })
}

View File

@@ -1,15 +0,0 @@
// +build !linux,!darwin
package ss
import (
"net"
)
func redirLocal(addr, server string, shadow func(net.Conn) net.Conn) {
logf("TCP redirect not supported")
}
func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
logf("TCP6 redirect not supported")
}

View File

@@ -1,253 +0,0 @@
package ss
import (
"fmt"
"net"
"sync"
"time"
"github.com/shadowsocks/go-shadowsocks2/socks"
)
type mode int
const (
remoteServer mode = iota
relayClient
socksClient
)
const udpBufSize = 64 * 1024
// Listen on laddr for UDP packets, encrypt and send to server to reach target.
func udpLocal(laddr, server, target string, shadow func(net.PacketConn) net.PacketConn) {
srvAddr, err := net.ResolveUDPAddr("udp", server)
if err != nil {
logf("UDP server address error: %v", err)
return
}
tgt := socks.ParseAddr(target)
if tgt == nil {
err = fmt.Errorf("invalid target address: %q", target)
logf("UDP target address error: %v", err)
return
}
c, err := net.ListenPacket("udp", laddr)
if err != nil {
logf("UDP local listen error: %v", err)
return
}
defer c.Close()
nm := newNATmap(config.UDPTimeout)
buf := make([]byte, udpBufSize)
copy(buf, tgt)
logf("UDP tunnel %s <-> %s <-> %s", laddr, server, target)
for {
n, raddr, err := c.ReadFrom(buf[len(tgt):])
if err != nil {
logf("UDP local read error: %v", err)
continue
}
pc := nm.Get(raddr.String())
if pc == nil {
pc, err = net.ListenPacket("udp", "")
if err != nil {
logf("UDP local listen error: %v", err)
continue
}
pc = shadow(pc)
nm.Add(raddr, c, pc, relayClient)
}
_, err = pc.WriteTo(buf[:len(tgt)+n], srvAddr)
if err != nil {
logf("UDP local write error: %v", err)
continue
}
}
}
// Listen on laddr for Socks5 UDP packets, encrypt and send to server to reach target.
func udpSocksLocal(laddr, server string, shadow func(net.PacketConn) net.PacketConn) {
srvAddr, err := net.ResolveUDPAddr("udp", server)
if err != nil {
logf("UDP server address error: %v", err)
return
}
c, err := net.ListenPacket("udp", laddr)
if err != nil {
logf("UDP local listen error: %v", err)
return
}
defer c.Close()
nm := newNATmap(config.UDPTimeout)
buf := make([]byte, udpBufSize)
for {
n, raddr, err := c.ReadFrom(buf)
if err != nil {
logf("UDP local read error: %v", err)
continue
}
pc := nm.Get(raddr.String())
if pc == nil {
pc, err = net.ListenPacket("udp", "")
if err != nil {
logf("UDP local listen error: %v", err)
continue
}
logf("UDP socks tunnel %s <-> %s <-> %s", laddr, server, socks.Addr(buf[3:]))
pc = shadow(pc)
nm.Add(raddr, c, pc, socksClient)
}
_, err = pc.WriteTo(buf[3:n], srvAddr)
if err != nil {
logf("UDP local write error: %v", err)
continue
}
}
}
// Listen on addr for encrypted packets and basically do UDP NAT.
func udpRemote(addr string, shadow func(net.PacketConn) net.PacketConn) {
c, err := net.ListenPacket("udp", addr)
if err != nil {
logf("UDP remote listen error: %v", err)
return
}
defer c.Close()
c = shadow(c)
nm := newNATmap(config.UDPTimeout)
buf := make([]byte, udpBufSize)
logf("listening UDP on %s", addr)
for {
n, raddr, err := c.ReadFrom(buf)
if err != nil {
logf("UDP remote read error: %v", err)
continue
}
tgtAddr := socks.SplitAddr(buf[:n])
if tgtAddr == nil {
logf("failed to split target address from packet: %q", buf[:n])
continue
}
tgtUDPAddr, err := net.ResolveUDPAddr("udp", tgtAddr.String())
if err != nil {
logf("failed to resolve target UDP address: %v", err)
continue
}
payload := buf[len(tgtAddr):n]
pc := nm.Get(raddr.String())
if pc == nil {
pc, err = net.ListenPacket("udp", "")
if err != nil {
logf("UDP remote listen error: %v", err)
continue
}
nm.Add(raddr, c, pc, remoteServer)
}
_, err = pc.WriteTo(payload, tgtUDPAddr) // accept only UDPAddr despite the signature
if err != nil {
logf("UDP remote write error: %v", err)
continue
}
}
}
// Packet NAT table
type natmap struct {
sync.RWMutex
m map[string]net.PacketConn
timeout time.Duration
}
func newNATmap(timeout time.Duration) *natmap {
m := &natmap{}
m.m = make(map[string]net.PacketConn)
m.timeout = timeout
return m
}
func (m *natmap) Get(key string) net.PacketConn {
m.RLock()
defer m.RUnlock()
return m.m[key]
}
func (m *natmap) Set(key string, pc net.PacketConn) {
m.Lock()
defer m.Unlock()
m.m[key] = pc
}
func (m *natmap) Del(key string) net.PacketConn {
m.Lock()
defer m.Unlock()
pc, ok := m.m[key]
if ok {
delete(m.m, key)
return pc
}
return nil
}
func (m *natmap) Add(peer net.Addr, dst, src net.PacketConn, role mode) {
m.Set(peer.String(), src)
go func() {
timedCopy(dst, peer, src, m.timeout, role)
if pc := m.Del(peer.String()); pc != nil {
pc.Close()
}
}()
}
// copy from src to dst at target with read timeout
func timedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration, role mode) error {
buf := make([]byte, udpBufSize)
for {
src.SetReadDeadline(time.Now().Add(timeout))
n, raddr, err := src.ReadFrom(buf)
if err != nil {
return err
}
switch role {
case remoteServer: // server -> client: add original packet source
srcAddr := socks.ParseAddr(raddr.String())
copy(buf[len(srcAddr):], buf[:n])
copy(buf, srcAddr)
_, err = dst.WriteTo(buf[:len(srcAddr)+n], target)
case relayClient: // client -> user: strip original packet source
srcAddr := socks.SplitAddr(buf[:n])
_, err = dst.WriteTo(buf[len(srcAddr):n], target)
case socksClient: // client -> socks5 program: just set RSV and FRAG = 0
_, err = dst.WriteTo(append([]byte{0, 0, 0}, buf[:n]...), target)
}
if err != nil {
return err
}
}
}

View File

@@ -1,4 +1,4 @@
// +build !linux,!darwin,!windows
//go:build !linux && !darwin && !windows
package water

View File

@@ -1,4 +1,4 @@
// +build linux,go1.11
//go:build linux && go1.11
package water

View File

@@ -1,4 +1,4 @@
// +build !linux,!darwin,!windows
//go:build !linux && !darwin && !windows
package water

View File

@@ -1,25 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
# IDEs
.idea/

View File

@@ -1,10 +0,0 @@
language: go
go:
- 1.13
- 1.x
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -service=travis-ci

View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Cenk Altı
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.

View File

@@ -1,32 +0,0 @@
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls]
This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
[Exponential backoff][exponential backoff wiki]
is an algorithm that uses feedback to multiplicatively decrease the rate of some process,
in order to gradually find an acceptable rate.
The retries exponentially increase and stop increasing when a certain threshold is met.
## Usage
Import path is `github.com/cenkalti/backoff/v4`. Please note the version part at the end.
Use https://pkg.go.dev/github.com/cenkalti/backoff/v4 to view the documentation.
## Contributing
* I would like to keep this library as small as possible.
* Please don't send a PR without opening an issue and discussing it first.
* If proposed change is not a common use case, I will probably not accept it.
[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v4
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
[travis]: https://travis-ci.org/cenkalti/backoff
[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master
[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master
[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master
[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java
[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff
[advanced example]: https://pkg.go.dev/github.com/cenkalti/backoff/v4?tab=doc#pkg-examples

View File

@@ -1,66 +0,0 @@
// Package backoff implements backoff algorithms for retrying operations.
//
// Use Retry function for retrying operations that may fail.
// If Retry does not meet your needs,
// copy/paste the function into your project and modify as you wish.
//
// There is also Ticker type similar to time.Ticker.
// You can use it if you need to work with channels.
//
// See Examples section below for usage examples.
package backoff
import "time"
// BackOff is a backoff policy for retrying an operation.
type BackOff interface {
// NextBackOff returns the duration to wait before retrying the operation,
// or backoff. Stop to indicate that no more retries should be made.
//
// Example usage:
//
// duration := backoff.NextBackOff();
// if (duration == backoff.Stop) {
// // Do not retry operation.
// } else {
// // Sleep for duration and retry operation.
// }
//
NextBackOff() time.Duration
// Reset to initial state.
Reset()
}
// Stop indicates that no more retries should be made for use in NextBackOff().
const Stop time.Duration = -1
// ZeroBackOff is a fixed backoff policy whose backoff time is always zero,
// meaning that the operation is retried immediately without waiting, indefinitely.
type ZeroBackOff struct{}
func (b *ZeroBackOff) Reset() {}
func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 }
// StopBackOff is a fixed backoff policy that always returns backoff.Stop for
// NextBackOff(), meaning that the operation should never be retried.
type StopBackOff struct{}
func (b *StopBackOff) Reset() {}
func (b *StopBackOff) NextBackOff() time.Duration { return Stop }
// ConstantBackOff is a backoff policy that always returns the same backoff delay.
// This is in contrast to an exponential backoff policy,
// which returns a delay that grows longer as you call NextBackOff() over and over again.
type ConstantBackOff struct {
Interval time.Duration
}
func (b *ConstantBackOff) Reset() {}
func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }
func NewConstantBackOff(d time.Duration) *ConstantBackOff {
return &ConstantBackOff{Interval: d}
}

View File

@@ -1,62 +0,0 @@
package backoff
import (
"context"
"time"
)
// BackOffContext is a backoff policy that stops retrying after the context
// is canceled.
type BackOffContext interface { // nolint: golint
BackOff
Context() context.Context
}
type backOffContext struct {
BackOff
ctx context.Context
}
// WithContext returns a BackOffContext with context ctx
//
// ctx must not be nil
func WithContext(b BackOff, ctx context.Context) BackOffContext { // nolint: golint
if ctx == nil {
panic("nil context")
}
if b, ok := b.(*backOffContext); ok {
return &backOffContext{
BackOff: b.BackOff,
ctx: ctx,
}
}
return &backOffContext{
BackOff: b,
ctx: ctx,
}
}
func getContext(b BackOff) context.Context {
if cb, ok := b.(BackOffContext); ok {
return cb.Context()
}
if tb, ok := b.(*backOffTries); ok {
return getContext(tb.delegate)
}
return context.Background()
}
func (b *backOffContext) Context() context.Context {
return b.ctx
}
func (b *backOffContext) NextBackOff() time.Duration {
select {
case <-b.ctx.Done():
return Stop
default:
return b.BackOff.NextBackOff()
}
}

View File

@@ -1,158 +0,0 @@
package backoff
import (
"math/rand"
"time"
)
/*
ExponentialBackOff is a backoff implementation that increases the backoff
period for each retry attempt using a randomization function that grows exponentially.
NextBackOff() is calculated using the following formula:
randomized interval =
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
In other words NextBackOff() will range between the randomization factor
percentage below and above the retry interval.
For example, given the following parameters:
RetryInterval = 2
RandomizationFactor = 0.5
Multiplier = 2
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
multiplied by the exponential, that is, between 2 and 6 seconds.
Note: MaxInterval caps the RetryInterval and not the randomized interval.
If the time elapsed since an ExponentialBackOff instance is created goes past the
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().
Example: Given the following default arguments, for 10 tries the sequence will be,
and assuming we go over the MaxElapsedTime on the 10th try:
Request # RetryInterval (seconds) Randomized Interval (seconds)
1 0.5 [0.25, 0.75]
2 0.75 [0.375, 1.125]
3 1.125 [0.562, 1.687]
4 1.687 [0.8435, 2.53]
5 2.53 [1.265, 3.795]
6 3.795 [1.897, 5.692]
7 5.692 [2.846, 8.538]
8 8.538 [4.269, 12.807]
9 12.807 [6.403, 19.210]
10 19.210 backoff.Stop
Note: Implementation is not thread-safe.
*/
type ExponentialBackOff struct {
InitialInterval time.Duration
RandomizationFactor float64
Multiplier float64
MaxInterval time.Duration
// After MaxElapsedTime the ExponentialBackOff returns Stop.
// It never stops if MaxElapsedTime == 0.
MaxElapsedTime time.Duration
Stop time.Duration
Clock Clock
currentInterval time.Duration
startTime time.Time
}
// Clock is an interface that returns current time for BackOff.
type Clock interface {
Now() time.Time
}
// Default values for ExponentialBackOff.
const (
DefaultInitialInterval = 500 * time.Millisecond
DefaultRandomizationFactor = 0.5
DefaultMultiplier = 1.5
DefaultMaxInterval = 60 * time.Second
DefaultMaxElapsedTime = 15 * time.Minute
)
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
func NewExponentialBackOff() *ExponentialBackOff {
b := &ExponentialBackOff{
InitialInterval: DefaultInitialInterval,
RandomizationFactor: DefaultRandomizationFactor,
Multiplier: DefaultMultiplier,
MaxInterval: DefaultMaxInterval,
MaxElapsedTime: DefaultMaxElapsedTime,
Stop: Stop,
Clock: SystemClock,
}
b.Reset()
return b
}
type systemClock struct{}
func (t systemClock) Now() time.Time {
return time.Now()
}
// SystemClock implements Clock interface that uses time.Now().
var SystemClock = systemClock{}
// Reset the interval back to the initial retry interval and restarts the timer.
// Reset must be called before using b.
func (b *ExponentialBackOff) Reset() {
b.currentInterval = b.InitialInterval
b.startTime = b.Clock.Now()
}
// NextBackOff calculates the next backoff interval using the formula:
// Randomized interval = RetryInterval * (1 ± RandomizationFactor)
func (b *ExponentialBackOff) NextBackOff() time.Duration {
// Make sure we have not gone over the maximum elapsed time.
elapsed := b.GetElapsedTime()
next := getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
b.incrementCurrentInterval()
if b.MaxElapsedTime != 0 && elapsed+next > b.MaxElapsedTime {
return b.Stop
}
return next
}
// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
// is created and is reset when Reset() is called.
//
// The elapsed time is computed using time.Now().UnixNano(). It is
// safe to call even while the backoff policy is used by a running
// ticker.
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
return b.Clock.Now().Sub(b.startTime)
}
// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackOff) incrementCurrentInterval() {
// Check for overflow, if overflow is detected set the current interval to the max interval.
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
b.currentInterval = b.MaxInterval
} else {
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
}
}
// Returns a random value from the following interval:
// [currentInterval - randomizationFactor * currentInterval, currentInterval + randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
var delta = randomizationFactor * float64(currentInterval)
var minInterval = float64(currentInterval) - delta
var maxInterval = float64(currentInterval) + delta
// Get a random value from the range [minInterval, maxInterval].
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
// we want a 33% chance for selecting either 1, 2 or 3.
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
}

View File

@@ -1,112 +0,0 @@
package backoff
import (
"errors"
"time"
)
// An Operation is executing by Retry() or RetryNotify().
// The operation will be retried using a backoff policy if it returns an error.
type Operation func() error
// Notify is a notify-on-error function. It receives an operation error and
// backoff delay if the operation failed (with an error).
//
// NOTE that if the backoff policy stated to stop retrying,
// the notify function isn't called.
type Notify func(error, time.Duration)
// Retry the operation o until it does not return error or BackOff stops.
// o is guaranteed to be run at least once.
//
// If o returns a *PermanentError, the operation is not retried, and the
// wrapped error is returned.
//
// Retry sleeps the goroutine for the duration returned by BackOff after a
// failed operation returns.
func Retry(o Operation, b BackOff) error {
return RetryNotify(o, b, nil)
}
// RetryNotify calls notify function with the error and wait duration
// for each failed attempt before sleep.
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
return RetryNotifyWithTimer(operation, b, notify, nil)
}
// RetryNotifyWithTimer calls notify function with the error and wait duration using the given Timer
// for each failed attempt before sleep.
// A default timer that uses system timer is used when nil is passed.
func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer) error {
var err error
var next time.Duration
if t == nil {
t = &defaultTimer{}
}
defer func() {
t.Stop()
}()
ctx := getContext(b)
b.Reset()
for {
if err = operation(); err == nil {
return nil
}
var permanent *PermanentError
if errors.As(err, &permanent) {
return permanent.Err
}
if next = b.NextBackOff(); next == Stop {
if cerr := ctx.Err(); cerr != nil {
return cerr
}
return err
}
if notify != nil {
notify(err, next)
}
t.Start(next)
select {
case <-ctx.Done():
return ctx.Err()
case <-t.C():
}
}
}
// PermanentError signals that the operation should not be retried.
type PermanentError struct {
Err error
}
func (e *PermanentError) Error() string {
return e.Err.Error()
}
func (e *PermanentError) Unwrap() error {
return e.Err
}
func (e *PermanentError) Is(target error) bool {
_, ok := target.(*PermanentError)
return ok
}
// Permanent wraps the given err in a *PermanentError.
func Permanent(err error) error {
if err == nil {
return nil
}
return &PermanentError{
Err: err,
}
}

View File

@@ -1,97 +0,0 @@
package backoff
import (
"context"
"sync"
"time"
)
// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
//
// Ticks will continue to arrive when the previous operation is still running,
// so operations that take a while to fail could run in quick succession.
type Ticker struct {
C <-chan time.Time
c chan time.Time
b BackOff
ctx context.Context
timer Timer
stop chan struct{}
stopOnce sync.Once
}
// NewTicker returns a new Ticker containing a channel that will send
// the time at times specified by the BackOff argument. Ticker is
// guaranteed to tick at least once. The channel is closed when Stop
// method is called or BackOff stops. It is not safe to manipulate the
// provided backoff policy (notably calling NextBackOff or Reset)
// while the ticker is running.
func NewTicker(b BackOff) *Ticker {
return NewTickerWithTimer(b, &defaultTimer{})
}
// NewTickerWithTimer returns a new Ticker with a custom timer.
// A default timer that uses system timer is used when nil is passed.
func NewTickerWithTimer(b BackOff, timer Timer) *Ticker {
if timer == nil {
timer = &defaultTimer{}
}
c := make(chan time.Time)
t := &Ticker{
C: c,
c: c,
b: b,
ctx: getContext(b),
timer: timer,
stop: make(chan struct{}),
}
t.b.Reset()
go t.run()
return t
}
// Stop turns off a ticker. After Stop, no more ticks will be sent.
func (t *Ticker) Stop() {
t.stopOnce.Do(func() { close(t.stop) })
}
func (t *Ticker) run() {
c := t.c
defer close(c)
// Ticker is guaranteed to tick at least once.
afterC := t.send(time.Now())
for {
if afterC == nil {
return
}
select {
case tick := <-afterC:
afterC = t.send(tick)
case <-t.stop:
t.c = nil // Prevent future ticks from being sent to the channel.
return
case <-t.ctx.Done():
return
}
}
}
func (t *Ticker) send(tick time.Time) <-chan time.Time {
select {
case t.c <- tick:
case <-t.stop:
return nil
}
next := t.b.NextBackOff()
if next == Stop {
t.Stop()
return nil
}
t.timer.Start(next)
return t.timer.C()
}

View File

@@ -1,35 +0,0 @@
package backoff
import "time"
type Timer interface {
Start(duration time.Duration)
Stop()
C() <-chan time.Time
}
// defaultTimer implements Timer interface using time.Timer
type defaultTimer struct {
timer *time.Timer
}
// C returns the timers channel which receives the current time when the timer fires.
func (t *defaultTimer) C() <-chan time.Time {
return t.timer.C
}
// Start starts the timer to fire after the given duration
func (t *defaultTimer) Start(duration time.Duration) {
if t.timer == nil {
t.timer = time.NewTimer(duration)
} else {
t.timer.Reset(duration)
}
}
// Stop is called when the timer is not used anymore and resources may be freed.
func (t *defaultTimer) Stop() {
if t.timer != nil {
t.timer.Stop()
}
}

View File

@@ -1,38 +0,0 @@
package backoff
import "time"
/*
WithMaxRetries creates a wrapper around another BackOff, which will
return Stop if NextBackOff() has been called too many times since
the last time Reset() was called
Note: Implementation is not thread-safe.
*/
func WithMaxRetries(b BackOff, max uint64) BackOff {
return &backOffTries{delegate: b, maxTries: max}
}
type backOffTries struct {
delegate BackOff
maxTries uint64
numTries uint64
}
func (b *backOffTries) NextBackOff() time.Duration {
if b.maxTries == 0 {
return Stop
}
if b.maxTries > 0 {
if b.maxTries <= b.numTries {
return Stop
}
b.numTries++
}
return b.delegate.NextBackOff()
}
func (b *backOffTries) Reset() {
b.numTries = 0
b.delegate.Reset()
}

View File

@@ -1,22 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe

View File

@@ -1,3 +0,0 @@
language: go
go: 1.2

View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Cenk Altı
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.

View File

@@ -1,5 +0,0 @@
hub
===
[![GoDoc](https://godoc.org/github.com/cenkalti/hub?status.png)](https://godoc.org/github.com/cenkalti/hub)
[![Build Status](https://travis-ci.org/cenkalti/hub.png)](https://travis-ci.org/cenkalti/hub)

View File

@@ -1,82 +0,0 @@
// Package hub provides a simple event dispatcher for publish/subscribe pattern.
package hub
import "sync"
type Kind int
// Event is an interface for published events.
type Event interface {
Kind() Kind
}
// Hub is an event dispatcher, publishes events to the subscribers
// which are subscribed for a specific event type.
// Optimized for publish calls.
// The handlers may be called in order different than they are registered.
type Hub struct {
subscribers map[Kind][]handler
m sync.RWMutex
seq uint64
}
type handler struct {
f func(Event)
id uint64
}
// Subscribe registers f for the event of a specific kind.
func (h *Hub) Subscribe(kind Kind, f func(Event)) (cancel func()) {
var cancelled bool
h.m.Lock()
h.seq++
id := h.seq
if h.subscribers == nil {
h.subscribers = make(map[Kind][]handler)
}
h.subscribers[kind] = append(h.subscribers[kind], handler{id: id, f: f})
h.m.Unlock()
return func() {
h.m.Lock()
if cancelled {
h.m.Unlock()
return
}
cancelled = true
a := h.subscribers[kind]
for i, f := range a {
if f.id == id {
a[i], h.subscribers[kind] = a[len(a)-1], a[:len(a)-1]
break
}
}
if len(a) == 0 {
delete(h.subscribers, kind)
}
h.m.Unlock()
}
}
// Publish an event to the subscribers.
func (h *Hub) Publish(e Event) {
h.m.RLock()
if handlers, ok := h.subscribers[e.Kind()]; ok {
for _, h := range handlers {
h.f(e)
}
}
h.m.RUnlock()
}
// DefaultHub is the default Hub used by Publish and Subscribe.
var DefaultHub Hub
// Subscribe registers f for the event of a specific kind in the DefaultHub.
func Subscribe(kind Kind, f func(Event)) (cancel func()) {
return DefaultHub.Subscribe(kind, f)
}
// Publish an event to the subscribers in DefaultHub.
func Publish(e Event) {
DefaultHub.Publish(e)
}

View File

@@ -1,23 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test

View File

@@ -1,9 +0,0 @@
language: go
go:
- 1.15
- tip
arch:
- amd64
- ppc64le

View File

@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Cenk Altı
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.

View File

@@ -1,82 +0,0 @@
rpc2
====
[![GoDoc](https://godoc.org/github.com/cenkalti/rpc2?status.png)](https://godoc.org/github.com/cenkalti/rpc2)
[![Build Status](https://travis-ci.org/cenkalti/rpc2.png)](https://travis-ci.org/cenkalti/rpc2)
rpc2 is a fork of net/rpc package in the standard library.
The main goal is to add bi-directional support to calls.
That means server can call the methods of client.
This is not possible with net/rpc package.
In order to do this it adds a `*Client` argument to method signatures.
Install
--------
go get github.com/cenkalti/rpc2
Example server
---------------
```go
package main
import (
"fmt"
"net"
"github.com/cenkalti/rpc2"
)
type Args struct{ A, B int }
type Reply int
func main() {
srv := rpc2.NewServer()
srv.Handle("add", func(client *rpc2.Client, args *Args, reply *Reply) error {
// Reversed call (server to client)
var rep Reply
client.Call("mult", Args{2, 3}, &rep)
fmt.Println("mult result:", rep)
*reply = Reply(args.A + args.B)
return nil
})
lis, _ := net.Listen("tcp", "127.0.0.1:5000")
srv.Accept(lis)
}
```
Example Client
---------------
```go
package main
import (
"fmt"
"net"
"github.com/cenkalti/rpc2"
)
type Args struct{ A, B int }
type Reply int
func main() {
conn, _ := net.Dial("tcp", "127.0.0.1:5000")
clt := rpc2.NewClient(conn)
clt.Handle("mult", func(client *rpc2.Client, args *Args, reply *Reply) error {
*reply = Reply(args.A * args.B)
return nil
})
go clt.Run()
var rep Reply
clt.Call("add", Args{1, 2}, &rep)
fmt.Println("add result:", rep)
}
```

View File

@@ -1,364 +0,0 @@
// Package rpc2 provides bi-directional RPC client and server similar to net/rpc.
package rpc2
import (
"context"
"errors"
"io"
"log"
"reflect"
"sync"
)
// Client represents an RPC Client.
// There may be multiple outstanding Calls associated
// with a single Client, and a Client may be used by
// multiple goroutines simultaneously.
type Client struct {
mutex sync.Mutex // protects pending, seq, request
sending sync.Mutex
request Request // temp area used in send()
seq uint64
pending map[uint64]*Call
closing bool
shutdown bool
server bool
codec Codec
handlers map[string]*handler
disconnect chan struct{}
State *State // additional information to associate with client
blocking bool // whether to block request handling
}
// NewClient returns a new Client to handle requests to the
// set of services at the other end of the connection.
// It adds a buffer to the write side of the connection so
// the header and payload are sent as a unit.
func NewClient(conn io.ReadWriteCloser) *Client {
return NewClientWithCodec(NewGobCodec(conn))
}
// NewClientWithCodec is like NewClient but uses the specified
// codec to encode requests and decode responses.
func NewClientWithCodec(codec Codec) *Client {
return &Client{
codec: codec,
pending: make(map[uint64]*Call),
handlers: make(map[string]*handler),
disconnect: make(chan struct{}),
seq: 1, // 0 means notification.
}
}
// SetBlocking puts the client in blocking mode.
// In blocking mode, received requests are processes synchronously.
// If you have methods that may take a long time, other subsequent requests may time out.
func (c *Client) SetBlocking(blocking bool) {
c.blocking = blocking
}
// Run the client's read loop.
// You must run this method before calling any methods on the server.
func (c *Client) Run() {
c.readLoop()
}
// DisconnectNotify returns a channel that is closed
// when the client connection has gone away.
func (c *Client) DisconnectNotify() chan struct{} {
return c.disconnect
}
// Handle registers the handler function for the given method. If a handler already exists for method, Handle panics.
func (c *Client) Handle(method string, handlerFunc interface{}) {
addHandler(c.handlers, method, handlerFunc)
}
// readLoop reads messages from codec.
// It reads a reqeust or a response to the previous request.
// If the message is request, calls the handler function.
// If the message is response, sends the reply to the associated call.
func (c *Client) readLoop() {
var err error
var req Request
var resp Response
for err == nil {
req = Request{}
resp = Response{}
if err = c.codec.ReadHeader(&req, &resp); err != nil {
break
}
if req.Method != "" {
// request comes to server
if err = c.readRequest(&req); err != nil {
debugln("rpc2: error reading request:", err.Error())
}
} else {
// response comes to client
if err = c.readResponse(&resp); err != nil {
debugln("rpc2: error reading response:", err.Error())
}
}
}
// Terminate pending calls.
c.sending.Lock()
c.mutex.Lock()
c.shutdown = true
closing := c.closing
if err == io.EOF {
if closing {
err = ErrShutdown
} else {
err = io.ErrUnexpectedEOF
}
}
for _, call := range c.pending {
call.Error = err
call.done()
}
c.mutex.Unlock()
c.sending.Unlock()
if err != io.EOF && !closing && !c.server {
debugln("rpc2: client protocol error:", err)
}
close(c.disconnect)
if !closing {
c.codec.Close()
}
}
func (c *Client) handleRequest(req Request, method *handler, argv reflect.Value) {
// Invoke the method, providing a new value for the reply.
replyv := reflect.New(method.replyType.Elem())
returnValues := method.fn.Call([]reflect.Value{reflect.ValueOf(c), argv, replyv})
// Do not send response if request is a notification.
if req.Seq == 0 {
return
}
// The return value for the method is an error.
errInter := returnValues[0].Interface()
errmsg := ""
if errInter != nil {
errmsg = errInter.(error).Error()
}
resp := &Response{
Seq: req.Seq,
Error: errmsg,
}
if err := c.codec.WriteResponse(resp, replyv.Interface()); err != nil {
debugln("rpc2: error writing response:", err.Error())
}
}
func (c *Client) readRequest(req *Request) error {
method, ok := c.handlers[req.Method]
if !ok {
resp := &Response{
Seq: req.Seq,
Error: "rpc2: can't find method " + req.Method,
}
return c.codec.WriteResponse(resp, resp)
}
// Decode the argument value.
var argv reflect.Value
argIsValue := false // if true, need to indirect before calling.
if method.argType.Kind() == reflect.Ptr {
argv = reflect.New(method.argType.Elem())
} else {
argv = reflect.New(method.argType)
argIsValue = true
}
// argv guaranteed to be a pointer now.
if err := c.codec.ReadRequestBody(argv.Interface()); err != nil {
return err
}
if argIsValue {
argv = argv.Elem()
}
if c.blocking {
c.handleRequest(*req, method, argv)
} else {
go c.handleRequest(*req, method, argv)
}
return nil
}
func (c *Client) readResponse(resp *Response) error {
seq := resp.Seq
c.mutex.Lock()
call := c.pending[seq]
delete(c.pending, seq)
c.mutex.Unlock()
var err error
switch {
case call == nil:
// We've got no pending call. That usually means that
// WriteRequest partially failed, and call was already
// removed; response is a server telling us about an
// error reading request body. We should still attempt
// to read error body, but there's no one to give it to.
err = c.codec.ReadResponseBody(nil)
if err != nil {
err = errors.New("reading error body: " + err.Error())
}
case resp.Error != "":
// We've got an error response. Give this to the request;
// any subsequent requests will get the ReadResponseBody
// error if there is one.
call.Error = ServerError(resp.Error)
err = c.codec.ReadResponseBody(nil)
if err != nil {
err = errors.New("reading error body: " + err.Error())
}
call.done()
default:
err = c.codec.ReadResponseBody(call.Reply)
if err != nil {
call.Error = errors.New("reading body " + err.Error())
}
call.done()
}
return err
}
// Close waits for active calls to finish and closes the codec.
func (c *Client) Close() error {
c.mutex.Lock()
if c.shutdown || c.closing {
c.mutex.Unlock()
return ErrShutdown
}
c.closing = true
c.mutex.Unlock()
return c.codec.Close()
}
// Go invokes the function asynchronously. It returns the Call structure representing
// the invocation. The done channel will signal when the call is complete by returning
// the same Call object. If done is nil, Go will allocate a new channel.
// If non-nil, done must be buffered or Go will deliberately crash.
func (c *Client) Go(method string, args interface{}, reply interface{}, done chan *Call) *Call {
call := new(Call)
call.Method = method
call.Args = args
call.Reply = reply
if done == nil {
done = make(chan *Call, 10) // buffered.
} else {
// If caller passes done != nil, it must arrange that
// done has enough buffer for the number of simultaneous
// RPCs that will be using that channel. If the channel
// is totally unbuffered, it's best not to run at all.
if cap(done) == 0 {
log.Panic("rpc2: done channel is unbuffered")
}
}
call.Done = done
c.send(call)
return call
}
// CallWithContext invokes the named function, waits for it to complete, and
// returns its error status, or an error from Context timeout.
func (c *Client) CallWithContext(ctx context.Context, method string, args interface{}, reply interface{}) error {
call := c.Go(method, args, reply, make(chan *Call, 1))
select {
case <-call.Done:
return call.Error
case <-ctx.Done():
return ctx.Err()
}
return nil
}
// Call invokes the named function, waits for it to complete, and returns its error status.
func (c *Client) Call(method string, args interface{}, reply interface{}) error {
return c.CallWithContext(context.Background(), method, args, reply)
}
func (call *Call) done() {
select {
case call.Done <- call:
// ok
default:
// We don't want to block here. It is the caller's responsibility to make
// sure the channel has enough buffer space. See comment in Go().
debugln("rpc2: discarding Call reply due to insufficient Done chan capacity")
}
}
// ServerError represents an error that has been returned from
// the remote side of the RPC connection.
type ServerError string
func (e ServerError) Error() string {
return string(e)
}
// ErrShutdown is returned when the connection is closing or closed.
var ErrShutdown = errors.New("connection is shut down")
// Call represents an active RPC.
type Call struct {
Method string // The name of the service and method to call.
Args interface{} // The argument to the function (*struct).
Reply interface{} // The reply from the function (*struct).
Error error // After completion, the error status.
Done chan *Call // Strobes when call is complete.
}
func (c *Client) send(call *Call) {
c.sending.Lock()
defer c.sending.Unlock()
// Register this call.
c.mutex.Lock()
if c.shutdown || c.closing {
call.Error = ErrShutdown
c.mutex.Unlock()
call.done()
return
}
seq := c.seq
c.seq++
c.pending[seq] = call
c.mutex.Unlock()
// Encode and send the request.
c.request.Seq = seq
c.request.Method = call.Method
err := c.codec.WriteRequest(&c.request, call.Args)
if err != nil {
c.mutex.Lock()
call = c.pending[seq]
delete(c.pending, seq)
c.mutex.Unlock()
if call != nil {
call.Error = err
call.done()
}
}
}
// Notify sends a request to the receiver but does not wait for a return value.
func (c *Client) Notify(method string, args interface{}) error {
c.sending.Lock()
defer c.sending.Unlock()
if c.shutdown || c.closing {
return ErrShutdown
}
c.request.Seq = 0
c.request.Method = method
return c.codec.WriteRequest(&c.request, args)
}

View File

@@ -1,125 +0,0 @@
package rpc2
import (
"bufio"
"encoding/gob"
"io"
"sync"
)
// A Codec implements reading and writing of RPC requests and responses.
// The client calls ReadHeader to read a message header.
// The implementation must populate either Request or Response argument.
// Depending on which argument is populated, ReadRequestBody or
// ReadResponseBody is called right after ReadHeader.
// ReadRequestBody and ReadResponseBody may be called with a nil
// argument to force the body to be read and then discarded.
type Codec interface {
// ReadHeader must read a message and populate either the request
// or the response by inspecting the incoming message.
ReadHeader(*Request, *Response) error
// ReadRequestBody into args argument of handler function.
ReadRequestBody(interface{}) error
// ReadResponseBody into reply argument of handler function.
ReadResponseBody(interface{}) error
// WriteRequest must be safe for concurrent use by multiple goroutines.
WriteRequest(*Request, interface{}) error
// WriteResponse must be safe for concurrent use by multiple goroutines.
WriteResponse(*Response, interface{}) error
// Close is called when client/server finished with the connection.
Close() error
}
// Request is a header written before every RPC call.
type Request struct {
Seq uint64 // sequence number chosen by client
Method string
}
// Response is a header written before every RPC return.
type Response struct {
Seq uint64 // echoes that of the request
Error string // error, if any.
}
type gobCodec struct {
rwc io.ReadWriteCloser
dec *gob.Decoder
enc *gob.Encoder
encBuf *bufio.Writer
mutex sync.Mutex
}
type message struct {
Seq uint64
Method string
Error string
}
// NewGobCodec returns a new rpc2.Codec using gob encoding/decoding on conn.
func NewGobCodec(conn io.ReadWriteCloser) Codec {
buf := bufio.NewWriter(conn)
return &gobCodec{
rwc: conn,
dec: gob.NewDecoder(conn),
enc: gob.NewEncoder(buf),
encBuf: buf,
}
}
func (c *gobCodec) ReadHeader(req *Request, resp *Response) error {
var msg message
if err := c.dec.Decode(&msg); err != nil {
return err
}
if msg.Method != "" {
req.Seq = msg.Seq
req.Method = msg.Method
} else {
resp.Seq = msg.Seq
resp.Error = msg.Error
}
return nil
}
func (c *gobCodec) ReadRequestBody(body interface{}) error {
return c.dec.Decode(body)
}
func (c *gobCodec) ReadResponseBody(body interface{}) error {
return c.dec.Decode(body)
}
func (c *gobCodec) WriteRequest(r *Request, body interface{}) (err error) {
c.mutex.Lock()
defer c.mutex.Unlock()
if err = c.enc.Encode(r); err != nil {
return
}
if err = c.enc.Encode(body); err != nil {
return
}
return c.encBuf.Flush()
}
func (c *gobCodec) WriteResponse(r *Response, body interface{}) (err error) {
c.mutex.Lock()
defer c.mutex.Unlock()
if err = c.enc.Encode(r); err != nil {
return
}
if err = c.enc.Encode(body); err != nil {
return
}
return c.encBuf.Flush()
}
func (c *gobCodec) Close() error {
return c.rwc.Close()
}

View File

@@ -1,12 +0,0 @@
package rpc2
import "log"
// DebugLog controls the printing of internal and I/O errors.
var DebugLog = false
func debugln(v ...interface{}) {
if DebugLog {
log.Println(v...)
}
}

View File

@@ -1,226 +0,0 @@
// Package jsonrpc implements a JSON-RPC ClientCodec and ServerCodec for the rpc2 package.
//
// Beside struct types, JSONCodec allows using positional arguments.
// Use []interface{} as the type of argument when sending and receiving methods.
//
// Positional arguments example:
// server.Handle("add", func(client *rpc2.Client, args []interface{}, result *float64) error {
// *result = args[0].(float64) + args[1].(float64)
// return nil
// })
//
// var result float64
// client.Call("add", []interface{}{1, 2}, &result)
//
package jsonrpc
import (
"encoding/json"
"errors"
"fmt"
"io"
"reflect"
"sync"
"github.com/cenkalti/rpc2"
)
type jsonCodec struct {
dec *json.Decoder // for reading JSON values
enc *json.Encoder // for writing JSON values
c io.Closer
// temporary work space
msg message
serverRequest serverRequest
clientResponse clientResponse
// JSON-RPC clients can use arbitrary json values as request IDs.
// Package rpc expects uint64 request IDs.
// We assign uint64 sequence numbers to incoming requests
// but save the original request ID in the pending map.
// When rpc responds, we use the sequence number in
// the response to find the original request ID.
mutex sync.Mutex // protects seq, pending
pending map[uint64]*json.RawMessage
seq uint64
}
// NewJSONCodec returns a new rpc2.Codec using JSON-RPC on conn.
func NewJSONCodec(conn io.ReadWriteCloser) rpc2.Codec {
return &jsonCodec{
dec: json.NewDecoder(conn),
enc: json.NewEncoder(conn),
c: conn,
pending: make(map[uint64]*json.RawMessage),
}
}
// serverRequest and clientResponse combined
type message struct {
Method string `json:"method"`
Params *json.RawMessage `json:"params"`
Id *json.RawMessage `json:"id"`
Result *json.RawMessage `json:"result"`
Error interface{} `json:"error"`
}
// Unmarshal to
type serverRequest struct {
Method string `json:"method"`
Params *json.RawMessage `json:"params"`
Id *json.RawMessage `json:"id"`
}
type clientResponse struct {
Id uint64 `json:"id"`
Result *json.RawMessage `json:"result"`
Error interface{} `json:"error"`
}
// to Marshal
type serverResponse struct {
Id *json.RawMessage `json:"id"`
Result interface{} `json:"result"`
Error interface{} `json:"error"`
}
type clientRequest struct {
Method string `json:"method"`
Params interface{} `json:"params"`
Id *uint64 `json:"id"`
}
func (c *jsonCodec) ReadHeader(req *rpc2.Request, resp *rpc2.Response) error {
c.msg = message{}
if err := c.dec.Decode(&c.msg); err != nil {
return err
}
if c.msg.Method != "" {
// request comes to server
c.serverRequest.Id = c.msg.Id
c.serverRequest.Method = c.msg.Method
c.serverRequest.Params = c.msg.Params
req.Method = c.serverRequest.Method
// JSON request id can be any JSON value;
// RPC package expects uint64. Translate to
// internal uint64 and save JSON on the side.
if c.serverRequest.Id == nil {
// Notification
} else {
c.mutex.Lock()
c.seq++
c.pending[c.seq] = c.serverRequest.Id
c.serverRequest.Id = nil
req.Seq = c.seq
c.mutex.Unlock()
}
} else {
// response comes to client
err := json.Unmarshal(*c.msg.Id, &c.clientResponse.Id)
if err != nil {
return err
}
c.clientResponse.Result = c.msg.Result
c.clientResponse.Error = c.msg.Error
resp.Error = ""
resp.Seq = c.clientResponse.Id
if c.clientResponse.Error != nil || c.clientResponse.Result == nil {
x, ok := c.clientResponse.Error.(string)
if !ok {
return fmt.Errorf("invalid error %v", c.clientResponse.Error)
}
if x == "" {
x = "unspecified error"
}
resp.Error = x
}
}
return nil
}
var errMissingParams = errors.New("jsonrpc: request body missing params")
func (c *jsonCodec) ReadRequestBody(x interface{}) error {
if x == nil {
return nil
}
if c.serverRequest.Params == nil {
return errMissingParams
}
var err error
// Check if x points to a slice of any kind
rt := reflect.TypeOf(x)
if rt.Kind() == reflect.Ptr && rt.Elem().Kind() == reflect.Slice {
// If it's a slice, unmarshal as is
err = json.Unmarshal(*c.serverRequest.Params, x)
} else {
// Anything else unmarshal into a slice containing x
params := &[]interface{}{x}
err = json.Unmarshal(*c.serverRequest.Params, params)
}
return err
}
func (c *jsonCodec) ReadResponseBody(x interface{}) error {
if x == nil {
return nil
}
return json.Unmarshal(*c.clientResponse.Result, x)
}
func (c *jsonCodec) WriteRequest(r *rpc2.Request, param interface{}) error {
req := &clientRequest{Method: r.Method}
// Check if param is a slice of any kind
if param != nil && reflect.TypeOf(param).Kind() == reflect.Slice {
// If it's a slice, leave as is
req.Params = param
} else {
// Put anything else into a slice
req.Params = []interface{}{param}
}
if r.Seq == 0 {
// Notification
req.Id = nil
} else {
seq := r.Seq
req.Id = &seq
}
return c.enc.Encode(req)
}
var null = json.RawMessage([]byte("null"))
func (c *jsonCodec) WriteResponse(r *rpc2.Response, x interface{}) error {
c.mutex.Lock()
b, ok := c.pending[r.Seq]
if !ok {
c.mutex.Unlock()
return errors.New("invalid sequence number in response")
}
delete(c.pending, r.Seq)
c.mutex.Unlock()
if b == nil {
// Invalid request so no id. Use JSON null.
b = &null
}
resp := serverResponse{Id: b}
if r.Error == "" {
resp.Result = x
} else {
resp.Error = r.Error
}
return c.enc.Encode(resp)
}
func (c *jsonCodec) Close() error {
return c.c.Close()
}

View File

@@ -1,181 +0,0 @@
package rpc2
import (
"io"
"log"
"net"
"reflect"
"unicode"
"unicode/utf8"
"github.com/cenkalti/hub"
)
// Precompute the reflect type for error. Can't use error directly
// because Typeof takes an empty interface value. This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
var typeOfClient = reflect.TypeOf((*Client)(nil))
const (
clientConnected hub.Kind = iota
clientDisconnected
)
// Server responds to RPC requests made by Client.
type Server struct {
handlers map[string]*handler
eventHub *hub.Hub
}
type handler struct {
fn reflect.Value
argType reflect.Type
replyType reflect.Type
}
type connectionEvent struct {
Client *Client
}
type disconnectionEvent struct {
Client *Client
}
func (connectionEvent) Kind() hub.Kind { return clientConnected }
func (disconnectionEvent) Kind() hub.Kind { return clientDisconnected }
// NewServer returns a new Server.
func NewServer() *Server {
return &Server{
handlers: make(map[string]*handler),
eventHub: &hub.Hub{},
}
}
// Handle registers the handler function for the given method. If a handler already exists for method, Handle panics.
func (s *Server) Handle(method string, handlerFunc interface{}) {
addHandler(s.handlers, method, handlerFunc)
}
func addHandler(handlers map[string]*handler, mname string, handlerFunc interface{}) {
if _, ok := handlers[mname]; ok {
panic("rpc2: multiple registrations for " + mname)
}
method := reflect.ValueOf(handlerFunc)
mtype := method.Type()
// Method needs three ins: *client, *args, *reply.
if mtype.NumIn() != 3 {
log.Panicln("method", mname, "has wrong number of ins:", mtype.NumIn())
}
// First arg must be a pointer to rpc2.Client.
clientType := mtype.In(0)
if clientType.Kind() != reflect.Ptr {
log.Panicln("method", mname, "client type not a pointer:", clientType)
}
if clientType != typeOfClient {
log.Panicln("method", mname, "first argument", clientType.String(), "not *rpc2.Client")
}
// Second arg need not be a pointer.
argType := mtype.In(1)
if !isExportedOrBuiltinType(argType) {
log.Panicln(mname, "argument type not exported:", argType)
}
// Third arg must be a pointer.
replyType := mtype.In(2)
if replyType.Kind() != reflect.Ptr {
log.Panicln("method", mname, "reply type not a pointer:", replyType)
}
// Reply type must be exported.
if !isExportedOrBuiltinType(replyType) {
log.Panicln("method", mname, "reply type not exported:", replyType)
}
// Method needs one out.
if mtype.NumOut() != 1 {
log.Panicln("method", mname, "has wrong number of outs:", mtype.NumOut())
}
// The return type of the method must be error.
if returnType := mtype.Out(0); returnType != typeOfError {
log.Panicln("method", mname, "returns", returnType.String(), "not error")
}
handlers[mname] = &handler{
fn: method,
argType: argType,
replyType: replyType,
}
}
// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// PkgPath will be non-empty even for an exported type,
// so we need to check the type name as well.
return isExported(t.Name()) || t.PkgPath() == ""
}
// Is this an exported - upper case - name?
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
// OnConnect registers a function to run when a client connects.
func (s *Server) OnConnect(f func(*Client)) {
s.eventHub.Subscribe(clientConnected, func(e hub.Event) {
go f(e.(connectionEvent).Client)
})
}
// OnDisconnect registers a function to run when a client disconnects.
func (s *Server) OnDisconnect(f func(*Client)) {
s.eventHub.Subscribe(clientDisconnected, func(e hub.Event) {
go f(e.(disconnectionEvent).Client)
})
}
// Accept accepts connections on the listener and serves requests
// for each incoming connection. Accept blocks; the caller typically
// invokes it in a go statement.
func (s *Server) Accept(lis net.Listener) {
for {
conn, err := lis.Accept()
if err != nil {
log.Print("rpc.Serve: accept:", err.Error())
return
}
go s.ServeConn(conn)
}
}
// ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
// ServeConn uses the gob wire format (see package gob) on the
// connection. To use an alternate codec, use ServeCodec.
func (s *Server) ServeConn(conn io.ReadWriteCloser) {
s.ServeCodec(NewGobCodec(conn))
}
// ServeCodec is like ServeConn but uses the specified codec to
// decode requests and encode responses.
func (s *Server) ServeCodec(codec Codec) {
s.ServeCodecWithState(codec, NewState())
}
// ServeCodecWithState is like ServeCodec but also gives the ability to
// associate a state variable with the client that persists across RPC calls.
func (s *Server) ServeCodecWithState(codec Codec, state *State) {
defer codec.Close()
// Client also handles the incoming connections.
c := NewClientWithCodec(codec)
c.server = true
c.handlers = s.handlers
c.State = state
s.eventHub.Publish(connectionEvent{c})
c.Run()
s.eventHub.Publish(disconnectionEvent{c})
}

View File

@@ -1,25 +0,0 @@
package rpc2
import "sync"
type State struct {
store map[string]interface{}
m sync.RWMutex
}
func NewState() *State {
return &State{store: make(map[string]interface{})}
}
func (s *State) Get(key string) (value interface{}, ok bool) {
s.m.RLock()
value, ok = s.store[key]
s.m.RUnlock()
return
}
func (s *State) Set(key string, value interface{}) {
s.m.Lock()
s.store[key] = value
s.m.Unlock()
}

View File

@@ -1,6 +0,0 @@
# CHANGELOG
## v1.0.0-rc1
This is the first logged release. Major changes (including breaking changes)
have occurred since earlier tags.

View File

@@ -1,17 +0,0 @@
# Contributing
Logr is open to pull-requests, provided they fit within the intended scope of
the project. Specifically, this library aims to be VERY small and minimalist,
with no external dependencies.
## Compatibility
This project intends to follow [semantic versioning](http://semver.org) and
is very strict about compatibility. Any proposed changes MUST follow those
rules.
## Performance
As a logging library, logr must be as light-weight as possible. Any proposed
code change must include results of running the [benchmark](./benchmark)
before and after the change.

View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,275 +0,0 @@
# A minimal logging API for Go
logr offers an(other) opinion on how Go programs and libraries can do logging
without becoming coupled to a particular logging implementation. This is not
an implementation of logging - it is an API. In fact it is two APIs with two
different sets of users.
The `Logger` type is intended for application and library authors. It provides
a relatively small API which can be used everywhere you want to emit logs. It
defers the actual act of writing logs (to files, to stdout, or whatever) to the
`LogSink` interface.
The `LogSink` interface is intended for logging library implementers. It is a
pure interface which can be implemented by logging frameworks to provide the actual logging
functionality.
This decoupling allows application and library developers to write code in
terms of `logr.Logger` (which has very low dependency fan-out) while the
implementation of logging is managed "up stack" (e.g. in or near `main()`.)
Application developers can then switch out implementations as necessary.
Many people assert that libraries should not be logging, and as such efforts
like this are pointless. Those people are welcome to convince the authors of
the tens-of-thousands of libraries that *DO* write logs that they are all
wrong. In the meantime, logr takes a more practical approach.
## Typical usage
Somewhere, early in an application's life, it will make a decision about which
logging library (implementation) it actually wants to use. Something like:
```
func main() {
// ... other setup code ...
// Create the "root" logger. We have chosen the "logimpl" implementation,
// which takes some initial parameters and returns a logr.Logger.
logger := logimpl.New(param1, param2)
// ... other setup code ...
```
Most apps will call into other libraries, create structures to govern the flow,
etc. The `logr.Logger` object can be passed to these other libraries, stored
in structs, or even used as a package-global variable, if needed. For example:
```
app := createTheAppObject(logger)
app.Run()
```
Outside of this early setup, no other packages need to know about the choice of
implementation. They write logs in terms of the `logr.Logger` that they
received:
```
type appObject struct {
// ... other fields ...
logger logr.Logger
// ... other fields ...
}
func (app *appObject) Run() {
app.logger.Info("starting up", "timestamp", time.Now())
// ... app code ...
```
## Background
If the Go standard library had defined an interface for logging, this project
probably would not be needed. Alas, here we are.
### Inspiration
Before you consider this package, please read [this blog post by the
inimitable Dave Cheney][warning-makes-no-sense]. We really appreciate what
he has to say, and it largely aligns with our own experiences.
### Differences from Dave's ideas
The main differences are:
1. Dave basically proposes doing away with the notion of a logging API in favor
of `fmt.Printf()`. We disagree, especially when you consider things like output
locations, timestamps, file and line decorations, and structured logging. This
package restricts the logging API to just 2 types of logs: info and error.
Info logs are things you want to tell the user which are not errors. Error
logs are, well, errors. If your code receives an `error` from a subordinate
function call and is logging that `error` *and not returning it*, use error
logs.
2. Verbosity-levels on info logs. This gives developers a chance to indicate
arbitrary grades of importance for info logs, without assigning names with
semantic meaning such as "warning", "trace", and "debug." Superficially this
may feel very similar, but the primary difference is the lack of semantics.
Because verbosity is a numerical value, it's safe to assume that an app running
with higher verbosity means more (and less important) logs will be generated.
## Implementations (non-exhaustive)
There are implementations for the following logging libraries:
- **a function**: [funcr](https://github.com/go-logr/logr/tree/master/funcr)
- **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr)
- **k8s.io/klog**: [klogr](https://git.k8s.io/klog/klogr)
- **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr)
- **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr)
- **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr)
- **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend)
- **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr)
## FAQ
### Conceptual
#### Why structured logging?
- **Structured logs are more easily queryable**: Since you've got
key-value pairs, it's much easier to query your structured logs for
particular values by filtering on the contents of a particular key --
think searching request logs for error codes, Kubernetes reconcilers for
the name and namespace of the reconciled object, etc.
- **Structured logging makes it easier to have cross-referenceable logs**:
Similarly to searchability, if you maintain conventions around your
keys, it becomes easy to gather all log lines related to a particular
concept.
- **Structured logs allow better dimensions of filtering**: if you have
structure to your logs, you've got more precise control over how much
information is logged -- you might choose in a particular configuration
to log certain keys but not others, only log lines where a certain key
matches a certain value, etc., instead of just having v-levels and names
to key off of.
- **Structured logs better represent structured data**: sometimes, the
data that you want to log is inherently structured (think tuple-link
objects.) Structured logs allow you to preserve that structure when
outputting.
#### Why V-levels?
**V-levels give operators an easy way to control the chattiness of log
operations**. V-levels provide a way for a given package to distinguish
the relative importance or verbosity of a given log message. Then, if
a particular logger or package is logging too many messages, the user
of the package can simply change the v-levels for that library.
#### Why not named levels, like Info/Warning/Error?
Read [Dave Cheney's post][warning-makes-no-sense]. Then read [Differences
from Dave's ideas](#differences-from-daves-ideas).
#### Why not allow format strings, too?
**Format strings negate many of the benefits of structured logs**:
- They're not easily searchable without resorting to fuzzy searching,
regular expressions, etc.
- They don't store structured data well, since contents are flattened into
a string.
- They're not cross-referenceable.
- They don't compress easily, since the message is not constant.
(Unless you turn positional parameters into key-value pairs with numerical
keys, at which point you've gotten key-value logging with meaningless
keys.)
### Practical
#### Why key-value pairs, and not a map?
Key-value pairs are *much* easier to optimize, especially around
allocations. Zap (a structured logger that inspired logr's interface) has
[performance measurements](https://github.com/uber-go/zap#performance)
that show this quite nicely.
While the interface ends up being a little less obvious, you get
potentially better performance, plus avoid making users type
`map[string]string{}` every time they want to log.
#### What if my V-levels differ between libraries?
That's fine. Control your V-levels on a per-logger basis, and use the
`WithName` method to pass different loggers to different libraries.
Generally, you should take care to ensure that you have relatively
consistent V-levels within a given logger, however, as this makes deciding
on what verbosity of logs to request easier.
#### But I really want to use a format string!
That's not actually a question. Assuming your question is "how do
I convert my mental model of logging with format strings to logging with
constant messages":
1. Figure out what the error actually is, as you'd write in a TL;DR style,
and use that as a message.
2. For every place you'd write a format specifier, look to the word before
it, and add that as a key value pair.
For instance, consider the following examples (all taken from spots in the
Kubernetes codebase):
- `klog.V(4).Infof("Client is returning errors: code %v, error %v",
responseCode, err)` becomes `logger.Error(err, "client returned an
error", "code", responseCode)`
- `klog.V(4).Infof("Got a Retry-After %ds response for attempt %d to %v",
seconds, retries, url)` becomes `logger.V(4).Info("got a retry-after
response when requesting url", "attempt", retries, "after
seconds", seconds, "url", url)`
If you *really* must use a format string, use it in a key's value, and
call `fmt.Sprintf` yourself. For instance: `log.Printf("unable to
reflect over type %T")` becomes `logger.Info("unable to reflect over
type", "type", fmt.Sprintf("%T"))`. In general though, the cases where
this is necessary should be few and far between.
#### How do I choose my V-levels?
This is basically the only hard constraint: increase V-levels to denote
more verbose or more debug-y logs.
Otherwise, you can start out with `0` as "you always want to see this",
`1` as "common logging that you might *possibly* want to turn off", and
`10` as "I would like to performance-test your log collection stack."
Then gradually choose levels in between as you need them, working your way
down from 10 (for debug and trace style logs) and up from 1 (for chattier
info-type logs.)
#### How do I choose my keys?
Keys are fairly flexible, and can hold more or less any string
value. For best compatibility with implementations and consistency
with existing code in other projects, there are a few conventions you
should consider.
- Make your keys human-readable.
- Constant keys are generally a good idea.
- Be consistent across your codebase.
- Keys should naturally match parts of the message string.
- Use lower case for simple keys and
[lowerCamelCase](https://en.wiktionary.org/wiki/lowerCamelCase) for
more complex ones. Kubernetes is one example of a project that has
[adopted that
convention](https://github.com/kubernetes/community/blob/HEAD/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments).
While key names are mostly unrestricted (and spaces are acceptable),
it's generally a good idea to stick to printable ascii characters, or at
least match the general character set of your log lines.
#### Why should keys be constant values?
The point of structured logging is to make later log processing easier. Your
keys are, effectively, the schema of each log message. If you use different
keys across instances of the same log line, you will make your structured logs
much harder to use. `Sprintf()` is for values, not for keys!
#### Why is this not a pure interface?
The Logger type is implemented as a struct in order to allow the Go compiler to
optimize things like high-V `Info` logs that are not triggered. Not all of
these implementations are implemented yet, but this structure was suggested as
a way to ensure they *can* be implemented. All of the real work is behind the
`LogSink` interface.
[warning-makes-no-sense]: http://dave.cheney.net/2015/11/05/lets-talk-about-logging

View File

@@ -1,54 +0,0 @@
/*
Copyright 2020 The logr Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logr
// Discard returns a Logger that discards all messages logged to it. It can be
// used whenever the caller is not interested in the logs. Logger instances
// produced by this function always compare as equal.
func Discard() Logger {
return Logger{
level: 0,
sink: discardLogSink{},
}
}
// discardLogSink is a LogSink that discards all messages.
type discardLogSink struct{}
// Verify that it actually implements the interface
var _ LogSink = discardLogSink{}
func (l discardLogSink) Init(RuntimeInfo) {
}
func (l discardLogSink) Enabled(int) bool {
return false
}
func (l discardLogSink) Info(int, string, ...interface{}) {
}
func (l discardLogSink) Error(error, string, ...interface{}) {
}
func (l discardLogSink) WithValues(...interface{}) LogSink {
return l
}
func (l discardLogSink) WithName(string) LogSink {
return l
}

View File

@@ -1,411 +0,0 @@
/*
Copyright 2021 The logr Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package funcr implements formatting of structured log messages and
// optionally captures the call site. This will not call String or
// Error methods on values.
//
// The simplest way to use it is via its implementation of a
// github.com/go-logr/logr.LogSink with output through an arbitrary
// "write" function. Alternatively, funcr can also be embedded inside
// a custom LogSink implementation. This is useful when the LogSink
// needs to implement additional methods.
package funcr
import (
"bytes"
"fmt"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"time"
"github.com/go-logr/logr"
)
// New returns a logr.Logger which is implemented by an arbitrary function.
func New(fn func(prefix, args string), opts Options) logr.Logger {
return logr.New(newSink(fn, opts))
}
// Underlier exposes access to the underlying logging function. Since
// callers only have a logr.Logger, they have to know which
// implementation is in use, so this interface is less of an
// abstraction and more of a way to test type conversion.
type Underlier interface {
GetUnderlying() func(prefix, args string)
}
func newSink(fn func(prefix, args string), opts Options) logr.LogSink {
l := &fnlogger{
Formatter: NewFormatter(opts),
write: fn,
}
// For skipping fnlogger.Info and fnlogger.Error.
l.Formatter.AddCallDepth(1)
return l
}
// Options carries parameters which influence the way logs are generated.
type Options struct {
// LogCaller tells funcr to add a "caller" key to some or all log lines.
// This has some overhead, so some users might not want it.
LogCaller MessageClass
// LogTimestamp tells funcr to add a "ts" key to log lines. This has some
// overhead, so some users might not want it.
LogTimestamp bool
// Verbosity tells funcr which V logs to be write. Higher values enable
// more logs.
Verbosity int
}
// MessageClass indicates which category or categories of messages to consider.
type MessageClass int
const (
// None ignores all message classes.
None MessageClass = iota
// All considers all message classes.
All
// Info only considers info messages.
Info
// Error only considers error messages.
Error
)
const timestampFmt = "2006-01-02 15:04:05.000000"
// fnlogger inherits some of its LogSink implementation from Formatter
// and just needs to add some glue code.
type fnlogger struct {
Formatter
write func(prefix, args string)
}
func (l fnlogger) WithName(name string) logr.LogSink {
l.Formatter.AddName(name)
return &l
}
func (l fnlogger) WithValues(kvList ...interface{}) logr.LogSink {
l.Formatter.AddValues(kvList)
return &l
}
func (l fnlogger) WithCallDepth(depth int) logr.LogSink {
l.Formatter.AddCallDepth(depth)
return &l
}
func (l fnlogger) Info(level int, msg string, kvList ...interface{}) {
prefix, args := l.FormatInfo(level, msg, kvList)
l.write(prefix, args)
}
func (l fnlogger) Error(err error, msg string, kvList ...interface{}) {
prefix, args := l.FormatError(err, msg, kvList)
l.write(prefix, args)
}
func (l fnlogger) GetUnderlying() func(prefix, args string) {
return l.write
}
// Assert conformance to the interfaces.
var _ logr.LogSink = &fnlogger{}
var _ logr.CallDepthLogSink = &fnlogger{}
var _ Underlier = &fnlogger{}
func flatten(kvList ...interface{}) string {
if len(kvList)%2 != 0 {
kvList = append(kvList, "<no-value>")
}
// Empirically bytes.Buffer is faster than strings.Builder for this.
buf := bytes.NewBuffer(make([]byte, 0, 1024))
for i := 0; i < len(kvList); i += 2 {
k, ok := kvList[i].(string)
if !ok {
k = fmt.Sprintf("<non-string-key-%d>", i/2)
}
v := kvList[i+1]
if i > 0 {
buf.WriteRune(' ')
}
buf.WriteRune('"')
buf.WriteString(k)
buf.WriteRune('"')
buf.WriteRune('=')
buf.WriteString(pretty(v))
}
return buf.String()
}
func pretty(value interface{}) string {
return prettyWithFlags(value, 0)
}
const (
flagRawString = 0x1
)
// TODO: This is not fast. Most of the overhead goes here.
func prettyWithFlags(value interface{}, flags uint32) string {
// Handling the most common types without reflect is a small perf win.
switch v := value.(type) {
case bool:
return strconv.FormatBool(v)
case string:
if flags&flagRawString > 0 {
return v
}
// This is empirically faster than strings.Builder.
return `"` + v + `"`
case int:
return strconv.FormatInt(int64(v), 10)
case int8:
return strconv.FormatInt(int64(v), 10)
case int16:
return strconv.FormatInt(int64(v), 10)
case int32:
return strconv.FormatInt(int64(v), 10)
case int64:
return strconv.FormatInt(int64(v), 10)
case uint:
return strconv.FormatUint(uint64(v), 10)
case uint8:
return strconv.FormatUint(uint64(v), 10)
case uint16:
return strconv.FormatUint(uint64(v), 10)
case uint32:
return strconv.FormatUint(uint64(v), 10)
case uint64:
return strconv.FormatUint(v, 10)
case uintptr:
return strconv.FormatUint(uint64(v), 10)
case float32:
return strconv.FormatFloat(float64(v), 'f', -1, 32)
case float64:
return strconv.FormatFloat(v, 'f', -1, 64)
}
buf := bytes.NewBuffer(make([]byte, 0, 256))
t := reflect.TypeOf(value)
if t == nil {
return "null"
}
v := reflect.ValueOf(value)
switch t.Kind() {
case reflect.Bool:
return strconv.FormatBool(v.Bool())
case reflect.String:
if flags&flagRawString > 0 {
return v.String()
}
// This is empirically faster than strings.Builder.
return `"` + v.String() + `"`
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(int64(v.Int()), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return strconv.FormatUint(uint64(v.Uint()), 10)
case reflect.Float32:
return strconv.FormatFloat(float64(v.Float()), 'f', -1, 32)
case reflect.Float64:
return strconv.FormatFloat(v.Float(), 'f', -1, 64)
case reflect.Struct:
buf.WriteRune('{')
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.PkgPath != "" {
// reflect says this field is only defined for non-exported fields.
continue
}
if i > 0 {
buf.WriteRune(',')
}
buf.WriteRune('"')
name := f.Name
if tag, found := f.Tag.Lookup("json"); found {
if comma := strings.Index(tag, ","); comma != -1 {
name = tag[:comma]
} else {
name = tag
}
}
buf.WriteString(name)
buf.WriteRune('"')
buf.WriteRune(':')
buf.WriteString(pretty(v.Field(i).Interface()))
}
buf.WriteRune('}')
return buf.String()
case reflect.Slice, reflect.Array:
buf.WriteRune('[')
for i := 0; i < v.Len(); i++ {
if i > 0 {
buf.WriteRune(',')
}
e := v.Index(i)
buf.WriteString(pretty(e.Interface()))
}
buf.WriteRune(']')
return buf.String()
case reflect.Map:
buf.WriteRune('{')
// This does not sort the map keys, for best perf.
it := v.MapRange()
i := 0
for it.Next() {
if i > 0 {
buf.WriteRune(',')
}
// JSON only does string keys.
buf.WriteRune('"')
buf.WriteString(prettyWithFlags(it.Key().Interface(), flagRawString))
buf.WriteRune('"')
buf.WriteRune(':')
buf.WriteString(pretty(it.Value().Interface()))
i++
}
buf.WriteRune('}')
return buf.String()
case reflect.Ptr, reflect.Interface:
return pretty(v.Elem().Interface())
}
return fmt.Sprintf(`"<unhandled-%s>"`, t.Kind().String())
}
type callerID struct {
File string `json:"file"`
Line int `json:"line"`
}
// NewFormatter constructs a Formatter.
func NewFormatter(opts Options) Formatter {
f := Formatter{
prefix: "",
values: nil,
depth: 0,
logCaller: opts.LogCaller,
logTimestamp: opts.LogTimestamp,
verbosity: opts.Verbosity,
}
return f
}
// Formatter is an opaque struct which can be embedded in a LogSink
// implementation. It should be constructed with NewFormatter. Some of
// its methods directly implement logr.LogSink.
type Formatter struct {
prefix string
values []interface{}
depth int
logCaller MessageClass
logTimestamp bool
verbosity int
}
func (f Formatter) caller() callerID {
// +1 for this frame, +1 for Info/Error.
_, file, line, ok := runtime.Caller(f.depth + 2)
if !ok {
return callerID{"<unknown>", 0}
}
return callerID{filepath.Base(file), line}
}
// Init configures this Formatter from runtime info, such as the call depth
// imposed by logr itself.
// Note that this receiver is a pointer, so depth can be saved.
func (f *Formatter) Init(info logr.RuntimeInfo) {
f.depth += info.CallDepth
}
// Enabled checks whether an info message at the given level should be logged.
func (f Formatter) Enabled(level int) bool {
return level <= f.verbosity
}
// GetDepth returns the current depth of this Formatter. This is useful for
// implementations which do their own caller attribution.
func (f Formatter) GetDepth() int {
return f.depth
}
// FormatInfo flattens an Info log message into strings.
// The prefix will be empty when no names were set.
func (f Formatter) FormatInfo(level int, msg string, kvList []interface{}) (prefix, argsStr string) {
args := make([]interface{}, 0, 64) // using a constant here impacts perf
if f.logTimestamp {
args = append(args, "ts", time.Now().Format(timestampFmt))
}
if f.logCaller == All || f.logCaller == Info {
args = append(args, "caller", f.caller())
}
args = append(args, "level", level, "msg", msg)
args = append(args, f.values...)
args = append(args, kvList...)
return f.prefix, flatten(args...)
}
// FormatError flattens an Error log message into strings.
// The prefix will be empty when no names were set.
func (f Formatter) FormatError(err error, msg string, kvList []interface{}) (prefix, argsStr string) {
args := make([]interface{}, 0, 64) // using a constant here impacts perf
if f.logTimestamp {
args = append(args, "ts", time.Now().Format(timestampFmt))
}
if f.logCaller == All || f.logCaller == Error {
args = append(args, "caller", f.caller())
}
args = append(args, "msg", msg)
var loggableErr interface{}
if err != nil {
loggableErr = err.Error()
}
args = append(args, "error", loggableErr)
args = append(args, f.values...)
args = append(args, kvList...)
return f.prefix, flatten(args...)
}
// AddName appends the specified name. funcr uses '/' characters to separate
// name elements. Callers should not pass '/' in the provided name string, but
// this library does not actually enforce that.
func (f *Formatter) AddName(name string) {
if len(f.prefix) > 0 {
f.prefix += "/"
}
f.prefix += name
}
// AddValues adds key-value pairs to the set of saved values to be logged with
// each log line.
func (f *Formatter) AddValues(kvList []interface{}) {
// Three slice args forces a copy.
n := len(f.values)
f.values = append(f.values[:n:n], kvList...)
}
// AddCallDepth increases the number of stack-frames to skip when attributing
// the log line to a file and line.
func (f *Formatter) AddCallDepth(depth int) {
f.depth += depth
}

View File

@@ -1,479 +0,0 @@
/*
Copyright 2019 The logr Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This design derives from Dave Cheney's blog:
// http://dave.cheney.net/2015/11/05/lets-talk-about-logging
// Package logr defines a general-purpose logging API and abstract interfaces
// to back that API. Packages in the Go ecosystem can depend on this package,
// while callers can implement logging with whatever backend is appropriate.
//
// # Usage
//
// Logging is done using a Logger instance. Logger is a concrete type with
// methods, which defers the actual logging to a LogSink interface. The main
// methods of Logger are Info() and Error(). Arguments to Info() and Error()
// are key/value pairs rather than printf-style formatted strings, emphasizing
// "structured logging".
//
// With Go's standard log package, we might write:
// log.Printf("setting target value %s", targetValue)
//
// With logr's structured logging, we'd write:
// logger.Info("setting target", "value", targetValue)
//
// Errors are much the same. Instead of:
// log.Printf("failed to open the pod bay door for user %s: %v", user, err)
//
// We'd write:
// logger.Error(err, "failed to open the pod bay door", "user", user)
//
// Info() and Error() are very similar, but they are separate methods so that
// LogSink implementations can choose to do things like attach additional
// information (such as stack traces) on calls to Error().
//
// # Verbosity
//
// Often we want to log information only when the application in "verbose
// mode". To write log lines that are more verbose, Logger has a V() method.
// The higher the V-level of a log line, the less critical it is considered.
// Log-lines with V-levels that are not enabled (as per the LogSink) will not
// be written. Level V(0) is the default, and logger.V(0).Info() has the same
// meaning as logger.Info(). Negative V-levels have the same meaning as V(0).
//
// Where we might have written:
// if flVerbose >= 2 {
// log.Printf("an unusual thing happened")
// }
//
// We can write:
// logger.V(2).Info("an unusual thing happened")
//
// # Logger Names
//
// Logger instances can have name strings so that all messages logged through
// that instance have additional context. For example, you might want to add
// a subsystem name:
//
// logger.WithName("compactor").Info("started", "time", time.Now())
//
// The WithName() method returns a new Logger, which can be passed to
// constructors or other functions for further use. Repeated use of WithName()
// will accumulate name "segments". These name segments will be joined in some
// way by the LogSink implementation. It is strongly recommended that name
// segments contain simple identifiers (letters, digits, and hyphen), and do
// not contain characters that could muddle the log output or confuse the
// joining operation (e.g. whitespace, commas, periods, slashes, brackets,
// quotes, etc).
//
// # Saved Values
//
// Logger instances can store any number of key/value pairs, which will be
// logged alongside all messages logged through that instance. For example,
// you might want to create a Logger instance per managed object:
//
// With the standard log package, we might write:
// log.Printf("decided to set field foo to value %q for object %s/%s",
// targetValue, object.Namespace, object.Name)
//
// With logr we'd write:
// // Elsewhere: set up the logger to log the object name.
// obj.logger = mainLogger.WithValues(
// "name", obj.name, "namespace", obj.namespace)
//
// // later on...
// obj.logger.Info("setting foo", "value", targetValue)
//
// # Best Practices
//
// Logger has very few hard rules, with the goal that LogSink implementations
// might have a lot of freedom to differentiate. There are, however, some
// things to consider.
//
// The log message consists of a constant message attached to the log line.
// This should generally be a simple description of what's occurring, and should
// never be a format string. Variable information can then be attached using
// named values.
//
// Keys are arbitrary strings, but should generally be constant values. Values
// may be any Go value, but how the value is formatted is determined by the
// LogSink implementation.
//
// # Key Naming Conventions
//
// Keys are not strictly required to conform to any specification or regex, but
// it is recommended that they:
// * be human-readable and meaningful (not auto-generated or simple ordinals)
// * be constant (not dependent on input data)
// * contain only printable characters
// * not contain whitespace or punctuation
// * use lower case for simple keys and lowerCamelCase for more complex ones
//
// These guidelines help ensure that log data is processed properly regardless
// of the log implementation. For example, log implementations will try to
// output JSON data or will store data for later database (e.g. SQL) queries.
//
// While users are generally free to use key names of their choice, it's
// generally best to avoid using the following keys, as they're frequently used
// by implementations:
//
// * "caller": the calling information (file/line) of a particular log line.
// * "error": the underlying error value in the `Error` method.
// * "level": the log level.
// * "logger": the name of the associated logger.
// * "msg": the log message.
// * "stacktrace": the stack trace associated with a particular log line or
// error (often from the `Error` message).
// * "ts": the timestamp for a log line.
//
// Implementations are encouraged to make use of these keys to represent the
// above concepts, when necessary (for example, in a pure-JSON output form, it
// would be necessary to represent at least message and timestamp as ordinary
// named values).
//
// # Break Glass
//
// Implementations may choose to give callers access to the underlying
// logging implementation. The recommended pattern for this is:
// // Underlier exposes access to the underlying logging implementation.
// // Since callers only have a logr.Logger, they have to know which
// // implementation is in use, so this interface is less of an abstraction
// // and more of way to test type conversion.
// type Underlier interface {
// GetUnderlying() <underlying-type>
// }
//
// Logger grants access to the sink to enable type assertions like this:
// func DoSomethingWithImpl(log logr.Logger) {
// if underlier, ok := log.GetSink()(impl.Underlier) {
// implLogger := underlier.GetUnderlying()
// ...
// }
// }
//
// Custom `With*` functions can be implemented by copying the complete
// Logger struct and replacing the sink in the copy:
// // WithFooBar changes the foobar parameter in the log sink and returns a
// // new logger with that modified sink. It does nothing for loggers where
// // the sink doesn't support that parameter.
// func WithFoobar(log logr.Logger, foobar int) logr.Logger {
// if foobarLogSink, ok := log.GetSink()(FoobarSink); ok {
// log = log.WithSink(foobarLogSink.WithFooBar(foobar))
// }
// return log
// }
//
// Don't use New to construct a new Logger with a LogSink retrieved from an
// existing Logger. Source code attribution might not work correctly and
// unexported fields in Logger get lost.
//
// Beware that the same LogSink instance may be shared by different logger
// instances. Calling functions that modify the LogSink will affect all of
// those.
package logr
import (
"context"
)
// New returns a new Logger instance. This is primarily used by libraries
// implementing LogSink, rather than end users.
func New(sink LogSink) Logger {
logger := Logger{}
logger.setSink(sink)
sink.Init(runtimeInfo)
return logger
}
// setSink stores the sink and updates any related fields. It mutates the
// logger and thus is only safe to use for loggers that are not currently being
// used concurrently.
func (l *Logger) setSink(sink LogSink) {
l.sink = sink
}
// GetSink returns the stored sink.
func (l Logger) GetSink() LogSink {
return l.sink
}
// WithSink returns a copy of the logger with the new sink.
func (l Logger) WithSink(sink LogSink) Logger {
l.setSink(sink)
return l
}
// Logger is an interface to an abstract logging implementation. This is a
// concrete type for performance reasons, but all the real work is passed on to
// a LogSink. Implementations of LogSink should provide their own constructors
// that return Logger, not LogSink.
//
// The underlying sink can be accessed through GetSink and be modified through
// WithSink. This enables the implementation of custom extensions (see "Break
// Glass" in the package documentation). Normally the sink should be used only
// indirectly.
type Logger struct {
sink LogSink
level int
}
// Enabled tests whether this Logger is enabled. For example, commandline
// flags might be used to set the logging verbosity and disable some info logs.
func (l Logger) Enabled() bool {
return l.sink.Enabled(l.level)
}
// Info logs a non-error message with the given key/value pairs as context.
//
// The msg argument should be used to add some constant description to the log
// line. The key/value pairs can then be used to add additional variable
// information. The key/value pairs must alternate string keys and arbitrary
// values.
func (l Logger) Info(msg string, keysAndValues ...interface{}) {
if l.Enabled() {
if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
withHelper.GetCallStackHelper()()
}
l.sink.Info(l.level, msg, keysAndValues...)
}
}
// Error logs an error, with the given message and key/value pairs as context.
// It functions similarly to Info, but may have unique behavior, and should be
// preferred for logging errors (see the package documentations for more
// information).
//
// The msg argument should be used to add context to any underlying error,
// while the err argument should be used to attach the actual error that
// triggered this log line, if present.
func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) {
if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
withHelper.GetCallStackHelper()()
}
l.sink.Error(err, msg, keysAndValues...)
}
// V returns a new Logger instance for a specific verbosity level, relative to
// this Logger. In other words, V-levels are additive. A higher verbosity
// level means a log message is less important. Negative V-levels are treated
// as 0.
func (l Logger) V(level int) Logger {
if level < 0 {
level = 0
}
l.level += level
return l
}
// WithValues returns a new Logger instance with additional key/value pairs.
// See Info for documentation on how key/value pairs work.
func (l Logger) WithValues(keysAndValues ...interface{}) Logger {
l.setSink(l.sink.WithValues(keysAndValues...))
return l
}
// WithName returns a new Logger instance with the specified name element added
// to the Logger's name. Successive calls with WithName append additional
// suffixes to the Logger's name. It's strongly recommended that name segments
// contain only letters, digits, and hyphens (see the package documentation for
// more information).
func (l Logger) WithName(name string) Logger {
l.setSink(l.sink.WithName(name))
return l
}
// WithCallDepth returns a Logger instance that offsets the call stack by the
// specified number of frames when logging call site information, if possible.
// This is useful for users who have helper functions between the "real" call
// site and the actual calls to Logger methods. If depth is 0 the attribution
// should be to the direct caller of this function. If depth is 1 the
// attribution should skip 1 call frame, and so on. Successive calls to this
// are additive.
//
// If the underlying log implementation supports a WithCallDepth(int) method,
// it will be called and the result returned. If the implementation does not
// support CallDepthLogSink, the original Logger will be returned.
//
// To skip one level, WithCallStackHelper() should be used instead of
// WithCallDepth(1) because it works with implementions that support the
// CallDepthLogSink and/or CallStackHelperLogSink interfaces.
func (l Logger) WithCallDepth(depth int) Logger {
if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
l.setSink(withCallDepth.WithCallDepth(depth))
}
return l
}
// WithCallStackHelper returns a new Logger instance that skips the direct
// caller when logging call site information, if possible. This is useful for
// users who have helper functions between the "real" call site and the actual
// calls to Logger methods and want to support loggers which depend on marking
// each individual helper function, like loggers based on testing.T.
//
// In addition to using that new logger instance, callers also must call the
// returned function.
//
// If the underlying log implementation supports a WithCallDepth(int) method,
// WithCallDepth(1) will be called to produce a new logger. If it supports a
// WithCallStackHelper() method, that will be also called. If the
// implementation does not support either of these, the original Logger will be
// returned.
func (l Logger) WithCallStackHelper() (func(), Logger) {
var helper func()
if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
l.setSink(withCallDepth.WithCallDepth(1))
}
if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
helper = withHelper.GetCallStackHelper()
} else {
helper = func() {}
}
return helper, l
}
// contextKey is how we find Loggers in a context.Context.
type contextKey struct{}
// FromContext returns a Logger from ctx or an error if no Logger is found.
func FromContext(ctx context.Context) (Logger, error) {
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
return v, nil
}
return Logger{}, notFoundError{}
}
// notFoundError exists to carry an IsNotFound method.
type notFoundError struct{}
func (notFoundError) Error() string {
return "no logr.Logger was present"
}
func (notFoundError) IsNotFound() bool {
return true
}
// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this
// returns a Logger that discards all log messages.
func FromContextOrDiscard(ctx context.Context) Logger {
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
return v
}
return Discard()
}
// NewContext returns a new Context, derived from ctx, which carries the
// provided Logger.
func NewContext(ctx context.Context, logger Logger) context.Context {
return context.WithValue(ctx, contextKey{}, logger)
}
// RuntimeInfo holds information that the logr "core" library knows which
// LogSinks might want to know.
type RuntimeInfo struct {
// CallDepth is the number of call frames the logr library adds between the
// end-user and the LogSink. LogSink implementations which choose to print
// the original logging site (e.g. file & line) should climb this many
// additional frames to find it.
CallDepth int
}
// runtimeInfo is a static global. It must not be changed at run time.
var runtimeInfo = RuntimeInfo{
CallDepth: 1,
}
// LogSink represents a logging implementation. End-users will generally not
// interact with this type.
type LogSink interface {
// Init receives optional information about the logr library for LogSink
// implementations that need it.
Init(info RuntimeInfo)
// Enabled tests whether this LogSink is enabled at the specified V-level.
// For example, commandline flags might be used to set the logging
// verbosity and disable some info logs.
Enabled(level int) bool
// Info logs a non-error message with the given key/value pairs as context.
// The level argument is provided for optional logging. This method will
// only be called when Enabled(level) is true. See Logger.Info for more
// details.
Info(level int, msg string, keysAndValues ...interface{})
// Error logs an error, with the given message and key/value pairs as
// context. See Logger.Error for more details.
Error(err error, msg string, keysAndValues ...interface{})
// WithValues returns a new LogSink with additional key/value pairs. See
// Logger.WithValues for more details.
WithValues(keysAndValues ...interface{}) LogSink
// WithName returns a new LogSink with the specified name appended. See
// Logger.WithName for more details.
WithName(name string) LogSink
}
// CallDepthLogSink represents a Logger that knows how to climb the call stack
// to identify the original call site and can offset the depth by a specified
// number of frames. This is useful for users who have helper functions
// between the "real" call site and the actual calls to Logger methods.
// Implementations that log information about the call site (such as file,
// function, or line) would otherwise log information about the intermediate
// helper functions.
//
// This is an optional interface and implementations are not required to
// support it.
type CallDepthLogSink interface {
// WithCallDepth returns a LogSink that will offset the call
// stack by the specified number of frames when logging call
// site information.
//
// If depth is 0, the LogSink should skip exactly the number
// of call frames defined in RuntimeInfo.CallDepth when Info
// or Error are called, i.e. the attribution should be to the
// direct caller of Logger.Info or Logger.Error.
//
// If depth is 1 the attribution should skip 1 call frame, and so on.
// Successive calls to this are additive.
WithCallDepth(depth int) LogSink
}
// CallStackHelperLogSink represents a Logger that knows how to climb
// the call stack to identify the original call site and can skip
// intermediate helper functions if they mark themselves as
// helper. Go's testing package uses that approach.
//
// This is useful for users who have helper functions between the
// "real" call site and the actual calls to Logger methods.
// Implementations that log information about the call site (such as
// file, function, or line) would otherwise log information about the
// intermediate helper functions.
//
// This is an optional interface and implementations are not required
// to support it. Implementations that choose to support this must not
// simply implement it as WithCallDepth(1), because
// Logger.WithCallStackHelper will call both methods if they are
// present. This should only be implemented for LogSinks that actually
// need it, as with testing.T.
type CallStackHelperLogSink interface {
// GetCallStackHelper returns a function that must be called
// to mark the direct caller as helper function when logging
// call site information.
GetCallStackHelper() func()
}

View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,4 +0,0 @@
# Minimal Go logging using logr and Go's standard library
This package implements the [logr interface](https://github.com/go-logr/logr)
in terms of Go's standard log package(https://godoc.org/std/log).

View File

@@ -1,170 +0,0 @@
/*
Copyright 2019 The logr Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package stdr implements github.com/go-logr/logr.Logger in terms of
// Go's standard log package.
package stdr
import (
"log"
"os"
"github.com/go-logr/logr"
"github.com/go-logr/logr/funcr"
)
// The global verbosity level. See SetVerbosity().
var globalVerbosity int
// SetVerbosity sets the global level against which all info logs will be
// compared. If this is greater than or equal to the "V" of the logger, the
// message will be logged. A higher value here means more logs will be written.
// The previous verbosity value is returned. This is not concurrent-safe -
// callers must be sure to call it from only one goroutine.
func SetVerbosity(v int) int {
old := globalVerbosity
globalVerbosity = v
return old
}
// New returns a logr.Logger which is implemented by Go's standard log package,
// or something like it. If std is nil, this will use a default logger
// instead.
//
// Example: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)))
func New(std StdLogger) logr.Logger {
return NewWithOptions(std, Options{})
}
// NewWithOptions returns a logr.Logger which is implemented by Go's standard
// log package, or something like it. See New for details.
func NewWithOptions(std StdLogger, opts Options) logr.Logger {
if std == nil {
// Go's log.Default() is only available in 1.16 and higher.
std = log.New(os.Stderr, "", log.LstdFlags)
}
if opts.Depth < 0 {
opts.Depth = 0
}
fopts := funcr.Options{
LogCaller: funcr.MessageClass(opts.LogCaller),
}
sl := &logger{
Formatter: funcr.NewFormatter(fopts),
std: std,
}
// For skipping our own logger.Info/Error.
sl.Formatter.AddCallDepth(1 + opts.Depth)
return logr.New(sl)
}
// Options carries parameters which influence the way logs are generated.
type Options struct {
// Depth biases the assumed number of call frames to the "true" caller.
// This is useful when the calling code calls a function which then calls
// stdr (e.g. a logging shim to another API). Values less than zero will
// be treated as zero.
Depth int
// LogCaller tells stdr to add a "caller" key to some or all log lines.
// Go's log package has options to log this natively, too.
LogCaller MessageClass
// TODO: add an option to log the date/time
}
// MessageClass indicates which category or categories of messages to consider.
type MessageClass int
const (
// None ignores all message classes.
None MessageClass = iota
// All considers all message classes.
All
// Info only considers info messages.
Info
// Error only considers error messages.
Error
)
// StdLogger is the subset of the Go stdlib log.Logger API that is needed for
// this adapter.
type StdLogger interface {
// Output is the same as log.Output and log.Logger.Output.
Output(calldepth int, logline string) error
}
type logger struct {
funcr.Formatter
std StdLogger
}
var _ logr.LogSink = &logger{}
var _ logr.CallDepthLogSink = &logger{}
func (l logger) Enabled(level int) bool {
return globalVerbosity >= level
}
func (l logger) Info(level int, msg string, kvList ...interface{}) {
prefix, args := l.FormatInfo(level, msg, kvList)
if prefix != "" {
args = prefix + ": " + args
}
_ = l.std.Output(l.Formatter.GetDepth()+1, args)
}
func (l logger) Error(err error, msg string, kvList ...interface{}) {
prefix, args := l.FormatError(err, msg, kvList)
if prefix != "" {
args = prefix + ": " + args
}
_ = l.std.Output(l.Formatter.GetDepth()+1, args)
}
func (l logger) WithName(name string) logr.LogSink {
l.Formatter.AddName(name)
return &l
}
func (l logger) WithValues(kvList ...interface{}) logr.LogSink {
l.Formatter.AddValues(kvList)
return &l
}
func (l logger) WithCallDepth(depth int) logr.LogSink {
l.Formatter.AddCallDepth(depth)
return &l
}
// Underlier exposes access to the underlying logging implementation. Since
// callers only have a logr.Logger, they have to know which implementation is
// in use, so this interface is less of an abstraction and more of way to test
// type conversion.
type Underlier interface {
GetUnderlying() StdLogger
}
// GetUnderlying returns the StdLogger underneath this logger. Since StdLogger
// is itself an interface, the result may or may not be a Go log.Logger.
func (l logger) GetUnderlying() StdLogger {
return l.std
}

View File

@@ -13,7 +13,6 @@ import (
"strings"
"sync"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/runtime/protoimpl"
@@ -63,7 +62,14 @@ func FileDescriptor(s filePath) fileDescGZIP {
// Find the descriptor in the v2 registry.
var b []byte
if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil {
b, _ = Marshal(protodesc.ToFileDescriptorProto(fd))
if fd, ok := fd.(interface{ ProtoLegacyRawDesc() []byte }); ok {
b = fd.ProtoLegacyRawDesc()
} else {
// TODO: Use protodesc.ToFileDescriptorProto to construct
// a descriptorpb.FileDescriptorProto and marshal it.
// However, doing so causes the proto package to have a dependency
// on descriptorpb, leading to cyclic dependency issues.
}
}
// Locally cache the raw descriptor form for the file.

View File

@@ -19,8 +19,6 @@ const urlPrefix = "type.googleapis.com/"
// AnyMessageName returns the message name contained in an anypb.Any message.
// Most type assertions should use the Is function instead.
//
// Deprecated: Call the any.MessageName method instead.
func AnyMessageName(any *anypb.Any) (string, error) {
name, err := anyMessageName(any)
return string(name), err
@@ -40,8 +38,6 @@ func anyMessageName(any *anypb.Any) (protoreflect.FullName, error) {
}
// MarshalAny marshals the given message m into an anypb.Any message.
//
// Deprecated: Call the anypb.New function instead.
func MarshalAny(m proto.Message) (*anypb.Any, error) {
switch dm := m.(type) {
case DynamicAny:
@@ -62,9 +58,6 @@ func MarshalAny(m proto.Message) (*anypb.Any, error) {
// Empty returns a new message of the type specified in an anypb.Any message.
// It returns protoregistry.NotFound if the corresponding message type could not
// be resolved in the global registry.
//
// Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead
// to resolve the message name and create a new instance of it.
func Empty(any *anypb.Any) (proto.Message, error) {
name, err := anyMessageName(any)
if err != nil {
@@ -83,8 +76,6 @@ func Empty(any *anypb.Any) (proto.Message, error) {
//
// The target message m may be a *DynamicAny message. If the underlying message
// type could not be resolved, then this returns protoregistry.NotFound.
//
// Deprecated: Call the any.UnmarshalTo method instead.
func UnmarshalAny(any *anypb.Any, m proto.Message) error {
if dm, ok := m.(*DynamicAny); ok {
if dm.Message == nil {
@@ -109,8 +100,6 @@ func UnmarshalAny(any *anypb.Any, m proto.Message) error {
}
// Is reports whether the Any message contains a message of the specified type.
//
// Deprecated: Call the any.MessageIs method instead.
func Is(any *anypb.Any, m proto.Message) bool {
if any == nil || m == nil {
return false
@@ -130,9 +119,6 @@ func Is(any *anypb.Any, m proto.Message) bool {
// var x ptypes.DynamicAny
// if err := ptypes.UnmarshalAny(a, &x); err != nil { ... }
// fmt.Printf("unmarshaled message: %v", x.Message)
//
// Deprecated: Use the any.UnmarshalNew method instead to unmarshal
// the any message contents into a new instance of the underlying message.
type DynamicAny struct{ proto.Message }
func (m DynamicAny) String() string {

View File

@@ -3,8 +3,4 @@
// license that can be found in the LICENSE file.
// Package ptypes provides functionality for interacting with well-known types.
//
// Deprecated: Well-known types have specialized functionality directly
// injected into the generated packages for each message type.
// See the deprecation notice for each function for the suggested alternative.
package ptypes

View File

@@ -21,8 +21,6 @@ const (
// Duration converts a durationpb.Duration to a time.Duration.
// Duration returns an error if dur is invalid or overflows a time.Duration.
//
// Deprecated: Call the dur.AsDuration and dur.CheckValid methods instead.
func Duration(dur *durationpb.Duration) (time.Duration, error) {
if err := validateDuration(dur); err != nil {
return 0, err
@@ -41,8 +39,6 @@ func Duration(dur *durationpb.Duration) (time.Duration, error) {
}
// DurationProto converts a time.Duration to a durationpb.Duration.
//
// Deprecated: Call the durationpb.New function instead.
func DurationProto(d time.Duration) *durationpb.Duration {
nanos := d.Nanoseconds()
secs := nanos / 1e9

View File

@@ -33,8 +33,6 @@ const (
//
// A nil Timestamp returns an error. The first return value in that case is
// undefined.
//
// Deprecated: Call the ts.AsTime and ts.CheckValid methods instead.
func Timestamp(ts *timestamppb.Timestamp) (time.Time, error) {
// Don't return the zero value on error, because corresponds to a valid
// timestamp. Instead return whatever time.Unix gives us.
@@ -48,8 +46,6 @@ func Timestamp(ts *timestamppb.Timestamp) (time.Time, error) {
}
// TimestampNow returns a google.protobuf.Timestamp for the current time.
//
// Deprecated: Call the timestamppb.Now function instead.
func TimestampNow() *timestamppb.Timestamp {
ts, err := TimestampProto(time.Now())
if err != nil {
@@ -60,8 +56,6 @@ func TimestampNow() *timestamppb.Timestamp {
// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto.
// It returns an error if the resulting Timestamp is invalid.
//
// Deprecated: Call the timestamppb.New function instead.
func TimestampProto(t time.Time) (*timestamppb.Timestamp, error) {
ts := &timestamppb.Timestamp{
Seconds: t.Unix(),
@@ -75,9 +69,6 @@ func TimestampProto(t time.Time) (*timestamppb.Timestamp, error) {
// TimestampString returns the RFC 3339 string for valid Timestamps.
// For invalid Timestamps, it returns an error message in parentheses.
//
// Deprecated: Call the ts.AsTime method instead,
// followed by a call to the Format method on the time.Time value.
func TimestampString(ts *timestamppb.Timestamp) string {
t, err := Timestamp(ts)
if err != nil {

View File

@@ -1,9 +0,0 @@
language: go
go:
- 1.4.3
- 1.5.3
- tip
script:
- go test -v ./...

Some files were not shown because too many files have changed in this diff Show More