mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-10 18:40:27 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7fefd2b9b | ||
|
|
b8e36b483e | ||
|
|
91ac841b9d | ||
|
|
5134446c3f | ||
|
|
82b6ebd6f1 | ||
|
|
f8ea009fe9 |
No files matched your search
@@ -69,6 +69,7 @@ require (
|
|||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-ieproxy v0.0.11 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
github.com/mschoch/smat v0.2.0 // indirect
|
||||||
github.com/pion/datachannel v1.5.5 // indirect
|
github.com/pion/datachannel v1.5.5 // indirect
|
||||||
|
|||||||
@@ -270,6 +270,8 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
|||||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-ieproxy v0.0.11 h1:MQ/5BuGSgDAHZOJe6YY80IF2UVCfGkwfo6AeD7HtHYo=
|
||||||
|
github.com/mattn/go-ieproxy v0.0.11/go.mod h1:/NsJd+kxZBmjMc5hrJCKMbP57B84rvq9BiDRbtO9AS0=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"github.com/GopeedLab/gopeed/pkg/base"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
GetConfig func(v any) bool
|
GetConfig func(v any) bool
|
||||||
ProxyUrl *url.URL
|
ProxyConfig *base.DownloaderProxyConfig
|
||||||
FileController
|
FileController
|
||||||
//ContextDialer() (proxy.Dialer, error)
|
//ContextDialer() (proxy.Dialer, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/GopeedLab/gopeed/pkg/util"
|
"github.com/GopeedLab/gopeed/pkg/util"
|
||||||
"github.com/anacrolix/torrent"
|
"github.com/anacrolix/torrent"
|
||||||
"github.com/anacrolix/torrent/metainfo"
|
"github.com/anacrolix/torrent/metainfo"
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -69,9 +68,7 @@ func (f *Fetcher) initClient() (err error) {
|
|||||||
cfg.Bep20 = fmt.Sprintf("-GP%s-", parseBep20())
|
cfg.Bep20 = fmt.Sprintf("-GP%s-", parseBep20())
|
||||||
cfg.ExtendedHandshakeClientVersion = fmt.Sprintf("Gopeed %s", base.Version)
|
cfg.ExtendedHandshakeClientVersion = fmt.Sprintf("Gopeed %s", base.Version)
|
||||||
cfg.ListenPort = f.config.ListenPort
|
cfg.ListenPort = f.config.ListenPort
|
||||||
if f.ctl.ProxyUrl != nil {
|
cfg.HTTPProxy = f.ctl.ProxyConfig.ToHandler()
|
||||||
cfg.HTTPProxy = http.ProxyURL(f.ctl.ProxyUrl)
|
|
||||||
}
|
|
||||||
cfg.DefaultStorage = newFileOpts(newFileClientOpts{
|
cfg.DefaultStorage = newFileOpts(newFileClientOpts{
|
||||||
ClientBaseDir: cfg.DataDir,
|
ClientBaseDir: cfg.DataDir,
|
||||||
HandleFileTorrent: func(infoHash metainfo.Hash, ft *fileTorrentImpl) {
|
HandleFileTorrent: func(infoHash metainfo.Hash, ft *fileTorrentImpl) {
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import (
|
|||||||
"github.com/GopeedLab/gopeed/internal/test"
|
"github.com/GopeedLab/gopeed/internal/test"
|
||||||
"github.com/GopeedLab/gopeed/pkg/base"
|
"github.com/GopeedLab/gopeed/pkg/base"
|
||||||
"github.com/GopeedLab/gopeed/pkg/protocol/bt"
|
"github.com/GopeedLab/gopeed/pkg/protocol/bt"
|
||||||
"github.com/GopeedLab/gopeed/pkg/util"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -60,7 +58,14 @@ func TestFetcher_ResolveWithProxy(t *testing.T) {
|
|||||||
proxyListener := test.StartSocks5Server(usr, pwd)
|
proxyListener := test.StartSocks5Server(usr, pwd)
|
||||||
defer proxyListener.Close()
|
defer proxyListener.Close()
|
||||||
|
|
||||||
doResolve(t, buildConfigFetcher(util.BuildProxyUrl("socks5", proxyListener.Addr().String(), usr, pwd)))
|
doResolve(t, buildConfigFetcher(&base.DownloaderProxyConfig{
|
||||||
|
Enable: true,
|
||||||
|
System: false,
|
||||||
|
Scheme: "socks5",
|
||||||
|
Host: proxyListener.Addr().String(),
|
||||||
|
Usr: usr,
|
||||||
|
Pwd: pwd,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
|
func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
|
||||||
@@ -100,7 +105,7 @@ func buildFetcher() fetcher.Fetcher {
|
|||||||
return fetcher
|
return fetcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildConfigFetcher(proxyUrl *url.URL) fetcher.Fetcher {
|
func buildConfigFetcher(proxyConfig *base.DownloaderProxyConfig) fetcher.Fetcher {
|
||||||
fetcher := new(FetcherBuilder).Build()
|
fetcher := new(FetcherBuilder).Build()
|
||||||
newController := controller.NewController()
|
newController := controller.NewController()
|
||||||
mockCfg := config{
|
mockCfg := config{
|
||||||
@@ -114,7 +119,7 @@ func buildConfigFetcher(proxyUrl *url.URL) fetcher.Fetcher {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
newController.ProxyUrl = proxyUrl
|
newController.ProxyConfig = proxyConfig
|
||||||
fetcher.Setup(newController)
|
fetcher.Setup(newController)
|
||||||
return fetcher
|
return fetcher
|
||||||
}
|
}
|
||||||
@@ -451,12 +451,11 @@ func (f *Fetcher) splitChunk() (chunks []*chunk) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fetcher) buildClient() *http.Client {
|
func (f *Fetcher) buildClient() *http.Client {
|
||||||
transport := &http.Transport{}
|
transport := &http.Transport{
|
||||||
|
Proxy: f.ctl.ProxyConfig.ToHandler(),
|
||||||
|
}
|
||||||
// Cookie handle
|
// Cookie handle
|
||||||
jar, _ := cookiejar.New(nil)
|
jar, _ := cookiejar.New(nil)
|
||||||
if f.ctl.ProxyUrl != nil {
|
|
||||||
transport.Proxy = http.ProxyURL(f.ctl.ProxyUrl)
|
|
||||||
}
|
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
Jar: jar,
|
Jar: jar,
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/GopeedLab/gopeed/internal/test"
|
"github.com/GopeedLab/gopeed/internal/test"
|
||||||
"github.com/GopeedLab/gopeed/pkg/base"
|
"github.com/GopeedLab/gopeed/pkg/base"
|
||||||
"github.com/GopeedLab/gopeed/pkg/protocol/http"
|
"github.com/GopeedLab/gopeed/pkg/protocol/http"
|
||||||
"github.com/GopeedLab/gopeed/pkg/util"
|
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -338,7 +337,11 @@ func downloadResume(listener net.Listener, connections int, t *testing.T) {
|
|||||||
func downloadWithProxy(httpListener net.Listener, proxyListener net.Listener, t *testing.T) {
|
func downloadWithProxy(httpListener net.Listener, proxyListener net.Listener, t *testing.T) {
|
||||||
fetcher := downloadReady(httpListener, 4, t)
|
fetcher := downloadReady(httpListener, 4, t)
|
||||||
ctl := controller.NewController()
|
ctl := controller.NewController()
|
||||||
ctl.ProxyUrl = util.BuildProxyUrl("socks5", proxyListener.Addr().String(), "", "")
|
ctl.ProxyConfig = &base.DownloaderProxyConfig{
|
||||||
|
Enable: true,
|
||||||
|
Scheme: "socks5",
|
||||||
|
Host: proxyListener.Addr().String(),
|
||||||
|
}
|
||||||
fetcher.Setup(ctl)
|
fetcher.Setup(ctl)
|
||||||
err := fetcher.Start()
|
err := fetcher.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ const (
|
|||||||
Dir = "./"
|
Dir = "./"
|
||||||
BuildFile = Dir + BuildName
|
BuildFile = Dir + BuildName
|
||||||
|
|
||||||
|
ExternalDownloadUrl = "https://raw.githubusercontent.com/GopeedLab/gopeed/v1.5.6/_docs/img/banner.png"
|
||||||
|
ExternalDownloadName = "banner.png"
|
||||||
|
ExternalDownloadSize = 26416
|
||||||
|
//ExternalDownloadMd5 = "c67c6e3cae79a95342485676571e8a5c"
|
||||||
|
|
||||||
DownloadName = "download.data"
|
DownloadName = "download.data"
|
||||||
DownloadRename = "download (1).data"
|
DownloadRename = "download (1).data"
|
||||||
DownloadFile = Dir + DownloadName
|
DownloadFile = Dir + DownloadName
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ package base
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/GopeedLab/gopeed/pkg/util"
|
"github.com/GopeedLab/gopeed/pkg/util"
|
||||||
|
"github.com/mattn/go-ieproxy"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -128,3 +131,88 @@ func ParseOptsExtra[E any](opts *Options) error {
|
|||||||
opts.Extra = &t
|
opts.Extra = &t
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DownloaderStoreConfig is the config that can restore the downloader.
|
||||||
|
type DownloaderStoreConfig struct {
|
||||||
|
FirstLoad bool `json:"-"` // FirstLoad is the flag that the config is first time init and not from store
|
||||||
|
|
||||||
|
DownloadDir string `json:"downloadDir"` // DownloadDir is the default directory to save the downloaded files
|
||||||
|
MaxRunning int `json:"maxRunning"` // MaxRunning is the max running download count
|
||||||
|
ProtocolConfig map[string]any `json:"protocolConfig"` // ProtocolConfig is special config for each protocol
|
||||||
|
Extra map[string]any `json:"extra"`
|
||||||
|
Proxy *DownloaderProxyConfig `json:"proxy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *DownloaderStoreConfig) Init() *DownloaderStoreConfig {
|
||||||
|
if cfg.MaxRunning == 0 {
|
||||||
|
cfg.MaxRunning = 5
|
||||||
|
}
|
||||||
|
if cfg.Proxy == nil {
|
||||||
|
cfg.Proxy = &DownloaderProxyConfig{}
|
||||||
|
}
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownloaderProxyConfig struct {
|
||||||
|
Enable bool `json:"enable"`
|
||||||
|
// System is the flag that use system proxy
|
||||||
|
System bool `json:"system"`
|
||||||
|
Scheme string `json:"scheme"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Usr string `json:"usr"`
|
||||||
|
Pwd string `json:"pwd"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *DownloaderProxyConfig) ToHandler() func(r *http.Request) (*url.URL, error) {
|
||||||
|
if cfg == nil || cfg.Enable == false {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if cfg.System {
|
||||||
|
ieproxy.ReloadConf()
|
||||||
|
return ieproxy.GetProxyFunc()
|
||||||
|
}
|
||||||
|
if cfg.Scheme == "" || cfg.Host == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return http.ProxyURL(util.BuildProxyUrl(cfg.Scheme, cfg.Host, cfg.Usr, cfg.Pwd))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUrl returns the proxy url, just for git clone
|
||||||
|
func (cfg *DownloaderProxyConfig) ToUrl() *url.URL {
|
||||||
|
if cfg == nil || cfg.Enable == false {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if cfg.System {
|
||||||
|
ieproxy.ReloadConf()
|
||||||
|
static := ieproxy.GetConf().Static
|
||||||
|
if static.Active && len(static.Protocols) > 0 {
|
||||||
|
// If only one protocol, use it
|
||||||
|
if len(static.Protocols) == 1 {
|
||||||
|
for _, v := range static.Protocols {
|
||||||
|
return parseUrlSafe(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check https
|
||||||
|
if v, ok := static.Protocols["https"]; ok {
|
||||||
|
return parseUrlSafe(v)
|
||||||
|
}
|
||||||
|
// Check http
|
||||||
|
if v, ok := static.Protocols["http"]; ok {
|
||||||
|
return parseUrlSafe(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if cfg.Scheme == "" || cfg.Host == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return util.BuildProxyUrl(cfg.Scheme, cfg.Host, cfg.Usr, cfg.Pwd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUrlSafe(rawUrl string) *url.URL {
|
||||||
|
u, err := url.Parse(rawUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
@@ -110,7 +110,7 @@ func (d *Downloader) Setup() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// load config from storage
|
// load config from storage
|
||||||
var cfg DownloaderStoreConfig
|
var cfg base.DownloaderStoreConfig
|
||||||
exist, err := d.storage.Get(bucketConfig, "config", &cfg)
|
exist, err := d.storage.Get(bucketConfig, "config", &cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -118,7 +118,7 @@ func (d *Downloader) Setup() error {
|
|||||||
if exist {
|
if exist {
|
||||||
d.cfg.DownloaderStoreConfig = &cfg
|
d.cfg.DownloaderStoreConfig = &cfg
|
||||||
} else {
|
} else {
|
||||||
d.cfg.DownloaderStoreConfig = &DownloaderStoreConfig{
|
d.cfg.DownloaderStoreConfig = &base.DownloaderStoreConfig{
|
||||||
FirstLoad: true,
|
FirstLoad: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ func (d *Downloader) setupFetcher(fetcher fetcher.Fetcher) {
|
|||||||
ctl.GetConfig = func(v any) bool {
|
ctl.GetConfig = func(v any) bool {
|
||||||
return d.getProtocolConfig(fetcher.Name(), v)
|
return d.getProtocolConfig(fetcher.Name(), v)
|
||||||
}
|
}
|
||||||
ctl.ProxyUrl = d.cfg.ProxyUrl()
|
ctl.ProxyConfig = d.cfg.Proxy
|
||||||
fetcher.Setup(ctl)
|
fetcher.Setup(ctl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,11 +603,11 @@ func (d *Downloader) GetTasksByStatues(statues []base.Status) []*Task {
|
|||||||
return tasks
|
return tasks
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Downloader) GetConfig() (*DownloaderStoreConfig, error) {
|
func (d *Downloader) GetConfig() (*base.DownloaderStoreConfig, error) {
|
||||||
return d.cfg.DownloaderStoreConfig, nil
|
return d.cfg.DownloaderStoreConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Downloader) PutConfig(v *DownloaderStoreConfig) error {
|
func (d *Downloader) PutConfig(v *base.DownloaderStoreConfig) error {
|
||||||
d.cfg.DownloaderStoreConfig = v
|
d.cfg.DownloaderStoreConfig = v
|
||||||
return d.storage.Put(bucketConfig, "config", v)
|
return d.storage.Put(bucketConfig, "config", v)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/GopeedLab/gopeed/internal/test"
|
"github.com/GopeedLab/gopeed/internal/test"
|
||||||
"github.com/GopeedLab/gopeed/pkg/base"
|
"github.com/GopeedLab/gopeed/pkg/base"
|
||||||
"github.com/GopeedLab/gopeed/pkg/protocol/http"
|
"github.com/GopeedLab/gopeed/pkg/protocol/http"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -85,37 +86,58 @@ func TestDownloader_Create(t *testing.T) {
|
|||||||
|
|
||||||
func TestDownloader_CreateWithProxy(t *testing.T) {
|
func TestDownloader_CreateWithProxy(t *testing.T) {
|
||||||
// No proxy
|
// No proxy
|
||||||
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
|
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
return nil
|
return nil
|
||||||
})
|
}, nil)
|
||||||
// Disable proxy
|
// Disable proxy
|
||||||
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
|
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
proxyCfg.Enable = false
|
proxyCfg.Enable = false
|
||||||
return proxyCfg
|
return proxyCfg
|
||||||
|
}, nil)
|
||||||
|
// Enable system proxy but not set proxy environment variable
|
||||||
|
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
|
proxyCfg.System = true
|
||||||
|
return proxyCfg
|
||||||
|
}, nil)
|
||||||
|
// Enable proxy but error proxy environment variable
|
||||||
|
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
|
os.Setenv("HTTP_PROXY", "http://127.0.0.1:1234")
|
||||||
|
os.Setenv("HTTPS_PROXY", "http://127.0.0.1:1234")
|
||||||
|
proxyCfg.System = true
|
||||||
|
return proxyCfg
|
||||||
|
}, func(err error) {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("doTestDownloaderCreateWithProxy() got = nil, want error")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
// Enable system proxy and set proxy environment variable
|
||||||
|
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
|
os.Setenv("HTTP_PROXY", proxyCfg.ToUrl().String())
|
||||||
|
os.Setenv("HTTPS_PROXY", proxyCfg.ToUrl().String())
|
||||||
|
proxyCfg.System = true
|
||||||
|
return proxyCfg
|
||||||
|
}, nil)
|
||||||
// Invalid proxy scheme
|
// Invalid proxy scheme
|
||||||
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
|
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
proxyCfg.Scheme = ""
|
proxyCfg.Scheme = ""
|
||||||
return proxyCfg
|
return proxyCfg
|
||||||
})
|
}, nil)
|
||||||
// Invalid proxy host
|
// Invalid proxy host
|
||||||
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
|
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
proxyCfg.Host = ""
|
proxyCfg.Host = ""
|
||||||
return proxyCfg
|
return proxyCfg
|
||||||
})
|
}, nil)
|
||||||
// Use proxy without auth
|
// Use proxy without auth
|
||||||
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
|
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
return proxyCfg
|
return proxyCfg
|
||||||
})
|
}, nil)
|
||||||
// Use proxy with auth
|
// Use proxy with auth
|
||||||
doTestDownloaderCreateWithProxy(t, true, func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig {
|
doTestDownloaderCreateWithProxy(t, true, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
return proxyCfg
|
return proxyCfg
|
||||||
})
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig func(proxyCfg *DownloaderProxyConfig) *DownloaderProxyConfig) {
|
func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig, errHandler func(err error)) {
|
||||||
httpListener := test.StartTestFileServer()
|
|
||||||
defer httpListener.Close()
|
|
||||||
usr, pwd := "", ""
|
usr, pwd := "", ""
|
||||||
if auth {
|
if auth {
|
||||||
usr, pwd = "admin", "123"
|
usr, pwd = "admin", "123"
|
||||||
@@ -128,7 +150,7 @@ func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig f
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer downloader.Clear()
|
defer downloader.Clear()
|
||||||
downloader.cfg.DownloaderStoreConfig.Proxy = buildProxyConfig(&DownloaderProxyConfig{
|
downloader.cfg.DownloaderStoreConfig.Proxy = buildProxyConfig(&base.DownloaderProxyConfig{
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Scheme: "socks5",
|
Scheme: "socks5",
|
||||||
Host: proxyListener.Addr().String(),
|
Host: proxyListener.Addr().String(),
|
||||||
@@ -137,20 +159,24 @@ func doTestDownloaderCreateWithProxy(t *testing.T, auth bool, buildProxyConfig f
|
|||||||
})
|
})
|
||||||
|
|
||||||
req := &base.Request{
|
req := &base.Request{
|
||||||
URL: "http://" + httpListener.Addr().String() + "/" + test.BuildName,
|
URL: test.ExternalDownloadUrl,
|
||||||
}
|
}
|
||||||
rr, err := downloader.Resolve(req)
|
rr, err := downloader.Resolve(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
if errHandler == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
errHandler(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
want := &base.Resource{
|
want := &base.Resource{
|
||||||
Size: test.BuildSize,
|
Size: test.ExternalDownloadSize,
|
||||||
Range: true,
|
Range: true,
|
||||||
Files: []*base.FileInfo{
|
Files: []*base.FileInfo{
|
||||||
{
|
{
|
||||||
Name: test.BuildName,
|
Name: test.ExternalDownloadName,
|
||||||
Path: "",
|
Path: "",
|
||||||
Size: test.BuildSize,
|
Size: test.ExternalDownloadSize,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -285,7 +311,7 @@ func TestDownloader_Protocol_Config(t *testing.T) {
|
|||||||
t.Errorf("getProtocolConfig() got = %v, want %v", exits, false)
|
t.Errorf("getProtocolConfig() got = %v, want %v", exits, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
storeCfg := &DownloaderStoreConfig{
|
storeCfg := &base.DownloaderStoreConfig{
|
||||||
DownloadDir: "./downloads",
|
DownloadDir: "./downloads",
|
||||||
ProtocolConfig: map[string]any{
|
ProtocolConfig: map[string]any{
|
||||||
"http": map[string]any{
|
"http": map[string]any{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package engine
|
|||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/GopeedLab/gopeed/pkg/base"
|
||||||
gojaerror "github.com/GopeedLab/gopeed/pkg/download/engine/inject/error"
|
gojaerror "github.com/GopeedLab/gopeed/pkg/download/engine/inject/error"
|
||||||
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file"
|
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file"
|
||||||
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/formdata"
|
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/formdata"
|
||||||
@@ -11,7 +12,6 @@ import (
|
|||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
"github.com/dop251/goja_nodejs/eventloop"
|
"github.com/dop251/goja_nodejs/eventloop"
|
||||||
gojaurl "github.com/dop251/goja_nodejs/url"
|
gojaurl "github.com/dop251/goja_nodejs/url"
|
||||||
"net/url"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ func (e *Engine) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ProxyURL *url.URL
|
ProxyConfig *base.DownloaderProxyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEngine(cfg *Config) *Engine {
|
func NewEngine(cfg *Config) *Engine {
|
||||||
@@ -135,7 +135,7 @@ func NewEngine(cfg *Config) *Engine {
|
|||||||
if err := formdata.Enable(runtime); err != nil {
|
if err := formdata.Enable(runtime); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := xhr.Enable(runtime, cfg.ProxyURL); err != nil {
|
if err := xhr.Enable(runtime, cfg.ProxyConfig.ToHandler()); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err := runtime.RunString(polyfillScript); err != nil {
|
if _, err := runtime.RunString(polyfillScript); err != nil {
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/GopeedLab/gopeed/internal/test"
|
"github.com/GopeedLab/gopeed/internal/test"
|
||||||
|
"github.com/GopeedLab/gopeed/pkg/base"
|
||||||
gojaerror "github.com/GopeedLab/gopeed/pkg/download/engine/inject/error"
|
gojaerror "github.com/GopeedLab/gopeed/pkg/download/engine/inject/error"
|
||||||
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file"
|
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file"
|
||||||
gojautil "github.com/GopeedLab/gopeed/pkg/download/engine/util"
|
gojautil "github.com/GopeedLab/gopeed/pkg/download/engine/util"
|
||||||
"github.com/GopeedLab/gopeed/pkg/util"
|
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -217,8 +217,16 @@ func doTestFetchWithProxy(t *testing.T, usr, pwd string) {
|
|||||||
|
|
||||||
proxyListener := test.StartSocks5Server(usr, pwd)
|
proxyListener := test.StartSocks5Server(usr, pwd)
|
||||||
defer proxyListener.Close()
|
defer proxyListener.Close()
|
||||||
|
engine := NewEngine(&Config{
|
||||||
engine := NewEngine(&Config{ProxyURL: util.BuildProxyUrl("socks5", proxyListener.Addr().String(), usr, pwd)})
|
ProxyConfig: &base.DownloaderProxyConfig{
|
||||||
|
Enable: true,
|
||||||
|
System: false,
|
||||||
|
Scheme: "socks5",
|
||||||
|
Host: proxyListener.Addr().String(),
|
||||||
|
Usr: usr,
|
||||||
|
Pwd: pwd,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
if _, err := engine.RunString(fmt.Sprintf("var host = 'http://%s';", httpListener.Addr().String())); err != nil {
|
if _, err := engine.RunString(fmt.Sprintf("var host = 'http://%s';", httpListener.Addr().String())); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package xhr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file"
|
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/file"
|
||||||
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/formdata"
|
"github.com/GopeedLab/gopeed/pkg/download/engine/inject/formdata"
|
||||||
"github.com/GopeedLab/gopeed/pkg/download/engine/util"
|
"github.com/GopeedLab/gopeed/pkg/download/engine/util"
|
||||||
@@ -120,7 +121,7 @@ type XMLHttpRequest struct {
|
|||||||
requestHeaders map[string]string
|
requestHeaders map[string]string
|
||||||
responseHeaders map[string]string
|
responseHeaders map[string]string
|
||||||
aborted bool
|
aborted bool
|
||||||
proxyUrl *url.URL
|
proxyHandler func(r *http.Request) (*url.URL, error)
|
||||||
|
|
||||||
Upload *XMLHttpRequestUpload `json:"upload"`
|
Upload *XMLHttpRequestUpload `json:"upload"`
|
||||||
Timeout int `json:"timeout"`
|
Timeout int `json:"timeout"`
|
||||||
@@ -202,9 +203,8 @@ func (xhr *XMLHttpRequest) Send(data goja.Value) {
|
|||||||
for k, v := range xhr.requestHeaders {
|
for k, v := range xhr.requestHeaders {
|
||||||
req.Header.Set(k, v)
|
req.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
transport := &http.Transport{}
|
transport := &http.Transport{
|
||||||
if xhr.proxyUrl != nil {
|
Proxy: xhr.proxyHandler,
|
||||||
transport.Proxy = http.ProxyURL(xhr.proxyUrl)
|
|
||||||
}
|
}
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
@@ -213,7 +213,8 @@ func (xhr *XMLHttpRequest) Send(data goja.Value) {
|
|||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// handle timeout error
|
// handle timeout error
|
||||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
var ne net.Error
|
||||||
|
if errors.As(err, &ne) && ne.Timeout() {
|
||||||
if xhr.Timeout > 0 {
|
if xhr.Timeout > 0 {
|
||||||
xhr.Upload.callOntimeout()
|
xhr.Upload.callOntimeout()
|
||||||
xhr.callOntimeout()
|
xhr.callOntimeout()
|
||||||
@@ -313,7 +314,7 @@ func (xhr *XMLHttpRequest) parseData(data goja.Value) any {
|
|||||||
return data.String()
|
return data.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Enable(runtime *goja.Runtime, proxyUrl *url.URL) error {
|
func Enable(runtime *goja.Runtime, proxyHandler func(r *http.Request) (*url.URL, error)) error {
|
||||||
progressEvent := runtime.ToValue(func(call goja.ConstructorCall) *goja.Object {
|
progressEvent := runtime.ToValue(func(call goja.ConstructorCall) *goja.Object {
|
||||||
if len(call.Arguments) < 1 {
|
if len(call.Arguments) < 1 {
|
||||||
util.ThrowTypeError(runtime, "Failed to construct 'ProgressEvent': 1 argument required, but only 0 present.")
|
util.ThrowTypeError(runtime, "Failed to construct 'ProgressEvent': 1 argument required, but only 0 present.")
|
||||||
@@ -327,7 +328,7 @@ func Enable(runtime *goja.Runtime, proxyUrl *url.URL) error {
|
|||||||
})
|
})
|
||||||
xhr := runtime.ToValue(func(call goja.ConstructorCall) *goja.Object {
|
xhr := runtime.ToValue(func(call goja.ConstructorCall) *goja.Object {
|
||||||
instance := &XMLHttpRequest{
|
instance := &XMLHttpRequest{
|
||||||
proxyUrl: proxyUrl,
|
proxyHandler: proxyHandler,
|
||||||
Upload: &XMLHttpRequestUpload{
|
Upload: &XMLHttpRequestUpload{
|
||||||
EventProp: &EventProp{
|
EventProp: &EventProp{
|
||||||
eventListeners: make(map[string]func(event *ProgressEvent)),
|
eventListeners: make(map[string]func(event *ProgressEvent)),
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ func (d *Downloader) fetchExtensionByGit(url string, handler func(tempExtPath st
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
proxyOptions := transport.ProxyOptions{}
|
proxyOptions := transport.ProxyOptions{}
|
||||||
proxyUrl := d.cfg.ProxyUrl()
|
proxyUrl := d.cfg.DownloaderStoreConfig.Proxy.ToUrl()
|
||||||
if proxyUrl != nil {
|
if proxyUrl != nil {
|
||||||
proxyOptions.URL = proxyUrl.Scheme + "://" + proxyUrl.Host
|
proxyOptions.URL = proxyUrl.Scheme + "://" + proxyUrl.Host
|
||||||
proxyOptions.Username = proxyUrl.User.Username()
|
proxyOptions.Username = proxyUrl.User.Username()
|
||||||
@@ -341,7 +341,7 @@ func doTrigger[T any](d *Downloader, event ActivationEvent, req *base.Request, c
|
|||||||
req.Labels = make(map[string]string)
|
req.Labels = make(map[string]string)
|
||||||
}
|
}
|
||||||
engine := engine.NewEngine(&engine.Config{
|
engine := engine.NewEngine(&engine.Config{
|
||||||
ProxyURL: d.cfg.ProxyUrl(),
|
ProxyConfig: d.cfg.Proxy,
|
||||||
})
|
})
|
||||||
defer engine.Close()
|
defer engine.Close()
|
||||||
err = engine.Runtime.Set("gopeed", gopeed)
|
err = engine.Runtime.Set("gopeed", gopeed)
|
||||||
|
|||||||
+1
-42
@@ -7,9 +7,7 @@ import (
|
|||||||
"github.com/GopeedLab/gopeed/internal/protocol/http"
|
"github.com/GopeedLab/gopeed/internal/protocol/http"
|
||||||
"github.com/GopeedLab/gopeed/pkg/base"
|
"github.com/GopeedLab/gopeed/pkg/base"
|
||||||
"github.com/GopeedLab/gopeed/pkg/util"
|
"github.com/GopeedLab/gopeed/pkg/util"
|
||||||
|
|
||||||
gonanoid "github.com/matoous/go-nanoid/v2"
|
gonanoid "github.com/matoous/go-nanoid/v2"
|
||||||
"net/url"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -87,7 +85,7 @@ type DownloaderConfig struct {
|
|||||||
|
|
||||||
ProductionMode bool
|
ProductionMode bool
|
||||||
|
|
||||||
*DownloaderStoreConfig
|
*base.DownloaderStoreConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *DownloaderConfig) Init() *DownloaderConfig {
|
func (cfg *DownloaderConfig) Init() *DownloaderConfig {
|
||||||
@@ -108,42 +106,3 @@ func (cfg *DownloaderConfig) Init() *DownloaderConfig {
|
|||||||
}
|
}
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloaderStoreConfig is the config that can restore the downloader.
|
|
||||||
type DownloaderStoreConfig struct {
|
|
||||||
FirstLoad bool `json:"-"` // FirstLoad is the flag that the config is first time init and not from store
|
|
||||||
|
|
||||||
DownloadDir string `json:"downloadDir"` // DownloadDir is the default directory to save the downloaded files
|
|
||||||
MaxRunning int `json:"maxRunning"` // MaxRunning is the max running download count
|
|
||||||
ProtocolConfig map[string]any `json:"protocolConfig"` // ProtocolConfig is special config for each protocol
|
|
||||||
Extra map[string]any `json:"extra"`
|
|
||||||
Proxy *DownloaderProxyConfig `json:"proxy"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *DownloaderStoreConfig) Init() *DownloaderStoreConfig {
|
|
||||||
if cfg.MaxRunning == 0 {
|
|
||||||
cfg.MaxRunning = 5
|
|
||||||
}
|
|
||||||
if cfg.Proxy == nil {
|
|
||||||
cfg.Proxy = &DownloaderProxyConfig{}
|
|
||||||
}
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *DownloaderStoreConfig) ProxyUrl() *url.URL {
|
|
||||||
if cfg.Proxy == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if cfg.Proxy.Enable == false || cfg.Proxy.Scheme == "" || cfg.Proxy.Host == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return util.BuildProxyUrl(cfg.Proxy.Scheme, cfg.Proxy.Host, cfg.Proxy.Usr, cfg.Proxy.Pwd)
|
|
||||||
}
|
|
||||||
|
|
||||||
type DownloaderProxyConfig struct {
|
|
||||||
Enable bool `json:"enable"`
|
|
||||||
Scheme string `json:"scheme"`
|
|
||||||
Host string `json:"host"`
|
|
||||||
Usr string `json:"usr"`
|
|
||||||
Pwd string `json:"pwd"`
|
|
||||||
}
|
|
||||||
+2
-2
@@ -142,7 +142,7 @@ func GetConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PutConfig(w http.ResponseWriter, r *http.Request) {
|
func PutConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
var cfg download.DownloaderStoreConfig
|
var cfg base.DownloaderStoreConfig
|
||||||
if ReadJson(r, w, &cfg) {
|
if ReadJson(r, w, &cfg) {
|
||||||
if err := Downloader.PutConfig(&cfg); err != nil {
|
if err := Downloader.PutConfig(&cfg); err != nil {
|
||||||
WriteJson(w, model.NewErrorResult(err.Error()))
|
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||||
@@ -309,7 +309,7 @@ func writeError(w http.ResponseWriter, msg string) {
|
|||||||
w.Write([]byte(msg))
|
w.Write([]byte(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServerConfig() *download.DownloaderStoreConfig {
|
func getServerConfig() *base.DownloaderStoreConfig {
|
||||||
cfg, _ := Downloader.GetConfig()
|
cfg, _ := Downloader.GetConfig()
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
@@ -324,7 +324,7 @@ func TestGetTasks(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetAndPutConfig(t *testing.T) {
|
func TestGetAndPutConfig(t *testing.T) {
|
||||||
doTest(func() {
|
doTest(func() {
|
||||||
cfg := httpRequestCheckOk[*download.DownloaderStoreConfig](http.MethodGet, "/api/v1/config", nil)
|
cfg := httpRequestCheckOk[*base.DownloaderStoreConfig](http.MethodGet, "/api/v1/config", nil)
|
||||||
cfg.DownloadDir = "./download"
|
cfg.DownloadDir = "./download"
|
||||||
cfg.Extra = map[string]any{
|
cfg.Extra = map[string]any{
|
||||||
"serverConfig": &Config{
|
"serverConfig": &Config{
|
||||||
@@ -335,7 +335,7 @@ func TestGetAndPutConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
httpRequestCheckOk[any](http.MethodPut, "/api/v1/config", cfg)
|
httpRequestCheckOk[any](http.MethodPut, "/api/v1/config", cfg)
|
||||||
|
|
||||||
newCfg := httpRequestCheckOk[*download.DownloaderStoreConfig](http.MethodGet, "/api/v1/config", nil)
|
newCfg := httpRequestCheckOk[*base.DownloaderStoreConfig](http.MethodGet, "/api/v1/config", nil)
|
||||||
if !test.JsonEqual(cfg, newCfg) {
|
if !test.JsonEqual(cfg, newCfg) {
|
||||||
t.Errorf("GetAndPutConfig() got = %v, want %v", test.ToJson(newCfg), test.ToJson(cfg))
|
t.Errorf("GetAndPutConfig() got = %v, want %v", test.ToJson(newCfg), test.ToJson(cfg))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package util
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -51,3 +52,14 @@ func BuildProxyUrl(scheme, host, usr, pwd string) *url.URL {
|
|||||||
Host: host,
|
Host: host,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProxyUrlToHandler gets the proxy handler from the proxy url.
|
||||||
|
func ProxyUrlToHandler(proxyUrl *url.URL) func(*http.Request) (*url.URL, error) {
|
||||||
|
if proxyUrl == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if proxyUrl.Scheme == "system" {
|
||||||
|
return http.ProxyFromEnvironment
|
||||||
|
}
|
||||||
|
return http.ProxyURL(proxyUrl)
|
||||||
|
}
|
||||||
@@ -78,6 +78,7 @@ class ExtraConfig {
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class ProxyConfig {
|
class ProxyConfig {
|
||||||
bool enable;
|
bool enable;
|
||||||
|
bool system;
|
||||||
String scheme;
|
String scheme;
|
||||||
String host;
|
String host;
|
||||||
String usr;
|
String usr;
|
||||||
@@ -85,6 +86,7 @@ class ProxyConfig {
|
|||||||
|
|
||||||
ProxyConfig({
|
ProxyConfig({
|
||||||
this.enable = false,
|
this.enable = false,
|
||||||
|
this.system = false,
|
||||||
this.scheme = '',
|
this.scheme = '',
|
||||||
this.host = '',
|
this.host = '',
|
||||||
this.usr = '',
|
this.usr = '',
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ part of 'downloader_config.dart';
|
|||||||
DownloaderConfig _$DownloaderConfigFromJson(Map<String, dynamic> json) =>
|
DownloaderConfig _$DownloaderConfigFromJson(Map<String, dynamic> json) =>
|
||||||
DownloaderConfig(
|
DownloaderConfig(
|
||||||
downloadDir: json['downloadDir'] as String? ?? '',
|
downloadDir: json['downloadDir'] as String? ?? '',
|
||||||
maxRunning: json['maxRunning'] as int? ?? 0,
|
maxRunning: (json['maxRunning'] as num?)?.toInt() ?? 0,
|
||||||
)
|
)
|
||||||
..protocolConfig = ProtocolConfig.fromJson(
|
..protocolConfig = ProtocolConfig.fromJson(
|
||||||
json['protocolConfig'] as Map<String, dynamic>?)
|
json['protocolConfig'] as Map<String, dynamic>?)
|
||||||
@@ -39,7 +39,7 @@ Map<String, dynamic> _$ProtocolConfigToJson(ProtocolConfig instance) =>
|
|||||||
HttpConfig _$HttpConfigFromJson(Map<String, dynamic> json) => HttpConfig(
|
HttpConfig _$HttpConfigFromJson(Map<String, dynamic> json) => HttpConfig(
|
||||||
userAgent: json['userAgent'] as String? ??
|
userAgent: json['userAgent'] as String? ??
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
|
||||||
connections: json['connections'] as int? ?? 16,
|
connections: (json['connections'] as num?)?.toInt() ?? 16,
|
||||||
useServerCtime: json['useServerCtime'] as bool? ?? false,
|
useServerCtime: json['useServerCtime'] as bool? ?? false,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ Map<String, dynamic> _$HttpConfigToJson(HttpConfig instance) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
BtConfig _$BtConfigFromJson(Map<String, dynamic> json) => BtConfig()
|
BtConfig _$BtConfigFromJson(Map<String, dynamic> json) => BtConfig()
|
||||||
..listenPort = json['listenPort'] as int
|
..listenPort = (json['listenPort'] as num).toInt()
|
||||||
..trackers =
|
..trackers =
|
||||||
(json['trackers'] as List<dynamic>).map((e) => e as String).toList();
|
(json['trackers'] as List<dynamic>).map((e) => e as String).toList();
|
||||||
|
|
||||||
@@ -74,6 +74,7 @@ Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
|
|||||||
|
|
||||||
ProxyConfig _$ProxyConfigFromJson(Map<String, dynamic> json) => ProxyConfig(
|
ProxyConfig _$ProxyConfigFromJson(Map<String, dynamic> json) => ProxyConfig(
|
||||||
enable: json['enable'] as bool? ?? false,
|
enable: json['enable'] as bool? ?? false,
|
||||||
|
system: json['system'] as bool? ?? false,
|
||||||
scheme: json['scheme'] as String? ?? '',
|
scheme: json['scheme'] as String? ?? '',
|
||||||
host: json['host'] as String? ?? '',
|
host: json['host'] as String? ?? '',
|
||||||
usr: json['usr'] as String? ?? '',
|
usr: json['usr'] as String? ?? '',
|
||||||
@@ -83,6 +84,7 @@ ProxyConfig _$ProxyConfigFromJson(Map<String, dynamic> json) => ProxyConfig(
|
|||||||
Map<String, dynamic> _$ProxyConfigToJson(ProxyConfig instance) =>
|
Map<String, dynamic> _$ProxyConfigToJson(ProxyConfig instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'enable': instance.enable,
|
'enable': instance.enable,
|
||||||
|
'system': instance.system,
|
||||||
'scheme': instance.scheme,
|
'scheme': instance.scheme,
|
||||||
'host': instance.host,
|
'host': instance.host,
|
||||||
'usr': instance.usr,
|
'usr': instance.usr,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Options _$OptionsFromJson(Map<String, dynamic> json) => Options(
|
|||||||
name: json['name'] as String,
|
name: json['name'] as String,
|
||||||
path: json['path'] as String,
|
path: json['path'] as String,
|
||||||
selectFiles: (json['selectFiles'] as List<dynamic>?)
|
selectFiles: (json['selectFiles'] as List<dynamic>?)
|
||||||
?.map((e) => e as int)
|
?.map((e) => (e as num).toInt())
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
const [],
|
||||||
extra: json['extra'],
|
extra: json['extra'],
|
||||||
@@ -35,7 +35,7 @@ Map<String, dynamic> _$OptionsToJson(Options instance) {
|
|||||||
|
|
||||||
OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) =>
|
OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) =>
|
||||||
OptsExtraHttp()
|
OptsExtraHttp()
|
||||||
..connections = json['connections'] as int
|
..connections = (json['connections'] as num).toInt()
|
||||||
..autoTorrent = json['autoTorrent'] as bool;
|
..autoTorrent = json['autoTorrent'] as bool;
|
||||||
|
|
||||||
Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) =>
|
Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) =>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ part of 'resource.dart';
|
|||||||
|
|
||||||
Resource _$ResourceFromJson(Map<String, dynamic> json) => Resource(
|
Resource _$ResourceFromJson(Map<String, dynamic> json) => Resource(
|
||||||
name: json['name'] as String? ?? "",
|
name: json['name'] as String? ?? "",
|
||||||
size: json['size'] as int? ?? 0,
|
size: (json['size'] as num?)?.toInt() ?? 0,
|
||||||
range: json['range'] as bool? ?? false,
|
range: json['range'] as bool? ?? false,
|
||||||
files: (json['files'] as List<dynamic>)
|
files: (json['files'] as List<dynamic>)
|
||||||
.map((e) => FileInfo.fromJson(e as Map<String, dynamic>))
|
.map((e) => FileInfo.fromJson(e as Map<String, dynamic>))
|
||||||
@@ -27,7 +27,7 @@ Map<String, dynamic> _$ResourceToJson(Resource instance) => <String, dynamic>{
|
|||||||
FileInfo _$FileInfoFromJson(Map<String, dynamic> json) => FileInfo(
|
FileInfo _$FileInfoFromJson(Map<String, dynamic> json) => FileInfo(
|
||||||
path: json['path'] as String? ?? "",
|
path: json['path'] as String? ?? "",
|
||||||
name: json['name'] as String,
|
name: json['name'] as String,
|
||||||
size: json['size'] as int? ?? 0,
|
size: (json['size'] as num?)?.toInt() ?? 0,
|
||||||
req: json['req'] == null
|
req: json['req'] == null
|
||||||
? null
|
? null
|
||||||
: Request.fromJson(json['req'] as Map<String, dynamic>),
|
: Request.fromJson(json['req'] as Map<String, dynamic>),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Result<T> _$ResultFromJson<T>(
|
|||||||
T Function(Object? json) fromJsonT,
|
T Function(Object? json) fromJsonT,
|
||||||
) =>
|
) =>
|
||||||
Result<T>(
|
Result<T>(
|
||||||
code: json['code'] as int,
|
code: (json['code'] as num).toInt(),
|
||||||
msg: json['msg'] as String?,
|
msg: json['msg'] as String?,
|
||||||
data: _$nullableGenericFromJson(json['data'], fromJsonT),
|
data: _$nullableGenericFromJson(json['data'], fromJsonT),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ const _$StatusEnumMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Progress _$ProgressFromJson(Map<String, dynamic> json) => Progress(
|
Progress _$ProgressFromJson(Map<String, dynamic> json) => Progress(
|
||||||
used: json['used'] as int,
|
used: (json['used'] as num).toInt(),
|
||||||
speed: json['speed'] as int,
|
speed: (json['speed'] as num).toInt(),
|
||||||
downloaded: json['downloaded'] as int,
|
downloaded: (json['downloaded'] as num).toInt(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$ProgressToJson(Progress instance) => <String, dynamic>{
|
Map<String, dynamic> _$ProgressToJson(Progress instance) => <String, dynamic>{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
import '../../../../api/model/downloader_config.dart';
|
||||||
import '../../../../i18n/message.dart';
|
import '../../../../i18n/message.dart';
|
||||||
import '../../../../util/input_formatter.dart';
|
import '../../../../util/input_formatter.dart';
|
||||||
import '../../../../util/locale_manager.dart';
|
import '../../../../util/locale_manager.dart';
|
||||||
@@ -408,29 +409,53 @@ class SettingView extends GetView<SettingController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// advanced config proxy items start
|
// advanced config proxy items start
|
||||||
|
final proxy = downloaderCfg.value.proxy;
|
||||||
final buildProxy = _buildConfigItem(
|
final buildProxy = _buildConfigItem(
|
||||||
'proxy',
|
'proxy',
|
||||||
() => downloaderCfg.value.proxy.enable
|
() {
|
||||||
? '${downloaderCfg.value.proxy.scheme}://${downloaderCfg.value.proxy.host}'
|
switch (proxy.proxyMode) {
|
||||||
: 'notSet'.tr,
|
case ProxyModeEnum.noProxy:
|
||||||
|
return 'noProxy'.tr;
|
||||||
|
case ProxyModeEnum.systemProxy:
|
||||||
|
return 'systemProxy'.tr;
|
||||||
|
case ProxyModeEnum.customProxy:
|
||||||
|
return '${downloaderCfg.value.proxy.scheme}://${downloaderCfg.value.proxy.host}';
|
||||||
|
}
|
||||||
|
},
|
||||||
(Key key) {
|
(Key key) {
|
||||||
final proxy = downloaderCfg.value.proxy;
|
final mode = SizedBox(
|
||||||
|
width: 150,
|
||||||
|
child: DropdownButtonFormField<ProxyModeEnum>(
|
||||||
|
value: proxy.proxyMode,
|
||||||
|
onChanged: (value) async {
|
||||||
|
if (value != null && value != proxy.proxyMode) {
|
||||||
|
proxy.proxyMode = value;
|
||||||
|
downloaderCfg.update((val) {
|
||||||
|
val!.proxy = proxy;
|
||||||
|
});
|
||||||
|
|
||||||
final switcher = Switch(
|
await debounceSave();
|
||||||
value: proxy.enable,
|
}
|
||||||
onChanged: (bool value) async {
|
},
|
||||||
if (value != proxy.enable) {
|
items: [
|
||||||
downloaderCfg.update((val) {
|
DropdownMenuItem<ProxyModeEnum>(
|
||||||
val!.proxy.enable = value;
|
value: ProxyModeEnum.noProxy,
|
||||||
});
|
child: Text('noProxy'.tr),
|
||||||
|
),
|
||||||
await debounceSave();
|
DropdownMenuItem<ProxyModeEnum>(
|
||||||
}
|
value: ProxyModeEnum.systemProxy,
|
||||||
},
|
child: Text('systemProxy'.tr),
|
||||||
|
),
|
||||||
|
DropdownMenuItem<ProxyModeEnum>(
|
||||||
|
value: ProxyModeEnum.customProxy,
|
||||||
|
child: Text('customProxy'.tr),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final scheme = SizedBox(
|
final scheme = SizedBox(
|
||||||
width: 100,
|
width: 150,
|
||||||
child: DropdownButtonFormField<String>(
|
child: DropdownButtonFormField<String>(
|
||||||
value: proxy.scheme,
|
value: proxy.scheme,
|
||||||
onChanged: (value) async {
|
onChanged: (value) async {
|
||||||
@@ -478,7 +503,7 @@ class SettingView extends GetView<SettingController> {
|
|||||||
|
|
||||||
ipController.addListener(updateAddress);
|
ipController.addListener(updateAddress);
|
||||||
portController.addListener(updateAddress);
|
portController.addListener(updateAddress);
|
||||||
final server = [
|
final server = Row(children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: ipController,
|
controller: ipController,
|
||||||
@@ -503,12 +528,12 @@ class SettingView extends GetView<SettingController> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
]);
|
||||||
|
|
||||||
final usrController = TextEditingController(text: proxy.usr);
|
final usrController = TextEditingController(text: proxy.usr);
|
||||||
final pwdController = TextEditingController(text: proxy.pwd);
|
final pwdController = TextEditingController(text: proxy.pwd);
|
||||||
|
|
||||||
final auth = [
|
final auth = Row(children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: usrController,
|
controller: usrController,
|
||||||
@@ -528,20 +553,21 @@ class SettingView extends GetView<SettingController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
]);
|
||||||
|
|
||||||
|
List<Widget> customView() {
|
||||||
|
if (proxy.proxyMode != ProxyModeEnum.customProxy) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return [scheme, server, auth];
|
||||||
|
}
|
||||||
|
|
||||||
return Form(
|
return Form(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _addPadding([
|
children: _addPadding([
|
||||||
switcher,
|
mode,
|
||||||
scheme,
|
...customView(),
|
||||||
Row(
|
|
||||||
children: server,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: auth,
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -857,3 +883,37 @@ class SettingView extends GetView<SettingController> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ProxyModeEnum {
|
||||||
|
noProxy,
|
||||||
|
systemProxy,
|
||||||
|
customProxy,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ProxyMode on ProxyConfig {
|
||||||
|
ProxyModeEnum get proxyMode {
|
||||||
|
if (!enable) {
|
||||||
|
return ProxyModeEnum.noProxy;
|
||||||
|
}
|
||||||
|
if (system) {
|
||||||
|
return ProxyModeEnum.systemProxy;
|
||||||
|
}
|
||||||
|
return ProxyModeEnum.customProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
set proxyMode(ProxyModeEnum value) {
|
||||||
|
switch (value) {
|
||||||
|
case ProxyModeEnum.noProxy:
|
||||||
|
enable = false;
|
||||||
|
break;
|
||||||
|
case ProxyModeEnum.systemProxy:
|
||||||
|
enable = true;
|
||||||
|
system = true;
|
||||||
|
break;
|
||||||
|
case ProxyModeEnum.customProxy:
|
||||||
|
enable = true;
|
||||||
|
system = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ StartConfig _$StartConfigFromJson(Map<String, dynamic> json) => StartConfig()
|
|||||||
..address = json['address'] as String
|
..address = json['address'] as String
|
||||||
..storage = json['storage'] as String
|
..storage = json['storage'] as String
|
||||||
..storageDir = json['storageDir'] as String
|
..storageDir = json['storageDir'] as String
|
||||||
..refreshInterval = json['refreshInterval'] as int
|
..refreshInterval = (json['refreshInterval'] as num).toInt()
|
||||||
..apiToken = json['apiToken'] as String;
|
..apiToken = json['apiToken'] as String;
|
||||||
|
|
||||||
Map<String, dynamic> _$StartConfigToJson(StartConfig instance) =>
|
Map<String, dynamic> _$StartConfigToJson(StartConfig instance) =>
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ const enUS = {
|
|||||||
'serviceText': 'Running',
|
'serviceText': 'Running',
|
||||||
'network': 'Network',
|
'network': 'Network',
|
||||||
'proxy': 'Proxy',
|
'proxy': 'Proxy',
|
||||||
|
'noProxy': 'No Proxy',
|
||||||
|
'systemProxy': 'System Proxy',
|
||||||
|
'customProxy': 'Custom Proxy',
|
||||||
'server': 'Server',
|
'server': 'Server',
|
||||||
'username': 'Username',
|
'username': 'Username',
|
||||||
'password': 'Password',
|
'password': 'Password',
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ const zhCN = {
|
|||||||
'serviceText': '运行中',
|
'serviceText': '运行中',
|
||||||
'network': '网络',
|
'network': '网络',
|
||||||
'proxy': '代理',
|
'proxy': '代理',
|
||||||
|
'noProxy': '不使用代理',
|
||||||
|
'systemProxy': '系统代理',
|
||||||
|
'customProxy': '自定义代理',
|
||||||
'server': '服务器',
|
'server': '服务器',
|
||||||
'username': '用户名',
|
'username': '用户名',
|
||||||
'password': '密码',
|
'password': '密码',
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ const zhTW = {
|
|||||||
'serviceText': '執行中',
|
'serviceText': '執行中',
|
||||||
'network': '網路',
|
'network': '網路',
|
||||||
'proxy': '代理',
|
'proxy': '代理',
|
||||||
|
'noProxy': '不使用代理',
|
||||||
|
'systemProxy': '系統代理',
|
||||||
|
'customProxy': '自定義代理',
|
||||||
'server': '伺服器',
|
'server': '伺服器',
|
||||||
'username': '名稱',
|
'username': '名稱',
|
||||||
'password': '密碼',
|
'password': '密碼',
|
||||||
|
|||||||
Reference in new issue
Block a user