webapi: Split Start func into New and Run funcs.
Separating the single func grants the calling code greater control over the webapi lifecycle.
This commit is contained in:
parent
a7bb0cd9d7
commit
0742e0ff1a
@ -136,10 +136,7 @@ func (v *vspd) run() int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitGroup for services to signal when they have shutdown cleanly.
|
// Create webapi server.
|
||||||
var shutdownWg sync.WaitGroup
|
|
||||||
|
|
||||||
// Create and start webapi server.
|
|
||||||
apiCfg := webapi.Config{
|
apiCfg := webapi.Config{
|
||||||
Listen: v.cfg.Listen,
|
Listen: v.cfg.Listen,
|
||||||
VSPFee: v.cfg.VSPFee,
|
VSPFee: v.cfg.VSPFee,
|
||||||
@ -153,15 +150,17 @@ func (v *vspd) run() int {
|
|||||||
MaxVoteChangeRecords: maxVoteChangeRecords,
|
MaxVoteChangeRecords: maxVoteChangeRecords,
|
||||||
VspdVersion: version.String(),
|
VspdVersion: version.String(),
|
||||||
}
|
}
|
||||||
err = webapi.Start(ctx, requestShutdown, &shutdownWg, v.db, v.cfg.logger("API"),
|
api, err := webapi.New(v.db, v.cfg.logger("API"), v.dcrd, v.wallets, apiCfg)
|
||||||
v.dcrd, v.wallets, apiCfg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.log.Errorf("Failed to initialize webapi: %v", err)
|
v.log.Errorf("Failed to initialize webapi: %v", err)
|
||||||
requestShutdown()
|
|
||||||
shutdownWg.Wait()
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WaitGroup for services to signal when they have shutdown cleanly.
|
||||||
|
var shutdownWg sync.WaitGroup
|
||||||
|
|
||||||
|
api.Run(ctx, requestShutdown, &shutdownWg)
|
||||||
|
|
||||||
// Start all background tasks and notification handlers.
|
// Start all background tasks and notification handlers.
|
||||||
shutdownWg.Add(1)
|
shutdownWg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@ -73,16 +73,17 @@ type WebAPI struct {
|
|||||||
cache *cache
|
cache *cache
|
||||||
signPrivKey ed25519.PrivateKey
|
signPrivKey ed25519.PrivateKey
|
||||||
signPubKey ed25519.PublicKey
|
signPubKey ed25519.PublicKey
|
||||||
|
server *http.Server
|
||||||
|
listener net.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGroup,
|
func New(vdb *database.VspDatabase, log slog.Logger, dcrd rpc.DcrdConnect,
|
||||||
vdb *database.VspDatabase, log slog.Logger, dcrd rpc.DcrdConnect,
|
wallets rpc.WalletConnect, cfg Config) (*WebAPI, error) {
|
||||||
wallets rpc.WalletConnect, cfg Config) error {
|
|
||||||
|
|
||||||
// Get keys for signing API responses from the database.
|
// Get keys for signing API responses from the database.
|
||||||
signPrivKey, signPubKey, err := vdb.KeyPair()
|
signPrivKey, signPubKey, err := vdb.KeyPair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("db.Keypair error: %w", err)
|
return nil, fmt.Errorf("db.Keypair error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate cached VSP stats before starting webserver.
|
// Populate cached VSP stats before starting webserver.
|
||||||
@ -97,23 +98,30 @@ func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGro
|
|||||||
// use them to initialize the address generator.
|
// use them to initialize the address generator.
|
||||||
idx, err := vdb.GetLastAddressIndex()
|
idx, err := vdb.GetLastAddressIndex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("db.GetLastAddressIndex error: %w", err)
|
return nil, fmt.Errorf("db.GetLastAddressIndex error: %w", err)
|
||||||
}
|
}
|
||||||
feeXPub, err := vdb.FeeXPub()
|
feeXPub, err := vdb.FeeXPub()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("db.GetFeeXPub error: %w", err)
|
return nil, fmt.Errorf("db.GetFeeXPub error: %w", err)
|
||||||
}
|
}
|
||||||
addrGen, err := newAddressGenerator(feeXPub, cfg.Network.Params, idx, log)
|
addrGen, err := newAddressGenerator(feeXPub, cfg.Network.Params, idx, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize fee address generator: %w", err)
|
return nil, fmt.Errorf("failed to initialize fee address generator: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the secret key used to initialize the cookie store.
|
// Get the secret key used to initialize the cookie store.
|
||||||
cookieSecret, err := vdb.CookieSecret()
|
cookieSecret, err := vdb.CookieSecret()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("db.GetCookieSecret error: %w", err)
|
return nil, fmt.Errorf("db.GetCookieSecret error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create TCP listener.
|
||||||
|
listener, err := net.Listen("tcp", cfg.Listen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Infof("Listening on %s", cfg.Listen)
|
||||||
|
|
||||||
w := &WebAPI{
|
w := &WebAPI{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
db: vdb,
|
db: vdb,
|
||||||
@ -122,48 +130,46 @@ func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGro
|
|||||||
cache: cache,
|
cache: cache,
|
||||||
signPrivKey: signPrivKey,
|
signPrivKey: signPrivKey,
|
||||||
signPubKey: signPubKey,
|
signPubKey: signPubKey,
|
||||||
|
listener: listener,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create TCP listener.
|
w.server = &http.Server{
|
||||||
var listenConfig net.ListenConfig
|
|
||||||
listener, err := listenConfig.Listen(ctx, "tcp", cfg.Listen)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Infof("Listening on %s", cfg.Listen)
|
|
||||||
|
|
||||||
srv := http.Server{
|
|
||||||
Handler: w.router(cookieSecret, dcrd, wallets),
|
Handler: w.router(cookieSecret, dcrd, wallets),
|
||||||
ReadTimeout: 5 * time.Second, // slow requests should not hold connections opened
|
ReadTimeout: 5 * time.Second, // slow requests should not hold connections opened
|
||||||
WriteTimeout: 60 * time.Second, // hung responses must die
|
WriteTimeout: 60 * time.Second, // hung responses must die
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WebAPI) Run(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGroup) {
|
||||||
|
|
||||||
// Add the graceful shutdown to the waitgroup.
|
// Add the graceful shutdown to the waitgroup.
|
||||||
shutdownWg.Add(1)
|
shutdownWg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
// Wait until shutdown is signaled before shutting down.
|
// Wait until shutdown is signaled before shutting down.
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
||||||
log.Debug("Stopping webserver...")
|
w.log.Debug("Stopping webserver...")
|
||||||
// Give the webserver 5 seconds to finish what it is doing.
|
// Give the webserver 10 seconds to finish what it is doing.
|
||||||
timeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
timeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := srv.Shutdown(timeoutCtx); err != nil {
|
if err := w.server.Shutdown(timeoutCtx); err != nil {
|
||||||
log.Errorf("Failed to stop webserver cleanly: %v", err)
|
w.log.Errorf("Failed to stop webserver cleanly: %v", err)
|
||||||
} else {
|
} else {
|
||||||
log.Debug("Webserver stopped")
|
w.log.Debug("Webserver stopped")
|
||||||
}
|
}
|
||||||
shutdownWg.Done()
|
shutdownWg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Start webserver.
|
// Start webserver.
|
||||||
go func() {
|
go func() {
|
||||||
err := srv.Serve(listener)
|
err := w.server.Serve(w.listener)
|
||||||
// If the server dies for any reason other than ErrServerClosed (from
|
// If the server dies for any reason other than ErrServerClosed (from
|
||||||
// graceful server.Shutdown), log the error and request vspd be
|
// graceful server.Shutdown), log the error and request vspd be
|
||||||
// shutdown.
|
// shutdown.
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
log.Errorf("Unexpected webserver error: %v", err)
|
w.log.Errorf("Unexpected webserver error: %v", err)
|
||||||
requestShutdown()
|
requestShutdown()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -185,13 +191,11 @@ func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGro
|
|||||||
case <-time.After(refresh):
|
case <-time.After(refresh):
|
||||||
err := w.cache.update()
|
err := w.cache.update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to update cached VSP stats: %v", err)
|
w.log.Errorf("Failed to update cached VSP stats: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WebAPI) router(cookieSecret []byte, dcrd rpc.DcrdConnect, wallets rpc.WalletConnect) *gin.Engine {
|
func (w *WebAPI) router(cookieSecret []byte, dcrd rpc.DcrdConnect, wallets rpc.WalletConnect) *gin.Engine {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user