131 lines
3.5 KiB
Go
131 lines
3.5 KiB
Go
package orbit
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type handlerCompletion struct {
|
|
attemptID string
|
|
result WorkerFinish
|
|
}
|
|
|
|
func (w *Worker) invoke(ctx context.Context, handler WorkerHandler, execution WorkerExecution, ref WorkerAttempt, results chan<- handlerCompletion, runDone <-chan struct{}) {
|
|
out := WorkerFinish{Ref: ref, Outcome: WorkerUnknown, ErrorCode: "HANDLER_ABORTED", Result: json.RawMessage(`{}`)}
|
|
completed := false
|
|
defer func() {
|
|
if recover() != nil {
|
|
out = unknownHandlerResult(ref, "HANDLER_PANIC")
|
|
} else if !completed {
|
|
out = unknownHandlerResult(ref, "HANDLER_ABORTED")
|
|
}
|
|
w.running.Add(-1)
|
|
select {
|
|
case results <- handlerCompletion{attemptID: ref.AttemptID, result: out}:
|
|
case <-runDone:
|
|
}
|
|
}()
|
|
if ctx.Err() != nil {
|
|
out = unknownHandlerResult(ref, "LEASE_LOST")
|
|
completed = true
|
|
return
|
|
}
|
|
w.started.Add(1)
|
|
result, err := handler.Handle(ctx, execution)
|
|
if err == nil {
|
|
result = append(json.RawMessage(nil), result...)
|
|
if validHandlerResult(result) {
|
|
out = WorkerFinish{Ref: ref, Outcome: WorkerSucceeded, Result: result}
|
|
} else {
|
|
out = unknownHandlerResult(ref, "INVALID_HANDLER_RESULT")
|
|
}
|
|
} else {
|
|
var known *WorkerFailure
|
|
if errors.As(err, &known) && known != nil && workerName(known.Code, 64) {
|
|
details := append(json.RawMessage(nil), known.Details...)
|
|
if len(details) == 0 {
|
|
details = json.RawMessage(`{}`)
|
|
}
|
|
if validHandlerResult(details) {
|
|
out = WorkerFinish{Ref: ref, Outcome: WorkerFailed, ErrorCode: known.Code, Result: details}
|
|
} else {
|
|
out = unknownHandlerResult(ref, "INVALID_HANDLER_RESULT")
|
|
}
|
|
} else if errors.Is(context.Cause(ctx), ErrExecutionLeaseLost) {
|
|
out = unknownHandlerResult(ref, "LEASE_LOST")
|
|
} else {
|
|
out = unknownHandlerResult(ref, "HANDLER_ERROR")
|
|
}
|
|
}
|
|
completed = true
|
|
}
|
|
func unknownHandlerResult(ref WorkerAttempt, code string) WorkerFinish {
|
|
return WorkerFinish{Ref: ref, Outcome: WorkerUnknown, ErrorCode: code, Result: json.RawMessage(`{}`)}
|
|
}
|
|
|
|
// validHandlerResult 提前拒绝服务端无法持久化的结果,避免失效报告无限重报。
|
|
// 只检验数值范围;不把精确业务数字改写成 float64。
|
|
func validHandlerResult(raw json.RawMessage) bool {
|
|
if len(raw) == 0 || len(raw) > 256<<10 || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return false
|
|
}
|
|
// 预留 task.finish 的信封/body 嵌套,避免合法结果单独校验后在帧校验阶段超深。
|
|
frame := append([]byte(`{"body":{"result":`), raw...)
|
|
frame = append(frame, []byte(`}}`)...)
|
|
if validateWorkerJSON(frame) != nil {
|
|
return false
|
|
}
|
|
d := json.NewDecoder(bytes.NewReader(raw))
|
|
d.UseNumber()
|
|
for {
|
|
value, err := d.Token()
|
|
if err == io.EOF {
|
|
return true
|
|
}
|
|
if err != nil {
|
|
return false
|
|
}
|
|
number, ok := value.(json.Number)
|
|
if !ok {
|
|
continue
|
|
}
|
|
s := string(number)
|
|
if len(s) > 256 {
|
|
return false
|
|
}
|
|
if _, err := strconv.ParseFloat(s, 64); err != nil {
|
|
return false
|
|
}
|
|
s = strings.TrimPrefix(s, "-")
|
|
exponent := 0
|
|
if i := strings.IndexAny(s, "eE"); i >= 0 {
|
|
var err error
|
|
exponent, err = strconv.Atoi(s[i+1:])
|
|
if err != nil || exponent < -308 || exponent > 308 {
|
|
return false
|
|
}
|
|
s = s[:i]
|
|
}
|
|
if i := strings.IndexByte(s, '.'); i >= 0 {
|
|
exponent -= len(s) - i - 1
|
|
s = s[:i] + s[i+1:]
|
|
}
|
|
s = strings.TrimLeft(s, "0")
|
|
if s == "" {
|
|
continue
|
|
}
|
|
for strings.HasSuffix(s, "0") {
|
|
s = s[:len(s)-1]
|
|
exponent++
|
|
}
|
|
if exponent < -308 || exponent > 308 || exponent+len(s)-1 > 308 {
|
|
return false
|
|
}
|
|
}
|
|
}
|