Skip to content

Commit

Permalink
share daemon instance among daemons that use each other in config str…
Browse files Browse the repository at this point in the history
…uct, to avoid duplicating daemon internal states, which may not be desirable especially for http server; get rid of two sets of dns servers that may be causing certificate errors
  • Loading branch information
HouzuoGuo committed Nov 26, 2017
1 parent 0f9b725 commit 74dbc13
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 308 deletions.
6 changes: 0 additions & 6 deletions daemon/dnsd/dnsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ var Forwarders = []string{
// Comodo SecureDNS (https://www.comodo.com/secure-dns/)
"8.26.56.26:53",
"8.20.247.20:53",
// Norton ConnectSafe A - Security (https://dns.norton.com/configureRouter.html)
"199.85.126.10:53",
"199.85.127.10:53",
// OpenDNS (https://www.opendns.com/home-internet-security/)
"208.67.222.222:53",
"208.67.222.220:53",
// Neustar free recursive DNS with threat protection (https://www.neustar.biz/security/dns-services/free-recursive-dns-service)
"156.154.70.2:53",
"156.154.71.2:53",
Expand Down
104 changes: 74 additions & 30 deletions daemon/httpd/httpd.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"net/url"
"os"
"reflect"
"strconv"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -67,8 +68,10 @@ type Daemon struct {
Processor *common.CommandProcessor `json:"-"` // Feature command processor
AllRateLimits map[string]*misc.RateLimit `json:"-"` // Aggregate all routes and their rate limit counters

server *http.Server // server is the HTTP service instance
logger misc.Logger
mux *http.ServeMux
serverWithTLS *http.Server // serverWithTLS is an instance of HTTP server that will be started with TLS listener.
serverNoTLS *http.Server // serverWithTLS is an instance of HTTP server that will be started with an ordinary listener.
logger misc.Logger
}

// Return path to Handler among special handlers that matches the specified type. Primarily used by test case code.
Expand Down Expand Up @@ -135,7 +138,7 @@ func (daemon *Daemon) Initialise() error {
return errors.New("httpd.Initialise: missing TLS certificate or key path")
}
// Install handlers with rate-limiting middleware
mux := new(http.ServeMux)
daemon.mux = new(http.ServeMux)
daemon.AllRateLimits = map[string]*misc.RateLimit{}
// Collect directory handlers
if daemon.ServeDirectories != nil {
Expand All @@ -155,7 +158,7 @@ func (daemon *Daemon) Initialise() error {
Logger: daemon.logger,
}
daemon.AllRateLimits[urlLocation] = rl
mux.HandleFunc(urlLocation, daemon.Middleware(rl, http.StripPrefix(urlLocation, http.FileServer(http.Dir(dirPath))).(http.HandlerFunc)))
daemon.mux.HandleFunc(urlLocation, daemon.Middleware(rl, http.StripPrefix(urlLocation, http.FileServer(http.Dir(dirPath))).(http.HandlerFunc)))
}
}
// Collect specialised handlers
Expand All @@ -169,53 +172,94 @@ func (daemon *Daemon) Initialise() error {
Logger: daemon.logger,
}
daemon.AllRateLimits[urlLocation] = rl
mux.HandleFunc(urlLocation, daemon.Middleware(rl, hand.Handle))
daemon.mux.HandleFunc(urlLocation, daemon.Middleware(rl, hand.Handle))
}
// Initialise all rate limits
for _, limit := range daemon.AllRateLimits {
limit.Initialise()
}
// Configure server with rather generous and sane defaults
daemon.server = &http.Server{
Addr: fmt.Sprintf("%s:%d", daemon.Address, daemon.Port),
Handler: mux,
return nil
}

/*
StartAndBlockNoTLS starts HTTP daemon and serve unencrypted connections. Blocks caller until StopNoTLS function is called.
You may call this function only after having called Initialise()!
*/
func (daemon *Daemon) StartAndBlockNoTLS() error {
/*
In order to determine the listener's port:
- On ElasticBeanstalk (and several other scenarios), listen on port number specified in environment PORT value.
- If TLS listener has not yet started, listen on the port specified by user configuration (prone to race condition).
In this way, caller of the function can determine whether to listen for TLS or ordinary traffic, one or the other.
*/
var port int
if envPort := strings.TrimSpace(os.Getenv("PORT")); envPort == "" {
if daemon.serverWithTLS == nil {
port = daemon.Port
} else {
return fmt.Errorf("httpd.StartAndBlockNoTLS: was about to listen on port %d but TLS listener is already using it", daemon.Port)
}
} else {
iPort, err := strconv.Atoi(envPort)
if err != nil {
return fmt.Errorf("httpd.StartAndBlockNoTLS: environment variable PORT value \"%s\" is not an integer", envPort)
}
port = iPort
}
// Configure servers with rather generous and sane defaults
daemon.serverNoTLS = &http.Server{
Addr: fmt.Sprintf("%s:%d", daemon.Address, port),
Handler: daemon.mux,
ReadTimeout: IOTimeoutSec * time.Second,
WriteTimeout: IOTimeoutSec * time.Second,
}
daemon.logger.Printf("StartAndBlockNoTLS", "", nil, "going to listen for HTTP connections")
if err := daemon.serverNoTLS.ListenAndServe(); err != nil {
if strings.Contains(err.Error(), "closed") {
return nil
}
return fmt.Errorf("httpd.StartAndBlockNoTLS: failed to listen on %s:%d - %v", daemon.Address, daemon.Port, err)
}
return nil
}

