2015-01-14 03:43:45 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2015-04-25 10:08:14 +08:00
|
|
|
"errors"
|
2015-01-14 07:14:00 +08:00
|
|
|
"flag"
|
2015-04-16 04:11:32 +08:00
|
|
|
"fmt"
|
2015-01-14 03:43:45 +08:00
|
|
|
"log"
|
2015-04-16 04:11:32 +08:00
|
|
|
"net"
|
2015-04-25 10:08:14 +08:00
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2015-01-14 03:43:45 +08:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/mholt/caddy/config"
|
|
|
|
"github.com/mholt/caddy/server"
|
|
|
|
)
|
|
|
|
|
2015-04-10 00:08:22 +08:00
|
|
|
var (
|
|
|
|
conf string
|
2015-04-24 03:28:05 +08:00
|
|
|
http2 bool // TODO: temporary flag until http2 is standard
|
|
|
|
quiet bool
|
2015-04-25 10:08:14 +08:00
|
|
|
cpu string
|
2015-04-10 00:08:22 +08:00
|
|
|
)
|
2015-01-19 14:11:21 +08:00
|
|
|
|
|
|
|
func init() {
|
2015-04-16 04:11:32 +08:00
|
|
|
flag.StringVar(&conf, "conf", config.DefaultConfigFile, "the configuration file to use")
|
2015-04-24 03:28:05 +08:00
|
|
|
flag.BoolVar(&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)")
|
2015-04-25 10:08:14 +08:00
|
|
|
flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
|
2015-04-30 12:30:12 +08:00
|
|
|
flag.StringVar(&config.Host, "host", config.DefaultHost, "Default host")
|
2015-04-29 12:13:00 +08:00
|
|
|
flag.StringVar(&config.Port, "port", config.DefaultPort, "Default port")
|
2015-04-24 03:28:05 +08:00
|
|
|
flag.Parse()
|
2015-01-19 14:11:21 +08:00
|
|
|
}
|
|
|
|
|
2015-01-14 03:43:45 +08:00
|
|
|
func main() {
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
2015-04-25 10:08:14 +08:00
|
|
|
// Set CPU cap
|
|
|
|
err := setCPU(cpu)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-04-16 04:11:32 +08:00
|
|
|
// Load config from file
|
|
|
|
allConfigs, err := config.Load(conf)
|
2015-01-14 03:43:45 +08:00
|
|
|
if err != nil {
|
|
|
|
if config.IsNotFound(err) {
|
2015-04-16 04:11:32 +08:00
|
|
|
allConfigs = config.Default()
|
2015-01-14 03:43:45 +08:00
|
|
|
} else {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2015-04-18 23:53:43 +08:00
|
|
|
if len(allConfigs) == 0 {
|
|
|
|
allConfigs = config.Default()
|
|
|
|
}
|
2015-01-14 03:43:45 +08:00
|
|
|
|
2015-04-18 23:53:43 +08:00
|
|
|
// Group by address (virtual hosts)
|
2015-04-16 04:11:32 +08:00
|
|
|
addresses, err := arrangeBindings(allConfigs)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start each server with its one or more configurations
|
|
|
|
for addr, configs := range addresses {
|
|
|
|
s, err := server.New(addr, configs, configs[0].TLS.Enabled)
|
2015-01-14 03:43:45 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2015-04-12 06:58:34 +08:00
|
|
|
s.HTTP2 = http2 // TODO: This setting is temporary
|
2015-01-14 03:43:45 +08:00
|
|
|
wg.Add(1)
|
|
|
|
go func(s *server.Server) {
|
|
|
|
defer wg.Done()
|
|
|
|
err := s.Serve()
|
|
|
|
if err != nil {
|
2015-03-29 06:24:00 +08:00
|
|
|
log.Println(err)
|
2015-01-14 03:43:45 +08:00
|
|
|
}
|
|
|
|
}(s)
|
2015-04-24 03:28:05 +08:00
|
|
|
|
|
|
|
if !quiet {
|
|
|
|
for _, config := range configs {
|
2015-04-25 10:09:31 +08:00
|
|
|
fmt.Println(config.Address())
|
2015-04-24 03:28:05 +08:00
|
|
|
}
|
|
|
|
}
|
2015-01-14 03:43:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
}
|
2015-04-16 04:11:32 +08:00
|
|
|
|
|
|
|
// 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 []config.Config) (map[string][]config.Config, error) {
|
|
|
|
addresses := make(map[string][]config.Config)
|
|
|
|
|
|
|
|
// Group configs by bind address
|
|
|
|
for _, conf := range allConfigs {
|
|
|
|
addr, err := net.ResolveTCPAddr("tcp", conf.Address())
|
|
|
|
if err != nil {
|
|
|
|
return addresses, err
|
|
|
|
}
|
|
|
|
addresses[addr.String()] = append(addresses[addr.String()], 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
|
|
|
|
}
|
2015-04-25 10:08:14 +08:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|