From 2f3c98b8d370f6b3a877fb17a0785356eebe56d2 Mon Sep 17 00:00:00 2001 From: wweir Date: Sun, 27 Jan 2019 08:35:12 +0800 Subject: [PATCH] Clean code --- conf/conf.go | 16 +----- go.mod | 17 +----- go.sum | 57 ++----------------- proxy/quic/client.go | 3 +- proxy/quic/util.go | 19 ------- util/http_ping.go | 21 +++---- util/{rev_sec_sort.go => rev_sec.go} | 18 ++++++ .../{rev_sec_sort_test.go => rev_sec_test.go} | 0 util/with_timeout.go | 23 ++++++++ 9 files changed, 62 insertions(+), 112 deletions(-) rename util/{rev_sec_sort.go => rev_sec.go} (65%) rename util/{rev_sec_sort_test.go => rev_sec_test.go} (100%) create mode 100644 util/with_timeout.go diff --git a/conf/conf.go b/conf/conf.go index 3a6bbc3..c0b6566 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -6,7 +6,6 @@ import ( "os" "os/exec" "path/filepath" - "sort" "strconv" "sync" "time" @@ -131,20 +130,9 @@ func watchConfigFile() { func AddSuggest(domain string) { mu.Lock() defer mu.Unlock() - { // uinque key - Conf.Suggestions = append(Conf.Suggestions, domain) - sort.Sort(util.NewReverseSecSlice(Conf.Suggestions)) - last := "" - suggestions := make([]string, 0, len(Conf.Suggestions)) - for _, suggestion := range Conf.Suggestions { - if suggestion != last { - suggestions = append(suggestions, suggestion) - } - last = suggestion - } - Conf.Suggestions = suggestions - } + Conf.Suggestions = append(Conf.Suggestions, domain) + Conf.Suggestions = util.NewReverseSecSlice(Conf.Suggestions).Sort().Uniq() // safe write f, err := os.OpenFile(Conf.ConfigFile+"~", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) diff --git a/go.mod b/go.mod index ce3da1d..58ec5bb 100644 --- a/go.mod +++ b/go.mod @@ -1,27 +1,18 @@ module github.com/wweir/sower require ( - github.com/BurntSushi/toml v0.3.1 // indirect github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 // indirect github.com/cheekybits/genny v1.0.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b - github.com/golang/mock v1.2.0 // indirect - github.com/guregu/null v3.4.0+incompatible // indirect github.com/hashicorp/golang-lru v0.5.0 // indirect github.com/klauspost/cpuid v1.2.0 // indirect github.com/klauspost/reedsolomon v1.8.0 // indirect - github.com/lib/pq v1.0.0 // indirect github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f // indirect github.com/lucas-clemente/quic-go v0.10.1 github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced // indirect github.com/miekg/dns v1.1.3 - github.com/onsi/ginkgo v1.7.0 // indirect - github.com/onsi/gomega v1.4.3 // indirect github.com/pelletier/go-toml v1.2.0 github.com/pkg/errors v0.8.1 - github.com/satori/go.uuid v1.2.0 // indirect - github.com/stretchr/testify v1.3.0 // indirect github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect github.com/tjfoc/gmsm v1.0.1 // indirect @@ -29,9 +20,7 @@ require ( github.com/wweir/fsnotify v1.4.8 github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296 github.com/xtaci/kcp-go v5.0.7+incompatible - golang.org/x/crypto v0.0.0-20190122013713-64072686203f - golang.org/x/net v0.0.0-20190119204137-ed066c81e75e // indirect - golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect - golang.org/x/sys v0.0.0-20190122071731-054c452bb702 // indirect - gopkg.in/yaml.v2 v2.2.2 // indirect + golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b + golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect + golang.org/x/sys v0.0.0-20190124100055-b90733256f2e // indirect ) diff --git a/go.sum b/go.sum index 1b9e53c..fbbd5f6 100644 --- a/go.sum +++ b/go.sum @@ -1,32 +1,15 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 h1:x17NvoJaphEzay72TFej4OSSsgu3xRYBLkbIwdofS/4= github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/guregu/null v3.4.0+incompatible h1:a4mw37gBO7ypcBlTJeZGuMpSxxFTV9qFfFKgWxQSGaM= -github.com/guregu/null v3.4.0+incompatible/go.mod h1:ePGpQaN9cw0tj45IR5E5ehMvsFlLlQZAkkOXZurJ3NM= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/reedsolomon v1.8.0 h1:lvvOkvk64cE1EGbBIgFk7WSOOsI1GexpuLiT7zjab6g= github.com/klauspost/reedsolomon v1.8.0/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= -github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f h1:sSeNEkJrs+0F9TUau0CgWTTNEwF23HST3Eq0A+QIx+A= github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04= github.com/lucas-clemente/quic-go v0.10.1 h1:ipcMmYP9RT+b1YytOKGUY1qndxPGOczVEQkAVz3CZrs= @@ -35,22 +18,10 @@ github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cce github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58= github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM= github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM= @@ -65,25 +36,9 @@ github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296 h1:/HkUfg+ZMx/tNdnyJd github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296/go.mod h1:k7rjBGWoJ+JKwvfe8juAX0zgybjo/Yo3JGkca5f/06s= github.com/xtaci/kcp-go v5.0.7+incompatible h1:zs9tc8XRID0m+aetu3qPWZFyRt2UIMqbXIBgw+vcnlE= github.com/xtaci/kcp-go v5.0.7+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= -golang.org/x/crypto v0.0.0-20190122013713-64072686203f h1:u1CmMhe3a44hy8VIgpInORnI01UVaUYheqR7x9BxT3c= -golang.org/x/crypto v0.0.0-20190122013713-64072686203f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190119204137-ed066c81e75e h1:MDa3fSUp6MdYHouVmCCNz/zaH2a6CRcxY3VhT/K3C5Q= -golang.org/x/net v0.0.0-20190119204137-ed066c81e75e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190122071731-054c452bb702 h1:Lk4tbZFnlyPgV+sLgTw5yGfzrlOn9kx4vSombi2FFlY= -golang.org/x/sys v0.0.0-20190122071731-054c452bb702/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b h1:Elez2XeF2p9uyVj0yEUDqQ56NFcDtcBNkYP7yv8YbUE= +golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sys v0.0.0-20190124100055-b90733256f2e h1:3GIlrlVLfkoipSReOMNAgApI0ajnalyLa/EZHHca/XI= +golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/proxy/quic/client.go b/proxy/quic/client.go index 97a00fd..6b6000b 100644 --- a/proxy/quic/client.go +++ b/proxy/quic/client.go @@ -7,6 +7,7 @@ import ( quic "github.com/lucas-clemente/quic-go" "github.com/pkg/errors" + "github.com/wweir/sower/util" ) type client struct { @@ -40,7 +41,7 @@ func (c *client) Dial(server string) (net.Conn, error) { } var stream quic.Stream - if err := WithTimeout(func() (err error) { + if err := util.WithTimeout(func() (err error) { if stream, err = c.sess.OpenStream(); err != nil { c.sess = nil } diff --git a/proxy/quic/util.go b/proxy/quic/util.go index ce867d1..8d503d5 100644 --- a/proxy/quic/util.go +++ b/proxy/quic/util.go @@ -8,11 +8,9 @@ import ( "encoding/pem" "math/big" "net" - "time" "github.com/golang/glog" quic "github.com/lucas-clemente/quic-go" - "github.com/pkg/errors" ) type streamConn struct { @@ -48,20 +46,3 @@ func mockTlsPem() *tls.Config { } return &tls.Config{Certificates: []tls.Certificate{tlsCert}} } - -func WithTimeout(fn func() error, timeout time.Duration) error { - var okCh = make(chan struct{}) - var err error - - go func() { - err = fn() - close(okCh) - }() - - select { - case <-okCh: - return err - case <-time.After(timeout): - return errors.New("timeout: " + timeout.String()) - } -} diff --git a/util/http_ping.go b/util/http_ping.go index 866c5ac..ea89983 100644 --- a/util/http_ping.go +++ b/util/http_ping.go @@ -4,15 +4,12 @@ import ( "bytes" "crypto/tls" "encoding/binary" - "errors" "io" "net" - "strings" "time" ) -// HTTPPing try connect to a http server with domain though the http addr - +// HTTPPing try connect to a http(s) server with domain though the http addr func HTTPPing(tcpAddr, domain string, timeout time.Duration) <-chan error { errCh := make(chan error) go func() { @@ -28,13 +25,13 @@ func httpPing(tcpAddr, domain string, timeout time.Duration) error { } defer conn.Close() - idx := strings.Index(tcpAddr, ":") - if idx <= 0 { - return errors.New("tcp address port is needed") + host, port, err := net.SplitHostPort(tcpAddr) + if err != nil { + return err } var msg []byte - switch tcpAddr[idx:] { + switch port { case ":80": msg = []byte("TRACE / HTTP/1.1\r\nHost: " + domain + "\r\n\r\n") case ":443": @@ -50,10 +47,8 @@ func httpPing(tcpAddr, domain string, timeout time.Duration) error { // err -> io.EOF: no such domain or connection refused // err -> timeout: tcp package has been dropped _, err = conn.Read(make([]byte, 1)) - if err == io.EOF { - if tcpAddr[:idx] == domain { - return nil - } + if err == io.EOF && host == domain { + return nil } return err } @@ -119,7 +114,7 @@ func NewClientHelloSNIMsg(domain string) []byte { }, } - buf := bytes.NewBuffer(make([]byte, 0, length+71)) + buf := bytes.NewBuffer(make([]byte, 0, length+61)) binary.Write(buf, binary.BigEndian, msg) buf.WriteString(domain) return buf.Bytes() diff --git a/util/rev_sec_sort.go b/util/rev_sec.go similarity index 65% rename from util/rev_sec_sort.go rename to util/rev_sec.go index 424cb66..281159d 100644 --- a/util/rev_sec_sort.go +++ b/util/rev_sec.go @@ -13,6 +13,24 @@ func NewReverseSecSlice(a []string) *ReverseSecSlice { return &ReverseSecSlice{sort.StringSlice(a)} } +func (p *ReverseSecSlice) Sort() *ReverseSecSlice { + sort.Sort(p) + return p +} +func (p *ReverseSecSlice) Uniq() []string { + olds := []string(p.StringSlice) + + last := "" + strs := make([]string, 0, len(olds)) + for _, str := range olds { + if str != last { + strs = append(strs, str) + } + last = str + } + return strs +} + func (p *ReverseSecSlice) Less(i, j int) bool { secsI := strings.Split(p.StringSlice[i], ".") secsJ := strings.Split(p.StringSlice[j], ".") diff --git a/util/rev_sec_sort_test.go b/util/rev_sec_test.go similarity index 100% rename from util/rev_sec_sort_test.go rename to util/rev_sec_test.go diff --git a/util/with_timeout.go b/util/with_timeout.go new file mode 100644 index 0000000..3f202c0 --- /dev/null +++ b/util/with_timeout.go @@ -0,0 +1,23 @@ +package util + +import ( + "errors" + "time" +) + +func WithTimeout(fn func() error, timeout time.Duration) error { + var okCh = make(chan struct{}) + var err error + + go func() { + err = fn() + close(okCh) + }() + + select { + case <-okCh: + return err + case <-time.After(timeout): + return errors.New("timeout: " + timeout.String()) + } +}