diff --git a/dns.go b/dns.go deleted file mode 100644 index 48649b9..0000000 --- a/dns.go +++ /dev/null @@ -1,157 +0,0 @@ -package main - -import ( - "context" - "crypto/tls" - "errors" - "net" - "sync" - "time" - - "github.com/fumiama/terasu" -) - -var ( - ErrNoDNSAvailable = errors.New("no dns available") -) - -var dnsdialer = net.Dialer{ - Timeout: time.Second * 8, -} - -type dnsstat struct { - A string - E bool -} - -type dnsservers struct { - sync.RWMutex - m map[string][]*dnsstat -} - -// hasrecord no lock, use under lock -func hasrecord(lst []*dnsstat, a string) bool { - for _, addr := range lst { - if addr.A == a { - return true - } - } - return false -} - -func (ds *dnsservers) add(m map[string][]string) { - ds.Lock() - defer ds.Unlock() - addList := map[string][]*dnsstat{} - for host, addrs := range m { - for _, addr := range addrs { - if !hasrecord(ds.m[host], addr) && !hasrecord(addList[host], addr) { - addList[host] = append(addList[host], &dnsstat{addr, true}) - } - } - } - for host, addrs := range addList { - ds.m[host] = append(ds.m[host], addrs...) - } -} - -func (ds *dnsservers) dial(ctx context.Context) (tlsConn *tls.Conn, err error) { - err = ErrNoDNSAvailable - - ds.RLock() - defer ds.RUnlock() - - if dnsdialer.Timeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, dnsdialer.Timeout) - defer cancel() - } - - if !dnsdialer.Deadline.IsZero() { - var cancel context.CancelFunc - ctx, cancel = context.WithDeadline(ctx, dnsdialer.Deadline) - defer cancel() - } - - var conn net.Conn - for host, addrs := range ds.m { - for _, addr := range addrs { - if !addr.E { - continue - } - conn, err = dnsdialer.DialContext(ctx, "tcp", addr.A) - if err != nil { - addr.E = false // no need to acquire write lock - continue - } - tlsConn = tls.Client(conn, &tls.Config{ServerName: host}) - err = terasu.Use(tlsConn).HandshakeContext(ctx) - if err == nil { - return - } - _ = tlsConn.Close() - addr.E = false // no need to acquire write lock - } - } - return -} - -var dotv6servers = dnsservers{ - m: map[string][]*dnsstat{ - "dot.sb": { - {"[2a09::]:853", true}, - {"[2a11::]:853", true}, - }, - "dns.google": { - {"[2001:4860:4860::8888]:853", true}, - {"[2001:4860:4860::8844]:853", true}, - }, - "cloudflare-dns.com": { - {"[2606:4700:4700::1111]:853", true}, - {"[2606:4700:4700::1001]:853", true}, - }, - "dns.umbrella.com": { - {"[2620:0:ccc::2]:853", true}, - {"[2620:0:ccd::2]:853", true}, - }, - "dns10.quad9.net": { - {"[2620:fe::10]:853", true}, - {"[2620:fe::fe:10]:853", true}, - }, - }, -} - -var dotv4servers = dnsservers{ - m: map[string][]*dnsstat{ - "dot.sb": { - {"185.222.222.222:853", true}, - {"45.11.45.11:853", true}, - }, - "dns.google": { - {"8.8.8.8:853", true}, - {"8.8.4.4:853", true}, - }, - "cloudflare-dns.com": { - {"1.1.1.1:853", true}, - {"1.0.0.1:853", true}, - }, - "dns.umbrella.com": { - {"208.67.222.222:853", true}, - {"208.67.220.220:853", true}, - }, - "dns10.quad9.net": { - {"9.9.9.10:853", true}, - {"149.112.112.10:853", true}, - }, - }, -} - -var resolver = &net.Resolver{ - PreferGo: true, - Dial: func(ctx context.Context, _, _ string) (net.Conn, error) { - if canUseIPv6.Get() { - return dotv6servers.dial(ctx) - } - return dotv4servers.dial(ctx) - }, -} diff --git a/dns_test.go b/dns_test.go deleted file mode 100644 index 6fb3bb1..0000000 --- a/dns_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package main - -import ( - "context" - "crypto/tls" - "fmt" - "net" - "testing" - "time" - - "github.com/fumiama/terasu" -) - -func TestResolver(t *testing.T) { - t.Log("canUseIPv6:", canUseIPv6.Get()) - addrs, err := resolver.LookupHost(context.TODO(), "api.mangacopy.com") - if err != nil { - t.Fatal(err) - } - t.Log(addrs) - if len(addrs) == 0 { - t.Fail() - } -} - -func TestDNS(t *testing.T) { - if canUseIPv6.Get() { - dotv6servers.test() - } - dotv4servers.test() - for i := 0; i < 100; i++ { - addrs, err := resolver.LookupHost(context.TODO(), "api.mangacopy.com") - if err != nil { - t.Fatal(err) - } - t.Log(addrs) - if len(addrs) == 0 { - t.Fail() - } - time.Sleep(time.Millisecond * 50) - } -} - -func TestBadDNS(t *testing.T) { - dotv6serversbak := dotv6servers.m - dotv4serversbak := dotv4servers.m - defer func() { - dotv6servers.m = dotv6serversbak - dotv4servers.m = dotv4serversbak - }() - if canUseIPv6.Get() { - dotv6servers = dnsservers{ - m: map[string][]*dnsstat{}, - } - dotv6servers.add(map[string][]string{"test.bad.host": {"169.254.122.111"}}) - } else { - dotv4servers = dnsservers{ - m: map[string][]*dnsstat{}, - } - dotv4servers.add(map[string][]string{"test.bad.host": {"169.254.122.111:853"}}) - } - for i := 0; i < 10; i++ { - addrs, err := resolver.LookupHost(context.TODO(), "api.mangacopy.com") - t.Log(err) - if err == nil && len(addrs) > 0 { - t.Fatal("unexpected") - } - time.Sleep(time.Millisecond * 50) - } -} - -func (ds *dnsservers) test() { - ds.RLock() - defer ds.RUnlock() - for host, addrs := range ds.m { - for _, addr := range addrs { - if !addr.E { - continue - } - fmt.Println("dial:", host, addr.A) - conn, err := net.Dial("tcp", addr.A) - if err != nil { - continue - } - tlsConn := tls.Client(conn, &tls.Config{ServerName: host}) - err = terasu.Use(tlsConn).Handshake() - _ = tlsConn.Close() - if err == nil { - fmt.Println("succ:", host, addr.A) - continue - } - fmt.Println("fail:", host, addr.A) - } - } -} diff --git a/go.mod b/go.mod index 3f20114..2858593 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,11 @@ module comandy go 1.22.1 -require ( - github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1 - github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e - github.com/fumiama/terasu v0.0.0-20240416061047-62d3c9f6be80 - golang.org/x/net v0.24.0 -) +require github.com/fumiama/terasu v0.0.0-20240418151245-719e0c16831b -require golang.org/x/text v0.14.0 // indirect +require ( + github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1 // indirect + github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/go.sum b/go.sum index 56ab9bd..11cd8a2 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1 h1:g4pTnDJUW4VbJ9NvoRfUvdjDrHz/6QhfN/LoIIpICbo= github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= -github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= -github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w= +github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMNNBxUjLtVmP/YWD6Yh79RfPv4ehU= +github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fumiama/terasu v0.0.0-20240416061047-62d3c9f6be80 h1:O1JJZzcd5ggUw/9X8V9KxBZ9JZGWFmX/r1q2TPg+pZQ= -github.com/fumiama/terasu v0.0.0-20240416061047-62d3c9f6be80/go.mod h1:BFl0X1+rGJf8bLHl/kO+v05ryHrj/R4kyCrK89NvegA= +github.com/fumiama/terasu v0.0.0-20240418151245-719e0c16831b h1:j6DMJg+jd4HPmhQtVwtiHBM1y9XskJgWhskUvWuhFuY= +github.com/fumiama/terasu v0.0.0-20240418151245-719e0c16831b/go.mod h1:afchyfKAb7J/zvaENtYzjIEPVbwiEjJaow05zzT4usM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= diff --git a/http.go b/http.go index acc9516..95b250a 100644 --- a/http.go +++ b/http.go @@ -1,93 +1,17 @@ package main import ( - "context" - "crypto/tls" "encoding/base64" "encoding/json" - "errors" "fmt" "io" - "net" "net/http" "reflect" "strings" - "sync" - "time" - "github.com/FloatTech/ttl" - "github.com/fumiama/terasu" - "golang.org/x/net/http2" + "github.com/fumiama/terasu/http2" ) -var ( - ErrEmptyHostAddress = errors.New("empty host addr") -) - -var httpdialer = net.Dialer{ - Timeout: time.Minute, -} - -var lookupTable = ttl.NewCache[string, []string](time.Hour) - -type comandyClient http.Client - -var cli = comandyClient(http.Client{ - Transport: &http2.Transport{ - DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { - if httpdialer.Timeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, httpdialer.Timeout) - defer cancel() - } - - if !httpdialer.Deadline.IsZero() { - var cancel context.CancelFunc - ctx, cancel = context.WithDeadline(ctx, httpdialer.Deadline) - defer cancel() - } - - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - addrs := lookupTable.Get(host) - if len(addrs) == 0 { - addrs, err = resolver.LookupHost(ctx, host) - if err != nil { - addrs, err = net.DefaultResolver.LookupHost(ctx, host) - if err != nil { - return nil, err - } - } - lookupTable.Set(host, addrs) - } - if len(addr) == 0 { - return nil, ErrEmptyHostAddress - } - var tlsConn *tls.Conn - for _, a := range addrs { - if strings.Contains(a, ":") { - a = "[" + a + "]:" + port - } else { - a += ":" + port - } - conn, err := httpdialer.DialContext(ctx, network, a) - if err != nil { - continue - } - tlsConn = tls.Client(conn, cfg) - err = terasu.Use(tlsConn).HandshakeContext(ctx) - if err == nil { - break - } - _ = tlsConn.Close() - } - return tlsConn, err - }, - }, -}) - type capsule struct { C int `json:"code,omitempty"` M string `json:"method,omitempty"` @@ -112,7 +36,7 @@ func (r *capsule) printstrerr(err string) string { return buf.String() } -func (cli *comandyClient) request(para string) (ret string) { +func gorequest(para string) (ret string) { r := capsule{} defer func() { err := recover() @@ -120,12 +44,6 @@ func (cli *comandyClient) request(para string) (ret string) { ret = r.printstrerr(fmt.Sprint()) } }() - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - defer wg.Done() - _ = canUseIPv6.Get() - }() err := json.Unmarshal(stringToBytes(para), &r) if err != nil { return r.printerr(err) @@ -160,8 +78,7 @@ func (cli *comandyClient) request(para string) (ret string) { return r.printstrerr("unsupported H type " + reflect.ValueOf(x).Type().Name()) } } - wg.Wait() - resp, err := (*http.Client)(cli).Do(req) + resp, err := http2.DefaultClient.Do(req) if err != nil { return r.printerr(err) } diff --git a/http_test.go b/http_test.go index 1e93856..5663d8e 100644 --- a/http_test.go +++ b/http_test.go @@ -3,41 +3,12 @@ package main import ( "encoding/base64" "encoding/json" - "io" "net/http" "testing" ) -func TestClientGet(t *testing.T) { - _ = canUseIPv6.Get() - req, err := http.NewRequest("GET", "https://api.mangacopy.com/api/v3/h5/homeIndex?platform=3", nil) - if err != nil { - t.Fatal(err) - } - req.Header.Add("user-agent", "COPY/2.1.7") - resp, err := (*http.Client)(&cli).Do(req) - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - t.Log("[T] response code", resp.StatusCode) - for k, vs := range resp.Header { - for _, v := range vs { - t.Log("[T] response header", k+":", v) - } - } - data, err := io.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - if len(data) == 0 { - t.Fail() - } - t.Log(bytesToString(data)) -} - func TestRequest(t *testing.T) { - r := cli.request(`{"code":0,"headers":{"authorization":"Token ","host":"api.mangacopy.com","source":"copyApp","webp":"1","region":"1","version":"2.1.7","platform":"3","user-agent":"COPY/2.1.7"},"method":"GET","url":"https://api.mangacopy.com/api/v3/h5/homeIndex?platform\u003d3"}`) + r := gorequest(`{"code":0,"headers":{"authorization":"Token ","host":"api.mangacopy.com","source":"copyApp","webp":"1","region":"1","version":"2.1.7","platform":"3","user-agent":"COPY/2.1.7"},"method":"GET","url":"https://api.mangacopy.com/api/v3/h5/homeIndex?platform\u003d3"}`) t.Log(r) c := capsule{} err := json.Unmarshal(stringToBytes(r), &c) diff --git a/ipv6.go b/ipv6.go deleted file mode 100644 index bb4b649..0000000 --- a/ipv6.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "context" - "net/http" - "time" - - "github.com/RomiChan/syncx" -) - -var canUseIPv6 = syncx.Lazy[bool]{Init: func() bool { - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - req, err := http.NewRequestWithContext(ctx, "GET", "http://v6.ipv6-test.com/json/widgetdata.php?callback=?", nil) - if err != nil { - return false - } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return false - } - _ = resp.Body.Close() - return true -}} diff --git a/main.go b/main.go index 0d27e8b..356f9b3 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,9 @@ import "C" import ( "encoding/json" + + "github.com/fumiama/terasu/dns" + "github.com/fumiama/terasu/ip" ) func main() {} @@ -18,13 +21,13 @@ func add_dns(para *C.char, is_ipv6 C.int) *C.char { return C.CString(err.Error()) } if is_ipv6 != 0 { - if !canUseIPv6.Get() { + if !ip.IsIPv6Available.Get() { return C.CString("cannot use ipv6") } - dotv6servers.add(m) + dns.IPv6Servers.Add(m) return nil } - dotv4servers.add(m) + dns.IPv4Servers.Add(m) return nil } @@ -40,5 +43,5 @@ func add_dns(para *C.char, is_ipv6 C.int) *C.char { // //export request func request(para *C.char) *C.char { - return C.CString(cli.request(C.GoString(para))) + return C.CString(gorequest(C.GoString(para))) } diff --git a/utils.go b/utils.go index d898d1d..edf0da1 100644 --- a/utils.go +++ b/utils.go @@ -14,10 +14,12 @@ type slice struct { cap int } +/* // bytesToString 没有内存开销的转换 func bytesToString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } +*/ // stringToBytes 没有内存开销的转换 func stringToBytes(s string) (b []byte) {