Files
umbrix-libcore/v2/independent_instance.go
2024-09-28 20:31:38 +02:00

173 lines
4.2 KiB
Go

package v2
import (
"fmt"
"io"
"net"
"net/http"
"time"
"github.com/hiddify/hiddify-core/config"
"golang.org/x/net/proxy"
"github.com/sagernet/sing-box/experimental/libbox"
"github.com/sagernet/sing-box/option"
)
func getRandomAvailblePort() uint16 {
// TODO: implement it
listener, err := net.Listen("tcp", ":0")
if err != nil {
panic(err)
}
defer listener.Close()
return uint16(listener.Addr().(*net.TCPAddr).Port)
}
func RunInstanceString(hiddifySettings *config.HiddifyOptions, proxiesInput string) (*HiddifyService, error) {
if hiddifySettings == nil {
hiddifySettings = config.DefaultHiddifyOptions()
}
singconfigs, err := config.ParseConfigContentToOptions(proxiesInput, true, hiddifySettings, false)
if err != nil {
return nil, err
}
return RunInstance(hiddifySettings, singconfigs)
}
func RunInstance(hiddifySettings *config.HiddifyOptions, singconfig *option.Options) (*HiddifyService, error) {
if hiddifySettings == nil {
hiddifySettings = config.DefaultHiddifyOptions()
}
hiddifySettings.EnableClashApi = false
hiddifySettings.InboundOptions.MixedPort = getRandomAvailblePort()
hiddifySettings.InboundOptions.EnableTun = false
hiddifySettings.InboundOptions.EnableTunService = false
hiddifySettings.InboundOptions.SetSystemProxy = false
hiddifySettings.InboundOptions.TProxyPort = 0
hiddifySettings.InboundOptions.LocalDnsPort = 0
hiddifySettings.Region = "other"
hiddifySettings.BlockAds = false
hiddifySettings.LogFile = "/dev/null"
finalConfigs, err := config.BuildConfig(*hiddifySettings, *singconfig)
if err != nil {
return nil, err
}
instance, err := NewService(*finalConfigs)
if err != nil {
return nil, err
}
err = instance.Start()
if err != nil {
return nil, err
}
<-time.After(250 * time.Millisecond)
hservice := &HiddifyService{libbox: instance, ListenPort: hiddifySettings.InboundOptions.MixedPort}
hservice.PingCloudflare()
return hservice, nil
}
type HiddifyService struct {
libbox *libbox.BoxService
ListenPort uint16
}
// dialer, err := s.libbox.GetInstance().Router().Dialer(context.Background())
func (s *HiddifyService) Close() error {
return s.libbox.Close()
}
func (s *HiddifyService) GetContent(url string) (string, error) {
return s.ContentFromURL("GET", url, 10*time.Second)
}
func (s *HiddifyService) ContentFromURL(method string, url string, timeout time.Duration) (string, error) {
if method == "" {
return "", fmt.Errorf("empty method")
}
if url == "" {
return "", fmt.Errorf("empty url")
}
req, err := http.NewRequest(method, url, nil)
if err != nil {
return "", err
}
dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", s.ListenPort), nil, proxy.Direct)
if err != nil {
return "", err
}
transport := &http.Transport{
Dial: dialer.Dial,
}
client := &http.Client{
Transport: transport,
Timeout: timeout,
}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
return "", fmt.Errorf("request failed with status code: %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
if body == nil {
return "", fmt.Errorf("empty body")
}
return string(body), nil
}
func (s *HiddifyService) PingCloudflare() (time.Duration, error) {
return s.Ping("http://cp.cloudflare.com")
}
// func (s *HiddifyService) RawConnection(ctx context.Context, url string) (net.Conn, error) {
// return
// }
func (s *HiddifyService) PingAverage(url string, count int) (time.Duration, error) {
if count <= 0 {
return -1, fmt.Errorf("count must be greater than 0")
}
var sum int
real_count := 0
for i := 0; i < count; i++ {
delay, err := s.Ping(url)
if err == nil {
real_count++
sum += int(delay.Milliseconds())
} else if real_count == 0 && i > count/2 {
return -1, fmt.Errorf("ping average failed")
}
}
return time.Duration(sum / real_count * int(time.Millisecond)), nil
}
func (s *HiddifyService) Ping(url string) (time.Duration, error) {
startTime := time.Now()
_, err := s.ContentFromURL("HEAD", url, 4*time.Second)
if err != nil {
return -1, err
}
duration := time.Since(startTime)
return duration, nil
}