| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- package httpclient
- import (
- "context"
- "sync"
- "github.com/sagernet/sing-box/adapter"
- "github.com/sagernet/sing-box/log"
- "github.com/sagernet/sing-box/option"
- E "github.com/sagernet/sing/common/exceptions"
- "github.com/sagernet/sing/common/logger"
- )
- var (
- _ adapter.HTTPClientManager = (*Manager)(nil)
- _ adapter.LifecycleService = (*Manager)(nil)
- )
- type Manager struct {
- ctx context.Context
- logger log.ContextLogger
- access sync.Mutex
- defines map[string]option.HTTPClient
- transports map[string]*Transport
- defaultTag string
- defaultTransport adapter.HTTPTransport
- defaultTransportFallback func() (*Transport, error)
- fallbackTransport *Transport
- }
- func NewManager(ctx context.Context, logger log.ContextLogger, clients []option.HTTPClient, defaultHTTPClient string) *Manager {
- defines := make(map[string]option.HTTPClient, len(clients))
- for _, client := range clients {
- defines[client.Tag] = client
- }
- defaultTag := defaultHTTPClient
- if defaultTag == "" && len(clients) > 0 {
- defaultTag = clients[0].Tag
- }
- return &Manager{
- ctx: ctx,
- logger: logger,
- defines: defines,
- transports: make(map[string]*Transport),
- defaultTag: defaultTag,
- }
- }
- func (m *Manager) Initialize(defaultTransportFallback func() (*Transport, error)) {
- m.defaultTransportFallback = defaultTransportFallback
- }
- func (m *Manager) Name() string {
- return "http-client"
- }
- func (m *Manager) Start(stage adapter.StartStage) error {
- if stage != adapter.StartStateStart {
- return nil
- }
- if m.defaultTag != "" {
- transport, err := m.resolveShared(m.defaultTag)
- if err != nil {
- return E.Cause(err, "resolve default http client")
- }
- m.defaultTransport = transport
- } else if m.defaultTransportFallback != nil {
- transport, err := m.defaultTransportFallback()
- if err != nil {
- return E.Cause(err, "create default http client")
- }
- m.defaultTransport = transport
- m.fallbackTransport = transport
- }
- return nil
- }
- func (m *Manager) DefaultTransport() adapter.HTTPTransport {
- if m.defaultTransport == nil {
- return nil
- }
- return &sharedTransport{m.defaultTransport}
- }
- func (m *Manager) ResolveTransport(ctx context.Context, logger logger.ContextLogger, options option.HTTPClientOptions) (adapter.HTTPTransport, error) {
- if options.Tag != "" {
- if options.ResolveOnDetour {
- define, loaded := m.defines[options.Tag]
- if !loaded {
- return nil, E.New("http_client not found: ", options.Tag)
- }
- resolvedOptions := define.Options()
- resolvedOptions.ResolveOnDetour = true
- return NewTransport(ctx, logger, options.Tag, resolvedOptions)
- }
- transport, err := m.resolveShared(options.Tag)
- if err != nil {
- return nil, err
- }
- return &sharedTransport{transport}, nil
- }
- return NewTransport(ctx, logger, "", options)
- }
- func (m *Manager) resolveShared(tag string) (adapter.HTTPTransport, error) {
- m.access.Lock()
- defer m.access.Unlock()
- if transport, loaded := m.transports[tag]; loaded {
- return transport, nil
- }
- define, loaded := m.defines[tag]
- if !loaded {
- return nil, E.New("http_client not found: ", tag)
- }
- transport, err := NewTransport(m.ctx, m.logger, tag, define.Options())
- if err != nil {
- return nil, E.Cause(err, "create shared http_client[", tag, "]")
- }
- m.transports[tag] = transport
- return transport, nil
- }
- type sharedTransport struct {
- adapter.HTTPTransport
- }
- func (t *sharedTransport) CloseIdleConnections() {
- }
- func (t *sharedTransport) Close() error {
- return nil
- }
- func (m *Manager) ResetNetwork() {
- m.access.Lock()
- defer m.access.Unlock()
- for _, transport := range m.transports {
- transport.CloseIdleConnections()
- }
- if m.fallbackTransport != nil {
- m.fallbackTransport.CloseIdleConnections()
- }
- }
- func (m *Manager) Close() error {
- m.access.Lock()
- defer m.access.Unlock()
- if m.transports == nil {
- return nil
- }
- var err error
- for _, transport := range m.transports {
- err = E.Append(err, transport.Close(), func(err error) error {
- return E.Cause(err, "close http client")
- })
- }
- if m.fallbackTransport != nil {
- err = E.Append(err, m.fallbackTransport.Close(), func(err error) error {
- return E.Cause(err, "close default http client")
- })
- }
- m.transports = nil
- return err
- }
|