/*
StartAndBlockWithTLS starts HTTP daemon and serve encrypted connections. Blocks caller until StopNoTLS function is called.
You may call this function only after having called Initialise()!
Start HTTP daemon and block caller until Stop function is called.
*/
func (daemon *Daemon) StartAndBlock() error {
if daemon.TLSCertPath == "" {
daemon.logger.Printf("StartAndBlock", "", nil, "going to listen for HTTP connections")
if err := daemon.server.ListenAndServe(); err != nil {
if strings.Contains(err.Error(), "closed") {
return nil
}
return fmt.Errorf("httpd.StartAndBlock: failed to listen on %s:%d - %v", daemon.Address, daemon.Port, err)
}
} else {
daemon.logger.Printf("StartAndBlock", "", nil, "going to listen for HTTPS connections")
if err := daemon.server.ListenAndServeTLS(daemon.TLSCertPath, daemon.TLSKeyPath); err != nil {
if strings.Contains(err.Error(), "closed") {
return nil
}
return fmt.Errorf("httpd.StartAndBlock: failed to listen on %s:%d - %v", daemon.Address, daemon.Port, err)
func (daemon *Daemon) StartAndBlockWithTLS() error {
daemon.serverWithTLS = &http.Server{
Addr: fmt.Sprintf("%s:%d", daemon.Address, daemon.Port),
Handler: daemon.mux,
ReadTimeout: IOTimeoutSec * time.Second,
WriteTimeout: IOTimeoutSec * time.Second,
}
daemon.logger.Printf("StartAndBlockWithTLS", "", nil, "going to listen for HTTPS connections")
if err := daemon.serverWithTLS.ListenAndServeTLS(daemon.TLSCertPath, daemon.TLSKeyPath); err != nil {
if strings.Contains(err.Error(), "closed") {
return nil
}
return fmt.Errorf("httpd.StartAndBlockWithTLS: failed to listen on %s:%d - %v", daemon.Address, daemon.Port, err)
}
return nil
}

// Stop HTTP daemon.
func (daemon *Daemon) Stop() {
// Stop HTTP daemon - the listener without TLS.
func (daemon *Daemon) StopNoTLS() {
constraints, cancel := context.WithTimeout(context.Background(), time.Duration(IOTimeoutSec+2)*time.Second)
defer cancel()
if err := daemon.serverNoTLS.Shutdown(constraints); err != nil {
daemon.logger.Warningf("StopNoTLS", "", err, "failed to shutdown")
}
}

// Stop HTTP daemon - the listener with TLS.
func (daemon *Daemon) StopTLS() {
constraints, cancel := context.WithTimeout(context.Background(), time.Duration(IOTimeoutSec+2)*time.Second)
defer cancel()
if err := daemon.server.Shutdown(constraints); err != nil {
daemon.logger.Warningf("Stop", "", err, "failed to shutdown")
if err := daemon.serverWithTLS.Shutdown(constraints); err != nil {
daemon.logger.Warningf("StopTLS", "", err, "failed to shutdown")
}
}

Expand Down
8 changes: 4 additions & 4 deletions daemon/httpd/httpd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func TestHTTPD_StartAndBlock(t *testing.T) {
// HTTP daemon is expected to start in two seconds
var stoppedNormally bool
go func() {
if err := daemon.StartAndBlock(); err != nil {
if err := daemon.StartAndBlockNoTLS(); err != nil {
t.Fatal(err)
}
stoppedNormally = true
Expand All @@ -99,12 +99,12 @@ func TestHTTPD_StartAndBlock(t *testing.T) {
TestAPIHandlers(&daemon, t)

// Daemon must stop in a second
daemon.Stop()
daemon.StopNoTLS()
time.Sleep(1 * time.Second)
if !stoppedNormally {
t.Fatal("did not stop")
}
// Repeatedly stopping the daemon should have no negative consequence
daemon.Stop()
daemon.Stop()
daemon.StopNoTLS()
daemon.StopNoTLS()
}

0 comments on commit 74dbc13

Please sign in to comment.