/* godlighty -- highly-customizable HTTP, HTTP/2, HTTPS server Copyright (C) 2021-2022 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package main import ( "context" "crypto/tls" "flag" "log" "net" "net/http" "os" "os/signal" "strconv" "strings" "syscall" "time" "github.com/davecgh/go-spew/spew" "golang.org/x/net/netutil" "go.stargrave.org/godlighty" _ "go.stargrave.org/godlighty/rc/cfg" ) const MaxConns = 128 var ( GracefulTime = 10 * time.Second RWTimeout = 30 * time.Second ) func main() { bind := flag.String("bind", "[::]:80", "Address to bind and listen on") doTLS := flag.Bool("tls", false, "Enable TLS") doSetUID := flag.Int("setuid", 0, "Set that UID after binding the socket") doSetGID := flag.Int("setgid", 0, "Set that GID after binding the socket") doSetGIDs := flag.String("setgids", "", "Comma-separated GIDs to set") log.SetFlags(log.Lshortfile) log.SetOutput(os.Stdout) flag.Parse() if *doTLS { godlighty.LoadCertificates() } shutdown := make(chan os.Signal) signal.Notify(shutdown, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP) exitErr := make(chan error) l, err := godlighty.DeadlinedListen("tcp", *bind, RWTimeout, RWTimeout) if err != nil { log.Fatalln(err) } if *doSetGID != 0 { if err := syscall.Setregid(*doSetGID, *doSetGID); err != nil { log.Fatal(err) } } if *doSetGIDs == "" { if *doSetGID != 0 { if err := syscall.Setgroups([]int{*doSetGID}); err != nil { log.Fatal(err) } } } else { gids := []int{} for _, g := range strings.Split(*doSetGIDs, ",") { gid, err := strconv.Atoi(g) if err != nil { log.Fatalln(err) } gids = append(gids, gid) } if err := syscall.Setgroups(gids); err != nil { log.Fatal(err) } } if *doSetUID != 0 { if err := syscall.Setreuid(*doSetUID, *doSetUID); err != nil { log.Fatal(err) } } info := make(chan os.Signal) signal.Notify(info, InfoSignal) go func() { for { <-info spew.Fdump(os.Stdout, godlighty.Hosts) } }() godlighty.BindAddr = *bind srv := http.Server{ Handler: godlighty.MainHandler, ReadHeaderTimeout: RWTimeout, IdleTimeout: time.Minute, } go func() { <-shutdown log.Println("shutting down") ctx, cancel := context.WithTimeout(context.TODO(), GracefulTime) exitErr <- srv.Shutdown(ctx) cancel() }() var ll net.Listener if *doTLS { tlsCfg := godlighty.NewTLSConfig() ll = tls.NewListener(netutil.LimitListener(l, MaxConns), tlsCfg) } else { ll = netutil.LimitListener(l, MaxConns) } log.Println( godlighty.Version, "bind:", *bind, "tls:", *doTLS, "hosts:", len(godlighty.Hosts), ) if err = srv.Serve(ll); err != http.ErrServerClosed { log.Fatalln(err) } if err := <-exitErr; err != nil { log.Fatal(err) } }