-
Notifications
You must be signed in to change notification settings - Fork 0
/
exec_response.go
115 lines (85 loc) · 2.37 KB
/
exec_response.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
// SPDX-FileCopyrightText: 2021 M. Shulhan <[email protected]>
// SPDX-License-Identifier: GPL-3.0-or-later
package awwan
import (
"fmt"
"strconv"
"sync"
"time"
"git.sr.ht/~shulhan/pakakeh.go/lib/http/sseclient"
)
// ExecResponse contains the request and output of command execution, from
// ExecRequest.
type ExecResponse struct {
// Queue that push the each line in Output as event.
eventq chan sseclient.Event
// Copy of request.
Mode string `json:"mode"`
Script string `json:"script"`
LineRange string `json:"line_range"`
// ID of execution request that can be used to stream output or
// got get full status.
ID string `json:"id"`
// BeginAt contains when the execution begin.
BeginAt string `json:"begin_at"`
// EndAt contains when the execution finished.
EndAt string `json:"end_at"`
Error string `json:"error"`
Output []string `json:"output"`
// mtxOutput protect read/write on Output.
mtxOutput sync.Mutex
}
func newExecResponse(req *ExecRequest) (execRes *ExecResponse) {
var now = timeNow().UTC()
execRes = &ExecResponse{
Mode: req.Mode,
Script: req.Script,
LineRange: req.LineRange,
ID: fmt.Sprintf(`%s:%s:%s:%d`, req.Mode, req.Script, req.LineRange, now.Unix()),
BeginAt: now.Format(time.RFC3339),
Output: make([]string, 0, 8),
eventq: make(chan sseclient.Event, 512),
}
// Use the ExecResponse itself as handler for output.
req.registerLogWriter(`response`, execRes)
return execRes
}
// Write convert the raw output from execution into multiline string, and
// push it to field Output.
func (execRes *ExecResponse) Write(out []byte) (n int, err error) {
if len(out) == 0 {
return 0, nil
}
execRes.mtxOutput.Lock()
var ev = sseclient.Event{
Data: string(out),
ID: strconv.FormatInt(int64(len(execRes.Output)), 10),
}
execRes.Output = append(execRes.Output, ev.Data)
select {
case execRes.eventq <- ev:
default:
}
execRes.mtxOutput.Unlock()
return len(out), nil
}
// end mark the execution completed, possibly with error.
func (execRes *ExecResponse) end(execErr error) {
var ev sseclient.Event
if execErr != nil {
execRes.Error = execErr.Error()
ev.Data = execRes.Error
select {
case execRes.eventq <- ev:
default:
}
}
execRes.EndAt = timeNow().UTC().Format(time.RFC3339)
ev.Type = `end`
ev.Data = execRes.EndAt
select {
case execRes.eventq <- ev:
default:
}
close(execRes.eventq)
}