mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-19 01:05:37 +03:00
Created app package, and better TLS compatibility with HTTP/2
This commit is contained in:
parent
ea9607302a
commit
5f72b7438a
6 changed files with 242 additions and 135 deletions
76
app/app.go
Normal file
76
app/app.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// Package app holds application-global state to make it accessible
|
||||||
|
// by other packages in the application.
|
||||||
|
//
|
||||||
|
// This package differs from config in that the things in app aren't
|
||||||
|
// really related to server configuration.
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Program name
|
||||||
|
Name = "Caddy"
|
||||||
|
|
||||||
|
// Program version
|
||||||
|
Version = "0.6.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Servers is a list of all the currently-listening servers
|
||||||
|
Servers []*server.Server
|
||||||
|
|
||||||
|
// This mutex protects the Servers slice during changes
|
||||||
|
ServersMutex sync.Mutex
|
||||||
|
|
||||||
|
// Waiting on Wg will block until all listeners have shut down.
|
||||||
|
Wg sync.WaitGroup
|
||||||
|
|
||||||
|
// Whether HTTP2 is enabled or not
|
||||||
|
Http2 bool // TODO: temporary flag until http2 is standard
|
||||||
|
|
||||||
|
// Quiet mode hides non-error initialization output
|
||||||
|
Quiet bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetCPU parses string cpu and sets GOMAXPROCS
|
||||||
|
// according to its value. It accepts either
|
||||||
|
// a number (e.g. 3) or a percent (e.g. 50%).
|
||||||
|
func SetCPU(cpu string) error {
|
||||||
|
var numCPU int
|
||||||
|
|
||||||
|
availCPU := runtime.NumCPU()
|
||||||
|
|
||||||
|
if strings.HasSuffix(cpu, "%") {
|
||||||
|
// Percent
|
||||||
|
var percent float32
|
||||||
|
pctStr := cpu[:len(cpu)-1]
|
||||||
|
pctInt, err := strconv.Atoi(pctStr)
|
||||||
|
if err != nil || pctInt < 1 || pctInt > 100 {
|
||||||
|
return errors.New("Invalid CPU value: percentage must be between 1-100")
|
||||||
|
}
|
||||||
|
percent = float32(pctInt) / 100
|
||||||
|
numCPU = int(float32(availCPU) * percent)
|
||||||
|
} else {
|
||||||
|
// Number
|
||||||
|
num, err := strconv.Atoi(cpu)
|
||||||
|
if err != nil || num < 1 {
|
||||||
|
return errors.New("Invalid CPU value: provide a number or percent greater than 0")
|
||||||
|
}
|
||||||
|
numCPU = num
|
||||||
|
}
|
||||||
|
|
||||||
|
if numCPU > availCPU {
|
||||||
|
numCPU = availCPU
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.GOMAXPROCS(numCPU)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,9 +1,13 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/app"
|
||||||
"github.com/mholt/caddy/config/parse"
|
"github.com/mholt/caddy/config/parse"
|
||||||
"github.com/mholt/caddy/config/setup"
|
"github.com/mholt/caddy/config/setup"
|
||||||
"github.com/mholt/caddy/middleware"
|
"github.com/mholt/caddy/middleware"
|
||||||
|
@ -41,8 +45,8 @@ func Load(filename string, input io.Reader) ([]server.Config, error) {
|
||||||
Root: Root,
|
Root: Root,
|
||||||
Middleware: make(map[string][]middleware.Middleware),
|
Middleware: make(map[string][]middleware.Middleware),
|
||||||
ConfigFile: filename,
|
ConfigFile: filename,
|
||||||
AppName: AppName,
|
AppName: app.Name,
|
||||||
AppVersion: AppVersion,
|
AppVersion: app.Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is crucial that directives are executed in the proper order.
|
// It is crucial that directives are executed in the proper order.
|
||||||
|
@ -81,6 +85,46 @@ func Load(filename string, input io.Reader) ([]server.Config, error) {
|
||||||
return configs, nil
|
return configs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ArrangeBindings groups configurations by their bind address. For example,
|
||||||
|
// a server that should listen on localhost and another on 127.0.0.1 will
|
||||||
|
// be grouped into the same address: 127.0.0.1. It will return an error
|
||||||
|
// if the address lookup fails or if a TLS listener is configured on the
|
||||||
|
// same address as a plaintext HTTP listener. The return value is a map of
|
||||||
|
// bind address to list of configs that would become VirtualHosts on that
|
||||||
|
// server.
|
||||||
|
func ArrangeBindings(allConfigs []server.Config) (map[*net.TCPAddr][]server.Config, error) {
|
||||||
|
addresses := make(map[*net.TCPAddr][]server.Config)
|
||||||
|
|
||||||
|
// Group configs by bind address
|
||||||
|
for _, conf := range allConfigs {
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", conf.Address())
|
||||||
|
if err != nil {
|
||||||
|
return addresses, errors.New("Could not serve " + conf.Address() + " - " + err.Error())
|
||||||
|
}
|
||||||
|
addresses[addr] = append(addresses[addr], conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow HTTP and HTTPS to be served on the same address
|
||||||
|
for _, configs := range addresses {
|
||||||
|
isTLS := configs[0].TLS.Enabled
|
||||||
|
for _, config := range configs {
|
||||||
|
if config.TLS.Enabled != isTLS {
|
||||||
|
thisConfigProto, otherConfigProto := "HTTP", "HTTP"
|
||||||
|
if config.TLS.Enabled {
|
||||||
|
thisConfigProto = "HTTPS"
|
||||||
|
}
|
||||||
|
if configs[0].TLS.Enabled {
|
||||||
|
otherConfigProto = "HTTPS"
|
||||||
|
}
|
||||||
|
return addresses, fmt.Errorf("Configuration error: Cannot multiplex %s (%s) and %s (%s) on same address",
|
||||||
|
configs[0].Address(), otherConfigProto, config.Address(), thisConfigProto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return addresses, nil
|
||||||
|
}
|
||||||
|
|
||||||
// validDirective returns true if d is a valid
|
// validDirective returns true if d is a valid
|
||||||
// directive; false otherwise.
|
// directive; false otherwise.
|
||||||
func validDirective(d string) bool {
|
func validDirective(d string) bool {
|
||||||
|
@ -109,7 +153,3 @@ var (
|
||||||
Host = DefaultHost
|
Host = DefaultHost
|
||||||
Port = DefaultPort
|
Port = DefaultPort
|
||||||
)
|
)
|
||||||
|
|
||||||
// The application should set these so that various middlewares
|
|
||||||
// can access the proper information for their own needs.
|
|
||||||
var AppName, AppVersion string
|
|
||||||
|
|
|
@ -6,33 +6,10 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/app"
|
||||||
"github.com/mholt/caddy/middleware"
|
"github.com/mholt/caddy/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Map of supported protocols
|
|
||||||
// SSLv3 will be not supported in next release
|
|
||||||
var supportedProtocols = map[string]uint16{
|
|
||||||
"ssl3.0": tls.VersionSSL30,
|
|
||||||
"tls1.0": tls.VersionTLS10,
|
|
||||||
"tls1.1": tls.VersionTLS11,
|
|
||||||
"tls1.2": tls.VersionTLS12,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map of supported ciphers
|
|
||||||
// For security reasons caddy will not support RC4 ciphers
|
|
||||||
var supportedCiphers = map[string]uint16{
|
|
||||||
"ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
"ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
"ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
"ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
"ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
||||||
"ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
||||||
"RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
"RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
"ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
"RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
}
|
|
||||||
|
|
||||||
func TLS(c *Controller) (middleware.Middleware, error) {
|
func TLS(c *Controller) (middleware.Middleware, error) {
|
||||||
c.TLS.Enabled = true
|
c.TLS.Enabled = true
|
||||||
if c.Port == "http" {
|
if c.Port == "http" {
|
||||||
|
@ -79,6 +56,9 @@ func TLS(c *Controller) (middleware.Middleware, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, c.Errf("Wrong cipher name or cipher not supported '%s'", c.Val())
|
return nil, c.Errf("Wrong cipher name or cipher not supported '%s'", c.Val())
|
||||||
}
|
}
|
||||||
|
if _, ok := http2CipherSuites[value]; app.Http2 && !ok {
|
||||||
|
return nil, c.Errf("Cipher suite %s is not allowed for HTTP/2", c.Val())
|
||||||
|
}
|
||||||
c.TLS.Ciphers = append(c.TLS.Ciphers, value)
|
c.TLS.Ciphers = append(c.TLS.Ciphers, value)
|
||||||
}
|
}
|
||||||
case "cache":
|
case "cache":
|
||||||
|
@ -87,7 +67,7 @@ func TLS(c *Controller) (middleware.Middleware, error) {
|
||||||
}
|
}
|
||||||
size, err := strconv.Atoi(c.Val())
|
size, err := strconv.Atoi(c.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, c.Errf("Cache parameter should be an number '%s': %v", c.Val(), err)
|
return nil, c.Errf("Cache parameter must be an number '%s': %v", c.Val(), err)
|
||||||
}
|
}
|
||||||
c.TLS.CacheSize = size
|
c.TLS.CacheSize = size
|
||||||
default:
|
default:
|
||||||
|
@ -96,10 +76,12 @@ func TLS(c *Controller) (middleware.Middleware, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no Ciphers provided, use all caddy supportedCiphers
|
// If no ciphers provided, use all that Caddy supports for the protocol
|
||||||
if len(c.TLS.Ciphers) == 0 {
|
if len(c.TLS.Ciphers) == 0 {
|
||||||
for _, v := range supportedCiphers {
|
for _, v := range supportedCiphers {
|
||||||
c.TLS.Ciphers = append(c.TLS.Ciphers, v)
|
if _, ok := http2CipherSuites[v]; !app.Http2 || ok {
|
||||||
|
c.TLS.Ciphers = append(c.TLS.Ciphers, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,9 +96,43 @@ func TLS(c *Controller) (middleware.Middleware, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//If no cachesize provided, set default to 64
|
//If no cachesize provided, set default to 64
|
||||||
if c.TLS.CacheSize == 0 {
|
if c.TLS.CacheSize <= 0 {
|
||||||
c.TLS.CacheSize = 64
|
c.TLS.CacheSize = 64
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map of supported protocols
|
||||||
|
// SSLv3 will be not supported in next release
|
||||||
|
// HTTP/2 only supports TLS 1.2 and higher
|
||||||
|
var supportedProtocols = map[string]uint16{
|
||||||
|
"ssl3.0": tls.VersionSSL30,
|
||||||
|
"tls1.0": tls.VersionTLS10,
|
||||||
|
"tls1.1": tls.VersionTLS11,
|
||||||
|
"tls1.2": tls.VersionTLS12,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of supported ciphers.
|
||||||
|
//
|
||||||
|
// Note that, at time of writing, HTTP/2 blacklists 276 cipher suites,
|
||||||
|
// including all but two of the suites below (the two GCM suites).
|
||||||
|
var supportedCiphers = map[string]uint16{
|
||||||
|
"ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
"ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
"ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
"ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
"RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
"RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
"RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set of cipher suites not blacklisted by HTTP/2 spec.
|
||||||
|
// See https://http2.github.io/http2-spec/#BadCipherSuites
|
||||||
|
var http2CipherSuites = map[uint16]struct{}{
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: struct{}{},
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: struct{}{},
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,29 @@ package setup
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/app"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestTLSParseBasic(t *testing.T) {
|
||||||
|
c := newTestController(`tls cert.pem key.pem`)
|
||||||
|
|
||||||
|
_, err := TLS(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Expected no errors, but had an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.TLS.Certificate != "cert.pem" {
|
||||||
|
t.Errorf("Expected certificate arg to be 'cert.pem', was '%s'", c.TLS.Certificate)
|
||||||
|
}
|
||||||
|
if c.TLS.Key != "key.pem" {
|
||||||
|
t.Errorf("Expected key arg to be 'key.pem', was '%s'", c.TLS.Key)
|
||||||
|
}
|
||||||
|
if !c.TLS.Enabled {
|
||||||
|
t.Error("Expected TLS Enabled=true, but was false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTLSParseNoOptional(t *testing.T) {
|
func TestTLSParseNoOptional(t *testing.T) {
|
||||||
c := newTestController(`tls cert.crt cert.key`)
|
c := newTestController(`tls cert.crt cert.key`)
|
||||||
|
|
||||||
|
@ -44,7 +65,6 @@ func TestTLSParseIncompleteParams(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected errors, but no error returned")
|
t.Errorf("Expected errors, but no error returned")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTLSParseWithOptionalParams(t *testing.T) {
|
func TestTLSParseWithOptionalParams(t *testing.T) {
|
||||||
|
@ -107,3 +127,43 @@ func TestTLSParseWithWrongOptionalParams(t *testing.T) {
|
||||||
t.Errorf("Expected errors, but no error returned")
|
t.Errorf("Expected errors, but no error returned")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTLSParseWithHTTP2Requirements(t *testing.T) {
|
||||||
|
params := `tls cert.crt cert.key`
|
||||||
|
c := newTestController(params)
|
||||||
|
|
||||||
|
// With HTTP2, cipher suites should be limited
|
||||||
|
app.Http2 = true
|
||||||
|
_, err := TLS(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no errors, got: %v", err)
|
||||||
|
}
|
||||||
|
if len(c.TLS.Ciphers) != len(http2CipherSuites) {
|
||||||
|
t.Errorf("With HTTP/2 on, expected %d supported ciphers, got %d",
|
||||||
|
len(http2CipherSuites), len(c.TLS.Ciphers))
|
||||||
|
}
|
||||||
|
|
||||||
|
params = `tls cert.crt cert.key {
|
||||||
|
ciphers RSA-AES128-CBC-SHA
|
||||||
|
}`
|
||||||
|
c = newTestController(params)
|
||||||
|
// Should not be able to specify a blacklisted cipher suite with HTTP2 on
|
||||||
|
_, err = TLS(c)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected an error because cipher suite is invalid for HTTP/2")
|
||||||
|
}
|
||||||
|
|
||||||
|
params = `tls cert.crt cert.key`
|
||||||
|
c = newTestController(params)
|
||||||
|
|
||||||
|
// Without HTTP2, cipher suites should not be as restricted
|
||||||
|
app.Http2 = false
|
||||||
|
_, err = TLS(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no errors, got: %v", err)
|
||||||
|
}
|
||||||
|
if len(c.TLS.Ciphers) != len(supportedCiphers) {
|
||||||
|
t.Errorf("With HTTP/2 off, expected %d supported ciphers, got %d",
|
||||||
|
len(supportedCiphers), len(c.TLS.Ciphers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
107
main.go
107
main.go
|
@ -2,58 +2,49 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/app"
|
||||||
"github.com/mholt/caddy/config"
|
"github.com/mholt/caddy/config"
|
||||||
"github.com/mholt/caddy/server"
|
"github.com/mholt/caddy/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
conf string
|
conf string
|
||||||
http2 bool // TODO: temporary flag until http2 is standard
|
|
||||||
quiet bool
|
|
||||||
cpu string
|
cpu string
|
||||||
version bool
|
version bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.StringVar(&conf, "conf", "", "Configuration file to use (default="+config.DefaultConfigFile+")")
|
flag.StringVar(&conf, "conf", "", "Configuration file to use (default="+config.DefaultConfigFile+")")
|
||||||
flag.BoolVar(&http2, "http2", true, "Enable HTTP/2 support") // TODO: temporary flag until http2 merged into std lib
|
flag.BoolVar(&app.Http2, "http2", true, "Enable HTTP/2 support") // TODO: temporary flag until http2 merged into std lib
|
||||||
flag.BoolVar(&quiet, "quiet", false, "Quiet mode (no initialization output)")
|
flag.BoolVar(&app.Quiet, "quiet", false, "Quiet mode (no initialization output)")
|
||||||
flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
|
flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
|
||||||
flag.StringVar(&config.Root, "root", config.DefaultRoot, "Root path to default site")
|
flag.StringVar(&config.Root, "root", config.DefaultRoot, "Root path to default site")
|
||||||
flag.StringVar(&config.Host, "host", config.DefaultHost, "Default host")
|
flag.StringVar(&config.Host, "host", config.DefaultHost, "Default host")
|
||||||
flag.StringVar(&config.Port, "port", config.DefaultPort, "Default port")
|
flag.StringVar(&config.Port, "port", config.DefaultPort, "Default port")
|
||||||
flag.BoolVar(&version, "version", false, "Show version")
|
flag.BoolVar(&version, "version", false, "Show version")
|
||||||
|
|
||||||
config.AppName = "Caddy"
|
|
||||||
config.AppVersion = "0.6.0"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if version {
|
if version {
|
||||||
fmt.Printf("%s %s\n", config.AppName, config.AppVersion)
|
fmt.Printf("%s %s\n", app.Name, app.Version)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
// Set CPU cap
|
// Set CPU cap
|
||||||
err := setCPU(cpu)
|
err := app.SetCPU(cpu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -65,7 +56,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group by address (virtual hosts)
|
// Group by address (virtual hosts)
|
||||||
addresses, err := arrangeBindings(allConfigs)
|
addresses, err := config.ArrangeBindings(allConfigs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -76,18 +67,21 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
s.HTTP2 = http2 // TODO: This setting is temporary
|
s.HTTP2 = app.Http2 // TODO: This setting is temporary
|
||||||
wg.Add(1)
|
app.Wg.Add(1)
|
||||||
go func(s *server.Server) {
|
go func(s *server.Server) {
|
||||||
defer wg.Done()
|
defer app.Wg.Done()
|
||||||
err := s.Serve()
|
err := s.Serve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err) // kill whole process to avoid a half-alive zombie server
|
log.Fatal(err) // kill whole process to avoid a half-alive zombie server
|
||||||
}
|
}
|
||||||
}(s)
|
}(s)
|
||||||
|
|
||||||
if !quiet {
|
app.Servers = append(app.Servers, s)
|
||||||
|
|
||||||
|
if !app.Quiet {
|
||||||
var checkedFdLimit bool
|
var checkedFdLimit bool
|
||||||
|
|
||||||
for addr, configs := range addresses {
|
for addr, configs := range addresses {
|
||||||
for _, conf := range configs {
|
for _, conf := range configs {
|
||||||
// Print address of site
|
// Print address of site
|
||||||
|
@ -117,7 +111,7 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
app.Wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func isLocalhost(s string) bool {
|
func isLocalhost(s string) bool {
|
||||||
|
@ -169,76 +163,3 @@ func loadConfigs() ([]server.Config, error) {
|
||||||
|
|
||||||
return config.Load(config.DefaultConfigFile, file)
|
return config.Load(config.DefaultConfigFile, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
// arrangeBindings groups configurations by their bind address. For example,
|
|
||||||
// a server that should listen on localhost and another on 127.0.0.1 will
|
|
||||||
// be grouped into the same address: 127.0.0.1. It will return an error
|
|
||||||
// if the address lookup fails or if a TLS listener is configured on the
|
|
||||||
// same address as a plaintext HTTP listener.
|
|
||||||
func arrangeBindings(allConfigs []server.Config) (map[*net.TCPAddr][]server.Config, error) {
|
|
||||||
addresses := make(map[*net.TCPAddr][]server.Config)
|
|
||||||
|
|
||||||
// Group configs by bind address
|
|
||||||
for _, conf := range allConfigs {
|
|
||||||
addr, err := net.ResolveTCPAddr("tcp", conf.Address())
|
|
||||||
if err != nil {
|
|
||||||
return addresses, errors.New("Could not serve " + conf.Address() + " - " + err.Error())
|
|
||||||
}
|
|
||||||
addresses[addr] = append(addresses[addr], conf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow HTTP and HTTPS to be served on the same address
|
|
||||||
for _, configs := range addresses {
|
|
||||||
isTLS := configs[0].TLS.Enabled
|
|
||||||
for _, config := range configs {
|
|
||||||
if config.TLS.Enabled != isTLS {
|
|
||||||
thisConfigProto, otherConfigProto := "HTTP", "HTTP"
|
|
||||||
if config.TLS.Enabled {
|
|
||||||
thisConfigProto = "HTTPS"
|
|
||||||
}
|
|
||||||
if configs[0].TLS.Enabled {
|
|
||||||
otherConfigProto = "HTTPS"
|
|
||||||
}
|
|
||||||
return addresses, fmt.Errorf("Configuration error: Cannot multiplex %s (%s) and %s (%s) on same address",
|
|
||||||
configs[0].Address(), otherConfigProto, config.Address(), thisConfigProto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return addresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// setCPU parses string cpu and sets GOMAXPROCS
|
|
||||||
// according to its value. It accepts either
|
|
||||||
// a number (e.g. 3) or a percent (e.g. 50%).
|
|
||||||
func setCPU(cpu string) error {
|
|
||||||
var numCPU int
|
|
||||||
|
|
||||||
availCPU := runtime.NumCPU()
|
|
||||||
|
|
||||||
if strings.HasSuffix(cpu, "%") {
|
|
||||||
// Percent
|
|
||||||
var percent float32
|
|
||||||
pctStr := cpu[:len(cpu)-1]
|
|
||||||
pctInt, err := strconv.Atoi(pctStr)
|
|
||||||
if err != nil || pctInt < 1 || pctInt > 100 {
|
|
||||||
return errors.New("Invalid CPU value: percentage must be between 1-100")
|
|
||||||
}
|
|
||||||
percent = float32(pctInt) / 100
|
|
||||||
numCPU = int(float32(availCPU) * percent)
|
|
||||||
} else {
|
|
||||||
// Number
|
|
||||||
num, err := strconv.Atoi(cpu)
|
|
||||||
if err != nil || num < 1 {
|
|
||||||
return errors.New("Invalid CPU value: provide a number or percent greater than 0")
|
|
||||||
}
|
|
||||||
numCPU = num
|
|
||||||
}
|
|
||||||
|
|
||||||
if numCPU > availCPU {
|
|
||||||
numCPU = availCPU
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.GOMAXPROCS(numCPU)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -132,17 +132,11 @@ func ListenAndServeTLSWithSNI(srv *http.Server, tlsConfigs []TLSConfig) error {
|
||||||
}
|
}
|
||||||
config.BuildNameToCertificate()
|
config.BuildNameToCertificate()
|
||||||
|
|
||||||
// Here we change some crypto/tls defaults based on caddyfile
|
// Customize our TLS configuration
|
||||||
// If no config provided, we set defaults focused in security
|
|
||||||
|
|
||||||
// Add a session cache LRU algorithm
|
|
||||||
config.ClientSessionCache = tls.NewLRUClientSessionCache(tlsConfigs[0].CacheSize)
|
config.ClientSessionCache = tls.NewLRUClientSessionCache(tlsConfigs[0].CacheSize)
|
||||||
|
|
||||||
config.MinVersion = tlsConfigs[0].ProtocolMinVersion
|
config.MinVersion = tlsConfigs[0].ProtocolMinVersion
|
||||||
config.MaxVersion = tlsConfigs[0].ProtocolMaxVersion
|
config.MaxVersion = tlsConfigs[0].ProtocolMaxVersion
|
||||||
config.CipherSuites = tlsConfigs[0].Ciphers
|
config.CipherSuites = tlsConfigs[0].Ciphers
|
||||||
|
|
||||||
// Server ciphers have priority over client ciphers
|
|
||||||
config.PreferServerCipherSuites = true
|
config.PreferServerCipherSuites = true
|
||||||
|
|
||||||
conn, err := net.Listen("tcp", addr)
|
conn, err := net.Listen("tcp", addr)
|
||||||
|
|
Loading…
Reference in a new issue