mirror of
https://github.com/bolucat/Archive.git
synced 2025-12-24 13:28:37 +08:00
Update On Sat Sep 6 20:35:54 CEST 2025
This commit is contained in:
@@ -79,12 +79,15 @@ type BoxInstance struct {
|
||||
pauseManager pause.Manager
|
||||
}
|
||||
|
||||
func NewSingBoxInstance(config string) (b *BoxInstance, err error) {
|
||||
func NewSingBoxInstance(config string, localTransport LocalDNSTransport) (b *BoxInstance, err error) {
|
||||
defer device.DeferPanicToError("NewSingBoxInstance", func(err_ error) { err = err_ })
|
||||
|
||||
// create box context
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx = box.Context(ctx, nekoboxAndroidInboundRegistry(), nekoboxAndroidOutboundRegistry(), nekoboxAndroidEndpointRegistry(), nekoboxAndroidDNSTransportRegistry(), nekoboxAndroidServiceRegistry())
|
||||
ctx = box.Context(ctx,
|
||||
nekoboxAndroidInboundRegistry(), nekoboxAndroidOutboundRegistry(), nekoboxAndroidEndpointRegistry(),
|
||||
nekoboxAndroidDNSTransportRegistry(localTransport), nekoboxAndroidServiceRegistry(),
|
||||
)
|
||||
ctx = service.ContextWithDefaultRegistry(ctx)
|
||||
service.MustRegister[platform.Interface](ctx, boxPlatformInterfaceInstance)
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package libcore
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
||||
"github.com/sagernet/sing-box/adapter/inbound"
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
@@ -11,6 +14,8 @@ import (
|
||||
"github.com/sagernet/sing-box/dns/transport/hosts"
|
||||
"github.com/sagernet/sing-box/dns/transport/local"
|
||||
"github.com/sagernet/sing-box/dns/transport/quic"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/protocol/anytls"
|
||||
"github.com/sagernet/sing-box/protocol/block"
|
||||
"github.com/sagernet/sing-box/protocol/direct"
|
||||
@@ -35,7 +40,6 @@ import (
|
||||
|
||||
_ "github.com/sagernet/sing-box/experimental/clashapi"
|
||||
_ "github.com/sagernet/sing-box/transport/v2rayquic"
|
||||
_ "github.com/sagernet/sing-dns/quic"
|
||||
)
|
||||
|
||||
func nekoboxAndroidInboundRegistry() *inbound.Registry {
|
||||
@@ -92,7 +96,7 @@ func nekoboxAndroidEndpointRegistry() *endpoint.Registry {
|
||||
return registry
|
||||
}
|
||||
|
||||
func nekoboxAndroidDNSTransportRegistry() *dns.TransportRegistry {
|
||||
func nekoboxAndroidDNSTransportRegistry(localTransport LocalDNSTransport) *dns.TransportRegistry {
|
||||
registry := dns.NewTransportRegistry()
|
||||
|
||||
transport.RegisterTCP(registry)
|
||||
@@ -100,12 +104,23 @@ func nekoboxAndroidDNSTransportRegistry() *dns.TransportRegistry {
|
||||
transport.RegisterTLS(registry)
|
||||
transport.RegisterHTTPS(registry)
|
||||
hosts.RegisterTransport(registry)
|
||||
local.RegisterTransport(registry)
|
||||
// local.RegisterTransport(registry)
|
||||
fakeip.RegisterTransport(registry)
|
||||
|
||||
quic.RegisterTransport(registry)
|
||||
quic.RegisterHTTP3Transport(registry)
|
||||
|
||||
if localTransport == nil {
|
||||
local.RegisterTransport(registry)
|
||||
} else {
|
||||
dns.RegisterTransport(registry, "local", func(ctx context.Context, logger log.ContextLogger, tag string, options option.LocalDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
return &platformLocalDNSTransport{
|
||||
iif: localTransport,
|
||||
tag: tag,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
return registry
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@ func GoDebug(any interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
func DeferPanicToError(name string, err func(error)) {
|
||||
func DeferPanicToError(name string, onError func(error)) {
|
||||
if r := recover(); r != nil {
|
||||
s := fmt.Errorf("%s panic: %s\n%s", name, r, string(debug.Stack()))
|
||||
err(s)
|
||||
if onError != nil {
|
||||
s := fmt.Errorf("%s panic: %s\n%s", name, r, string(debug.Stack()))
|
||||
onError(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -23,29 +25,14 @@ type LocalDNSTransport interface {
|
||||
Exchange(ctx *ExchangeContext, message []byte) error
|
||||
}
|
||||
|
||||
func RegisterLocalDNSTransport(transport LocalDNSTransport) {
|
||||
if transport == nil {
|
||||
dns.RegisterTransport([]string{"local"}, dns.CreateTransport)
|
||||
} else {
|
||||
dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||
return &platformLocalDNSTransport{
|
||||
iif: transport,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var _ dns.Transport = (*platformLocalDNSTransport)(nil)
|
||||
var gLocalDNSTransport *platformLocalDNSTransport = nil
|
||||
|
||||
type platformLocalDNSTransport struct {
|
||||
iif LocalDNSTransport
|
||||
tag string
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Name() string {
|
||||
return "local"
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Start() error {
|
||||
func (p *platformLocalDNSTransport) Start(adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -82,12 +69,12 @@ func (p *platformLocalDNSTransport) Exchange(ctx context.Context, message *mDNS.
|
||||
})
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
|
||||
func (p *platformLocalDNSTransport) Lookup(ctx context.Context, domain string, strategy constant.DomainStrategy) ([]netip.Addr, error) {
|
||||
var network string
|
||||
switch strategy {
|
||||
case dns.DomainStrategyUseIPv4:
|
||||
case constant.DomainStrategyIPv4Only:
|
||||
network = "ip4"
|
||||
case dns.DomainStrategyPreferIPv6:
|
||||
case constant.DomainStrategyPreferIPv6:
|
||||
network = "ip6"
|
||||
default:
|
||||
network = "ip"
|
||||
@@ -105,11 +92,11 @@ func (p *platformLocalDNSTransport) Lookup(ctx context.Context, domain string, s
|
||||
return response.error
|
||||
}
|
||||
switch strategy {
|
||||
case dns.DomainStrategyUseIPv4:
|
||||
case constant.DomainStrategyIPv4Only:
|
||||
responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool {
|
||||
return it.Is4()
|
||||
})
|
||||
case dns.DomainStrategyPreferIPv6:
|
||||
case constant.DomainStrategyPreferIPv6:
|
||||
responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool {
|
||||
return it.Is6()
|
||||
})
|
||||
@@ -123,6 +110,18 @@ func (p *platformLocalDNSTransport) Lookup(ctx context.Context, domain string, s
|
||||
})
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Tag() string {
|
||||
return p.tag
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Type() string {
|
||||
return "local"
|
||||
}
|
||||
|
||||
func (p *platformLocalDNSTransport) Dependencies() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Func interface {
|
||||
Invoke() error
|
||||
}
|
||||
@@ -157,7 +156,7 @@ func (c *ExchangeContext) RawSuccess(result []byte) {
|
||||
}
|
||||
|
||||
func (c *ExchangeContext) ErrorCode(code int32) {
|
||||
c.error = dns.RCodeError(code)
|
||||
c.error = dns.RcodeError(code)
|
||||
}
|
||||
|
||||
func (c *ExchangeContext) ErrnoCode(code int32) {
|
||||
|
||||
83
nekobox-android/libcore/ech/ech.go
Normal file
83
nekobox-android/libcore/ech/ech.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package ech
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type ECHClientConfig struct {
|
||||
*tls.Config
|
||||
domain string
|
||||
localDnsTransport adapter.DNSTransport
|
||||
}
|
||||
|
||||
func NewECHClientConfig(domain string, tlsConfig *tls.Config, localDnsTransport adapter.DNSTransport) *ECHClientConfig {
|
||||
config := tlsConfig.Clone()
|
||||
config.ServerName = domain
|
||||
return &ECHClientConfig{
|
||||
Config: config,
|
||||
domain: domain,
|
||||
localDnsTransport: localDnsTransport,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientHandshake 封装 TLS 握手
|
||||
func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (*tls.Conn, error) {
|
||||
tlsConn, err := s.fetchAndHandshake(ctx, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = tlsConn.HandshakeContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
// fetchAndHandshake 查询 ECHConfigList 并完成 TLS 连接
|
||||
func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (*tls.Conn, error) {
|
||||
message := &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
RecursionDesired: true,
|
||||
},
|
||||
Question: []mDNS.Question{
|
||||
{
|
||||
Name: mDNS.Fqdn(s.domain),
|
||||
Qtype: mDNS.TypeHTTPS,
|
||||
Qclass: mDNS.ClassINET,
|
||||
},
|
||||
},
|
||||
}
|
||||
if s.localDnsTransport == nil {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
response, err := s.localDnsTransport.Exchange(ctx, message)
|
||||
if err != nil {
|
||||
return nil, exceptions.Cause(err, "fetch ECH config list")
|
||||
}
|
||||
if response.Rcode != mDNS.RcodeSuccess {
|
||||
return nil, exceptions.Cause(dns.RcodeError(response.Rcode), "fetch ECH config list")
|
||||
}
|
||||
for _, rr := range response.Answer {
|
||||
switch resource := rr.(type) {
|
||||
case *mDNS.HTTPS:
|
||||
for _, value := range resource.Value {
|
||||
if value.Key().String() == "ech" {
|
||||
echConfigList, err := base64.StdEncoding.DecodeString(value.String())
|
||||
if err == nil {
|
||||
s.Config.EncryptedClientHelloConfigList = echConfigList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tls.Client(conn, s.Config), nil
|
||||
}
|
||||
@@ -8,9 +8,9 @@ require (
|
||||
github.com/matsuridayo/libneko v1.0.0 // replaced
|
||||
github.com/miekg/dns v1.1.67
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/sagernet/quic-go v0.52.0-beta.1
|
||||
github.com/sagernet/sing v0.7.6-0.20250825114712-2aeec120ce28
|
||||
github.com/sagernet/sing-box v1.0.0 // replaced
|
||||
github.com/sagernet/sing-dns v0.4.1
|
||||
github.com/sagernet/sing-tun v0.7.0-beta.1
|
||||
github.com/ulikunitz/xz v0.5.11
|
||||
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da
|
||||
@@ -52,7 +52,6 @@ require (
|
||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||
github.com/sagernet/quic-go v0.52.0-beta.1 // indirect
|
||||
github.com/sagernet/sing-mux v0.3.3 // indirect
|
||||
github.com/sagernet/sing-quic v0.5.0 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8 // indirect
|
||||
@@ -90,5 +89,3 @@ replace github.com/sagernet/sing-box => ../../sing-box
|
||||
// replace github.com/sagernet/sing-quic => ../../sing-quic
|
||||
|
||||
// replace github.com/sagernet/sing => ../../sing
|
||||
|
||||
// replace github.com/sagernet/sing-dns => ../../sing-dns
|
||||
|
||||
@@ -91,8 +91,6 @@ github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9Gy
|
||||
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing v0.7.6-0.20250825114712-2aeec120ce28 h1:C8Lnqd0Q+C15kwaMiDsfq5S45rhhaQMBG91TT+6oFVo=
|
||||
github.com/sagernet/sing v0.7.6-0.20250825114712-2aeec120ce28/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.4.1 h1:nozS7iqpxZ7aV73oHbkD/8haOvf3XXDCgT//8NdYirk=
|
||||
github.com/sagernet/sing-dns v0.4.1/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8=
|
||||
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
|
||||
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
||||
github.com/sagernet/sing-quic v0.5.0 h1:jNLIyVk24lFPvu8A4x+ZNEnZdI+Tg1rp7eCJ6v0Csak=
|
||||
|
||||
@@ -10,24 +10,34 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"libcore/device"
|
||||
"libcore/ech"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
"github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
"github.com/sagernet/sing/protocol/socks/socks5"
|
||||
)
|
||||
|
||||
var errFailConnectSocks5 = errors.New("fail connect socks5")
|
||||
|
||||
type HTTPClient interface {
|
||||
RestrictedTLS()
|
||||
ModernTLS()
|
||||
PinnedTLS12()
|
||||
PinnedSHA256(sumHex string)
|
||||
TrySocks5(port int32)
|
||||
TryH3Direct()
|
||||
KeepAlive()
|
||||
NewRequest() HTTPRequest
|
||||
Close()
|
||||
@@ -58,16 +68,18 @@ var (
|
||||
)
|
||||
|
||||
type httpClient struct {
|
||||
tls tls.Config
|
||||
client http.Client
|
||||
transport http.Transport
|
||||
tls tls.Config
|
||||
h1h2Transport http.Transport
|
||||
h1h2Client http.Client
|
||||
trySocks5 bool
|
||||
tryH3Direct bool
|
||||
}
|
||||
|
||||
func NewHttpClient() HTTPClient {
|
||||
client := new(httpClient)
|
||||
client.client.Transport = &client.transport
|
||||
client.transport.TLSClientConfig = &client.tls
|
||||
client.transport.DisableKeepAlives = true
|
||||
client.h1h2Client.Transport = &client.h1h2Transport
|
||||
client.h1h2Transport.TLSClientConfig = &client.tls
|
||||
client.h1h2Transport.DisableKeepAlives = true
|
||||
return client
|
||||
}
|
||||
|
||||
@@ -104,25 +116,36 @@ func (c *httpClient) PinnedSHA256(sumHex string) {
|
||||
|
||||
func (c *httpClient) TrySocks5(port int32) {
|
||||
dialer := new(net.Dialer)
|
||||
c.transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
c.h1h2Transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
for {
|
||||
socksConn, err := dialer.DialContext(ctx, "tcp", "127.0.0.1:"+strconv.Itoa(int(port)))
|
||||
if err != nil {
|
||||
if c.tryH3Direct {
|
||||
return nil, errFailConnectSocks5
|
||||
}
|
||||
break
|
||||
}
|
||||
_, err = socks.ClientHandshake5(socksConn, socks5.CommandConnect, metadata.ParseSocksaddr(addr), "", "")
|
||||
if err != nil {
|
||||
if c.tryH3Direct {
|
||||
return nil, errFailConnectSocks5
|
||||
}
|
||||
break
|
||||
}
|
||||
return socksConn, err
|
||||
}
|
||||
return dialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
c.trySocks5 = true
|
||||
}
|
||||
|
||||
func (c *httpClient) TryH3Direct() {
|
||||
c.tryH3Direct = true
|
||||
}
|
||||
|
||||
func (c *httpClient) KeepAlive() {
|
||||
c.transport.ForceAttemptHTTP2 = true
|
||||
c.transport.DisableKeepAlives = false
|
||||
c.h1h2Transport.ForceAttemptHTTP2 = true
|
||||
c.h1h2Transport.DisableKeepAlives = false
|
||||
}
|
||||
|
||||
func (c *httpClient) NewRequest() HTTPRequest {
|
||||
@@ -135,7 +158,7 @@ func (c *httpClient) NewRequest() HTTPRequest {
|
||||
}
|
||||
|
||||
func (c *httpClient) Close() {
|
||||
c.transport.CloseIdleConnections()
|
||||
c.h1h2Transport.CloseIdleConnections()
|
||||
}
|
||||
|
||||
type httpRequest struct {
|
||||
@@ -184,8 +207,17 @@ func (r *httpRequest) SetContentString(content string) {
|
||||
}
|
||||
|
||||
func (r *httpRequest) Execute() (HTTPResponse, error) {
|
||||
response, err := r.client.Do(&r.request)
|
||||
defer device.DeferPanicToError("http execute", func(err error) { log.Println(err) })
|
||||
// full direct
|
||||
if r.tryH3Direct && !r.trySocks5 {
|
||||
return r.doH3Direct()
|
||||
}
|
||||
response, err := r.h1h2Client.Do(&r.request)
|
||||
if err != nil {
|
||||
// trySocks5 && tryH3Direct
|
||||
if r.tryH3Direct && errors.Is(err, errFailConnectSocks5) {
|
||||
return r.doH3Direct()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
httpResp := &httpResponse{Response: response}
|
||||
@@ -195,6 +227,133 @@ func (r *httpRequest) Execute() (HTTPResponse, error) {
|
||||
return httpResp, nil
|
||||
}
|
||||
|
||||
type requestFunc func() (response *http.Response, err error)
|
||||
|
||||
func (r *httpRequest) doH3Direct() (HTTPResponse, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
successCh := make(chan *http.Response, 1)
|
||||
var finalErr error
|
||||
var failedCount atomic.Uint32
|
||||
var successCount atomic.Uint32
|
||||
var mu sync.Mutex
|
||||
|
||||
funcs := []requestFunc{
|
||||
// 普通,不再重试 socks5
|
||||
func() (response *http.Response, err error) {
|
||||
request := r.request.Clone(context.Background())
|
||||
h1h2Client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
}
|
||||
return h1h2Client.Do(request)
|
||||
},
|
||||
// ECH HTTPS
|
||||
func() (response *http.Response, err error) {
|
||||
request := r.request.Clone(context.Background())
|
||||
echClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
var d net.Dialer
|
||||
c, err := d.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
domain := addr
|
||||
if host, _, _ := net.SplitHostPort(addr); host != "" {
|
||||
domain = host
|
||||
}
|
||||
echTls := ech.NewECHClientConfig(domain, &r.tls, gLocalDNSTransport)
|
||||
return echTls.ClientHandshake(ctx, c)
|
||||
},
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
}
|
||||
return echClient.Do(request)
|
||||
},
|
||||
// H3 HTTPS
|
||||
func() (response *http.Response, err error) {
|
||||
request := r.request.Clone(context.Background())
|
||||
h3Client := &http.Client{
|
||||
Transport: &http3.Transport{
|
||||
TLSClientConfig: r.tls.Clone(),
|
||||
QUICConfig: &quic.Config{
|
||||
MaxIdleTimeout: time.Second,
|
||||
},
|
||||
},
|
||||
}
|
||||
return h3Client.Do(request)
|
||||
},
|
||||
}
|
||||
|
||||
if r.request.URL.Scheme == "http" {
|
||||
funcs = funcs[:1]
|
||||
}
|
||||
|
||||
for i, f := range funcs {
|
||||
go func(f requestFunc) {
|
||||
defer device.DeferPanicToError("http", func(err error) { log.Println(err) })
|
||||
defer func() {
|
||||
if successCount.Load() == 0 {
|
||||
if failedCount.Add(1) >= uint32(len(funcs)) {
|
||||
// 全部失败了
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var t string
|
||||
switch i {
|
||||
case 0:
|
||||
t = "h1h2"
|
||||
case 1:
|
||||
t = "ech"
|
||||
case 2:
|
||||
t = "h3"
|
||||
}
|
||||
|
||||
// 执行HTTP请求
|
||||
rsp, err := f()
|
||||
if rsp == nil || err != nil {
|
||||
mu.Lock()
|
||||
finalErr = errors.Join(finalErr, fmt.Errorf("%s: %w", t, err))
|
||||
mu.Unlock()
|
||||
if rsp != nil && rsp.Body != nil {
|
||||
rsp.Body.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 处理 HTTP 状态码
|
||||
if rsp.StatusCode != http.StatusOK {
|
||||
hr := &httpResponse{Response: rsp}
|
||||
err = fmt.Errorf("%s: %s", t, hr.errorString())
|
||||
mu.Lock()
|
||||
finalErr = errors.Join(finalErr, err)
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case successCh <- rsp:
|
||||
// 第一个成功的请求,不要关闭 body
|
||||
successCount.Add(1)
|
||||
default:
|
||||
rsp.Body.Close()
|
||||
}
|
||||
}(f)
|
||||
}
|
||||
|
||||
select {
|
||||
case result := <-successCh:
|
||||
return &httpResponse{Response: result}, nil
|
||||
case <-ctx.Done():
|
||||
return nil, finalErr
|
||||
}
|
||||
}
|
||||
|
||||
type httpResponse struct {
|
||||
*http.Response
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"libcore/device"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
_ "unsafe"
|
||||
|
||||
@@ -29,12 +29,12 @@ func NekoLogClear() {
|
||||
}
|
||||
|
||||
func ForceGc() {
|
||||
go runtime.GC()
|
||||
go debug.FreeOSMemory()
|
||||
}
|
||||
|
||||
func InitCore(process, cachePath, internalAssets, externalAssets string,
|
||||
maxLogSizeKb int32, logEnable bool,
|
||||
if1 NB4AInterface, if2 BoxPlatformInterface,
|
||||
if1 NB4AInterface, if2 BoxPlatformInterface, if3 LocalDNSTransport,
|
||||
) {
|
||||
defer device.DeferPanicToError("InitCore", func(err error) { log.Println(err) })
|
||||
isBgProcess = strings.HasSuffix(process, ":bg")
|
||||
@@ -43,6 +43,7 @@ func InitCore(process, cachePath, internalAssets, externalAssets string,
|
||||
intfNB4A = if1
|
||||
intfBox = if2
|
||||
useProcfs = intfBox.UseProcFS()
|
||||
gLocalDNSTransport = &platformLocalDNSTransport{iif: if3}
|
||||
|
||||
// Working dir
|
||||
tmp := filepath.Join(cachePath, "../no_backup")
|
||||
@@ -51,6 +52,8 @@ func InitCore(process, cachePath, internalAssets, externalAssets string,
|
||||
|
||||
// sing-box fs
|
||||
resourcePaths = append(resourcePaths, externalAssets)
|
||||
externalAssetsPath = externalAssets
|
||||
internalAssetsPath = internalAssets
|
||||
|
||||
// Set up log
|
||||
if maxLogSizeKb < 50 {
|
||||
@@ -68,9 +71,6 @@ func InitCore(process, cachePath, internalAssets, externalAssets string,
|
||||
defer device.DeferPanicToError("InitCore-go", func(err error) { log.Println(err) })
|
||||
device.GoDebug(process)
|
||||
|
||||
externalAssetsPath = externalAssets
|
||||
internalAssetsPath = internalAssets
|
||||
|
||||
// certs
|
||||
pem, err := os.ReadFile(externalAssetsPath + "ca.pem")
|
||||
if err == nil {
|
||||
|
||||
Reference in New Issue
Block a user