-
-
Notifications
You must be signed in to change notification settings - Fork 875
Expand file tree
/
Copy pathrunner.go
More file actions
151 lines (129 loc) · 4.46 KB
/
Copy pathrunner.go
File metadata and controls
151 lines (129 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package runner
import (
"context"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/gotify/server/v2/config"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert"
)
// Run starts the http server and if configured a https server.
func Run(router http.Handler, conf *config.Configuration) error {
shutdown := make(chan error)
go doShutdownOnSignal(shutdown)
s := &http.Server{Handler: router}
hasListener := false
if conf.Server.Port >= 0 {
httpListener, err := startListening("plain connection", conf.Server.ListenAddr, conf.Server.Port, conf.Server.KeepAlivePeriodSeconds)
if err != nil {
return err
}
hasListener = true
defer httpListener.Close()
go func() {
err := s.Serve(httpListener)
doShutdown(shutdown, err)
}()
}
if conf.Server.SSL.Enabled {
if conf.Server.SSL.LetsEncrypt.Enabled {
applyLetsEncrypt(s, conf)
} else if conf.Server.SSL.CertFile == "" || conf.Server.SSL.CertKey == "" {
log.Fatalln("CertFile and CertKey must be set to use HTTPS when LetsEncrypt is disabled, please set GOTIFY_SERVER_SSL_CERTFILE and GOTIFY_SERVER_SSL_CERTKEY")
}
httpsListener, err := startListening("TLS connection", conf.Server.SSL.ListenAddr, conf.Server.SSL.Port, conf.Server.KeepAlivePeriodSeconds)
if err != nil {
return err
}
hasListener = true
defer httpsListener.Close()
go func() {
err := s.ServeTLS(httpsListener, conf.Server.SSL.CertFile, conf.Server.SSL.CertKey)
doShutdown(shutdown, err)
}()
}
if !hasListener {
log.Fatalln("No listener started, both plain and TLS listeners are disabled")
}
err := <-shutdown
fmt.Println("Shutting down:", err)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
return s.Shutdown(ctx)
}
func doShutdownOnSignal(shutdown chan<- error) {
onSignal := make(chan os.Signal, 1)
signal.Notify(onSignal, os.Interrupt, syscall.SIGTERM)
sig := <-onSignal
doShutdown(shutdown, fmt.Errorf("received signal %s", sig))
}
func doShutdown(shutdown chan<- error, err error) {
select {
case shutdown <- err:
default:
// If there is no one listening on the shutdown channel, then the
// shutdown is already initiated and we can ignore these errors.
}
}
func startListening(connectionType, listenAddr string, port, keepAlive int) (net.Listener, error) {
network, addr := getNetworkAndAddr(listenAddr, port)
lc := net.ListenConfig{KeepAlive: time.Duration(keepAlive) * time.Second}
oldMask := umask(0)
defer umask(oldMask)
l, err := lc.Listen(context.Background(), network, addr)
if err == nil {
fmt.Println("Started listening for", connectionType, "on", l.Addr().Network(), l.Addr().String())
}
return l, err
}
func getNetworkAndAddr(listenAddr string, port int) (string, string) {
if strings.HasPrefix(listenAddr, "unix:") {
return "unix", strings.TrimPrefix(listenAddr, "unix:")
}
return "tcp", fmt.Sprintf("%s:%d", listenAddr, port)
}
type LoggingRoundTripper struct {
Name string
RoundTripper http.RoundTripper
}
func (l *LoggingRoundTripper) RoundTrip(r *http.Request) (resp *http.Response, err error) {
resp, err = l.RoundTripper.RoundTrip(r)
if resp.StatusCode == 429 {
log.Printf("%s Rate Limited: Retry-After %s on %s %s\n", l.Name, resp.Header.Get("Retry-After"), r.Method, r.URL.String())
} else if resp.StatusCode >= 400 {
log.Printf("%s Request Failed: Unexpected status code %d on %s %s\n", l.Name, resp.StatusCode, r.Method, r.URL.String())
} else if err != nil {
log.Printf("%s Request Failed: %s on %s %s\n", l.Name, err.Error(), r.Method, r.URL.String())
}
return resp, err
}
func applyLetsEncrypt(s *http.Server, conf *config.Configuration) {
httpClient := &http.Client{
Transport: &LoggingRoundTripper{Name: "Let's Encrypt", RoundTripper: http.DefaultTransport},
Timeout: 60 * time.Second,
}
acmeClient := &acme.Client{
HTTPClient: httpClient,
DirectoryURL: conf.Server.SSL.LetsEncrypt.DirectoryURL,
}
certManager := autocert.Manager{
Client: acmeClient,
Prompt: func(tosURL string) bool {
if !conf.Server.SSL.LetsEncrypt.AcceptTOS {
log.Fatalf("Let's Encrypt TOS must be accepted to use Let's Encrypt, please acknowledge TOS at %s and set GOTIFY_SERVER_SSL_LETSENCRYPT_ACCEPTTOS=true\n", tosURL)
}
return true
},
HostPolicy: autocert.HostWhitelist(conf.Server.SSL.LetsEncrypt.Hosts...),
Cache: autocert.DirCache(conf.Server.SSL.LetsEncrypt.Cache),
}
s.Handler = certManager.HTTPHandler(s.Handler)
s.TLSConfig = certManager.TLSConfig()
}