fix: content length & dns
This commit is contained in:
parent
fdee77d778
commit
90d3d676f3
2
Makefile
2
Makefile
@ -12,7 +12,7 @@ TARGET_SDK := android23
|
||||
TARGET_ARCH := aarch64 # optional: armv7a i686 x86_64
|
||||
|
||||
CGO_ENABLED := 1
|
||||
GO_SRC := $(shell find . -name '*.go')
|
||||
GO_SRC := $(shell find . -name '*.go' | grep -v '_test.go$$')
|
||||
NDK_TOOLCHAIN := ~/Library/Android/sdk/ndk/$(NDK_VERSION)/toolchains/llvm/prebuilt/$(BUILD_MACHINE)-$(BUILD_ARCH)
|
||||
CC := $(NDK_TOOLCHAIN)/bin/$(TARGET_ARCH)-linux-$(TARGET_SDK)-clang
|
||||
TEST_OUTPUT = '$(shell cd $(BUILD_PATH) && ./test | head -c 12)'
|
||||
|
19
dns.go
19
dns.go
@ -48,14 +48,28 @@ func (ds *dnsservers) add(m map[string][]string) {
|
||||
func (ds *dnsservers) dial(ctx context.Context) (tlsConn *tls.Conn, err error) {
|
||||
ds.RLock()
|
||||
defer ds.RUnlock()
|
||||
|
||||
if dialer.Timeout != 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, dialer.Timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
if !dialer.Deadline.IsZero() {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithDeadline(ctx, dialer.Deadline)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
var conn net.Conn
|
||||
for host, addrs := range ds.m {
|
||||
for _, addr := range addrs {
|
||||
if !addr.E {
|
||||
continue
|
||||
}
|
||||
conn, err = net.Dial("tcp", addr.A)
|
||||
conn, err = dialer.DialContext(ctx, "tcp", addr.A)
|
||||
if err != nil {
|
||||
addr.E = false // no need to acquire write lock
|
||||
continue
|
||||
}
|
||||
tlsConn = terasu.Use(tls.Client(conn, &tls.Config{ServerName: host}))
|
||||
@ -63,6 +77,7 @@ func (ds *dnsservers) dial(ctx context.Context) (tlsConn *tls.Conn, err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
_ = tlsConn.Close()
|
||||
addr.E = false // no need to acquire write lock
|
||||
}
|
||||
}
|
||||
@ -129,7 +144,7 @@ var dotv4servers = dnsservers{
|
||||
|
||||
var resolver = &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
Dial: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
if canUseIPv6.Get() {
|
||||
return dotv6servers.dial(ctx)
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ func TestResolver(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(addrs)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
func TestDNS(t *testing.T) {
|
||||
|
6
go.mod
6
go.mod
@ -3,6 +3,10 @@ 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-20240414143030-44fae3a81905
|
||||
github.com/fumiama/terasu v0.0.0-20240415131749-e65650a52c3c
|
||||
golang.org/x/net v0.24.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.14.0 // indirect
|
||||
|
18
go.sum
18
go.sum
@ -1,4 +1,18 @@
|
||||
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/fumiama/terasu v0.0.0-20240414143030-44fae3a81905 h1:PHf84+ujLpFGJbfytrwZT6/D7KojmjFm5Itv6te6WUA=
|
||||
github.com/fumiama/terasu v0.0.0-20240414143030-44fae3a81905/go.mod h1:BFl0X1+rGJf8bLHl/kO+v05ryHrj/R4kyCrK89NvegA=
|
||||
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-20240415131749-e65650a52c3c h1:RxkHkeanPDrZrEEcUcosgBULmL8UDkSasvwP+jpdIZQ=
|
||||
github.com/fumiama/terasu v0.0.0-20240415131749-e65650a52c3c/go.mod h1:BFl0X1+rGJf8bLHl/kO+v05ryHrj/R4kyCrK89NvegA=
|
||||
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=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
143
http.go
143
http.go
@ -5,36 +5,81 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
var dialer = net.Dialer{
|
||||
Timeout: time.Minute,
|
||||
}
|
||||
|
||||
var cli = http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
conn, err := dialer.DialContext(ctx, "tcp", addr)
|
||||
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 dialer.Timeout != 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, dialer.Timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
if !dialer.Deadline.IsZero() {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithDeadline(ctx, dialer.Deadline)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host, _, 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 {
|
||||
return nil, err
|
||||
}
|
||||
lookupTable.Set(host, addrs)
|
||||
}
|
||||
return terasu.Use(tls.Client(conn, &tls.Config{
|
||||
ServerName: host,
|
||||
InsecureSkipVerify: true,
|
||||
})), nil
|
||||
if len(addr) == 0 {
|
||||
return nil, errors.New("empty host addr")
|
||||
}
|
||||
var tlsConn *tls.Conn
|
||||
for _, a := range addrs {
|
||||
if strings.Contains(a, ":") {
|
||||
a = "[" + a + "]:" + port
|
||||
} else {
|
||||
a += ":" + port
|
||||
}
|
||||
conn, err := dialer.DialContext(ctx, network, a)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
tlsConn = terasu.Use(tls.Client(conn, cfg))
|
||||
err = tlsConn.HandshakeContext(ctx)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
_ = tlsConn.Close()
|
||||
}
|
||||
return tlsConn, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
type capsule struct {
|
||||
C int `json:"code,omitempty"`
|
||||
@ -59,3 +104,77 @@ func (r *capsule) printstrerr(err string) string {
|
||||
_ = json.NewEncoder(&buf).Encode(r)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (cli *comandyClient) request(para string) string {
|
||||
r := capsule{}
|
||||
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)
|
||||
}
|
||||
if r.U == "" || !strings.HasPrefix(r.U, "https://") {
|
||||
return r.printstrerr("invalid url '" + r.U + "'")
|
||||
}
|
||||
if r.M != "GET" && r.M != "POST" && r.M != "DELETE" {
|
||||
return r.printstrerr("invalid method '" + r.U + "'")
|
||||
}
|
||||
var body io.Reader
|
||||
if len(r.D) > 0 {
|
||||
body = strings.NewReader(r.D)
|
||||
}
|
||||
req, err := http.NewRequest(r.M, r.U, body)
|
||||
if err != nil {
|
||||
return r.printerr(err)
|
||||
}
|
||||
for k, vs := range r.H {
|
||||
lk := strings.ToLower(k)
|
||||
if strings.HasPrefix(lk, "x-") {
|
||||
continue
|
||||
}
|
||||
switch x := vs.(type) {
|
||||
case string:
|
||||
req.Header.Add(k, x)
|
||||
case []string:
|
||||
for _, v := range x {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
default:
|
||||
return r.printstrerr("unsupported H type " + reflect.ValueOf(x).Type().Name())
|
||||
}
|
||||
}
|
||||
fmt.Println(r.U)
|
||||
wg.Wait()
|
||||
resp, err := (*http.Client)(cli).Do(req)
|
||||
if err != nil {
|
||||
return r.printerr(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
sb := strings.Builder{}
|
||||
enc := base64.NewEncoder(base64.StdEncoding, &sb)
|
||||
_, err = io.Copy(enc, resp.Body)
|
||||
_ = enc.Close()
|
||||
if err != nil {
|
||||
return r.printerr(err)
|
||||
}
|
||||
r.C = resp.StatusCode
|
||||
r.H = make(map[string]any, len(resp.Header)*2)
|
||||
for k, vs := range resp.Header {
|
||||
if len(vs) == 1 {
|
||||
r.H[k] = vs[0]
|
||||
continue
|
||||
}
|
||||
r.H[k] = vs
|
||||
}
|
||||
r.D = sb.String()
|
||||
outbuf := strings.Builder{}
|
||||
err = json.NewEncoder(&outbuf).Encode(&r)
|
||||
if err != nil {
|
||||
return r.printerr(err)
|
||||
}
|
||||
return outbuf.String()
|
||||
}
|
||||
|
49
http_test.go
Normal file
49
http_test.go
Normal file
@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
t.Log(bytesToString(data))
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
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"}`)
|
||||
t.Log(r)
|
||||
c := capsule{}
|
||||
err := json.Unmarshal(stringToBytes(r), &c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c.C != http.StatusOK {
|
||||
t.Fatal("status code", c.C)
|
||||
}
|
||||
if len(c.D) == 0 {
|
||||
t.Fatal("empty data")
|
||||
}
|
||||
}
|
69
main.go
69
main.go
@ -3,12 +3,7 @@ package main
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
@ -45,67 +40,5 @@ func add_dns(para *C.char, is_ipv6 C.int) *C.char {
|
||||
//
|
||||
//export request
|
||||
func request(para *C.char) *C.char {
|
||||
r := capsule{}
|
||||
err := json.Unmarshal(stringToBytes(C.GoString(para)), &r)
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
if r.U == "" || !strings.HasPrefix(r.U, "https://") {
|
||||
return C.CString(r.printstrerr("invalid url '" + r.U + "'"))
|
||||
}
|
||||
if r.M != "GET" && r.M != "POST" && r.M != "DELETE" {
|
||||
return C.CString(r.printstrerr("invalid method '" + r.U + "'"))
|
||||
}
|
||||
var body io.Reader
|
||||
if len(r.D) > 0 {
|
||||
body = strings.NewReader(r.D)
|
||||
}
|
||||
req, err := http.NewRequest(r.M, r.U, body)
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
for k, vs := range r.H {
|
||||
lk := strings.ToLower(k)
|
||||
if strings.HasPrefix(lk, "x-") {
|
||||
continue
|
||||
}
|
||||
switch x := vs.(type) {
|
||||
case string:
|
||||
req.Header.Add(k, x)
|
||||
case []string:
|
||||
for _, v := range x {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
default:
|
||||
return C.CString(r.printstrerr("unsupported H type " + reflect.ValueOf(x).Type().Name()))
|
||||
}
|
||||
}
|
||||
resp, err := cli.Do(req)
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
sb := strings.Builder{}
|
||||
enc := base64.NewEncoder(base64.StdEncoding, &sb)
|
||||
_, err = io.CopyN(enc, resp.Body, resp.ContentLength)
|
||||
_ = enc.Close()
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
r.C = resp.StatusCode
|
||||
r.H = make(map[string]any, len(resp.Header)*2)
|
||||
for k, vs := range resp.Header {
|
||||
if len(vs) == 1 {
|
||||
r.H[k] = vs[0]
|
||||
continue
|
||||
}
|
||||
r.H[k] = vs
|
||||
}
|
||||
r.D = sb.String()
|
||||
outbuf := strings.Builder{}
|
||||
err = json.NewEncoder(&outbuf).Encode(&r)
|
||||
if err != nil {
|
||||
return C.CString(r.printerr(err))
|
||||
}
|
||||
return C.CString(outbuf.String())
|
||||
return C.CString(cli.request(C.GoString(para)))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user