mirror of
https://github.com/amnezia-vpn/amnezia-libxray.git
synced 2026-05-17 06:55:44 +03:00
reorganize lib, nodep can be used by other project
This commit is contained in:
@@ -12,10 +12,6 @@ This is an Xray wrapper focusing on improving the experience of [Xray-core](http
|
||||
|
||||
转换 Clash yaml,Clash.Meta yaml 为 Xray Json。
|
||||
|
||||
### controller.go
|
||||
|
||||
试验性的 Android Protect(fd)。
|
||||
|
||||
### file.go
|
||||
|
||||
文件写入。
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package libXray
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
xinternet "github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
type DialerController interface {
|
||||
FdCallback(int) bool
|
||||
}
|
||||
|
||||
func RegisterDialerController(controller DialerController) {
|
||||
xinternet.RegisterDialerController(func(network, address string, conn syscall.RawConn) error {
|
||||
//controller.FdCallback(int(fd))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
9
geo.go
9
geo.go
@@ -7,11 +7,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/libxray/nodep"
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Read geo data and write all codes to text file.
|
||||
// dir means the dir which geosite.dat and geoip.dat are in.
|
||||
func LoadGeoData(dir string) string {
|
||||
if err := loadGeoSite(dir); err != nil {
|
||||
return err.Error()
|
||||
@@ -22,7 +25,7 @@ func LoadGeoData(dir string) string {
|
||||
ts := time.Now().Unix()
|
||||
tsText := strconv.FormatInt(ts, 10)
|
||||
tsPath := path.Join(dir, "timestamp.txt")
|
||||
if err := writeText(tsText, tsPath); err != nil {
|
||||
if err := nodep.WriteText(tsText, tsPath); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
@@ -53,7 +56,7 @@ func loadGeoSite(dir string) error {
|
||||
}
|
||||
}
|
||||
text := strings.Join(countries, "\n")
|
||||
if err := writeText(text, txtPath); err != nil {
|
||||
if err := nodep.WriteText(text, txtPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -78,7 +81,7 @@ func loadGeoIP(dir string) error {
|
||||
}
|
||||
|
||||
text := strings.Join(countries, "\n")
|
||||
if err := writeText(text, txtPath); err != nil {
|
||||
if err := nodep.WriteText(text, txtPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
37
memory.go
37
memory.go
@@ -1,37 +0,0 @@
|
||||
package libXray
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
)
|
||||
|
||||
func forceFree(interval time.Duration) {
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(interval)
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func readForceFreeInterval() int {
|
||||
const key = "XRAY_MEMORY_FORCEFREE"
|
||||
const defaultValue = 0
|
||||
interval := platform.EnvFlag{
|
||||
Name: key,
|
||||
AltName: platform.NormalizeEnvName(key),
|
||||
}.GetValueAsInt(defaultValue)
|
||||
return interval
|
||||
}
|
||||
|
||||
func initForceFree(maxMemory int64) {
|
||||
debug.SetGCPercent(10)
|
||||
debug.SetMemoryLimit(maxMemory)
|
||||
interval := readForceFreeInterval()
|
||||
if interval > 0 {
|
||||
duration := time.Duration(interval) * time.Second
|
||||
forceFree(duration)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package libXray
|
||||
package nodep
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func writeBytes(bytes []byte, path string) error {
|
||||
func WriteBytes(bytes []byte, path string) error {
|
||||
fi, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0664)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -18,7 +18,7 @@ func writeBytes(bytes []byte, path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeText(text string, path string) error {
|
||||
func WriteText(text string, path string) error {
|
||||
fi, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0664)
|
||||
if err != nil {
|
||||
return err
|
||||
101
nodep/measure.go
Normal file
101
nodep/measure.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package nodep
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
PingDelayTimeout int64 = 11000
|
||||
PingDelayError int64 = 10000
|
||||
)
|
||||
|
||||
type geoLocation struct {
|
||||
Ip string `json:"ip,omitempty"`
|
||||
Cc string `json:"cc,omitempty"`
|
||||
}
|
||||
|
||||
// Find the delay and country code of some outbound.
|
||||
// timeout means how long the http request will be cancelled if no response, in units of seconds.
|
||||
// url means the website we use to test speed. "https://www.google.com/gen_204" is a good choice for most cases.
|
||||
// times means how many times we should test the url.
|
||||
// proxy means the local http/socks5 proxy, like "http://127.0.0.1:1080".
|
||||
|
||||
func MeasureDelay(timeout int, url string, times int, proxy string) string {
|
||||
httpTimeout := time.Second * time.Duration(timeout)
|
||||
c, err := coreHTTPClient(httpTimeout, proxy)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%d::%s", PingDelayError, err)
|
||||
}
|
||||
delaySum := int64(0)
|
||||
count := int64(0)
|
||||
isValid := false
|
||||
lastErr := ""
|
||||
for i := 0; i < times; i++ {
|
||||
delay, err := pingHTTPRequest(c, url)
|
||||
if delay != PingDelayTimeout {
|
||||
delaySum += delay
|
||||
count += 1
|
||||
isValid = true
|
||||
} else {
|
||||
lastErr = err.Error()
|
||||
}
|
||||
}
|
||||
if !isValid {
|
||||
return fmt.Sprintf("%d::%s", PingDelayTimeout, lastErr)
|
||||
}
|
||||
country, err := geolocationHTTPRequest(c)
|
||||
if err != nil {
|
||||
fmt.Println("geolocation error: ", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d:%s:%s", delaySum/count, country, lastErr)
|
||||
}
|
||||
|
||||
func coreHTTPClient(timeout time.Duration, proxy string) (*http.Client, error) {
|
||||
tr := &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
Proxy: func(r *http.Request) (*url.URL, error) {
|
||||
return url.Parse(proxy)
|
||||
},
|
||||
}
|
||||
|
||||
c := &http.Client{
|
||||
Transport: tr,
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func pingHTTPRequest(c *http.Client, url string) (int64, error) {
|
||||
start := time.Now()
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
_, err := c.Do(req)
|
||||
if err != nil {
|
||||
return PingDelayTimeout, err
|
||||
}
|
||||
return time.Since(start).Milliseconds(), nil
|
||||
}
|
||||
|
||||
func geolocationHTTPRequest(c *http.Client) (string, error) {
|
||||
req, _ := http.NewRequest("GET", "https://ident.me/json", nil)
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var location geoLocation
|
||||
if err = json.Unmarshal(body, &location); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return location.Cc, nil
|
||||
}
|
||||
24
nodep/memory.go
Normal file
24
nodep/memory.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package nodep
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
)
|
||||
|
||||
func forceFree(interval time.Duration) {
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(interval)
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func InitForceFree(maxMemory int64, interval int) {
|
||||
debug.SetGCPercent(10)
|
||||
debug.SetMemoryLimit(maxMemory)
|
||||
if interval > 0 {
|
||||
duration := time.Duration(interval) * time.Second
|
||||
forceFree(duration)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package libXray
|
||||
package nodep
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
122
ping.go
122
ping.go
@@ -1,123 +1,31 @@
|
||||
package libXray
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
xnet "github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/libxray/nodep"
|
||||
)
|
||||
|
||||
const (
|
||||
pingDelayTimeout int64 = 11000
|
||||
pingDelayError int64 = 10000
|
||||
)
|
||||
|
||||
type geoLocation struct {
|
||||
Ip string `json:"ip,omitempty"`
|
||||
Cc string `json:"cc,omitempty"`
|
||||
}
|
||||
|
||||
func Ping(datDir string, config string, timeout int, url string, times int) string {
|
||||
// Ping Xray config and find the delay and country code of its outbound.
|
||||
// datDir means the dir which geosite.dat and geoip.dat are in.
|
||||
// configPath means the config.json file path.
|
||||
// timeout means how long the http request will be cancelled if no response, in units of seconds.
|
||||
// url means the website we use to test speed. "https://www.google.com/gen_204" is a good choice for most cases.
|
||||
// times means how many times we should test the url.
|
||||
// proxy means the local http/socks5 proxy, like "http://127.0.0.1:1080".
|
||||
//
|
||||
// note: you must use http protocol as inbound.
|
||||
func Ping(datDir string, configPath string, timeout int, url string, times int, proxy string) string {
|
||||
initEnv(datDir)
|
||||
server, err := startXray(config)
|
||||
server, err := startXray(configPath)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%d::%s", pingDelayError, err)
|
||||
return fmt.Sprintf("%d::%s", nodep.PingDelayError, err)
|
||||
}
|
||||
|
||||
if err := server.Start(); err != nil {
|
||||
return fmt.Sprintf("%d::%s", pingDelayError, err)
|
||||
return fmt.Sprintf("%d::%s", nodep.PingDelayError, err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
return measureDelay(server, timeout, url, times)
|
||||
}
|
||||
|
||||
func measureDelay(inst *core.Instance, timeout int, url string, times int) string {
|
||||
httpTimeout := time.Second * time.Duration(timeout)
|
||||
c, err := coreHTTPClient(inst, httpTimeout)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%d::%s", pingDelayError, err)
|
||||
}
|
||||
delaySum := int64(0)
|
||||
count := int64(0)
|
||||
isValid := false
|
||||
lastErr := ""
|
||||
for i := 0; i < times; i++ {
|
||||
delay, err := pingHTTPRequest(c, url)
|
||||
if delay != pingDelayTimeout {
|
||||
delaySum += delay
|
||||
count += 1
|
||||
isValid = true
|
||||
} else {
|
||||
lastErr = err.Error()
|
||||
}
|
||||
}
|
||||
if !isValid {
|
||||
return fmt.Sprintf("%d::%s", pingDelayTimeout, lastErr)
|
||||
}
|
||||
country, err := geolocationHTTPRequest(c)
|
||||
if err != nil {
|
||||
fmt.Println("geolocation error: ", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d:%s:%s", delaySum/count, country, lastErr)
|
||||
}
|
||||
|
||||
func coreHTTPClient(inst *core.Instance, timeout time.Duration) (*http.Client, error) {
|
||||
if inst == nil {
|
||||
return nil, errors.New("core instance nil")
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dest, err := xnet.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return core.Dial(ctx, inst, dest)
|
||||
},
|
||||
}
|
||||
|
||||
c := &http.Client{
|
||||
Transport: tr,
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func pingHTTPRequest(c *http.Client, url string) (int64, error) {
|
||||
start := time.Now()
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
_, err := c.Do(req)
|
||||
if err != nil {
|
||||
return pingDelayTimeout, err
|
||||
}
|
||||
return time.Since(start).Milliseconds(), nil
|
||||
}
|
||||
|
||||
func geolocationHTTPRequest(c *http.Client) (string, error) {
|
||||
req, _ := http.NewRequest("GET", "https://ident.me/json", nil)
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var location geoLocation
|
||||
if err = json.Unmarshal(body, &location); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return location.Cc, nil
|
||||
return nodep.MeasureDelay(timeout, url, times, proxy)
|
||||
}
|
||||
|
||||
5
share.go
5
share.go
@@ -9,9 +9,12 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/xtls/libxray/nodep"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
)
|
||||
|
||||
// Convert share text to xrayJson
|
||||
// support xrayJson, v2rayN plain text, v2rayN base64 text, clash yaml, Clash.Meta yaml
|
||||
func ParseShareText(textPath string, xrayPath string) string {
|
||||
textBytes, err := filesystem.ReadFile(textPath)
|
||||
if err != nil {
|
||||
@@ -135,7 +138,7 @@ func writeXrayJson(xray *xrayJson, xrayPath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeBytes(xrayBytes, xrayPath)
|
||||
return nodep.WriteBytes(xrayBytes, xrayPath)
|
||||
}
|
||||
|
||||
type xrayShareLink struct {
|
||||
|
||||
@@ -8,9 +8,12 @@ import (
|
||||
|
||||
"net/url"
|
||||
|
||||
"github.com/xtls/libxray/nodep"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
)
|
||||
|
||||
// Convert XrayJson to share links.
|
||||
// VMess will generate VMessAEAD link.
|
||||
func ConvertXrayJsonToShareText(xrayPath string, textPath string) string {
|
||||
xrayBytes, err := filesystem.ReadFile(xrayPath)
|
||||
if err != nil {
|
||||
@@ -40,7 +43,7 @@ func ConvertXrayJsonToShareText(xrayPath string, textPath string) string {
|
||||
return "no valid outbounds"
|
||||
}
|
||||
text := strings.Join(links, "\n")
|
||||
err = writeText(text, textPath)
|
||||
err = nodep.WriteText(text, textPath)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
1
uuid.go
1
uuid.go
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/uuid"
|
||||
)
|
||||
|
||||
// convert text to uuid
|
||||
func CustomUUID(text string) string {
|
||||
id, err := uuid.ParseString(text)
|
||||
if err != nil {
|
||||
|
||||
18
xray.go
18
xray.go
@@ -1,9 +1,11 @@
|
||||
// libXray is an Xray wrapper focusing on improving the experience of Xray-core mobile development.
|
||||
package libXray
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/xtls/libxray/nodep"
|
||||
"github.com/xtls/xray-core/common/cmdarg"
|
||||
"github.com/xtls/xray-core/core"
|
||||
_ "github.com/xtls/xray-core/main/distro/all"
|
||||
@@ -33,10 +35,13 @@ func initEnv(datDir string) {
|
||||
}
|
||||
|
||||
func setMaxMemory(maxMemory int64) {
|
||||
os.Setenv("XRAY_MEMORY_FORCEFREE", "1")
|
||||
initForceFree(maxMemory)
|
||||
nodep.InitForceFree(maxMemory, 1)
|
||||
}
|
||||
|
||||
// Run Xray instance.
|
||||
// datDir means the dir which geosite.dat and geoip.dat are in.
|
||||
// configPath means the config.json file path.
|
||||
// maxMemory means the soft memory limit of golang, see SetMemoryLimit to find more information.
|
||||
func RunXray(datDir string, configPath string, maxMemory int64) string {
|
||||
initEnv(datDir)
|
||||
if maxMemory > 0 {
|
||||
@@ -55,6 +60,7 @@ func RunXray(datDir string, configPath string, maxMemory int64) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Stop Xray instance.
|
||||
func StopXray() string {
|
||||
if coreServer != nil {
|
||||
err := coreServer.Close()
|
||||
@@ -66,6 +72,14 @@ func StopXray() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Xray's version
|
||||
func XrayVersion() string {
|
||||
return core.Version()
|
||||
}
|
||||
|
||||
// Wrapper of nodep.GetFreePorts
|
||||
// count means how many ports you need.
|
||||
// return ports divided by ":", like "1080:1081"
|
||||
func GetFreePorts(count int) string {
|
||||
return nodep.GetFreePorts(count)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user