Ctrl K
ComunidadGuías…

internal/pluginhost/http_stream_bridge.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package pluginhost

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/router-for-me/CLIProxyAPI/v8/sdk/pluginapi"
)

type hostHTTPStreamKey struct {
	pluginID string
	instance *hostCallbackInstance
	streamID string
}

type hostHTTPStreamBridge struct {
	next    atomic.Uint64
	mu      sync.Mutex
	streams map[hostHTTPStreamKey]hostHTTPStreamEntry
}

type hostHTTPStreamEntry struct {
	chunks  <-chan pluginapi.HTTPStreamChunk
	cancel  context.CancelFunc
	onClose func()
}

func newHostHTTPStreamBridge() *hostHTTPStreamBridge {
	return &hostHTTPStreamBridge{streams: make(map[hostHTTPStreamKey]hostHTTPStreamEntry)}
}

func (b *hostHTTPStreamBridge) open(pluginID string, instance *hostCallbackInstance, chunks <-chan pluginapi.HTTPStreamChunk, cancel context.CancelFunc, onClose func()) string {
	if b == nil || chunks == nil {
		if cancel != nil {
			cancel()
		}
		if onClose != nil {
			onClose()
		}
		return ""
	}
	id := strconv.FormatUint(b.next.Add(1), 10)
	key := hostHTTPStreamKey{pluginID: strings.TrimSpace(pluginID), instance: instance, streamID: id}
	b.mu.Lock()
	b.streams[key] = hostHTTPStreamEntry{chunks: chunks, cancel: cancel, onClose: onClose}
	b.mu.Unlock()
	return id
}

func (b *hostHTTPStreamBridge) read(ctx context.Context, pluginID string, instance *hostCallbackInstance, id string) (pluginapi.HTTPStreamChunk, bool, error) {
	if b == nil || id == "" {
		return pluginapi.HTTPStreamChunk{}, true, fmt.Errorf("http stream id is required")
	}
	key := hostHTTPStreamKey{pluginID: strings.TrimSpace(pluginID), instance: instance, streamID: strings.TrimSpace(id)}
	b.mu.Lock()
	entry := b.streams[key]
	b.mu.Unlock()
	if entry.chunks == nil {
		return pluginapi.HTTPStreamChunk{}, true, fmt.Errorf("http stream %s is not open", id)
	}
	if ctx == nil {
		ctx = context.Background()
	}
	select {
	case <-ctx.Done():
		b.close(pluginID, instance, id)
		return pluginapi.HTTPStreamChunk{}, true, ctx.Err()
	case chunk, ok := <-entry.chunks:
		if !ok {
			b.close(pluginID, instance, id)
			return pluginapi.HTTPStreamChunk{}, true, nil
		}
		if chunk.Err != nil {
			b.close(pluginID, instance, id)
			return chunk, true, nil
		}
		return chunk, false, nil
	}
}

func (b *hostHTTPStreamBridge) close(pluginID string, instance *hostCallbackInstance, id string) {
	if b == nil || id == "" {
		return
	}
	key := hostHTTPStreamKey{pluginID: strings.TrimSpace(pluginID), instance: instance, streamID: strings.TrimSpace(id)}
	b.mu.Lock()
	entry := b.streams[key]
	delete(b.streams, key)
	b.mu.Unlock()
	closeHostHTTPStreamEntry(entry)
}

func (b *hostHTTPStreamBridge) closeInstance(pluginID string, instance *hostCallbackInstance) {
	if b == nil || instance == nil {
		return
	}
	b.closeMatching(pluginID, instance, false)
}

func (b *hostHTTPStreamBridge) closePlugin(pluginID string) {
	if b == nil || strings.TrimSpace(pluginID) == "" {
		return
	}
	b.closeMatching(pluginID, nil, false)
}

func (b *hostHTTPStreamBridge) closeAll() {
	if b == nil {
		return
	}
	b.closeMatching("", nil, true)
}

func (b *hostHTTPStreamBridge) closeMatching(pluginID string, instance *hostCallbackInstance, all bool) {
	pluginID = strings.TrimSpace(pluginID)
	b.mu.Lock()
	entries := make([]hostHTTPStreamEntry, 0)
	for key, entry := range b.streams {
		if !all && (key.pluginID != pluginID || (instance != nil && key.instance != instance)) {
			continue
		}
		delete(b.streams, key)
		entries = append(entries, entry)
	}
	b.mu.Unlock()
	for _, entry := range entries {
		closeHostHTTPStreamEntry(entry)
	}
}

func closeHostHTTPStreamEntry(entry hostHTTPStreamEntry) {
	if entry.cancel != nil {
		entry.cancel()
	}
	if entry.onClose != nil {
		entry.onClose()
	}
}