"Fossies" - the Fresh Open Source Software Archive 
Member "AdGuardHome-0.104.3/internal/home/web.go" (19 Nov 2020, 6047 Bytes) of package /linux/misc/dns/AdGuardHome-0.104.3.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the latest
Fossies "Diffs" side-by-side code changes report for "web.go":
0.104.1_vs_0.104.3.
1 package home
2
3 import (
4 "context"
5 "crypto/tls"
6 golog "log"
7 "net"
8 "net/http"
9 "strconv"
10 "strings"
11 "sync"
12
13 "github.com/AdguardTeam/AdGuardHome/internal/util"
14 "github.com/AdguardTeam/golibs/log"
15 "github.com/NYTimes/gziphandler"
16 "github.com/gobuffalo/packr"
17 )
18
19 type WebConfig struct {
20 firstRun bool
21 BindHost string
22 BindPort int
23 PortHTTPS int
24 }
25
26 // HTTPSServer - HTTPS Server
27 type HTTPSServer struct {
28 server *http.Server
29 cond *sync.Cond
30 condLock sync.Mutex
31 shutdown bool // if TRUE, don't restart the server
32 enabled bool
33 cert tls.Certificate
34 }
35
36 // Web - module object
37 type Web struct {
38 conf *WebConfig
39 forceHTTPS bool
40 portHTTPS int
41 httpServer *http.Server // HTTP module
42 httpsServer HTTPSServer // HTTPS module
43 errLogger *golog.Logger
44 }
45
46 // Proxy between Go's "log" and "golibs/log"
47 type logWriter struct {
48 }
49
50 // HTTP server calls this function to log an error
51 func (w *logWriter) Write(p []byte) (int, error) {
52 log.Debug("Web: %s", string(p))
53 return 0, nil
54 }
55
56 // CreateWeb - create module
57 func CreateWeb(conf *WebConfig) *Web {
58 log.Info("Initialize web module")
59
60 w := Web{}
61 w.conf = conf
62
63 lw := logWriter{}
64 w.errLogger = golog.New(&lw, "", 0)
65
66 // Initialize and run the admin Web interface
67 box := packr.NewBox("../../build/static")
68
69 // if not configured, redirect / to /install.html, otherwise redirect /install.html to /
70 http.Handle("/", postInstallHandler(optionalAuthHandler(gziphandler.GzipHandler(http.FileServer(box)))))
71
72 // add handlers for /install paths, we only need them when we're not configured yet
73 if conf.firstRun {
74 log.Info("This is the first launch of AdGuard Home, redirecting everything to /install.html ")
75 http.Handle("/install.html", preInstallHandler(http.FileServer(box)))
76 w.registerInstallHandlers()
77 } else {
78 registerControlHandlers()
79 }
80
81 w.httpsServer.cond = sync.NewCond(&w.httpsServer.condLock)
82 return &w
83 }
84
85 // WebCheckPortAvailable - check if port is available
86 // BUT: if we are already using this port, no need
87 func WebCheckPortAvailable(port int) bool {
88 alreadyRunning := false
89 if Context.web.httpsServer.server != nil {
90 alreadyRunning = true
91 }
92 if !alreadyRunning {
93 err := util.CheckPortAvailable(config.BindHost, port)
94 if err != nil {
95 return false
96 }
97 }
98 return true
99 }
100
101 // TLSConfigChanged - called when TLS configuration has changed
102 func (web *Web) TLSConfigChanged(tlsConf tlsConfigSettings) {
103 log.Debug("Web: applying new TLS configuration")
104 web.conf.PortHTTPS = tlsConf.PortHTTPS
105 web.forceHTTPS = (tlsConf.ForceHTTPS && tlsConf.Enabled && tlsConf.PortHTTPS != 0)
106 web.portHTTPS = tlsConf.PortHTTPS
107
108 enabled := tlsConf.Enabled &&
109 tlsConf.PortHTTPS != 0 &&
110 len(tlsConf.PrivateKeyData) != 0 &&
111 len(tlsConf.CertificateChainData) != 0
112 var cert tls.Certificate
113 var err error
114 if enabled {
115 cert, err = tls.X509KeyPair(tlsConf.CertificateChainData, tlsConf.PrivateKeyData)
116 if err != nil {
117 log.Fatal(err)
118 }
119 }
120
121 web.httpsServer.cond.L.Lock()
122 if web.httpsServer.server != nil {
123 _ = web.httpsServer.server.Shutdown(context.TODO())
124 }
125 web.httpsServer.enabled = enabled
126 web.httpsServer.cert = cert
127 web.httpsServer.cond.Broadcast()
128 web.httpsServer.cond.L.Unlock()
129 }
130
131 // Start - start serving HTTP requests
132 func (web *Web) Start() {
133 // for https, we have a separate goroutine loop
134 go web.tlsServerLoop()
135
136 // this loop is used as an ability to change listening host and/or port
137 for !web.httpsServer.shutdown {
138 printHTTPAddresses("http")
139
140 // we need to have new instance, because after Shutdown() the Server is not usable
141 address := net.JoinHostPort(web.conf.BindHost, strconv.Itoa(web.conf.BindPort))
142 web.httpServer = &http.Server{
143 ErrorLog: web.errLogger,
144 Addr: address,
145 Handler: filterPPROF(http.DefaultServeMux),
146 }
147 err := web.httpServer.ListenAndServe()
148 if err != http.ErrServerClosed {
149 cleanupAlways()
150 log.Fatal(err)
151 }
152 // We use ErrServerClosed as a sign that we need to rebind on new address, so go back to the start of the loop
153 }
154 }
155
156 // TODO(a.garipov): We currently have to use this, because everything registers
157 // its HTTP handlers in http.DefaultServeMux. In the future, refactor our HTTP
158 // API initialization process and stop using the gosh darn http.DefaultServeMux
159 // for anything at all. Gosh darn global variables.
160 func filterPPROF(h http.Handler) (filtered http.Handler) {
161 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
162 if strings.HasPrefix(r.URL.Path, "/debug/pprof") {
163 http.NotFound(w, r)
164
165 return
166 }
167
168 h.ServeHTTP(w, r)
169 })
170 }
171
172 // Close - stop HTTP server, possibly waiting for all active connections to be closed
173 func (web *Web) Close() {
174 log.Info("Stopping HTTP server...")
175 web.httpsServer.cond.L.Lock()
176 web.httpsServer.shutdown = true
177 web.httpsServer.cond.L.Unlock()
178 if web.httpsServer.server != nil {
179 _ = web.httpsServer.server.Shutdown(context.TODO())
180 }
181 if web.httpServer != nil {
182 _ = web.httpServer.Shutdown(context.TODO())
183 }
184
185 log.Info("Stopped HTTP server")
186 }
187
188 func (web *Web) tlsServerLoop() {
189 for {
190 web.httpsServer.cond.L.Lock()
191 if web.httpsServer.shutdown {
192 web.httpsServer.cond.L.Unlock()
193 break
194 }
195
196 // this mechanism doesn't let us through until all conditions are met
197 for !web.httpsServer.enabled { // sleep until necessary data is supplied
198 web.httpsServer.cond.Wait()
199 if web.httpsServer.shutdown {
200 web.httpsServer.cond.L.Unlock()
201 return
202 }
203 }
204
205 web.httpsServer.cond.L.Unlock()
206
207 // prepare HTTPS server
208 address := net.JoinHostPort(web.conf.BindHost, strconv.Itoa(web.conf.PortHTTPS))
209 web.httpsServer.server = &http.Server{
210 ErrorLog: web.errLogger,
211 Addr: address,
212 TLSConfig: &tls.Config{
213 Certificates: []tls.Certificate{web.httpsServer.cert},
214 MinVersion: tls.VersionTLS12,
215 RootCAs: Context.tlsRoots,
216 CipherSuites: Context.tlsCiphers,
217 },
218 }
219
220 printHTTPAddresses("https")
221 err := web.httpsServer.server.ListenAndServeTLS("", "")
222 if err != http.ErrServerClosed {
223 cleanupAlways()
224 log.Fatal(err)
225 }
226 }
227 }