Files

322 lines
8.8 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package runtimecontext supplies an optional, process-scoped request context.
package runtimecontext
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
"unicode"
"unicode/utf8"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/requestmeta"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/runtimepayload"
)
const (
HeaderName = requestmeta.DingTalkExtHeader
PayloadVersion = runtimepayload.PayloadVersion
Environment = "online"
initializationEnvironment int32 = 0
maxHeaderBytes = 8 * 1024
defaultTimeout = 3 * time.Second
)
type State string
const (
StateReady State = "ready"
StateUnavailable State = "unavailable"
StateTimeout State = "timeout"
StateError State = "error"
)
// Result is the immutable process result. Sensitive values stay private and
// cannot be serialized or formatted accidentally.
type Result struct {
State State `json:"state"`
PayloadVersion string `json:"payload_version"`
Environment string `json:"environment"`
Elapsed time.Duration `json:"-"`
ErrorCategory string `json:"error_category,omitempty"`
token string
callbackCode int32
hasCallbackCode bool
}
func (r Result) String() string {
return fmt.Sprintf("runtime context state=%s", r.State)
}
func (r Result) GoString() string { return r.String() }
// HeaderValue returns the compact request header payload when initialization
// completed successfully.
func (r Result) HeaderValue() (string, bool) {
if r.State != StateReady || r.token == "" {
return "", false
}
payload, err := json.Marshal(struct {
UMID string `json:"umid"`
}{UMID: r.token})
if err != nil || len(payload) > maxHeaderBytes {
return "", false
}
return string(payload), true
}
// AttachToURL attaches the private runtime value to a login authorization URL.
// The URL may be opened in a browser or shown as a manual login link. Callers
// must never send it to diagnostic logs or persist it as application state.
func (r Result) AttachToURL(rawURL string) (string, bool) {
if r.State != StateReady {
return rawURL, false
}
if _, err := validateToken([]byte(r.token)); err != nil {
return rawURL, false
}
parsed, err := url.Parse(rawURL)
if err != nil || parsed.Host == "" || (parsed.Scheme != "https" && parsed.Scheme != "http") || parsed.User != nil {
return rawURL, false
}
query, err := url.ParseQuery(parsed.RawQuery)
if err != nil {
return rawURL, false
}
query.Set("callerUmt", r.token)
query.Set("caller", "dws")
parsed.RawQuery = query.Encode()
return parsed.String(), true
}
// DiagnosticDetail returns redacted, stable diagnostic fields.
func (r Result) DiagnosticDetail() map[string]any {
detail := map[string]any{
"state": r.State,
"payload_version": r.PayloadVersion,
"environment": r.Environment,
"elapsed_ms": r.Elapsed.Milliseconds(),
"token_length": len(r.token),
"token_fingerprint": "",
}
if r.token != "" {
sum := sha256.Sum256([]byte(r.token))
detail["token_fingerprint"] = hex.EncodeToString(sum[:])[:12]
}
if r.ErrorCategory != "" {
detail["error_category"] = r.ErrorCategory
}
if r.hasCallbackCode {
detail["callback_code"] = r.callbackCode
}
return detail
}
type callbackResult struct {
token string
code int32
err error
}
type nativeSession struct {
handle uintptr
callback uintptr
}
type nativeStarter func(string, func([]byte, int32, error)) (nativeSession, int32, error)
type libraryLocator func() (string, error)
var (
errNativeLoad = errors.New("runtime library load failed")
errNativeSymbol = errors.New("runtime symbol unavailable")
errNativeBinding = errors.New("runtime binding failed")
)
type provider struct {
once sync.Once
result Result
session nativeSession
start nativeStarter
locate libraryLocator
timeout time.Duration
}
func newProvider(start nativeStarter, locate libraryLocator, timeout time.Duration) *provider {
return &provider{start: start, locate: locate, timeout: timeout}
}
var defaultProvider = newProvider(startNative, resolveLibraryPath, defaultTimeout)
// Resolve initializes and returns the process-scoped context. It never
// retries: every caller observes the same immutable outcome.
func Resolve() Result {
return defaultProvider.resolve()
}
func (p *provider) resolve() Result {
p.once.Do(func() {
p.result = p.initialize()
})
return p.result
}
func (p *provider) initialize() Result {
started := time.Now()
result := Result{PayloadVersion: PayloadVersion, Environment: Environment}
path, err := p.locate()
if err != nil {
result.State = StateUnavailable
result.ErrorCategory = classifyLocationError(err)
result.Elapsed = time.Since(started)
return result
}
callback := make(chan callbackResult, 1)
session, accepted, err := p.start(path, func(raw []byte, code int32, callbackErr error) {
value := callbackResult{code: code, err: callbackErr}
if callbackErr == nil {
value.token, value.err = validateToken(raw)
}
select {
case callback <- value:
default:
}
})
p.session = session
if err != nil {
result.State = StateError
result.ErrorCategory = classifyStartError(err)
result.Elapsed = time.Since(started)
return result
}
if accepted == 0 {
result.State = StateError
result.ErrorCategory = "initialization_rejected"
result.Elapsed = time.Since(started)
return result
}
timer := time.NewTimer(p.timeout)
defer timer.Stop()
select {
case value := <-callback:
result.Elapsed = time.Since(started)
if value.code != 0 {
result.State = StateError
result.ErrorCategory = "callback_failed"
result.callbackCode = value.code
result.hasCallbackCode = true
return result
}
if value.err != nil {
result.State = StateError
result.ErrorCategory = "invalid_value"
return result
}
result.State = StateReady
result.token = value.token
return result
case <-timer.C:
result.State = StateTimeout
result.ErrorCategory = "initialization_timeout"
result.Elapsed = time.Since(started)
return result
}
}
func validateToken(raw []byte) (string, error) {
if len(raw) == 0 || len(raw) > maxHeaderBytes || !utf8.Valid(raw) {
return "", errors.New("invalid runtime value")
}
value := string(raw)
for _, r := range value {
if unicode.IsControl(r) {
return "", errors.New("invalid runtime value")
}
}
payload, err := json.Marshal(struct {
UMID string `json:"umid"`
}{UMID: value})
if err != nil || len(payload) > maxHeaderBytes {
return "", errors.New("invalid runtime value")
}
return value, nil
}
var (
osExecutable = os.Executable
evalSymlinks = filepath.EvalSymlinks
userCacheDir = os.UserCacheDir
materializeRuntimePayload = func(cacheDir, goos, goarch string) (string, error) {
return runtimepayload.Materialize(runtimepayload.Embedded(), cacheDir, goos, goarch)
}
materializeAdjacentPayload = func(root, goos, goarch string) (string, error) {
return runtimepayload.MaterializeAdjacent(runtimepayload.Embedded(), root, goos, goarch)
}
currentGOOS = runtime.GOOS
currentGOARCH = runtime.GOARCH
)
func resolveLibraryPath() (string, error) {
if _, err := libraryName(currentGOOS, currentGOARCH); err != nil {
return "", err
}
if executable, err := osExecutable(); err == nil {
if resolved, err := evalSymlinks(executable); err == nil {
if path, err := materializeAdjacentPayload(filepath.Dir(resolved), currentGOOS, currentGOARCH); err == nil {
return path, nil
}
}
}
if cacheDir, err := userCacheDir(); err == nil {
if path, err := materializeRuntimePayload(cacheDir, currentGOOS, currentGOARCH); err == nil {
return path, nil
}
}
return "", errors.New("runtime payload unavailable")
}
func libraryName(goos, goarch string) (string, error) {
return runtimepayload.LibraryName(goos, goarch)
}
func classifyLocationError(err error) string {
if err != nil && strings.Contains(err.Error(), "unsupported runtime platform") {
return "unsupported_platform"
}
return "payload_unavailable"
}
func classifyStartError(err error) string {
switch {
case errors.Is(err, errNativeSymbol):
return "symbol_unavailable"
case errors.Is(err, errNativeBinding):
return "binding_failed"
default:
return "load_failed"
}
}