summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorValery Piashchynski <[email protected]>2021-01-21 20:15:10 +0300
committerValery Piashchynski <[email protected]>2021-01-21 20:15:10 +0300
commit4d60db85d1c0bfeddffe1de3e28d3464949c5f6d (patch)
treeaf1598be614ea234f063815e0f6e7ef84c66812e /plugins
parent788e7b8d01874208504a8b56159b4de378d4a0bc (diff)
Allow https to listen on unix sockets
Diffstat (limited to 'plugins')
-rw-r--r--plugins/http/config/http.go34
-rw-r--r--plugins/http/config/ssl.go69
-rw-r--r--plugins/http/config/ssl_config_test.go116
-rw-r--r--plugins/http/plugin.go61
4 files changed, 227 insertions, 53 deletions
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