0
Fork 0
mirror of https://github.com/caddyserver/caddy.git synced 2024-12-23 22:27:38 -05:00
caddy/main.go

161 lines
3.9 KiB
Go
Raw Normal View History

2015-01-13 14:43:45 -05:00
package main
import (
"errors"
"flag"
2015-04-15 15:11:32 -05:00
"fmt"
2015-01-13 14:43:45 -05:00
"log"
2015-04-15 15:11:32 -05:00
"net"
"runtime"
"strconv"
"strings"
2015-01-13 14:43:45 -05:00
"sync"
"github.com/mholt/caddy/config"
"github.com/mholt/caddy/server"
)
var (
conf string
http2 bool // TODO: temporary flag until http2 is standard
quiet bool
cpu string
)
func init() {
2015-04-15 15:11:32 -05:00
flag.StringVar(&conf, "conf", config.DefaultConfigFile, "the configuration file to use")
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)")
flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
2015-04-29 23:30:12 -05:00
flag.StringVar(&config.Host, "host", config.DefaultHost, "Default host")
flag.StringVar(&config.Port, "port", config.DefaultPort, "Default port")
flag.Parse()
}
2015-01-13 14:43:45 -05:00
func main() {
var wg sync.WaitGroup
// Set CPU cap
err := setCPU(cpu)
if err != nil {
log.Fatal(err)
}
2015-04-15 15:11:32 -05:00
// Load config from file
allConfigs, err := config.Load(conf)
2015-01-13 14:43:45 -05:00
if err != nil {
if config.IsNotFound(err) {
2015-04-15 15:11:32 -05:00
allConfigs = config.Default()
2015-01-13 14:43:45 -05:00
} else {
log.Fatal(err)
}
}
2015-04-18 10:53:43 -05:00
if len(allConfigs) == 0 {
allConfigs = config.Default()
}
2015-01-13 14:43:45 -05:00
2015-04-18 10:53:43 -05:00
// Group by address (virtual hosts)
2015-04-15 15:11:32 -05: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-13 14:43:45 -05:00
if err != nil {
log.Fatal(err)
}
2015-04-11 17:58:34 -05:00
s.HTTP2 = http2 // TODO: This setting is temporary
2015-01-13 14:43:45 -05:00
wg.Add(1)
go func(s *server.Server) {
defer wg.Done()
err := s.Serve()
if err != nil {
log.Fatal(err) // kill whole process to avoid a half-alive zombie server
2015-01-13 14:43:45 -05:00
}
}(s)
if !quiet {
for _, config := range configs {
2015-04-24 21:09:31 -05:00
fmt.Println(config.Address())
}
}
2015-01-13 14:43:45 -05:00
}
wg.Wait()
}
2015-04-15 15:11:32 -05: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
}
// 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
}