]> Sergey Matveev's repositories - godlighty.git/blob - cmd/godlighty/main.go
2373ed628a8f8b95322810dda56e6932107c11d8
[godlighty.git] / cmd / godlighty / main.go
1 /*
2 godlighty -- highly-customizable HTTP, HTTP/2, HTTPS server
3 Copyright (C) 2021-2023 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package main
19
20 import (
21         "context"
22         "crypto/tls"
23         "flag"
24         "log"
25         "net"
26         "net/http"
27         "os"
28         "os/signal"
29         "strconv"
30         "strings"
31         "syscall"
32         "time"
33
34         "github.com/davecgh/go-spew/spew"
35         "golang.org/x/net/netutil"
36
37         "go.stargrave.org/godlighty"
38         _ "go.stargrave.org/godlighty/rc/cfg"
39 )
40
41 const MaxConns = 128
42
43 var (
44         GracefulTime = 10 * time.Second
45         RWTimeout    = 30 * time.Second
46 )
47
48 func main() {
49         bind := flag.String("bind", "[::]:80", "Address to bind and listen on")
50         doTLS := flag.Bool("tls", false, "Enable TLS")
51         doSetUID := flag.Int("setuid", 0, "Set that UID after binding the socket")
52         doSetGID := flag.Int("setgid", 0, "Set that GID after binding the socket")
53         doSetGIDs := flag.String("setgids", "", "Comma-separated GIDs to set")
54         log.SetFlags(log.Lshortfile)
55         log.SetOutput(os.Stdout)
56         flag.Parse()
57         if *doTLS {
58                 godlighty.LoadCertificates()
59         }
60         shutdown := make(chan os.Signal, 1)
61         signal.Notify(shutdown, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
62         exitErr := make(chan error)
63         l, err := godlighty.DeadlinedListen("tcp", *bind, RWTimeout, RWTimeout)
64         if err != nil {
65                 log.Fatalln(err)
66         }
67
68         if *doSetGID != 0 {
69                 if err := syscall.Setregid(*doSetGID, *doSetGID); err != nil {
70                         log.Fatal(err)
71                 }
72         }
73         if *doSetGIDs == "" {
74                 if *doSetGID != 0 {
75                         if err := syscall.Setgroups([]int{*doSetGID}); err != nil {
76                                 log.Fatal(err)
77                         }
78                 }
79         } else {
80                 gids := []int{}
81                 for _, g := range strings.Split(*doSetGIDs, ",") {
82                         gid, err := strconv.Atoi(g)
83                         if err != nil {
84                                 log.Fatalln(err)
85                         }
86                         gids = append(gids, gid)
87                 }
88                 if err := syscall.Setgroups(gids); err != nil {
89                         log.Fatal(err)
90                 }
91         }
92         if *doSetUID != 0 {
93                 if err := syscall.Setreuid(*doSetUID, *doSetUID); err != nil {
94                         log.Fatal(err)
95                 }
96         }
97
98         info := make(chan os.Signal, 1)
99         signal.Notify(info, InfoSignal)
100         go func() {
101                 for {
102                         <-info
103                         spew.Fdump(os.Stdout, godlighty.Hosts)
104                 }
105         }()
106
107         godlighty.BindAddr = *bind
108         srv := http.Server{
109                 Handler:           godlighty.MainHandler,
110                 ReadHeaderTimeout: RWTimeout,
111                 IdleTimeout:       time.Minute,
112         }
113         go func() {
114                 <-shutdown
115                 log.Println("shutting down")
116                 ctx, cancel := context.WithTimeout(context.TODO(), GracefulTime)
117                 exitErr <- srv.Shutdown(ctx)
118                 cancel()
119         }()
120         var ll net.Listener
121         if *doTLS {
122                 tlsCfg := godlighty.NewTLSConfig()
123                 ll = tls.NewListener(netutil.LimitListener(l, MaxConns), tlsCfg)
124         } else {
125                 ll = netutil.LimitListener(l, MaxConns)
126         }
127         log.Println(
128                 godlighty.Version,
129                 "bind:", *bind,
130                 "tls:", *doTLS,
131                 "hosts:", len(godlighty.Hosts),
132         )
133         if err = srv.Serve(ll); err != http.ErrServerClosed {
134                 log.Fatalln(err)
135         }
136         if err := <-exitErr; err != nil {
137                 log.Fatal(err)
138         }
139 }