summaryrefslogtreecommitdiff
path: root/plugins/http/config
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/http/config')
-rw-r--r--plugins/http/config/fcgi.go7
-rw-r--r--plugins/http/config/http.go180
-rw-r--r--plugins/http/config/http2.go28
-rw-r--r--plugins/http/config/ip.go26
-rw-r--r--plugins/http/config/ssl.go84
-rw-r--r--plugins/http/config/ssl_config_test.go116
-rw-r--r--plugins/http/config/uploads_config.go46
7 files changed, 487 insertions, 0 deletions
diff --git a/plugins/http/config/fcgi.go b/plugins/http/config/fcgi.go
new file mode 100644
index 00000000..3d4acbe1
--- /dev/null
+++ b/plugins/http/config/fcgi.go
@@ -0,0 +1,7 @@
+package config
+
+// FCGI for FastCGI server.
+type FCGI struct {
+ // Address and port to handle as http server.
+ Address string
+}
diff --git a/plugins/http/config/http.go b/plugins/http/config/http.go
new file mode 100644
index 00000000..bfbc1af6
--- /dev/null
+++ b/plugins/http/config/http.go
@@ -0,0 +1,180 @@
+package config
+
+import (
+ "net"
+ "runtime"
+ "strings"
+ "time"
+
+ "github.com/spiral/errors"
+ poolImpl "github.com/spiral/roadrunner/v2/pkg/pool"
+)
+
+// HTTP configures RoadRunner HTTP server.
+type HTTP struct {
+ // Host and port to handle as http server.
+ Address string
+
+ // SSLConfig defines https server options.
+ SSLConfig *SSL `mapstructure:"ssl"`
+
+ // FCGIConfig configuration. You can use FastCGI without HTTP server.
+ FCGIConfig *FCGI `mapstructure:"fcgi"`
+
+ // HTTP2Config configuration
+ HTTP2Config *HTTP2 `mapstructure:"http2"`
+
+ // MaxRequestSize specified max size for payload body in megabytes, set 0 to unlimited.
+ MaxRequestSize uint64 `mapstructure:"max_request_size"`
+
+ // TrustedSubnets declare IP subnets which are allowed to set ip using X-Real-Ip and X-Forwarded-For
+ TrustedSubnets []string `mapstructure:"trusted_subnets"`
+
+ // Uploads configures uploads configuration.
+ Uploads *Uploads `mapstructure:"uploads"`
+
+ // Pool configures worker pool.
+ Pool *poolImpl.Config `mapstructure:"pool"`
+
+ // Env is environment variables passed to the http pool
+ Env map[string]string
+
+ // List of the middleware names (order will be preserved)
+ Middleware []string
+
+ // slice of net.IPNet
+ Cidrs Cidrs
+}
+
+// EnableHTTP is true when http server must run.
+func (c *HTTP) EnableHTTP() bool {
+ return c.Address != ""
+}
+
+// EnableTLS returns true if pool must listen TLS connections.
+func (c *HTTP) EnableTLS() bool {
+ return c.SSLConfig.Key != "" || c.SSLConfig.Cert != "" || c.SSLConfig.RootCA != ""
+}
+
+// EnableH2C when HTTP/2 extension must be enabled on TCP.
+func (c *HTTP) EnableH2C() bool {
+ return c.HTTP2Config.H2C
+}
+
+// EnableFCGI is true when FastCGI server must be enabled.
+func (c *HTTP) EnableFCGI() bool {
+ return c.FCGIConfig.Address != ""
+}
+
+// InitDefaults must populate HTTP values using given HTTP source. Must return error if HTTP is not valid.
+func (c *HTTP) InitDefaults() error {
+ if c.Pool == nil {
+ // default pool
+ c.Pool = &poolImpl.Config{
+ Debug: false,
+ NumWorkers: uint64(runtime.NumCPU()),
+ MaxJobs: 1000,
+ AllocateTimeout: time.Second * 60,
+ DestroyTimeout: time.Second * 60,
+ Supervisor: nil,
+ }
+ }
+
+ if c.HTTP2Config == nil {
+ c.HTTP2Config = &HTTP2{}
+ }
+
+ if c.FCGIConfig == nil {
+ c.FCGIConfig = &FCGI{}
+ }
+
+ if c.Uploads == nil {
+ c.Uploads = &Uploads{}
+ }
+
+ if c.SSLConfig == nil {
+ c.SSLConfig = &SSL{}
+ }
+
+ if c.SSLConfig.Address == "" {
+ c.SSLConfig.Address = ":443"
+ }
+
+ err := c.HTTP2Config.InitDefaults()
+ if err != nil {
+ return err
+ }
+ err = c.Uploads.InitDefaults()
+ if err != nil {
+ return err
+ }
+
+ if c.TrustedSubnets == nil {
+ // @see https://en.wikipedia.org/wiki/Reserved_IP_addresses
+ c.TrustedSubnets = []string{
+ "10.0.0.0/8",
+ "127.0.0.0/8",
+ "172.16.0.0/12",
+ "192.168.0.0/16",
+ "::1/128",
+ "fc00::/7",
+ "fe80::/10",
+ }
+ }
+
+ cidrs, err := ParseCIDRs(c.TrustedSubnets)
+ if err != nil {
+ return err
+ }
+ c.Cidrs = cidrs
+
+ return c.Valid()
+}
+
+// ParseCIDRs parse IPNet addresses and return slice of its
+func ParseCIDRs(subnets []string) (Cidrs, error) {
+ c := make(Cidrs, 0, len(subnets))
+ for _, cidr := range subnets {
+ _, cr, err := net.ParseCIDR(cidr)
+ if err != nil {
+ return nil, err
+ }
+
+ c = append(c, cr)
+ }
+
+ return c, nil
+}
+
+// Valid validates the configuration.
+func (c *HTTP) Valid() error {
+ const op = errors.Op("validation")
+ if c.Uploads == nil {
+ return errors.E(op, errors.Str("malformed uploads config"))
+ }
+
+ if c.HTTP2Config == nil {
+ return errors.E(op, errors.Str("malformed http2 config"))
+ }
+
+ if c.Pool == nil {
+ return errors.E(op, "malformed pool config")
+ }
+
+ if !c.EnableHTTP() && !c.EnableTLS() && !c.EnableFCGI() {
+ return errors.E(op, errors.Str("unable to run http service, no method has been specified (http, https, http/2 or FastCGI)"))
+ }
+
+ if c.Address != "" && !strings.Contains(c.Address, ":") {
+ return errors.E(op, errors.Str("malformed http server address"))
+ }
+
+ if c.EnableTLS() {
+ err := c.SSLConfig.Valid()
+ if err != nil {
+ return errors.E(op, err)
+ }
+ }
+
+ return nil
+}
diff --git a/plugins/http/config/http2.go b/plugins/http/config/http2.go
new file mode 100644
index 00000000..b1e109e9
--- /dev/null
+++ b/plugins/http/config/http2.go
@@ -0,0 +1,28 @@
+package config
+
+// HTTP2 HTTP/2 server customizations.
+type HTTP2 struct {
+ // h2cHandler is a Handler which implements h2c by hijacking the HTTP/1 traffic
+ // that should be h2c traffic. There are two ways to begin a h2c connection
+ // (RFC 7540 Section 3.2 and 3.4): (1) Starting with Prior Knowledge - this
+ // works by starting an h2c connection with a string of bytes that is valid
+ // HTTP/1, but unlikely to occur in practice and (2) Upgrading from HTTP/1 to
+ // h2c - this works by using the HTTP/1 Upgrade header to request an upgrade to
+ // h2c. When either of those situations occur we hijack the HTTP/1 connection,
+ // convert it to a HTTP/2 connection and pass the net.Conn to http2.ServeConn.
+
+ // H2C enables HTTP/2 over TCP
+ H2C bool
+
+ // MaxConcurrentStreams defaults to 128.
+ MaxConcurrentStreams uint32 `mapstructure:"max_concurrent_streams"`
+}
+
+// InitDefaults sets default values for HTTP/2 configuration.
+func (cfg *HTTP2) InitDefaults() error {
+ if cfg.MaxConcurrentStreams == 0 {
+ cfg.MaxConcurrentStreams = 128
+ }
+
+ return nil
+}
diff --git a/plugins/http/config/ip.go b/plugins/http/config/ip.go
new file mode 100644
index 00000000..c4981f74
--- /dev/null
+++ b/plugins/http/config/ip.go
@@ -0,0 +1,26 @@
+package config
+
+import "net"
+
+// Cidrs is a slice of IPNet addresses
+type Cidrs []*net.IPNet
+
+// IsTrusted checks if the ip address exists in the provided in the config addresses
+func (c *Cidrs) IsTrusted(ip string) bool {
+ if len(*c) == 0 {
+ return false
+ }
+
+ i := net.ParseIP(ip)
+ if i == nil {
+ return false
+ }
+
+ for _, cird := range *c {
+ if cird.Contains(i) {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/plugins/http/config/ssl.go b/plugins/http/config/ssl.go
new file mode 100644
index 00000000..eb2b72b5
--- /dev/null
+++ b/plugins/http/config/ssl.go
@@ -0,0 +1,84 @@
+package config
+
+import (
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/spiral/errors"
+)
+
+// SSL defines https server configuration.
+type SSL struct {
+ // 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
+
+ // Key defined private server key.
+ Key string
+
+ // Cert is https certificate.
+ Cert string
+
+ // Root CA file
+ RootCA string `mapstructure:"root_ca"`
+
+ // 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/config/uploads_config.go b/plugins/http/config/uploads_config.go
new file mode 100644
index 00000000..5edb0ab7
--- /dev/null
+++ b/plugins/http/config/uploads_config.go
@@ -0,0 +1,46 @@
+package config
+
+import (
+ "os"
+ "path"
+ "strings"
+)
+
+// Uploads describes file location and controls access to them.
+type Uploads struct {
+ // Dir contains name of directory to control access to.
+ Dir string
+
+ // Forbid specifies list of file extensions which are forbidden for access.
+ // Example: .php, .exe, .bat, .htaccess and etc.
+ Forbid []string
+}
+
+// InitDefaults sets missing values to their default values.
+func (cfg *Uploads) InitDefaults() error {
+ cfg.Forbid = []string{".php", ".exe", ".bat"}
+ cfg.Dir = os.TempDir()
+ return nil
+}
+
+// TmpDir returns temporary directory.
+func (cfg *Uploads) TmpDir() string {
+ if cfg.Dir != "" {
+ return cfg.Dir
+ }
+
+ return os.TempDir()
+}
+
+// Forbids must return true if file extension is not allowed for the upload.
+func (cfg *Uploads) Forbids(filename string) bool {
+ ext := strings.ToLower(path.Ext(filename))
+
+ for _, v := range cfg.Forbid {
+ if ext == v {
+ return true
+ }
+ }
+
+ return false
+}