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