コード例 #1
0
ファイル: app.go プロジェクト: jacksonh/go-horizon
// Serve starts the go-horizon system, binding it to a socket, setting up
// the shutdown signals and starting the appropriate db-streaming pumps.
func (a *App) Serve() {

	a.web.router.Compile()
	http.Handle("/", a.web.router)

	listenStr := fmt.Sprintf(":%d", a.config.Port)
	listener := bind.Socket(listenStr)
	log.Infof(a.ctx, "Starting horizon on %s", listener.Addr())

	graceful.HandleSignals()
	bind.Ready()
	graceful.PreHook(func() {
		log.Info(a.ctx, "received signal, gracefully stopping")
		a.Cancel()
	})
	graceful.PostHook(func() {
		log.Info(a.ctx, "stopped")
	})

	if a.config.Autopump {
		sse.SetPump(a.ctx, sse.AutoPump)
	} else {
		sse.SetPump(a.ctx, db.NewLedgerClosePump(a.ctx, a.historyDb))
	}

	err := graceful.Serve(listener, http.DefaultServeMux)

	if err != nil {
		log.Panic(a.ctx, err)
	}

	graceful.Wait()
}
コード例 #2
0
ファイル: gossip.go プロジェクト: parkr/gossip
func serve() {
	goji.DefaultMux.Compile()
	// Install our handler at the root of the standard net/http default mux.
	// This allows packages like expvar to continue working as expected.
	http.Handle("/", goji.DefaultMux)

	listener := bind.Socket(bind.Sniff())
	log.Println("Starting Goji on", listener.Addr())

	graceful.HandleSignals()
	bind.Ready()
	graceful.PreHook(func() { log.Printf("Goji received signal, gracefully stopping") })
	graceful.PostHook(func() {
		log.Printf("Goji stopped")
		log.Printf("Shutting down the server")
		handler.DB.Close()
		log.Printf("Database shut down. Terminating the process.")
	})

	err := graceful.Serve(listener, http.DefaultServeMux)

	if err != nil {
		log.Fatal(err)
	}

	graceful.Wait()
}
コード例 #3
0
ファイル: serve.go プロジェクト: gunosy/kami
// Serve starts kami with reasonable defaults.
// It works (exactly) like Goji, looking for Einhorn, the bind flag, GOJI_BIND...
func Serve() {
	if !flag.Parsed() {
		flag.Parse()
	}

	// Install our handler at the root of the standard net/http default mux.
	// This allows packages like expvar to continue working as expected.
	http.Handle("/", Handler())

	listener := bind.Default()
	log.Println("Starting kami on", listener.Addr())

	graceful.HandleSignals()
	bind.Ready()
	graceful.PreHook(func() { log.Printf("kami received signal, gracefully stopping") })
	graceful.PostHook(func() { log.Printf("kami stopped") })

	err := graceful.Serve(listener, http.DefaultServeMux)

	if err != nil {
		log.Fatal(err)
	}

	graceful.Wait()
}
コード例 #4
0
ファイル: main.go プロジェクト: palaiyacw/imgry
func main() {
	var err error
	flags.Parse(os.Args[1:])

	conf, err := server.NewConfigFromFile(*confFile, os.Getenv("CONFIG"))
	if err != nil {
		log.Fatal(err)
	}

	srv := server.New(conf)
	if err := srv.Configure(); err != nil {
		log.Fatal(err)
	}

	lg.Infof("** Imgry Server v%s at %s **", imgry.VERSION, srv.Config.Bind)
	lg.Infof("** Engine: %s", srv.ImageEngine.Version())

	graceful.AddSignal(syscall.SIGINT, syscall.SIGTERM)
	graceful.Timeout(30 * time.Second)
	graceful.PreHook(srv.Close)
	graceful.PostHook(srv.Shutdown)

	err = graceful.ListenAndServe(srv.Config.Bind, srv.NewRouter())
	if err != nil {
		lg.Fatal(err.Error())
	}
	graceful.Wait()
}
コード例 #5
0
ファイル: app.go プロジェクト: zenododobird/horizon
// Serve starts the horizon system, binding it to a socket, setting up
// the shutdown signals and starting the appropriate db-streaming pumps.
func (a *App) Serve() {

	a.web.router.Compile()
	http.Handle("/", a.web.router)

	listenStr := fmt.Sprintf(":%d", a.config.Port)
	listener := bind.Socket(listenStr)
	log.Infof("Starting horizon on %s", listener.Addr())

	graceful.HandleSignals()
	bind.Ready()
	graceful.PreHook(func() {
		log.Info("received signal, gracefully stopping")
		a.Close()
	})
	graceful.PostHook(func() {
		log.Info("stopped")
	})

	sse.SetPump(a.pump.Subscribe())

	err := graceful.Serve(listener, http.DefaultServeMux)

	if err != nil {
		log.Panic(err)
	}

	graceful.Wait()
}
コード例 #6
0
func main() {
	filename := flag.String("config", "config.toml", "Path to configuration file")

	flag.Parse()
	defer glog.Flush()

	var application = &system.Application{}

	application.Init(filename)
	application.LoadTemplates()

	// Setup static files
	static := web.New()
	publicPath := application.Config.Get("general.public_path").(string)
	static.Get("/assets/*", http.StripPrefix("/assets/", http.FileServer(http.Dir(publicPath))))

	http.Handle("/assets/", static)

	// Apply middleware
	goji.Use(application.ApplyTemplates)
	goji.Use(application.ApplySessions)
	goji.Use(application.ApplyDbMap)
	goji.Use(application.ApplyAuth)
	goji.Use(application.ApplyIsXhr)
	goji.Use(application.ApplyCsrfProtection)
	goji.Use(context.ClearHandler)

	controller := &controllers.MainController{}

	// Couple of files - in the real world you would use nginx to serve them.
	goji.Get("/robots.txt", http.FileServer(http.Dir(publicPath)))
	goji.Get("/favicon.ico", http.FileServer(http.Dir(publicPath+"/images")))

	// Home page
	goji.Get("/", application.Route(controller, "Index"))

	// Sign In routes
	goji.Get("/signin", application.Route(controller, "SignIn"))
	goji.Post("/signin", application.Route(controller, "SignInPost"))

	// Sign Up routes
	goji.Get("/signup", application.Route(controller, "SignUp"))
	goji.Post("/signup", application.Route(controller, "SignUpPost"))

	// KTHXBYE
	goji.Get("/logout", application.Route(controller, "Logout"))

	graceful.PostHook(func() {
		application.Close()
	})
	goji.Serve()
}
コード例 #7
0
ファイル: server.go プロジェクト: alexnnovak/annsto1
func main() {
	filename := flag.String("config", "config.json", "Path to configuration file")

	flag.Parse()
	defer glog.Flush()

	var application = &system.Application{}

	application.Init(filename)
	application.LoadTemplates()
	application.ConnectToDatabase()

	// Setup static files
	static := gojiweb.New()
	static.Get("/assets/*", http.StripPrefix("/assets/", http.FileServer(http.Dir(application.Configuration.PublicPath))))

	http.Handle("/assets/", static)

	// Apply middleware
	goji.Use(application.ApplyTemplates)
	goji.Use(application.ApplySessions)
	goji.Use(application.ApplyDatabase)
	goji.Use(application.ApplyAuth)

	controller := &web.Controller{}

	// Couple of files - in the real world you would use nginx to serve them.
	goji.Get("/robots.txt", http.FileServer(http.Dir(application.Configuration.PublicPath)))
	goji.Get("/favicon.ico", http.FileServer(http.Dir(application.Configuration.PublicPath+"/images")))

	// Homec page
	goji.Get("/", application.Route(controller, "Index"))

	// Sign In routes
	goji.Get("/signin", application.Route(controller, "SignIn"))
	goji.Post("/signin", application.Route(controller, "SignInPost"))

	// Sign Up routes
	goji.Get("/signup", application.Route(controller, "SignUp"))
	goji.Post("/signup", application.Route(controller, "SignUpPost"))

	// KTHXBYE
	goji.Get("/logout", application.Route(controller, "Logout"))

	graceful.PostHook(func() {
		application.Close()
	})
	goji.Serve()
}
コード例 #8
0
ファイル: server.go プロジェクト: backerman/eveindy
func serve(mux *web.Mux, bindProtocol, bindPort string) {
	// For now, this is completely lifted from goji's default handler.
	http.Handle("/", mux)
	log.Printf("Starting on %v/%v", bindProtocol, bindPort)
	graceful.HandleSignals()
	listener, err := net.Listen(bindProtocol, bindPort)
	if err != nil {
		log.Fatalf("Couldn't open socket on %v/%v: %v", bindProtocol, bindPort, err)
	}
	graceful.PreHook(func() { log.Info("Received signal, gracefully stopping.") })
	graceful.PostHook(func() { log.Info("Stopped.") })
	err = graceful.Serve(listener, http.DefaultServeMux)
	if err != nil {
		log.Fatalf("Couldn't serve on %v/%v: %v", bindProtocol, bindPort, err)
	}
	graceful.Wait()
}
コード例 #9
0
ファイル: server.go プロジェクト: johnwilson/webapp
func main() {
	webapp.Application.Init("config.toml")

	// serve static files
	goji.Use(webapp.Application.ApplyStatic)

	// plugins
	webapp.Application.RegisterPlugin("orm", new(plugins.Gorm))

	// controller
	pg := new(Page)
	pg.NewJobQueue("mailer", pg.SendMail, 2)
	goji.Get("/", pg.hello)
	goji.Get("/mail/:from/:to", pg.mailer)

	graceful.PostHook(func() {
		webapp.Application.Close()
	})
	goji.Serve()
}
コード例 #10
0
ファイル: server.go プロジェクト: rand99/taillachat
func main() {
	filename := flag.String("config", "config.json", "Path to configuration file")

	flag.Parse()
	defer glog.Flush()

	var application = &system.Application{}

	application.Init(filename)
	application.LoadTemplates()
	application.ConnectToDatabase()

	// Setup static files
	static := web.New()
	static.Get("/assets/*", http.StripPrefix("/assets/", http.FileServer(http.Dir(application.Configuration.PublicPath))))

	http.Handle("/assets/", static)

	// That's probably a terrible idea
	controllers.Template = application.Template
	controllers.MediaContent = application.Configuration.PublicPath + "/uploads/"

	http.Handle("/chat/", sockjs.NewHandler("/chat", sockjs.DefaultOptions, controllers.Chat))

	// Apply middleware
	goji.Use(application.ApplyTemplates)
	goji.Use(application.ApplySessions)
	goji.Use(application.ApplyDatabase)
	goji.Use(application.ApplyAuth)

	controller := &controllers.MainController{}

	goji.Get("/", application.Route(controller, "Index"))
	goji.Get("/terms", application.Route(controller, "Terms"))
	goji.Get("/privacy", application.Route(controller, "Privacy"))

	graceful.PostHook(func() {
		application.Close()
	})
	goji.Serve()
}
コード例 #11
0
ファイル: main.go プロジェクト: rightscale/go-boilerplate
// SetupMainServer allocates a listener socket and starts a web server with graceful restart
// on the specified IP address and port. The ipPort has the format "ip_address:port" or
// ":port" for 0.0.0.0/port.
func SetupMainServer(ipPort string, mux *web.Mux) {
	listener, err := net.Listen("tcp4", ipPort)
	if err != nil {
		FatalError(err.Error())
	}

	// Install our handler at the root of the standard net/http default mux.
	// This allows packages like expvar to continue working as expected.
	mux.Compile()
	http.Handle("/", mux)

	graceful.HandleSignals()
	graceful.PreHook(func() { log15.Warn("Gracefully stopping on signal") })
	graceful.PostHook(func() { log.Printf("Gracefully stopped") })

	err = graceful.Serve(listener, http.DefaultServeMux)
	if err != nil {
		FatalError(err.Error())
	}

	graceful.Wait()
}
コード例 #12
0
ファイル: kami-tls.go プロジェクト: yauhen-l/petrucho
/**
*There was no support of TLS in kami
*Copy-paste from Goji
**/
func ServeTLS(config *tls.Config) {
	if !flag.Parsed() {
		flag.Parse()
	}

	http.Handle("/", kami.Handler())

	listener := tls.NewListener(bind.Default(), config)
	log.Println("Starting kami on", listener.Addr())

	graceful.HandleSignals()
	bind.Ready()
	graceful.PreHook(func() { log.Printf("kami received signal, gracefully stopping") })
	graceful.PostHook(func() { log.Printf("kami stopped") })

	err := graceful.Serve(listener, http.DefaultServeMux)

	if err != nil {
		log.Fatal(err)
	}

	graceful.Wait()
}
コード例 #13
0
ファイル: server.go プロジェクト: denisbakhtin/defaultproject
func main() {
	env := flag.String("e", "development", "Application environment: development, production, testing")
	migration := flag.String("migrate", "", "Run DB migrations: up, down, redo, new [MIGRATION_NAME] and then os.Exit(0)")

	flag.Parse()
	logrus.SetFormatter(&logrus.TextFormatter{})
	logrus.SetOutput(os.Stderr)
	logrus.SetLevel(logrus.InfoLevel)

	var application = &system.Application{}

	//create rice boxes for folders with data
	configBox := rice.MustFindBox("config")         //config dir
	migrationsBox := rice.MustFindBox("migrations") //migrations dir
	viewsBox := rice.MustFindBox("views")           //views dir
	publicBox := rice.MustFindBox("public")         //public dir
	imagesBox := rice.MustFindBox("public/images")  //public/images dir

	application.Init(env, configBox)
	application.ConnectToDatabase()
	if len(*migration) > 0 {
		//Read https://github.com/rubenv/sql-migrate for more info about migrations
		application.RunMigrations(migrationsBox, migration)
		application.Close()
		os.Exit(0)
	}
	err := application.LoadTemplates(viewsBox)
	if err != nil {
		logrus.Fatal(err)
	}

	// Setup static files
	static := gojiweb.New()

	//static.Get("/assets/*", http.StripPrefix("/assets/", http.FileServer(http.Dir(application.Configuration.PublicPath))))
	static.Get("/assets/*", http.StripPrefix("/assets/", http.FileServer(publicBox.HTTPBox())))
	http.Handle("/assets/", static)

	// Couple of files - in the real world you would use nginx to serve them.
	goji.Get("/robots.txt", http.FileServer(publicBox.HTTPBox()))
	goji.Get("/favicon.ico", http.FileServer(imagesBox.HTTPBox()))

	//Apply middlewares
	goji.Use(application.ApplyTemplates)
	goji.Use(application.ApplySessions)
	goji.Use(application.ApplyDatabase)
	goji.Use(application.ApplyAuth)

	// Home page
	goji.Get("/", application.Route(web.Index))

	// Sign In routes
	goji.Get("/signin", application.Route(web.SignIn))
	goji.Post("/signin", application.Route(web.SignInPost))

	// Sign Up routes
	goji.Get("/signup", application.Route(web.SignUp))
	goji.Post("/signup", application.Route(web.SignUpPost))

	// KTHXBYE
	goji.Get("/logout", application.Route(web.Logout))

	graceful.PostHook(func() {
		application.Close()
	})
	goji.Serve()
}
コード例 #14
0
ファイル: main.go プロジェクト: Zenithar/golang-base-project
func main() {
	// Parse the flags
	flag.Parse()

	logrus.Infoln("**********************************************************")
	logrus.Infoln("goproject server starting ...")
	logrus.Infof("Version : %s (%s-%s)", version.Version, version.Revision, version.Branch)

	// Set localtime to UTC
	time.Local = time.UTC

	// Put config into the environment package
	shared.Config = &shared.Flags{
		BindAddress:      *bindAddress,
		LogFormatterType: *logFormatterType,
		ForceColors:      *forceColors,
		RavenDSN:         *ravenDSN,

		DatabaseDriver:    *databaseDriver,
		DatabaseHost:      *databaseHost,
		DatabaseNamespace: *databaseNamespace,
		DatabaseUser:      *databaseUser,
		DatabasePassword:  *databasePassword,

		MemcachedHosts: *memcachedHosts,
		RedisHost:      *redisHost,
	}

	// Generate a mux
	mux := system.Setup(shared.Config)

	// Make the mux handle every request
	http.Handle("/", mux)

	// Log that we're starting the server
	shared.Log.WithFields(logrus.Fields{
		"address": shared.Config.BindAddress,
	}).Info("Starting the HTTP server")

	// Initialize the goroutine listening to signals passed to the app
	graceful.HandleSignals()

	// Pre-graceful shutdown event
	graceful.PreHook(func() {
		shared.Log.Info("Received a signal, stopping the application")
	})

	// Post-shutdown event
	graceful.PostHook(func() {
		shared.Log.Info("Stopped the application")
	})

	// Listen to the passed address
	listener, err := net.Listen("tcp", shared.Config.BindAddress)
	if err != nil {
		shared.Log.WithFields(logrus.Fields{
			"error":   err,
			"address": *bindAddress,
		}).Fatal("Cannot set up a TCP listener")
	}

	// Start the listening
	err = graceful.Serve(listener, http.DefaultServeMux)
	if err != nil {
		// Don't use .Fatal! We need the code to shut down properly.
		shared.Log.Error(err)
	}

	// If code reaches this place, it means that it was forcefully closed.

	// Wait until open connections close.
	graceful.Wait()
}
コード例 #15
0
ファイル: server.go プロジェクト: decred/dcrstakepool
func runMain() int {
	// Load configuration and parse command line.  This function also
	// initializes logging and configures it accordingly.
	loadedCfg, _, err := loadConfig()
	if err != nil {
		return 1
	}
	cfg = loadedCfg
	log.Infof("Version: %s", version())
	log.Infof("Network: %s", activeNetParams.Params.Name)

	defer backendLog.Flush()

	var application = &system.Application{}

	application.Init(cfg.APISecret, cfg.BaseURL, cfg.CookieSecret,
		cfg.CookieSecure, cfg.DBHost, cfg.DBName, cfg.DBPassword, cfg.DBPort,
		cfg.DBUser)
	if err = application.LoadTemplates(cfg.TemplatePath); err != nil {
		log.Criticalf("Failed to load templates: %v", err)
		return 2
	}

	// Set up signal handler
	// SIGUSR1 = Reload html templates (On nix systems)
	system.ReloadTemplatesSig(application)

	dcrrpcclient.UseLogger(log)

	// Setup static files
	assetHandler := http.StripPrefix("/assets/",
		http.FileServer(http.Dir(cfg.PublicPath)))

	// Apply middleware
	app := web.New()
	app.Handle("/assets/*", assetHandler)

	app.Use(middleware.RequestID)
	app.Use(middleware.Logger) // TODO: reimplement to use our logger
	app.Use(middleware.Recoverer)

	// Execute various middleware functions.  The order is very important
	// as each function establishes part of the application environment/context
	// that the next function will assume has been setup successfully.
	app.Use(application.ApplyTemplates)
	app.Use(application.ApplySessions)
	app.Use(application.ApplyDbMap)
	app.Use(application.ApplyAPI)
	app.Use(application.ApplyAuth)
	app.Use(application.ApplyIsXhr)
	app.Use(application.ApplyCsrfProtection)
	app.Use(context.ClearHandler)

	// Supported API versions are advertised in the API stats result
	APIVersionsSupported := []int{1}

	controller, err := controllers.NewMainController(activeNetParams.Params,
		cfg.AdminIPs, cfg.APISecret, APIVersionsSupported, cfg.BaseURL,
		cfg.ClosePool, cfg.ClosePoolMsg, cfg.ColdWalletExtPub, cfg.PoolEmail,
		cfg.PoolFees, cfg.PoolLink, cfg.RecaptchaSecret, cfg.RecaptchaSitekey,
		cfg.SMTPFrom, cfg.SMTPHost, cfg.SMTPUsername, cfg.SMTPPassword,
		cfg.Version, cfg.WalletHosts, cfg.WalletCerts, cfg.WalletUsers,
		cfg.WalletPasswords, cfg.MinServers)
	if err != nil {
		application.Close()
		log.Errorf("Failed to initialize the main controller: %v",
			err)
		fmt.Fprintf(os.Stderr, "Fatal error in controller init: %v",
			err)
		return 3
	}

	err = controller.RPCSync(application.DbMap, cfg.SkipVoteBitsSync)
	if err != nil {
		application.Close()
		log.Errorf("Failed to sync the wallets: %v",
			err)
		return 4
	}

	controller.RPCStart()

	// Couple of files - in the real world you would use nginx to serve them.
	app.Get("/robots.txt", http.FileServer(http.Dir(cfg.PublicPath)))
	app.Get("/favicon.ico", http.FileServer(http.Dir(cfg.PublicPath+"/images")))

	// Home page
	app.Get("/", application.Route(controller, "Index"))

	// Address form
	app.Get("/address", application.Route(controller, "Address"))
	app.Post("/address", application.Route(controller, "AddressPost"))

	// API
	app.Handle("/api/v1/:command", application.APIHandler(controller.API))
	app.Handle("/api/*", gojify(system.APIInvalidHandler))

	// Email change/update confirmation
	app.Get("/emailupdate", application.Route(controller, "EmailUpdate"))

	// Email verification
	app.Get("/emailverify", application.Route(controller, "EmailVerify"))

	// Error page
	app.Get("/error", application.Route(controller, "Error"))

	// Password Reset routes
	app.Get("/passwordreset", application.Route(controller, "PasswordReset"))
	app.Post("/passwordreset", application.Route(controller, "PasswordResetPost"))

	// Password Update routes
	app.Get("/passwordupdate", application.Route(controller, "PasswordUpdate"))
	app.Post("/passwordupdate", application.Route(controller, "PasswordUpdatePost"))

	// Settings routes
	app.Get("/settings", application.Route(controller, "Settings"))
	app.Post("/settings", application.Route(controller, "SettingsPost"))

	// Sign In routes
	app.Get("/signin", application.Route(controller, "SignIn"))
	app.Post("/signin", application.Route(controller, "SignInPost"))

	// Sign Up routes
	app.Get("/signup", application.Route(controller, "SignUp"))
	app.Post("/signup", application.Route(controller, "SignUpPost"))

	// Stats
	app.Get("/stats", application.Route(controller, "Stats"))

	// Status
	app.Get("/status", application.Route(controller, "Status"))

	// Tickets routes
	app.Get("/tickets", application.Route(controller, "Tickets"))
	app.Post("/tickets", application.Route(controller, "TicketsPost"))

	// KTHXBYE
	app.Get("/logout", application.Route(controller, "Logout"))

	graceful.PostHook(func() {
		controller.RPCStop()
		application.Close()
	})
	app.Abandon(middleware.Logger)
	app.Compile()

	server := &http.Server{Handler: app}
	listener, err := listenTo(cfg.Listen)
	if err != nil {
		log.Errorf("could not bind %v", err)
		return 5
	}

	log.Infof("listening on %v", listener.Addr())

	if err = server.Serve(listener); err != nil {
		log.Errorf("Serve error: %s", err.Error())
		return 6
	}

	return 0
}
コード例 #16
0
ファイル: main.go プロジェクト: carriercomm/api-1
func main() {
	// Parse the flags
	flag.Parse()

	// Put config into the environment package
	env.Config = &env.Flags{
		BindAddress:      *bindAddress,
		APIVersion:       *apiVersion,
		LogFormatterType: *logFormatterType,
		ForceColors:      *forceColors,
		EmailDomain:      *emailDomain,

		SessionDuration: *sessionDuration,

		RedisAddress:  *redisAddress,
		RedisDatabase: *redisDatabase,
		RedisPassword: *redisPassword,

		RethinkDBAddress:  *rethinkdbAddress,
		RethinkDBKey:      *rethinkdbKey,
		RethinkDBDatabase: *rethinkdbDatabase,

		NSQdAddress:    *nsqdAddress,
		LookupdAddress: *lookupdAddress,

		YubiCloudID:  *yubiCloudID,
		YubiCloudKey: *yubiCloudKey,

		SlackURL:      *slackURL,
		SlackLevels:   *slackLevels,
		SlackChannel:  *slackChannel,
		SlackIcon:     *slackIcon,
		SlackUsername: *slackUsername,

		BloomFilter: *bloomFilter,
		BloomCount:  *bloomCount,

		RavenDSN: *ravenDSN,
	}

	// Generate a mux
	mux := setup.PrepareMux(env.Config)

	// Make the mux handle every request
	http.Handle("/", mux)

	// Log that we're starting the server
	env.Log.WithFields(logrus.Fields{
		"address": env.Config.BindAddress,
	}).Info("Starting the HTTP server")

	// Initialize the goroutine listening to signals passed to the app
	graceful.HandleSignals()

	// Pre-graceful shutdown event
	graceful.PreHook(func() {
		env.Log.Info("Received a singnal, stopping the application")
	})

	// Post-shutdown event
	graceful.PostHook(func() {
		env.Log.Info("Stopped the application")
	})

	// Listen to the passed address
	listener, err := net.Listen("tcp", env.Config.BindAddress)
	if err != nil {
		env.Log.WithFields(logrus.Fields{
			"error":   err,
			"address": *bindAddress,
		}).Fatal("Cannot set up a TCP listener")
	}

	// Start the listening
	err = graceful.Serve(listener, http.DefaultServeMux)
	if err != nil {
		// Don't use .Fatal! We need the code to shut down properly.
		env.Log.Error(err)
	}

	// If code reaches this place, it means that it was forcefully closed.

	// Wait until open connections close.
	graceful.Wait()
}