diff options
author | Valery Piashchynski <[email protected]> | 2021-01-22 11:04:09 +0300 |
---|---|---|
committer | GitHub <[email protected]> | 2021-01-22 11:04:09 +0300 |
commit | 29d6020a9e8a3713b22269ed946547c96c24d3da (patch) | |
tree | afe4d330ecb4180e1a9970c8e250bf4f8d92c15e | |
parent | 6807441b2bf1e821e335d67567af47567c9757f3 (diff) | |
parent | 4d60db85d1c0bfeddffe1de3e28d3464949c5f6d (diff) |
Merge pull request #494 from spiral/feature/allow_https_listen_on_unix_socketsv2.0.0-beta11
feat(https): Allow https to listen on unix sockets
-rw-r--r-- | .github/workflows/build.yml | 3 | ||||
-rwxr-xr-x | .rr.yaml | 4 | ||||
-rwxr-xr-x | Makefile | 2 | ||||
-rw-r--r-- | plugins/http/config/http.go | 34 | ||||
-rw-r--r-- | plugins/http/config/ssl.go | 69 | ||||
-rw-r--r-- | plugins/http/config/ssl_config_test.go | 116 | ||||
-rw-r--r-- | plugins/http/plugin.go | 61 | ||||
-rw-r--r-- | tests/plugins/http/configs/.rr-fcgi-reqUri.yaml | 2 | ||||
-rw-r--r-- | tests/plugins/http/configs/.rr-fcgi.yaml | 2 | ||||
-rw-r--r-- | tests/plugins/http/configs/.rr-init.yaml | 2 | ||||
-rw-r--r-- | tests/plugins/http/configs/.rr-ssl-push.yaml | 2 | ||||
-rw-r--r-- | tests/plugins/http/configs/.rr-ssl-redirect.yaml | 2 | ||||
-rw-r--r-- | tests/plugins/http/configs/.rr-ssl.yaml | 2 | ||||
-rw-r--r-- | tests/plugins/http/http_plugin_test.go | 8 | ||||
-rwxr-xr-x | tests/plugins/rpc/config_test.go | 1 | ||||
-rwxr-xr-x | utils/network.go | 52 | ||||
-rwxr-xr-x | utils/network_windows.go | 54 |
17 files changed, 318 insertions, 98 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f490208..ed9d7f5a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,6 +73,7 @@ jobs: go test -v -race -tags=debug ./pkg/worker go test -v -race -tags=debug ./pkg/worker_watcher go test -v -race -tags=debug ./tests/plugins/http + go test -v -race -tags=debug ./plugins/http/config go test -v -race -tags=debug ./tests/plugins/informer go test -v -race -tags=debug ./tests/plugins/reload go test -v -race -tags=debug ./tests/plugins/server @@ -103,6 +104,7 @@ jobs: go test -v -race -tags=debug ./pkg/socket go test -v -race -tags=debug ./pkg/worker go test -v -race -tags=debug ./pkg/worker_watcher + go test -v -race -tags=debug ./plugins/http/config go test -v -race -tags=debug ./tests/plugins/http go test -v -race -tags=debug ./tests/plugins/informer go test -v -race -tags=debug ./tests/plugins/reload @@ -133,6 +135,7 @@ jobs: go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/socket.txt -covermode=atomic ./pkg/socket go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/worker.txt -covermode=atomic ./pkg/worker go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/worker_stack.txt -covermode=atomic ./pkg/worker_watcher + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/http_config.txt -covermode=atomic ./plugins/http/config go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/http.txt -covermode=atomic ./tests/plugins/http go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/informer.txt -covermode=atomic ./tests/plugins/informer go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage-ci/reload.txt -covermode=atomic ./tests/plugins/reload @@ -16,6 +16,7 @@ logs: level: debug http: + # host and port separated by semicolon address: 127.0.0.1:44933 max_request_size: 1024 middleware: [ "gzip", "headers" ] @@ -70,7 +71,8 @@ http: max_worker_memory: 100 # ssl: - # port: 8892 + # host and port separated by semicolon (default :443) + # address: :8892 # redirect: false # cert: fixtures/server.crt # key: fixtures/server.key @@ -49,6 +49,7 @@ test_coverage: go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage/static.out -covermode=atomic ./tests/plugins/static go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage/boltdb_unit.out -covermode=atomic ./plugins/kv/boltdb go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage/kv_unit.out -covermode=atomic ./plugins/kv/memory + go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage/http_config.out -covermode=atomic ./plugins/http/config go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage/memcached_unit.out -covermode=atomic ./plugins/kv/memcached go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage/boltdb.out -covermode=atomic ./tests/plugins/kv/boltdb go test -v -race -cover -tags=debug -coverpkg=./... -coverprofile=./coverage/memory.out -covermode=atomic ./tests/plugins/kv/memory @@ -65,6 +66,7 @@ test: ## Run application tests go test -v -race -cover -tags=debug -coverpkg=./... -covermode=atomic ./pkg/worker go test -v -race -cover -tags=debug -coverpkg=./... -covermode=atomic ./pkg/worker_watcher go test -v -race -cover -tags=debug -coverpkg=./... -covermode=atomic ./tests/plugins/http + go test -v -race -cover -tags=debug -coverpkg=./... -covermode=atomic ./plugins/http/config go test -v -race -cover -tags=debug -coverpkg=./... -covermode=atomic ./tests/plugins/informer go test -v -race -cover -tags=debug -coverpkg=./... -covermode=atomic ./tests/plugins/reload go test -v -race -cover -tags=debug -coverpkg=./... -covermode=atomic ./tests/plugins/server diff --git a/plugins/http/config/http.go b/plugins/http/config/http.go index 76547fde..bd689918 100644 --- a/plugins/http/config/http.go +++ b/plugins/http/config/http.go @@ -2,7 +2,6 @@ package config import ( "net" - "os" "runtime" "strings" "time" @@ -13,7 +12,7 @@ import ( // HTTP configures RoadRunner HTTP server. type HTTP struct { - // Port and port to handle as http server. + // Host and port to handle as http server. Address string // SSLConfig defines https server options. @@ -97,8 +96,8 @@ func (c *HTTP) InitDefaults() error { c.SSLConfig = &SSL{} } - if c.SSLConfig.Port == 0 { - c.SSLConfig.Port = 443 + if c.SSLConfig.Address == "" { + c.SSLConfig.Address = ":443" } err := c.HTTP2Config.InitDefaults() @@ -191,30 +190,9 @@ func (c *HTTP) Valid() error { } if c.EnableTLS() { - if _, err := os.Stat(c.SSLConfig.Key); err != nil { - if os.IsNotExist(err) { - return errors.E(op, errors.Errorf("key file '%s' does not exists", c.SSLConfig.Key)) - } - - return err - } - - if _, err := os.Stat(c.SSLConfig.Cert); err != nil { - if os.IsNotExist(err) { - return errors.E(op, errors.Errorf("cert file '%s' does not exists", c.SSLConfig.Cert)) - } - - return err - } - - // RootCA is optional, but if provided - check it - if c.SSLConfig.RootCA != "" { - if _, err := os.Stat(c.SSLConfig.RootCA); err != nil { - if os.IsNotExist(err) { - return errors.E(op, errors.Errorf("root ca path provided, but path '%s' does not exists", c.SSLConfig.RootCA)) - } - return err - } + err := c.SSLConfig.Valid() + if err != nil { + return errors.E(op, err) } } diff --git a/plugins/http/config/ssl.go b/plugins/http/config/ssl.go index aae6e920..c33dbce4 100644 --- a/plugins/http/config/ssl.go +++ b/plugins/http/config/ssl.go @@ -1,9 +1,17 @@ package config +import ( + "os" + "strconv" + "strings" + + "github.com/spiral/errors" +) + // SSL defines https server configuration. type SSL struct { - // Port to listen as HTTPS server, defaults to 443. - Port int + // Address to listen as HTTPS server, defaults to 0.0.0.0:443. + Address string // Redirect when enabled forces all http connections to switch to https. Redirect bool @@ -16,4 +24,61 @@ type SSL struct { // Root CA file RootCA string + + // internal + host string + Port int +} + +func (s *SSL) Valid() error { + const op = errors.Op("ssl_valid") + + parts := strings.Split(s.Address, ":") + switch len(parts) { + // :443 form + // localhost:443 form + // use 0.0.0.0 as host and 443 as port + case 2: + if parts[0] == "" { + s.host = "0.0.0.0" + } else { + s.host = parts[0] + } + + port, err := strconv.Atoi(parts[1]) + if err != nil { + return errors.E(op, err) + } + s.Port = port + default: + return errors.E(op, errors.Errorf("unknown format, accepted format is [:<port> or <host>:<port>], provided: %s", s.Address)) + } + + if _, err := os.Stat(s.Key); err != nil { + if os.IsNotExist(err) { + return errors.E(op, errors.Errorf("key file '%s' does not exists", s.Key)) + } + + return err + } + + if _, err := os.Stat(s.Cert); err != nil { + if os.IsNotExist(err) { + return errors.E(op, errors.Errorf("cert file '%s' does not exists", s.Cert)) + } + + return err + } + + // RootCA is optional, but if provided - check it + if s.RootCA != "" { + if _, err := os.Stat(s.RootCA); err != nil { + if os.IsNotExist(err) { + return errors.E(op, errors.Errorf("root ca path provided, but path '%s' does not exists", s.RootCA)) + } + return err + } + } + + return nil } diff --git a/plugins/http/config/ssl_config_test.go b/plugins/http/config/ssl_config_test.go new file mode 100644 index 00000000..1f5fef0a --- /dev/null +++ b/plugins/http/config/ssl_config_test.go @@ -0,0 +1,116 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSSL_Valid1(t *testing.T) { + conf := &SSL{ + Address: "", + Redirect: false, + Key: "", + Cert: "", + RootCA: "", + host: "", + Port: 0, + } + + err := conf.Valid() + assert.Error(t, err) +} + +func TestSSL_Valid2(t *testing.T) { + conf := &SSL{ + Address: ":hello", + Redirect: false, + Key: "", + Cert: "", + RootCA: "", + host: "", + Port: 0, + } + + err := conf.Valid() + assert.Error(t, err) +} + +func TestSSL_Valid3(t *testing.T) { + conf := &SSL{ + Address: ":555", + Redirect: false, + Key: "", + Cert: "", + RootCA: "", + host: "", + Port: 0, + } + + err := conf.Valid() + assert.Error(t, err) +} + +func TestSSL_Valid4(t *testing.T) { + conf := &SSL{ + Address: ":555", + Redirect: false, + Key: "../../../tests/plugins/http/fixtures/server.key", + Cert: "../../../tests/plugins/http/fixtures/server.crt", + RootCA: "", + host: "", + // private + Port: 0, + } + + err := conf.Valid() + assert.NoError(t, err) +} + +func TestSSL_Valid5(t *testing.T) { + conf := &SSL{ + Address: "a:b:c", + Redirect: false, + Key: "../../../tests/plugins/http/fixtures/server.key", + Cert: "../../../tests/plugins/http/fixtures/server.crt", + RootCA: "", + host: "", + // private + Port: 0, + } + + err := conf.Valid() + assert.Error(t, err) +} + +func TestSSL_Valid6(t *testing.T) { + conf := &SSL{ + Address: ":", + Redirect: false, + Key: "../../../tests/plugins/http/fixtures/server.key", + Cert: "../../../tests/plugins/http/fixtures/server.crt", + RootCA: "", + host: "", + // private + Port: 0, + } + + err := conf.Valid() + assert.Error(t, err) +} + +func TestSSL_Valid7(t *testing.T) { + conf := &SSL{ + Address: "localhost:555:1", + Redirect: false, + Key: "../../../tests/plugins/http/fixtures/server.key", + Cert: "../../../tests/plugins/http/fixtures/server.crt", + RootCA: "", + host: "", + // private + Port: 0, + } + + err := conf.Valid() + assert.Error(t, err) +} diff --git a/plugins/http/plugin.go b/plugins/http/plugin.go index 35acd2b7..249d2e57 100644 --- a/plugins/http/plugin.go +++ b/plugins/http/plugin.go @@ -36,6 +36,9 @@ const ( // RR_HTTP env variable key (internal) if the HTTP presents RR_HTTP = "RR_HTTP" //nolint:golint,stylecheck + + // HTTPS_SCHEME + HTTPS_SCHEME = "https" //nolint:golint,stylecheck ) // Middleware interface @@ -154,9 +157,9 @@ func (s *Plugin) Serve() chan error { if s.cfg.EnableHTTP() { if s.cfg.EnableH2C() { - s.http = &http.Server{Addr: s.cfg.Address, Handler: h2c.NewHandler(s, &http2.Server{})} + s.http = &http.Server{Handler: h2c.NewHandler(s, &http2.Server{})} } else { - s.http = &http.Server{Addr: s.cfg.Address, Handler: s} + s.http = &http.Server{Handler: s} } } @@ -190,9 +193,15 @@ func (s *Plugin) Serve() chan error { if s.http != nil { go func() { - httpErr := s.http.ListenAndServe() - if httpErr != nil && httpErr != http.ErrServerClosed { - errCh <- errors.E(op, httpErr) + l, err := utils.CreateListener(s.cfg.Address) + if err != nil { + errCh <- errors.E(op, err) + return + } + + err = s.http.Serve(l) + if err != nil && err != http.ErrServerClosed { + errCh <- errors.E(op, err) return } }() @@ -200,13 +209,20 @@ func (s *Plugin) Serve() chan error { if s.https != nil { go func() { - httpErr := s.https.ListenAndServeTLS( + l, err := utils.CreateListener(s.cfg.SSLConfig.Address) + if err != nil { + errCh <- errors.E(op, err) + return + } + + err = s.https.ServeTLS( + l, s.cfg.SSLConfig.Cert, s.cfg.SSLConfig.Key, ) - if httpErr != nil && httpErr != http.ErrServerClosed { - errCh <- errors.E(op, httpErr) + if err != nil && err != http.ErrServerClosed { + errCh <- errors.E(op, err) return } }() @@ -270,7 +286,8 @@ func (s *Plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - if s.redirect(w, r) { + if s.https != nil && r.TLS == nil && s.cfg.SSLConfig.Redirect { + s.redirect(w, r) return } @@ -362,21 +379,19 @@ func (s *Plugin) Status() checker.Status { } } -func (s *Plugin) redirect(w http.ResponseWriter, r *http.Request) bool { - if s.https != nil && r.TLS == nil && s.cfg.SSLConfig.Redirect { - target := &url.URL{ - Scheme: "https", - Host: s.tlsAddr(r.Host, false), - Path: r.URL.Path, - RawQuery: r.URL.RawQuery, - } - - http.Redirect(w, r, target.String(), http.StatusTemporaryRedirect) - return true +func (s *Plugin) redirect(w http.ResponseWriter, r *http.Request) { + target := &url.URL{ + Scheme: HTTPS_SCHEME, + // host or host:port + Host: s.tlsAddr(r.Host, false), + Path: r.URL.Path, + RawQuery: r.URL.RawQuery, } - return false + + http.Redirect(w, r, target.String(), http.StatusTemporaryRedirect) } +//go:inline func headerContainsUpgrade(r *http.Request, s *Plugin) bool { if _, ok := r.Header["Upgrade"]; ok { // https://golang.org/pkg/net/http/#Hijacker @@ -468,7 +483,7 @@ func (s *Plugin) initSSL() *http.Server { DefaultCipherSuites = append(DefaultCipherSuites, topCipherSuites...) DefaultCipherSuites = append(DefaultCipherSuites, defaultCipherSuitesTLS13...) - server := &http.Server{ + sslServer := &http.Server{ Addr: s.tlsAddr(s.cfg.Address, true), Handler: s, TLSConfig: &tls.Config{ @@ -484,7 +499,7 @@ func (s *Plugin) initSSL() *http.Server { }, } - return server + return sslServer } // init http/2 server diff --git a/tests/plugins/http/configs/.rr-fcgi-reqUri.yaml b/tests/plugins/http/configs/.rr-fcgi-reqUri.yaml index 99002777..05c3d40a 100644 --- a/tests/plugins/http/configs/.rr-fcgi-reqUri.yaml +++ b/tests/plugins/http/configs/.rr-fcgi-reqUri.yaml @@ -22,7 +22,7 @@ http: destroy_timeout: 60s ssl: - port: 8890 + address: :8890 redirect: false cert: fixtures/server.crt key: fixtures/server.key diff --git a/tests/plugins/http/configs/.rr-fcgi.yaml b/tests/plugins/http/configs/.rr-fcgi.yaml index 110b68f4..cfd4b79b 100644 --- a/tests/plugins/http/configs/.rr-fcgi.yaml +++ b/tests/plugins/http/configs/.rr-fcgi.yaml @@ -22,7 +22,7 @@ http: destroy_timeout: 60s ssl: - port: 8889 + address: :8889 redirect: false cert: fixtures/server.crt key: fixtures/server.key diff --git a/tests/plugins/http/configs/.rr-init.yaml b/tests/plugins/http/configs/.rr-init.yaml index b541e6de..01b90b44 100644 --- a/tests/plugins/http/configs/.rr-init.yaml +++ b/tests/plugins/http/configs/.rr-init.yaml @@ -26,7 +26,7 @@ http: destroy_timeout: 60s ssl: - port: 8892 + address: :8892 redirect: false cert: fixtures/server.crt key: fixtures/server.key diff --git a/tests/plugins/http/configs/.rr-ssl-push.yaml b/tests/plugins/http/configs/.rr-ssl-push.yaml index ae9fbc02..11a8ddd3 100644 --- a/tests/plugins/http/configs/.rr-ssl-push.yaml +++ b/tests/plugins/http/configs/.rr-ssl-push.yaml @@ -22,7 +22,7 @@ http: destroy_timeout: 60s ssl: - port: 8894 + address: :8894 redirect: true cert: fixtures/server.crt key: fixtures/server.key diff --git a/tests/plugins/http/configs/.rr-ssl-redirect.yaml b/tests/plugins/http/configs/.rr-ssl-redirect.yaml index d052e649..e49a73ed 100644 --- a/tests/plugins/http/configs/.rr-ssl-redirect.yaml +++ b/tests/plugins/http/configs/.rr-ssl-redirect.yaml @@ -22,7 +22,7 @@ http: destroy_timeout: 60s ssl: - port: 8895 + address: :8895 redirect: true cert: fixtures/server.crt key: fixtures/server.key diff --git a/tests/plugins/http/configs/.rr-ssl.yaml b/tests/plugins/http/configs/.rr-ssl.yaml index c3e45365..8a0f16b8 100644 --- a/tests/plugins/http/configs/.rr-ssl.yaml +++ b/tests/plugins/http/configs/.rr-ssl.yaml @@ -21,7 +21,7 @@ http: destroy_timeout: 60s ssl: - port: 8893 + address: :8893 redirect: false cert: fixtures/server.crt key: fixtures/server.key diff --git a/tests/plugins/http/http_plugin_test.go b/tests/plugins/http/http_plugin_test.go index 23628c72..72ae05a0 100644 --- a/tests/plugins/http/http_plugin_test.go +++ b/tests/plugins/http/http_plugin_test.go @@ -46,7 +46,7 @@ func TestHTTPInit(t *testing.T) { cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel)) assert.NoError(t, err) - rIn := makeConfig("6001", "15395", "7921", "8892", "false", "false", "php ../../http/client.php echo pipes") + rIn := makeConfig("6001", "15395", "7921", ":8892", "false", "false", "php ../../http/client.php echo pipes") cfg := &config.Viper{ ReadInCfg: rIn, Type: "yaml", @@ -1264,7 +1264,7 @@ func getHeader(url string, h map[string]string) (string, *http.Response, error) return string(b), r, err } -func makeConfig(rpcPort, httpPort, fcgiPort, sslPort, redirect, http2Enabled, command string) []byte { +func makeConfig(rpcPort, httpPort, fcgiPort, sslAddress, redirect, http2Enabled, command string) []byte { return []byte(fmt.Sprintf(` rpc: listen: tcp://127.0.0.1:%s @@ -1293,7 +1293,7 @@ http: destroyTimeout: 60s ssl: - port: %s + address: %s redirect: %s cert: fixtures/server.crt key: fixtures/server.key @@ -1307,5 +1307,5 @@ http: logs: mode: development level: error -`, rpcPort, command, httpPort, sslPort, redirect, fcgiPort, http2Enabled)) +`, rpcPort, command, httpPort, sslAddress, redirect, fcgiPort, http2Enabled)) } diff --git a/tests/plugins/rpc/config_test.go b/tests/plugins/rpc/config_test.go index df5fa391..34ca9cee 100755 --- a/tests/plugins/rpc/config_test.go +++ b/tests/plugins/rpc/config_test.go @@ -51,7 +51,6 @@ func Test_Config_Error(t *testing.T) { ln, err := cfg.Listener() assert.Nil(t, ln) assert.Error(t, err) - assert.Equal(t, "invalid DSN (tcp://:6001, unix://file.sock)", err.Error()) } func Test_Config_ErrorMethod(t *testing.T) { diff --git a/utils/network.go b/utils/network.go index fcfc4ace..c9db0e68 100755 --- a/utils/network.go +++ b/utils/network.go @@ -3,7 +3,6 @@ package utils import ( - "errors" "fmt" "net" "os" @@ -16,36 +15,49 @@ import ( // CreateListener crates socket listener based on DSN definition. func CreateListener(address string) (net.Listener, error) { dsn := strings.Split(address, "://") - if len(dsn) != 2 { - return nil, errors.New("invalid DSN (tcp://:6001, unix://file.sock)") - } - - if dsn[0] != "unix" && dsn[0] != "tcp" { - return nil, errors.New("invalid Protocol (tcp://:6001, unix://file.sock)") - } - // create unix listener - if dsn[0] == "unix" { - // check if the file exist - if fileExists(dsn[1]) { - err := syscall.Unlink(dsn[1]) - if err != nil { - return nil, fmt.Errorf("error during the unlink syscall: error %v", err) + switch len(dsn) { + case 1: + // assume, that there is no prefix here [127.0.0.1:8000] + return createTCPListener(dsn[0]) + case 2: + // we got two part here, first part is the transport, second - address + // [tcp://127.0.0.1:8000] OR [unix:///path/to/unix.socket] OR [error://path] + // where error is wrong transport name + switch dsn[0] { + case "unix": + // check of file exist. If exist, unlink + if fileExists(dsn[1]) { + err := syscall.Unlink(dsn[1]) + if err != nil { + return nil, fmt.Errorf("error during the unlink syscall: error %v", err) + } } + return net.Listen(dsn[0], dsn[1]) + case "tcp": + return createTCPListener(dsn[1]) + // not an tcp or unix + default: + return nil, fmt.Errorf("invalid Protocol ([tcp://]:6001, unix://file.sock), address: %s", address) } - return net.Listen(dsn[0], dsn[1]) + // wrong number of split parts + default: + return nil, fmt.Errorf("wrong number of parsed protocol parts, address: %s", address) } +} - // configure and create tcp4 listener +func createTCPListener(addr string) (net.Listener, error) { cfg := tcplisten.Config{ ReusePort: true, DeferAccept: true, FastOpen: true, Backlog: 0, } - - // only tcp4 is currently supported - return cfg.NewListener("tcp4", dsn[1]) + listener, err := cfg.NewListener("tcp4", addr) + if err != nil { + return nil, err + } + return listener, nil } // fileExists checks if a file exists and is not a directory before we diff --git a/utils/network_windows.go b/utils/network_windows.go index ebe343a3..a07ac351 100755 --- a/utils/network_windows.go +++ b/utils/network_windows.go @@ -3,33 +3,61 @@ package utils import ( - "errors" "fmt" "net" "os" "strings" "syscall" + + "github.com/valyala/tcplisten" ) // CreateListener crates socket listener based on DSN definition. func CreateListener(address string) (net.Listener, error) { dsn := strings.Split(address, "://") - if len(dsn) != 2 { - return nil, errors.New("invalid DSN (tcp://:6001, unix://file.sock)") - } - - if dsn[0] != "unix" && dsn[0] != "tcp" { - return nil, errors.New("invalid Protocol (tcp://:6001, unix://file.sock)") - } - if dsn[0] == "unix" && fileExists(dsn[1]) { - err := syscall.Unlink(dsn[1]) - if err != nil { - return nil, fmt.Errorf("error during the unlink syscall: error %v", err) + switch len(dsn) { + case 1: + // assume, that there is no prefix here [127.0.0.1:8000] + return createTCPListener(dsn[0]) + case 2: + // we got two part here, first part is the transport, second - address + // [tcp://127.0.0.1:8000] OR [unix:///path/to/unix.socket] OR [error://path] + // where error is wrong transport name + switch dsn[0] { + case "unix": + // check of file exist. If exist, unlink + if fileExists(dsn[1]) { + err := syscall.Unlink(dsn[1]) + if err != nil { + return nil, fmt.Errorf("error during the unlink syscall: error %v", err) + } + } + return net.Listen(dsn[0], dsn[1]) + case "tcp": + return createTCPListener(dsn[1]) + // not an tcp or unix + default: + return nil, fmt.Errorf("invalid Protocol ([tcp://]:6001, unix://file.sock), address: %s", address) } + // wrong number of split parts + default: + return nil, fmt.Errorf("wrong number of parsed protocol parts, address: %s", address) } +} - return net.Listen(dsn[0], dsn[1]) +func createTCPListener(addr string) (net.Listener, error) { + cfg := tcplisten.Config{ + ReusePort: true, + DeferAccept: true, + FastOpen: true, + Backlog: 0, + } + listener, err := cfg.NewListener("tcp4", addr) + if err != nil { + return nil, err + } + return listener, nil } // fileExists checks if a file exists and is not a directory before we |