102 lines
3.1 KiB
Go
102 lines
3.1 KiB
Go
package upstreamimpl
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model"
|
|
)
|
|
|
|
const (
|
|
defaultUpstreamHTTPTimeout = 10 * time.Second
|
|
upstreamHTTPTimeoutKey = "dependencies.upstream_http.timeout"
|
|
)
|
|
|
|
func templateConfigJSON(channelConfig *model.SimChannelConfigItemCache) string {
|
|
if channelConfig == nil || channelConfig.Template == nil {
|
|
return ""
|
|
}
|
|
return channelConfig.Template.TempConfig
|
|
}
|
|
|
|
// parseUpstreamDuration accepts Go duration strings and positive numeric
|
|
// seconds so template and application configuration use the same contract.
|
|
func parseUpstreamDuration(raw json.RawMessage, field string) (time.Duration, error) {
|
|
if len(raw) == 0 || string(raw) == "null" {
|
|
return 0, fmt.Errorf("%s is empty", field)
|
|
}
|
|
var value any
|
|
if err := json.Unmarshal(raw, &value); err != nil {
|
|
return 0, fmt.Errorf("invalid %s: %w", field, err)
|
|
}
|
|
switch v := value.(type) {
|
|
case float64:
|
|
if v <= 0 || v != float64(int64(v)) {
|
|
return 0, fmt.Errorf("invalid %s: expected positive seconds", field)
|
|
}
|
|
return time.Duration(v) * time.Second, nil
|
|
case string:
|
|
value := strings.TrimSpace(v)
|
|
if value == "" {
|
|
return 0, fmt.Errorf("invalid %s: empty value", field)
|
|
}
|
|
duration, err := time.ParseDuration(value)
|
|
if err != nil || duration <= 0 {
|
|
return 0, fmt.Errorf("invalid %s: expected positive duration", field)
|
|
}
|
|
return duration, nil
|
|
default:
|
|
return 0, fmt.Errorf("invalid %s: expected duration or seconds", field)
|
|
}
|
|
}
|
|
|
|
func parseUpstreamConfigDuration(value any, field string) (time.Duration, error) {
|
|
raw, err := json.Marshal(value)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("encode %s: %w", field, err)
|
|
}
|
|
return parseUpstreamDuration(raw, field)
|
|
}
|
|
|
|
func loadDefaultUpstreamHTTPTimeout(ctx context.Context) time.Duration {
|
|
value := g.Cfg().MustGet(ctx, upstreamHTTPTimeoutKey, defaultUpstreamHTTPTimeout.String()).Val()
|
|
timeout, err := parseUpstreamConfigDuration(value, upstreamHTTPTimeoutKey)
|
|
if err != nil {
|
|
g.Log().Warningf(ctx, "%v, fallback to %s", err, defaultUpstreamHTTPTimeout)
|
|
return defaultUpstreamHTTPTimeout
|
|
}
|
|
return timeout
|
|
}
|
|
|
|
// resolveUpstreamHTTPTimeout applies the single timeout override for a
|
|
// template. Missing or invalid template values fall back to application
|
|
// configuration and finally the code default.
|
|
func resolveUpstreamHTTPTimeout(ctx context.Context, tempConfig string) time.Duration {
|
|
defaultTimeout := loadDefaultUpstreamHTTPTimeout(ctx)
|
|
if strings.TrimSpace(tempConfig) == "" {
|
|
return defaultTimeout
|
|
}
|
|
var raw struct {
|
|
HTTP struct {
|
|
Timeout json.RawMessage `json:"timeout"`
|
|
} `json:"http"`
|
|
}
|
|
if err := json.Unmarshal([]byte(tempConfig), &raw); err != nil {
|
|
g.Log().Warningf(ctx, "parse upstream template config failed: %v, fallback to %s", err, defaultTimeout)
|
|
return defaultTimeout
|
|
}
|
|
if len(raw.HTTP.Timeout) == 0 || string(raw.HTTP.Timeout) == "null" {
|
|
return defaultTimeout
|
|
}
|
|
timeout, err := parseUpstreamDuration(raw.HTTP.Timeout, "http.timeout")
|
|
if err != nil {
|
|
g.Log().Warningf(ctx, "%v, fallback to %s", err, defaultTimeout)
|
|
return defaultTimeout
|
|
}
|
|
return timeout
|
|
}
|