/
server.go
96 lines (84 loc) · 2.92 KB
/
server.go
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
package common
import (
"context"
"errors"
"github.com/cossacklabs/acra/decryptor/base"
"github.com/cossacklabs/acra/network"
log "github.com/sirupsen/logrus"
"os"
"sync"
"time"
)
// TimeoutToExit timeout for exit operation for SServer component
const TimeoutToExit = time.Second
// ErrShutdownAttemptWithoutExit is an error occurred when context cancelled but Exit function is not called
var ErrShutdownAttemptWithoutExit = errors.New("unexpected way to shutdown service")
// NewEEAcraServerMainComponent creates new SServer wrapper
func NewEEAcraServerMainComponent(config *Config, proxyFactory base.ProxyFactory, errorChan chan os.Signal, restartChan chan os.Signal) (*SServer, error) {
return &SServer{
config: config,
connectionManager: network.NewConnectionManager(),
errorSignalChannel: errorChan,
restartSignalsChannel: restartChan,
proxyFactory: proxyFactory,
stopListenersSignal: make(chan bool),
errCh: make(chan error),
}, nil
}
// Exit exits SServer by sending the input error to the internal channel
// that is listened in Start function
func (server *SServer) Exit(err error) {
server.errCh <- err
close(server.errCh)
}
// StartServer starts SServer
func (server *SServer) StartServer(parentContext context.Context, group *sync.WaitGroup, enableHTTPApi bool) error {
if enableHTTPApi {
group.Add(1)
go func() {
defer group.Done()
server.StartCommands(parentContext)
}()
}
group.Add(1)
go func() {
defer group.Done()
server.Start(parentContext)
}()
// here we block execution until global context is done.
// Start and StartCommands also blocks on it so we are in sync when shutdown occurs
<-parentContext.Done()
return server.checkShutdownViaExitFunc()
}
// StartServerFromFileDescriptor starts SServer with appropriate file descriptors
func (server *SServer) StartServerFromFileDescriptor(parentContext context.Context, group *sync.WaitGroup, enableHTTPApi bool, fdAcra, fdAPI uintptr) error {
if enableHTTPApi {
group.Add(1)
go func() {
defer group.Done()
server.StartCommandsFromFileDescriptor(parentContext, fdAPI)
}()
}
group.Add(1)
go func() {
defer group.Done()
server.StartFromFileDescriptor(parentContext, fdAcra)
}()
// here we block execution until global context is done.
// StartFromFileDescriptor and StartCommandsFromFileDescriptor also blocks on it so we are in sync when shutdown occurs
<-parentContext.Done()
return server.checkShutdownViaExitFunc()
}
func (server *SServer) checkShutdownViaExitFunc() error {
// server has been stopped by global `cancel`. Here we check if some errors occurred. If so, it will exit with non-zero code
select {
case <-time.After(TimeoutToExit):
log.Errorf("Unexpected way to shutdown the service (Exit func is not called). Exit with non-zero status after timeout")
return ErrShutdownAttemptWithoutExit
case err := <-server.errCh:
if err != nil {
return err
}
return nil
}
}