mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-17 03:27:02 +08:00
- Introduced a new task for running the Cursor protocol debugging proxy. - Added a build task for the Cursor protocol debugging proxy. - Updated documentation to include usage instructions for the debugging tool.
163 lines
3.8 KiB
Go
163 lines
3.8 KiB
Go
package proxydebugger
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type exchangeStore struct {
|
|
mu sync.RWMutex
|
|
max int
|
|
order []string
|
|
exchanges map[string]*Exchange
|
|
subscribers map[chan storeEvent]struct{}
|
|
}
|
|
|
|
func newExchangeStore(max int) *exchangeStore {
|
|
return &exchangeStore{
|
|
max: max,
|
|
exchanges: make(map[string]*Exchange),
|
|
subscribers: make(map[chan storeEvent]struct{}),
|
|
}
|
|
}
|
|
|
|
func (store *exchangeStore) create(exchange *Exchange) {
|
|
store.mu.Lock()
|
|
store.exchanges[exchange.ID] = exchange
|
|
store.order = append([]string{exchange.ID}, store.order...)
|
|
for len(store.order) > store.max {
|
|
oldest := store.order[len(store.order)-1]
|
|
store.order = store.order[:len(store.order)-1]
|
|
delete(store.exchanges, oldest)
|
|
}
|
|
store.mu.Unlock()
|
|
store.publish(storeEvent{Type: "created", ID: exchange.ID})
|
|
}
|
|
|
|
func (store *exchangeStore) update(id string, apply func(*Exchange)) {
|
|
store.mu.Lock()
|
|
if exchange := store.exchanges[id]; exchange != nil {
|
|
apply(exchange)
|
|
}
|
|
store.mu.Unlock()
|
|
store.publish(storeEvent{Type: "updated", ID: id})
|
|
}
|
|
|
|
func (store *exchangeStore) summaries() []ExchangeSummary {
|
|
store.mu.RLock()
|
|
defer store.mu.RUnlock()
|
|
result := make([]ExchangeSummary, 0, len(store.order))
|
|
for _, id := range store.order {
|
|
if exchange := store.exchanges[id]; exchange != nil {
|
|
result = append(result, exchange.ExchangeSummary)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (store *exchangeStore) get(id string) (Exchange, bool) {
|
|
store.mu.RLock()
|
|
defer store.mu.RUnlock()
|
|
exchange := store.exchanges[id]
|
|
if exchange == nil {
|
|
return Exchange{}, false
|
|
}
|
|
return cloneExchange(*exchange), true
|
|
}
|
|
|
|
func (store *exchangeStore) clear() {
|
|
store.mu.Lock()
|
|
store.order = nil
|
|
store.exchanges = make(map[string]*Exchange)
|
|
store.mu.Unlock()
|
|
store.publish(storeEvent{Type: "cleared"})
|
|
}
|
|
|
|
func (store *exchangeStore) subscribe() (<-chan storeEvent, func()) {
|
|
updates := make(chan storeEvent, 32)
|
|
store.mu.Lock()
|
|
store.subscribers[updates] = struct{}{}
|
|
store.mu.Unlock()
|
|
return updates, func() {
|
|
store.mu.Lock()
|
|
if _, ok := store.subscribers[updates]; ok {
|
|
delete(store.subscribers, updates)
|
|
close(updates)
|
|
}
|
|
store.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
func (store *exchangeStore) publish(event storeEvent) {
|
|
store.mu.RLock()
|
|
defer store.mu.RUnlock()
|
|
for subscriber := range store.subscribers {
|
|
select {
|
|
case subscriber <- event:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
func cloneExchange(exchange Exchange) Exchange {
|
|
exchange.Request = clonePayload(exchange.Request)
|
|
exchange.Response = clonePayload(exchange.Response)
|
|
return exchange
|
|
}
|
|
|
|
func clonePayload(payload Payload) Payload {
|
|
payload.Headers = append([]Header(nil), payload.Headers...)
|
|
payload.Frames = append([]FrameView(nil), payload.Frames...)
|
|
return payload
|
|
}
|
|
|
|
func elapsedMS(startedAt time.Time) int64 {
|
|
if startedAt.IsZero() {
|
|
return 0
|
|
}
|
|
return time.Since(startedAt).Milliseconds()
|
|
}
|
|
|
|
func sortedHeaders(headers map[string][]string) []Header {
|
|
result := make([]Header, 0, len(headers))
|
|
for name, values := range headers {
|
|
value := ""
|
|
for index, item := range values {
|
|
if index > 0 {
|
|
value += ", "
|
|
}
|
|
value += item
|
|
}
|
|
if isSensitiveHeader(name) && value != "" {
|
|
value = "[已隐藏]"
|
|
}
|
|
result = append(result, Header{Name: name, Value: value})
|
|
}
|
|
sort.Slice(result, func(left, right int) bool {
|
|
return result[left].Name < result[right].Name
|
|
})
|
|
return result
|
|
}
|
|
|
|
func isSensitiveHeader(name string) bool {
|
|
switch httpCanonicalLower(name) {
|
|
case "authorization", "cookie", "set-cookie", "proxy-authorization", "x-api-key":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func httpCanonicalLower(value string) string {
|
|
buffer := make([]byte, len(value))
|
|
for index := range value {
|
|
character := value[index]
|
|
if character >= 'A' && character <= 'Z' {
|
|
character += 'a' - 'A'
|
|
}
|
|
buffer[index] = character
|
|
}
|
|
return string(buffer)
|
|
}
|