Ctrl K
ComunidadGuías…

internal/reach/reach.go

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

Archivo original ↗Preguntar a KiBot
package reach

import (
	"context"
	"errors"
	"net/http"
	"sync"
)

type Sink struct {
	mu        sync.Mutex
	answering bool
	reason    string
	changed   chan struct{}
}

func New() *Sink {
	return &Sink{answering: true, changed: make(chan struct{}, 1)}
}

func (s *Sink) Wrap(next http.RoundTripper) http.RoundTripper {
	return &watched{next: next, sink: s}
}

func (s *Sink) Saw(err error) {
	if s == nil {
		return
	}
	if errors.Is(err, context.Canceled) {
		return
	}
	if err == nil {
		s.record(true, "")
		return
	}
	s.record(false, err.Error())
}

func (s *Sink) Changed() <-chan struct{} {
	if s == nil {
		return nil
	}
	return s.changed
}

func (s *Sink) State() (bool, string) {
	if s == nil {
		return true, ""
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.answering, s.reason
}

func (s *Sink) record(answering bool, reason string) {
	s.mu.Lock()
	changed := s.answering != answering
	if s.reason != reason {
		changed = true
	}
	s.answering = answering
	s.reason = reason
	s.mu.Unlock()
	if !changed {
		return
	}
	s.tell()
}

func (s *Sink) tell() {
	select {
	case s.changed <- struct{}{}:
	default:
	}
}

type watched struct {
	next http.RoundTripper
	sink *Sink
}

func (w *watched) RoundTrip(req *http.Request) (*http.Response, error) {
	resp, err := w.next.RoundTrip(req)
	w.sink.Saw(err)
	return resp, err
}