/
task.go
133 lines (113 loc) · 2.9 KB
/
task.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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package provider
import (
"net"
"net/url"
"sync"
"time"
log "github.com/Sirupsen/logrus"
"github.com/mistifyio/acomm"
)
// TaskHandler if the request handler function for a particular task. It should
// return results or an error, but not both.
type TaskHandler func(*acomm.Request) (interface{}, *url.URL, error)
// task contains the request listener and handler for a task.
type task struct {
name string
handler TaskHandler
reqTimeout time.Duration
reqListener *acomm.UnixListener
waitgroup sync.WaitGroup
}
// newTask creates and initializes a new task.
func newTask(name, socketPath string, reqTimeout time.Duration, handler TaskHandler) *task {
return &task{
name: name,
handler: handler,
reqTimeout: reqTimeout,
reqListener: acomm.NewUnixListener(socketPath, 0),
}
}
// start starts the task handler.
func (t *task) start() error {
if err := t.reqListener.Start(); err != nil {
return err
}
go t.handleConns()
return nil
}
// stop shuts down the task handler.
func (t *task) stop() {
// Stop request listener and handle all open connections
t.reqListener.Stop(0)
// Wait for all actively handled requests
t.waitgroup.Wait()
}
func (t *task) handleConns() {
for {
conn := t.reqListener.NextConn()
if conn == nil {
return
}
go t.acceptRequest(conn)
}
}
func (t *task) acceptRequest(conn net.Conn) {
defer t.reqListener.DoneConn(conn)
var respErr error
req := &acomm.Request{}
if err := acomm.UnmarshalConnData(conn, req); err != nil {
respErr = err
}
if err := req.Validate(); err != nil {
respErr = err
}
// Respond to the initial request
resp, err := acomm.NewResponse(req, nil, nil, respErr)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"req": req,
"respErr": respErr,
}).Error("failed to create initial response")
return
}
if err := acomm.SendConnData(conn, resp); err != nil {
return
}
if respErr != nil {
return
}
// Actually perform the task
t.waitgroup.Add(1)
go t.handleRequest(req)
}
// handleRequest runs the task-specific handler and sends the results to the
// request's response hook.
func (t *task) handleRequest(req *acomm.Request) {
defer t.waitgroup.Done()
// Run the task-specific request handler
result, streamAddr, taskErr := t.handler(req)
// Note: The acomm calls log the error already, but we want to have a log
// of the request and response data as well.
resp, err := acomm.NewResponse(req, result, streamAddr, taskErr)
if err != nil {
log.WithFields(log.Fields{
"task": t.name,
"req": req,
"taskResult": result,
"taskErr": taskErr,
"error": err,
}).Error("failed to create response")
return
}
if err := req.Respond(resp); err != nil {
log.WithFields(log.Fields{
"task": t.name,
"req": req,
"taskResult": result,
"taskErr": taskErr,
"error": err,
}).Error("failed to send response")
return
}
}