From 4d4d3b446ac4bc309e94ded21a6dfbbb76641a10 Mon Sep 17 00:00:00 2001 From: levi Date: Thu, 27 Oct 2022 13:56:52 +0800 Subject: [PATCH] feat: support flutter web --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- README.md | 8 +- _examples/basic/main.go | 4 +- bind/desktop/main.go | 8 +- bind/mobile/main.go | 6 +- cmd/gopeed/{gopeed.go => main.go} | 6 +- cmd/server/main.go | 16 ++ go.mod | 4 +- go.sum | 4 + internal/fetcher/fetcher.go | 4 +- internal/protocol/bt/fetcher.go | 8 +- internal/protocol/bt/fetcher_test.go | 6 +- internal/protocol/http/fetcher.go | 6 +- internal/protocol/http/fetcher_test.go | 8 +- internal/protocol/http/model.go | 2 +- pkg/download/downloader.go | 14 +- pkg/download/downloader_test.go | 4 +- pkg/download/storage.go | 23 ++- pkg/download/task.go | 6 +- pkg/rest/api.go | 8 +- pkg/rest/model/task.go | 2 +- pkg/rest/server.go | 122 +++++++----- pkg/rest/server_test.go | 8 +- pkg/rest/util/util.go | 2 +- ui/flutter/include/libgopeed.h | 2 +- ui/flutter/lib/api/api.dart | 29 +-- .../core/entry/libgopeed_boot_browser.dart | 22 +++ .../lib/core/entry/libgopeed_boot_native.dart | 173 +++++++++++++++++ ui/flutter/lib/core/libgopeed_boot.dart | 177 ++---------------- ui/flutter/lib/core/libgopeed_boot_stub.dart | 3 + ui/flutter/lib/main.dart | 2 +- ui/flutter/lib/pages/create/create_view.dart | 8 +- .../lib/pages/task/task_controller.dart | 2 +- ui/flutter/lib/setting/setting.dart | 10 +- ui/flutter/lib/util/mac_secure_util.dart | 6 +- ui/flutter/lib/util/util.dart | 20 ++ .../macos/Runner/DebugProfile.entitlements | 6 +- ui/flutter/macos/Runner/Release.entitlements | 6 +- 39 files changed, 450 insertions(+), 299 deletions(-) rename cmd/gopeed/{gopeed.go => main.go} (92%) create mode 100644 cmd/server/main.go create mode 100644 ui/flutter/lib/core/entry/libgopeed_boot_browser.dart create mode 100644 ui/flutter/lib/core/entry/libgopeed_boot_native.dart create mode 100644 ui/flutter/lib/core/libgopeed_boot_stub.dart diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 186917e7..9e8e6e73 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: for goos in "${goos_arr[@]}"; do for goarch in "${goarch_arr[@]}"; do dir="dist/gopeed-$VERSION-$goos-$goarch/" - (GOOS=$goos GOARCH=$goarch go build -tags nosqlite -ldflags="-s -w" -o $dir github.com/monkeyWie/gopeed-core/cmd/gopeed \ + (GOOS=$goos GOARCH=$goarch go build -tags nosqlite -ldflags="-s -w" -o $dir github.com/monkeyWie/gopeed/cmd/gopeed \ && cd $dir \ && zip -r ../zip/gopeed-$VERSION-$goos-$goarch.zip * \ && cd ../..) \ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 27e3d657..59165ec5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -53,4 +53,4 @@ jobs: go-version: '^1.18' - uses: actions/checkout@v2 - name: Build - run: go build github.com/monkeyWie/gopeed-core/cmd/gopeed \ No newline at end of file + run: go build github.com/monkeyWie/gopeed/cmd/gopeed \ No newline at end of file diff --git a/README.md b/README.md index 6910afec..78eef839 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![Test Status](https://github.com/monkeyWie/gopeed-core/workflows/test/badge.svg)](https://github.com/monkeyWie/gopeed-core/actions?query=workflow%3Atest) +[![Test Status](https://github.com/monkeyWie/gopeed/workflows/test/badge.svg)](https://github.com/monkeyWie/gopeed/actions?query=workflow%3Atest) [![Codecov](https://codecov.io/gh/monkeyWie/gopeed-core/branch/master/graph/badge.svg)](https://codecov.io/gh/monkeyWie/gopeed-core) -[![Release](https://img.shields.io/github/release/monkeyWie/gopeed-core.svg?style=flat-square)](https://github.com/monkeyWie/gopeed-core/releases) +[![Release](https://img.shields.io/github/release/monkeyWie/gopeed-core.svg?style=flat-square)](https://github.com/monkeyWie/gopeed/releases) ## gopeed @@ -9,7 +9,7 @@ ## 安装 ```sh -go install github.com/monkeyWie/gopeed-core +go install github.com/monkeyWie/gopeed ``` ## 示例 @@ -24,7 +24,7 @@ go install github.com/monkeyWie/gopeed-core ### 下载 -前往[releases](https://github.com/monkeyWie/gopeed-core/releases)页面 +前往[releases](https://github.com/monkeyWie/gopeed/releases)页面 ## TODO diff --git a/_examples/basic/main.go b/_examples/basic/main.go index 7e8fb147..0963cad0 100644 --- a/_examples/basic/main.go +++ b/_examples/basic/main.go @@ -2,8 +2,8 @@ package main import ( "fmt" - "github.com/monkeyWie/gopeed-core/pkg/base" - "github.com/monkeyWie/gopeed-core/pkg/download" + "github.com/monkeyWie/gopeed/pkg/base" + "github.com/monkeyWie/gopeed/pkg/download" ) func main() { diff --git a/bind/desktop/main.go b/bind/desktop/main.go index 7e219115..b4f724dd 100644 --- a/bind/desktop/main.go +++ b/bind/desktop/main.go @@ -1,12 +1,12 @@ -// go build -ldflags="-w -s" -buildmode=c-shared -o bin/libgopeed.dll github.com/monkeyWie/gopeed-core/bind/desktop -// go build -ldflags="-w -s" -buildmode=c-shared -o bin/libgopeed.dylib github.com/monkeyWie/gopeed-core/bind/desktop +// go build -ldflags="-w -s" -buildmode=c-shared -o bin/libgopeed.dll github.com/monkeyWie/gopeed/bind/desktop +// go build -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/macos//libgopeed.dylib github.com/monkeyWie/gopeed/bind/desktop package main import "C" import ( "encoding/json" - "github.com/monkeyWie/gopeed-core/pkg/rest" - "github.com/monkeyWie/gopeed-core/pkg/rest/model" + "github.com/monkeyWie/gopeed/pkg/rest" + "github.com/monkeyWie/gopeed/pkg/rest/model" ) func main() {} diff --git a/bind/mobile/main.go b/bind/mobile/main.go index 924bdd51..ab5fde25 100644 --- a/bind/mobile/main.go +++ b/bind/mobile/main.go @@ -1,10 +1,10 @@ -// gomobile bind -ldflags="-w -s" -o libgopeed.aar -target=android -androidapi 19 -javapkg=com.gopeed github.com/monkeyWie/gopeed-core/bind/mobile +// gomobile bind -ldflags="-w -s" -o libgopeed.aar -target=android -androidapi 19 -javapkg=com.gopeed github.com/monkeyWie/gopeed/bind/mobile package libgopeed import ( "encoding/json" - "github.com/monkeyWie/gopeed-core/pkg/rest" - "github.com/monkeyWie/gopeed-core/pkg/rest/model" + "github.com/monkeyWie/gopeed/pkg/rest" + "github.com/monkeyWie/gopeed/pkg/rest/model" ) func Start(cfg string) (int, error) { diff --git a/cmd/gopeed/gopeed.go b/cmd/gopeed/main.go similarity index 92% rename from cmd/gopeed/gopeed.go rename to cmd/gopeed/main.go index 8ed55895..4b41de95 100644 --- a/cmd/gopeed/gopeed.go +++ b/cmd/gopeed/main.go @@ -2,9 +2,9 @@ package main import ( "fmt" - "github.com/monkeyWie/gopeed-core/pkg/base" - "github.com/monkeyWie/gopeed-core/pkg/download" - "github.com/monkeyWie/gopeed-core/pkg/util" + "github.com/monkeyWie/gopeed/pkg/base" + "github.com/monkeyWie/gopeed/pkg/download" + "github.com/monkeyWie/gopeed/pkg/util" "strings" "sync" ) diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 00000000..76948b7c --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/monkeyWie/gopeed/pkg/rest" + "github.com/monkeyWie/gopeed/pkg/rest/model" +) + +func main() { + if err := rest.StartSync(&model.StartConfig{ + Network: "tcp", + Address: "127.0.0.1:9999", + Storage: model.StorageMem, + }); err != nil { + panic(err) + } +} diff --git a/go.mod b/go.mod index 0cc07815..1b6f9d8a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/monkeyWie/gopeed-core +module github.com/monkeyWie/gopeed go 1.18 @@ -36,8 +36,10 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect + github.com/felixge/httpsnoop v1.0.1 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/lispad/go-generics-tools v1.0.0 // indirect diff --git a/go.sum b/go.sum index d4342366..0a6e25f4 100644 --- a/go.sum +++ b/go.sum @@ -106,6 +106,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -159,6 +161,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORR github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= diff --git a/internal/fetcher/fetcher.go b/internal/fetcher/fetcher.go index 01ff3592..0d4ad2f8 100644 --- a/internal/fetcher/fetcher.go +++ b/internal/fetcher/fetcher.go @@ -1,8 +1,8 @@ package fetcher import ( - "github.com/monkeyWie/gopeed-core/internal/controller" - "github.com/monkeyWie/gopeed-core/pkg/base" + "github.com/monkeyWie/gopeed/internal/controller" + "github.com/monkeyWie/gopeed/pkg/base" ) // 对应协议的下载支持 diff --git a/internal/protocol/bt/fetcher.go b/internal/protocol/bt/fetcher.go index 52afadc8..35b31050 100644 --- a/internal/protocol/bt/fetcher.go +++ b/internal/protocol/bt/fetcher.go @@ -4,10 +4,10 @@ import ( "github.com/anacrolix/torrent" "github.com/anacrolix/torrent/metainfo" "github.com/anacrolix/torrent/storage" - "github.com/monkeyWie/gopeed-core/internal/controller" - "github.com/monkeyWie/gopeed-core/internal/fetcher" - "github.com/monkeyWie/gopeed-core/pkg/base" - "github.com/monkeyWie/gopeed-core/pkg/util" + "github.com/monkeyWie/gopeed/internal/controller" + "github.com/monkeyWie/gopeed/internal/fetcher" + "github.com/monkeyWie/gopeed/pkg/base" + "github.com/monkeyWie/gopeed/pkg/util" "path/filepath" "sync" "sync/atomic" diff --git a/internal/protocol/bt/fetcher_test.go b/internal/protocol/bt/fetcher_test.go index 1acacd2a..9b012a44 100644 --- a/internal/protocol/bt/fetcher_test.go +++ b/internal/protocol/bt/fetcher_test.go @@ -1,9 +1,9 @@ package bt import ( - "github.com/monkeyWie/gopeed-core/internal/controller" - "github.com/monkeyWie/gopeed-core/internal/fetcher" - "github.com/monkeyWie/gopeed-core/pkg/base" + "github.com/monkeyWie/gopeed/internal/controller" + "github.com/monkeyWie/gopeed/internal/fetcher" + "github.com/monkeyWie/gopeed/pkg/base" "reflect" "testing" ) diff --git a/internal/protocol/http/fetcher.go b/internal/protocol/http/fetcher.go index 61909a7e..7300680e 100644 --- a/internal/protocol/http/fetcher.go +++ b/internal/protocol/http/fetcher.go @@ -4,9 +4,9 @@ import ( "bytes" "context" "fmt" - "github.com/monkeyWie/gopeed-core/internal/fetcher" - "github.com/monkeyWie/gopeed-core/pkg/base" - "github.com/monkeyWie/gopeed-core/pkg/util" + "github.com/monkeyWie/gopeed/internal/fetcher" + "github.com/monkeyWie/gopeed/pkg/base" + "github.com/monkeyWie/gopeed/pkg/util" "golang.org/x/sync/errgroup" "io" "mime" diff --git a/internal/protocol/http/fetcher_test.go b/internal/protocol/http/fetcher_test.go index 66b079f6..f4515ade 100644 --- a/internal/protocol/http/fetcher_test.go +++ b/internal/protocol/http/fetcher_test.go @@ -2,10 +2,10 @@ package http import ( "fmt" - "github.com/monkeyWie/gopeed-core/internal/controller" - "github.com/monkeyWie/gopeed-core/internal/fetcher" - "github.com/monkeyWie/gopeed-core/internal/test" - "github.com/monkeyWie/gopeed-core/pkg/base" + "github.com/monkeyWie/gopeed/internal/controller" + "github.com/monkeyWie/gopeed/internal/fetcher" + "github.com/monkeyWie/gopeed/internal/test" + "github.com/monkeyWie/gopeed/pkg/base" "net" "reflect" "testing" diff --git a/internal/protocol/http/model.go b/internal/protocol/http/model.go index dcf97c2e..af58c452 100644 --- a/internal/protocol/http/model.go +++ b/internal/protocol/http/model.go @@ -1,6 +1,6 @@ package http -import "github.com/monkeyWie/gopeed-core/pkg/base" +import "github.com/monkeyWie/gopeed/pkg/base" type chunk struct { Status base.Status diff --git a/pkg/download/downloader.go b/pkg/download/downloader.go index 577573f2..f9251938 100644 --- a/pkg/download/downloader.go +++ b/pkg/download/downloader.go @@ -2,12 +2,12 @@ package download import ( "errors" - "github.com/monkeyWie/gopeed-core/internal/controller" - "github.com/monkeyWie/gopeed-core/internal/fetcher" - "github.com/monkeyWie/gopeed-core/internal/protocol/bt" - "github.com/monkeyWie/gopeed-core/internal/protocol/http" - "github.com/monkeyWie/gopeed-core/pkg/base" - "github.com/monkeyWie/gopeed-core/pkg/util" + "github.com/monkeyWie/gopeed/internal/controller" + "github.com/monkeyWie/gopeed/internal/fetcher" + "github.com/monkeyWie/gopeed/internal/protocol/bt" + "github.com/monkeyWie/gopeed/internal/protocol/http" + "github.com/monkeyWie/gopeed/pkg/base" + "github.com/monkeyWie/gopeed/pkg/util" "strings" "sync" "sync/atomic" @@ -380,7 +380,7 @@ func (d *Downloader) GetTasks() []*Task { return d.tasks } -func (d *Downloader) GetConfig(v any) error { +func (d *Downloader) GetConfig(v any) (bool, error) { return d.storage.Get(bucketConfig, "config", v) } diff --git a/pkg/download/downloader_test.go b/pkg/download/downloader_test.go index f881693f..f36cace6 100644 --- a/pkg/download/downloader_test.go +++ b/pkg/download/downloader_test.go @@ -1,8 +1,8 @@ package download import ( - "github.com/monkeyWie/gopeed-core/internal/test" - "github.com/monkeyWie/gopeed-core/pkg/base" + "github.com/monkeyWie/gopeed/internal/test" + "github.com/monkeyWie/gopeed/pkg/base" "reflect" "sync" "testing" diff --git a/pkg/download/storage.go b/pkg/download/storage.go index 71692398..a8d40926 100644 --- a/pkg/download/storage.go +++ b/pkg/download/storage.go @@ -11,7 +11,7 @@ import ( type Storage interface { Setup(buckets []string) error Put(bucket string, key string, v any) error - Get(bucket string, key string, v any) error + Get(bucket string, key string, v any) (bool, error) List(bucket string, v any) error Pop(bucket string, key string, v any) error Delete(bucket string, key string) error @@ -67,9 +67,12 @@ func (n *MemStorage) Put(bucket string, key string, v any) error { return nil } -func (n *MemStorage) Get(bucket string, key string, v any) error { - changeValue(v, memData[bucket][key]) - return nil +func (n *MemStorage) Get(bucket string, key string, v any) (bool, error) { + if dv, ok := memData[bucket][key]; ok { + changeValue(v, dv) + return true, nil + } + return false, nil } func (n *MemStorage) List(bucket string, v any) error { @@ -147,7 +150,7 @@ func (b *BoltStorage) Put(bucket string, key string, v any) error { }) } -func (b *BoltStorage) Get(bucket string, key string, v any) error { +func (b *BoltStorage) Get(bucket string, key string, v any) (bool, error) { var data []byte err := b.db.View(func(tx *bbolt.Tx) error { b := tx.Bucket([]byte(bucket)) @@ -155,9 +158,15 @@ func (b *BoltStorage) Get(bucket string, key string, v any) error { return nil }) if err != nil { - return err + return false, err } - return json.Unmarshal(data, v) + if data == nil { + return false, nil + } + if err := json.Unmarshal(data, v); err != nil { + return false, err + } + return true, nil } func (b *BoltStorage) List(bucket string, v any) error { diff --git a/pkg/download/task.go b/pkg/download/task.go index 2178586f..a8d11d88 100644 --- a/pkg/download/task.go +++ b/pkg/download/task.go @@ -2,9 +2,9 @@ package download import ( gonanoid "github.com/matoous/go-nanoid/v2" - "github.com/monkeyWie/gopeed-core/internal/fetcher" - "github.com/monkeyWie/gopeed-core/pkg/base" - "github.com/monkeyWie/gopeed-core/pkg/util" + "github.com/monkeyWie/gopeed/internal/fetcher" + "github.com/monkeyWie/gopeed/pkg/base" + "github.com/monkeyWie/gopeed/pkg/util" "sync" "time" ) diff --git a/pkg/rest/api.go b/pkg/rest/api.go index ad8b2437..ffcb373f 100644 --- a/pkg/rest/api.go +++ b/pkg/rest/api.go @@ -2,10 +2,10 @@ package rest import ( "github.com/gorilla/mux" - "github.com/monkeyWie/gopeed-core/pkg/base" - "github.com/monkeyWie/gopeed-core/pkg/download" - "github.com/monkeyWie/gopeed-core/pkg/rest/model" - "github.com/monkeyWie/gopeed-core/pkg/rest/util" + "github.com/monkeyWie/gopeed/pkg/base" + "github.com/monkeyWie/gopeed/pkg/download" + "github.com/monkeyWie/gopeed/pkg/rest/model" + "github.com/monkeyWie/gopeed/pkg/rest/util" "net/http" "strings" ) diff --git a/pkg/rest/model/task.go b/pkg/rest/model/task.go index e43548df..e6b759e3 100644 --- a/pkg/rest/model/task.go +++ b/pkg/rest/model/task.go @@ -1,6 +1,6 @@ package model -import "github.com/monkeyWie/gopeed-core/pkg/base" +import "github.com/monkeyWie/gopeed/pkg/base" type CreateTask struct { Res *base.Resource `json:"res"` diff --git a/pkg/rest/server.go b/pkg/rest/server.go index d242c023..cd7c1e6d 100644 --- a/pkg/rest/server.go +++ b/pkg/rest/server.go @@ -2,10 +2,11 @@ package rest import ( "context" + "github.com/gorilla/handlers" "github.com/gorilla/mux" - "github.com/monkeyWie/gopeed-core/pkg/download" - "github.com/monkeyWie/gopeed-core/pkg/rest/model" - "github.com/monkeyWie/gopeed-core/pkg/util" + "github.com/monkeyWie/gopeed/pkg/download" + "github.com/monkeyWie/gopeed/pkg/rest/model" + "github.com/monkeyWie/gopeed/pkg/util" "net" "net/http" "strconv" @@ -18,59 +19,34 @@ var ( ) func Start(startCfg *model.StartConfig) (int, error) { - if startCfg == nil { - startCfg = &model.StartConfig{} - } - startCfg.Init() - - downloadCfg := &download.DownloaderConfig{} - if startCfg.Storage == model.StorageBolt { - downloadCfg.Storage = download.NewBoltStorage(startCfg.StorageDir) - } else { - downloadCfg.Storage = download.NewMemStorage() - } - downloadCfg.RefreshInterval = startCfg.RefreshInterval - Downloader = download.NewDownloader(downloadCfg.Init()) - if err := Downloader.Setup(); err != nil { - return 0, err - } - - serverConfig := getAndPutServerConfig(startCfg) - var address string - if startCfg.Network == "unix" { - address = startCfg.Address - util.SafeRemove(address) - } else { - address = net.JoinHostPort(serverConfig.Host, strconv.Itoa(serverConfig.Port)) - } - - listener, err := net.Listen(startCfg.Network, address) + srv, listener, err := buildServer(startCfg) if err != nil { return 0, err } - var r = mux.NewRouter() - r.Methods(http.MethodPost).Path("/api/v1/resolve").HandlerFunc(Resolve) - r.Methods(http.MethodPost).Path("/api/v1/tasks").HandlerFunc(CreateTask) - r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/pause").HandlerFunc(PauseTask) - r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/continue").HandlerFunc(ContinueTask) - r.Methods(http.MethodDelete).Path("/api/v1/tasks/{id}").HandlerFunc(DeleteTask) - r.Methods(http.MethodGet).Path("/api/v1/tasks/{id}").HandlerFunc(GetTask) - r.Methods(http.MethodGet).Path("/api/v1/tasks").HandlerFunc(GetTasks) - r.Methods(http.MethodGet).Path("/api/v1/config").HandlerFunc(GetConfig) - r.Methods(http.MethodPut).Path("/api/v1/config").HandlerFunc(PutConfig) - - srv = &http.Server{Handler: r} go func() { if err := srv.Serve(listener); err != nil && err != http.ErrServerClosed { // TODO log panic(err) } }() + + port := 0 if addr, ok := listener.Addr().(*net.TCPAddr); ok { - return addr.Port, nil + port = addr.Port } - return 0, nil + return port, nil +} + +func StartSync(startCfg *model.StartConfig) error { + srv, listener, err := buildServer(startCfg) + if err != nil { + return err + } + if err := srv.Serve(listener); err != nil && err != http.ErrServerClosed { + return err + } + return nil } func Stop() { @@ -86,11 +62,65 @@ func Stop() { } } +func buildServer(startCfg *model.StartConfig) (*http.Server, net.Listener, error) { + if startCfg == nil { + startCfg = &model.StartConfig{} + } + startCfg.Init() + + downloadCfg := &download.DownloaderConfig{} + if startCfg.Storage == model.StorageBolt { + downloadCfg.Storage = download.NewBoltStorage(startCfg.StorageDir) + } else { + downloadCfg.Storage = download.NewMemStorage() + } + downloadCfg.RefreshInterval = startCfg.RefreshInterval + Downloader = download.NewDownloader(downloadCfg.Init()) + if err := Downloader.Setup(); err != nil { + return nil, nil, err + } + + serverConfig := getAndPutServerConfig(startCfg) + var address string + if startCfg.Network == "unix" { + address = startCfg.Address + util.SafeRemove(address) + } else { + address = net.JoinHostPort(serverConfig.Host, strconv.Itoa(serverConfig.Port)) + } + + listener, err := net.Listen(startCfg.Network, address) + if err != nil { + return nil, nil, err + } + + var r = mux.NewRouter() + r.Methods(http.MethodPost).Path("/api/v1/resolve").HandlerFunc(Resolve) + r.Methods(http.MethodPost).Path("/api/v1/tasks").HandlerFunc(CreateTask) + r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/pause").HandlerFunc(PauseTask) + r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/continue").HandlerFunc(ContinueTask) + r.Methods(http.MethodDelete).Path("/api/v1/tasks/{id}").HandlerFunc(DeleteTask) + r.Methods(http.MethodGet).Path("/api/v1/tasks/{id}").HandlerFunc(GetTask) + r.Methods(http.MethodGet).Path("/api/v1/tasks").HandlerFunc(GetTasks) + r.Methods(http.MethodGet).Path("/api/v1/config").HandlerFunc(GetConfig) + r.Methods(http.MethodPut).Path("/api/v1/config").HandlerFunc(PutConfig) + + srv = &http.Server{Handler: handlers.CORS( + handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}), + handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}), + handlers.AllowedOrigins([]string{"*"}), + )(r)} + return srv, listener, nil +} + func getAndPutServerConfig(startCfg *model.StartConfig) *model.ServerConfig { var serverConfig model.ServerConfig - err := Downloader.GetConfig(&serverConfig) - // first start + exist, err := Downloader.GetConfig(&serverConfig) if err != nil { + // TODO log + } + // first start + if !exist { if startCfg.Network == "tcp" { host, port, _ := net.SplitHostPort(startCfg.Address) serverConfig.Host = host diff --git a/pkg/rest/server_test.go b/pkg/rest/server_test.go index 146dd4bc..63c53be8 100644 --- a/pkg/rest/server_test.go +++ b/pkg/rest/server_test.go @@ -5,10 +5,10 @@ import ( "encoding/json" "errors" "fmt" - "github.com/monkeyWie/gopeed-core/internal/test" - "github.com/monkeyWie/gopeed-core/pkg/base" - "github.com/monkeyWie/gopeed-core/pkg/download" - "github.com/monkeyWie/gopeed-core/pkg/rest/model" + "github.com/monkeyWie/gopeed/internal/test" + "github.com/monkeyWie/gopeed/pkg/base" + "github.com/monkeyWie/gopeed/pkg/download" + "github.com/monkeyWie/gopeed/pkg/rest/model" "io" "net" "net/http" diff --git a/pkg/rest/util/util.go b/pkg/rest/util/util.go index 8c86fdeb..d80f16bc 100644 --- a/pkg/rest/util/util.go +++ b/pkg/rest/util/util.go @@ -2,7 +2,7 @@ package util import ( "encoding/json" - "github.com/monkeyWie/gopeed-core/pkg/rest/model" + "github.com/monkeyWie/gopeed/pkg/rest/model" "net/http" ) diff --git a/ui/flutter/include/libgopeed.h b/ui/flutter/include/libgopeed.h index 7911582e..a8ffe189 100644 --- a/ui/flutter/include/libgopeed.h +++ b/ui/flutter/include/libgopeed.h @@ -1,6 +1,6 @@ /* Code generated by cmd/cgo; DO NOT EDIT. */ -/* package github.com/monkeyWie/gopeed-core/bind/desktop */ +/* package github.com/monkeyWie/gopeed/bind/desktop */ #line 1 "cgo-builtin-export-prolog" diff --git a/ui/flutter/lib/api/api.dart b/ui/flutter/lib/api/api.dart index 1bacfaf8..90b4f08b 100644 --- a/ui/flutter/lib/api/api.dart +++ b/ui/flutter/lib/api/api.dart @@ -23,20 +23,23 @@ class _Client { if (_instance == null) { _instance = _Client._internal(); var dio = Dio(); - dio.options.baseUrl = "http://127.0.0.1:${LibgopeedBoot.instance.port}"; + dio.options.baseUrl = "http://${LibgopeedBoot.instance.config.address}"; _instance!.dio = dio; - (_instance!.dio.httpClientAdapter as DefaultHttpClientAdapter) - .onHttpClientCreate = (client) { - if (LibgopeedBoot.instance.network == "unix") { - client.connectionFactory = - (Uri uri, String? proxyHost, int? proxyPort) { - var address = InternetAddress(LibgopeedBoot.instance.address, - type: InternetAddressType.unix); - return Socket.startConnect(address, 0); - }; - } - return client; - }; + if(_instance!.dio.httpClientAdapter is DefaultHttpClientAdapter){ + (_instance!.dio.httpClientAdapter as DefaultHttpClientAdapter) + .onHttpClientCreate = (client) { + if (LibgopeedBoot.instance.config.network == "unix") { + client.connectionFactory = + (Uri uri, String? proxyHost, int? proxyPort) { + var address = InternetAddress(LibgopeedBoot.instance.config.address, + type: InternetAddressType.unix); + return Socket.startConnect(address, 0); + }; + } + return client; + }; + } + } return _instance!; } diff --git a/ui/flutter/lib/core/entry/libgopeed_boot_browser.dart b/ui/flutter/lib/core/entry/libgopeed_boot_browser.dart new file mode 100644 index 00000000..5e711b57 --- /dev/null +++ b/ui/flutter/lib/core/entry/libgopeed_boot_browser.dart @@ -0,0 +1,22 @@ +import 'dart:async'; + +import '../libgopeed_boot.dart'; + +LibgopeedBoot create() => LibgopeedBootBrowser(); + +class LibgopeedBootBrowser implements LibgopeedBoot { + late LibgopeedConfig _config; + + LibgopeedBootBrowser() { + _config = LibgopeedConfig(); + } + + @override + LibgopeedConfig get config => _config; + + @override + Future start() async {} + + @override + Future stop() async {} +} diff --git a/ui/flutter/lib/core/entry/libgopeed_boot_native.dart b/ui/flutter/lib/core/entry/libgopeed_boot_native.dart new file mode 100644 index 00000000..299b6889 --- /dev/null +++ b/ui/flutter/lib/core/entry/libgopeed_boot_native.dart @@ -0,0 +1,173 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'dart:io'; +import 'dart:isolate'; + +import 'package:flutter/foundation.dart'; +import 'package:gopeed/core/common/libgopeed_channel.dart'; +import 'package:gopeed/util/util.dart'; +import 'package:path_provider/path_provider.dart'; + +import '../common/libgopeed_ffi.dart'; +import '../common/libgopeed_interface.dart'; +import '../common/start_config.dart'; +import '../ffi/libgopeed_bind.dart'; +import '../libgopeed_boot.dart'; + +LibgopeedBoot create() => LibgopeedBootNative(); + +class LibgopeedBootNative implements LibgopeedBoot { + late LibgopeedConfig _config; + late LibgopeedInterface _libgopeed; + late SendPort _childSendPort; + + LibgopeedBootNative() { + _config = LibgopeedConfig(); + if (!Util.isDesktop()) { + _libgopeed = LibgopeedChannel(); + } + } + + @override + LibgopeedConfig get config => _config; + + @override + Future start() async { + // if is debug mode,use local http server + if (kDebugMode) { + return; + } + + var storageDir = "./"; + if (!Util.isUnix()) { + // not support unix socket, use tcp + _config.network = "tcp"; + _config.address = "127.0.0.1:0"; + } else { + _config.network = "unix"; + if (Util.isDesktop()) { + _config.address = LibgopeedBoot.unixSocketPath; + } else if (Platform.isAndroid) { + _config.address = + "${(await getTemporaryDirectory()).path}/${LibgopeedBoot.unixSocketPath}"; + storageDir = (await getExternalStorageDirectory())?.path ?? ""; + } + } + final cfg = StartConfig( + network: _config.network, + address: _config.address, + storage: 'bolt', + storageDir: storageDir, + refreshInterval: _config.refreshInterval); + var port = + Util.isDesktop() ? await _ffiStart(cfg) : await _libgopeed.start(cfg); + if (_config.network == "tcp") { + _config.address = "${_config.address.split(":")[0]}:$port"; + } + } + + @override + Future stop() async { + if (Util.isDesktop()) { + await _ffiStop(); + } else { + await _libgopeed.stop(); + } + } + + _ffiInit() { + if (Platform.isMacOS) { + _libgopeed = + LibgopeedFFi(LibgopeedBind(DynamicLibrary.open('libgopeed.dylib'))); + } + } + + // FFI run in isolate + Future _ffiStart(StartConfig cfg) async { + var startCompleter = Completer(); + + var mainReceive = ReceivePort(); + var isolate = await Isolate.spawn((SendPort sendPort) { + _ffiInit(); + var childReceive = ReceivePort(); + sendPort.send(childReceive.sendPort); + + childReceive.listen((message) async { + late _Result result; + try { + if (message is _StartParam) { + result = _StartResult(); + (result as _StartResult).port = await _libgopeed.start(message.cfg); + } + if (message is _StopParam) { + result = _StopResult(); + await _libgopeed.stop(); + childReceive.close(); + Isolate.exit(sendPort); + } + } on Exception catch (e) { + result.exception = e; + } + sendPort.send(result); + }); + }, mainReceive.sendPort); + + var childCompleter = Completer(); + mainReceive.listen((message) { + if (message is SendPort) { + _childSendPort = message; + childCompleter.complete(); + } + if (message is _StartResult) { + if (message.exception != null) { + startCompleter.completeError(message.exception!); + } else { + startCompleter.complete(message.port!); + } + } + if (message is _StopResult) { + isolate.kill(); + if (message.exception != null) { + _stopCompleter.completeError(message.exception!); + } else { + _stopCompleter.complete(); + } + } + }); + await childCompleter.future; + _childSendPort.send(_StartParam(cfg: cfg)); + return startCompleter.future; + } + + late Completer _stopCompleter; + + Future _ffiStop() async { + _stopCompleter = Completer(); + _childSendPort.send(_StopParam()); + return _stopCompleter.future; + } +} + +class _Result { + Exception? exception; + + _Result(); +} + +class _StartParam { + final StartConfig cfg; + + _StartParam({required this.cfg}); +} + +class _StartResult extends _Result { + int? port; + + _StartResult(); +} + +class _StopParam {} + +class _StopResult extends _Result { + _StopResult(); +} diff --git a/ui/flutter/lib/core/libgopeed_boot.dart b/ui/flutter/lib/core/libgopeed_boot.dart index f7e1dff2..9ef0405a 100644 --- a/ui/flutter/lib/core/libgopeed_boot.dart +++ b/ui/flutter/lib/core/libgopeed_boot.dart @@ -1,171 +1,34 @@ -import 'dart:async'; -import 'dart:ffi'; -import 'dart:io'; -import 'dart:isolate'; +import "./libgopeed_boot_stub.dart" + if (dart.library.html) 'entry/libgopeed_boot_browser.dart' + if (dart.library.io) 'entry/libgopeed_boot_native.dart'; -import 'package:gopeed/core/common/libgopeed_channel.dart'; -import 'package:gopeed/core/common/libgopeed_ffi.dart'; -import 'package:gopeed/core/common/start_config.dart'; -import 'package:path_provider/path_provider.dart'; +class LibgopeedConfig { + late String network; + late String address; + late int refreshInterval; -import '../util/util.dart'; -import 'common/libgopeed_interface.dart'; -import 'ffi/libgopeed_bind.dart'; + LibgopeedConfig({ + this.network = "tcp", + this.address = "127.0.0.1:9999", + this.refreshInterval = 500, + }); +} -const unixSocketPath = 'gopeed.sock'; - -class LibgopeedBoot { - String network = ""; - String address = ""; - int port = 0; - int refreshInterval = 250; - - late LibgopeedInterface _libgopeed; - late SendPort _childSendPort; +abstract class LibgopeedBoot { + static const unixSocketPath = 'gopeed.sock'; static LibgopeedBoot? _instance; static LibgopeedBoot get instance { - if (_instance == null) { - _instance = LibgopeedBoot._internal(); - if (!Util.isDesktop()) { - _instance!._libgopeed = LibgopeedChannel(); - } - } + _instance ??= LibgopeedBoot(); return _instance!; } - LibgopeedBoot._internal(); + factory LibgopeedBoot() => create(); - Future start() async { - var storageDir = "./"; - if (!Util.isUnix()) { - // not support unix socket, use tcp - network = "tcp"; - address = "127.0.0.1:0"; - } else { - network = "unix"; - if (Util.isDesktop()) { - address = unixSocketPath; - } else if (Platform.isAndroid) { - address = "${(await getTemporaryDirectory()).path}/$unixSocketPath"; - storageDir = (await getExternalStorageDirectory())?.path ?? ""; - } - } - final cfg = StartConfig( - network: network, - address: address, - storage: 'bolt', - storageDir: storageDir, - refreshInterval: refreshInterval); - if (Util.isDesktop()) { - port = await _ffiStart(cfg); - } else { - port = await _libgopeed.start(cfg); - } - } + Future start(); - Future stop() async { - if (Util.isDesktop()) { - await _ffiStop(); - } else { - await _libgopeed.stop(); - } - } + Future stop(); - _ffiInit() { - if (Platform.isMacOS) { - _libgopeed = - LibgopeedFFi(LibgopeedBind(DynamicLibrary.open('libgopeed.dylib'))); - } - } - - // FFI run in isolate - Future _ffiStart(StartConfig cfg) async { - var startCompleter = Completer(); - - var mainReceive = ReceivePort(); - var isolate = await Isolate.spawn((SendPort sendPort) { - _ffiInit(); - var childReceive = ReceivePort(); - sendPort.send(childReceive.sendPort); - - childReceive.listen((message) async { - late _Result result; - try { - if (message is _StartParam) { - result = _StartResult(); - (result as _StartResult).port = await _libgopeed.start(message.cfg); - } - if (message is _StopParam) { - result = _StopResult(); - await _libgopeed.stop(); - childReceive.close(); - Isolate.exit(sendPort); - } - } on Exception catch (e) { - result.exception = e; - } - sendPort.send(result); - }); - }, mainReceive.sendPort); - - var childCompleter = Completer(); - mainReceive.listen((message) { - if (message is SendPort) { - _childSendPort = message; - childCompleter.complete(); - } - if (message is _StartResult) { - if (message.exception != null) { - startCompleter.completeError(message.exception!); - } else { - startCompleter.complete(message.port!); - } - } - if (message is _StopResult) { - isolate.kill(); - if (message.exception != null) { - _stopCompleter.completeError(message.exception!); - } else { - _stopCompleter.complete(); - } - } - }); - await childCompleter.future; - _childSendPort.send(_StartParam(cfg: cfg)); - return startCompleter.future; - } - - late Completer _stopCompleter; - - Future _ffiStop() async { - _stopCompleter = Completer(); - _childSendPort.send(_StopParam()); - return _stopCompleter.future; - } -} - -class _Result { - Exception? exception; - - _Result(); -} - -class _StartParam { - final StartConfig cfg; - - _StartParam({required this.cfg}); -} - -class _StartResult extends _Result { - int? port; - - _StartResult(); -} - -class _StopParam {} - -class _StopResult extends _Result { - _StopResult(); + LibgopeedConfig get config; } diff --git a/ui/flutter/lib/core/libgopeed_boot_stub.dart b/ui/flutter/lib/core/libgopeed_boot_stub.dart new file mode 100644 index 00000000..ae539aeb --- /dev/null +++ b/ui/flutter/lib/core/libgopeed_boot_stub.dart @@ -0,0 +1,3 @@ +import 'libgopeed_boot.dart'; + +LibgopeedBoot create() => throw UnimplementedError(); diff --git a/ui/flutter/lib/main.dart b/ui/flutter/lib/main.dart index 9d51c05d..ca20cf8b 100644 --- a/ui/flutter/lib/main.dart +++ b/ui/flutter/lib/main.dart @@ -21,4 +21,4 @@ void main() async { themeMode: Setting.instance.themeMode, getPages: Routes.routes, )); -} +} \ No newline at end of file diff --git a/ui/flutter/lib/pages/create/create_view.dart b/ui/flutter/lib/pages/create/create_view.dart index 98e301dc..b613f4c2 100644 --- a/ui/flutter/lib/pages/create/create_view.dart +++ b/ui/flutter/lib/pages/create/create_view.dart @@ -1,4 +1,3 @@ -import 'dart:developer' as developer; import 'dart:io'; import 'package:flutter/material.dart'; @@ -15,6 +14,7 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:rounded_loading_button/rounded_loading_button.dart'; import '../../routes/router.dart'; +import '../../util/util.dart'; import '../../widget/directory_selector.dart'; class CreateView extends GetView { @@ -114,7 +114,7 @@ class CreateView extends GetView { width: width, child: Form( key: createFormKey, - autovalidateMode: AutovalidateMode.always, //开启自动校验 + autovalidateMode: AutovalidateMode.always, child: Column( children: [ Expanded( @@ -159,7 +159,7 @@ class CreateView extends GetView { try { if (createFormKey.currentState!.validate()) { downloadController.start(); - if (Platform.isAndroid) { + if (Util.isAndroid()) { if (!await Permission.storage.request().isGranted) { Get.snackbar("错误", "需要开启存储权限"); return; @@ -183,7 +183,7 @@ class CreateView extends GetView { } } catch (e) { Get.snackbar("错误", e.toString()); - developer.log(e.toString(), name: 'flutter'); + rethrow; } finally { downloadController.reset(); } diff --git a/ui/flutter/lib/pages/task/task_controller.dart b/ui/flutter/lib/pages/task/task_controller.dart index 3e65f983..89ab65c1 100644 --- a/ui/flutter/lib/pages/task/task_controller.dart +++ b/ui/flutter/lib/pages/task/task_controller.dart @@ -55,7 +55,7 @@ class TaskController extends GetxController { loadTab(); _timer = Timer.periodic( - Duration(milliseconds: LibgopeedBoot.instance.refreshInterval), + Duration(milliseconds: LibgopeedBoot.instance.config.refreshInterval), (timer) async { loadTab(); }); diff --git a/ui/flutter/lib/setting/setting.dart b/ui/flutter/lib/setting/setting.dart index 409ce4bb..ecc587f5 100644 --- a/ui/flutter/lib/setting/setting.dart +++ b/ui/flutter/lib/setting/setting.dart @@ -1,9 +1,5 @@ -import 'dart:convert'; -import 'dart:io'; - import 'package:external_path/external_path.dart'; import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:gopeed/api/api.dart'; import 'package:path_provider/path_provider.dart'; @@ -48,7 +44,11 @@ class Setting { this.themeMode = ThemeMode.values.byName(themeMode); } - if (Platform.isAndroid) { + if(Util.isWeb()){ + downloadDir = "./"; + return; + } + if (Util.isAndroid()) { downloadDir = '${await ExternalPath.getExternalStoragePublicDirectory(ExternalPath.DIRECTORY_DOWNLOADS)}/com.gopeed'; return; diff --git a/ui/flutter/lib/util/mac_secure_util.dart b/ui/flutter/lib/util/mac_secure_util.dart index 666eab5b..69f6bdd4 100644 --- a/ui/flutter/lib/util/mac_secure_util.dart +++ b/ui/flutter/lib/util/mac_secure_util.dart @@ -3,11 +3,13 @@ import 'dart:io'; import 'package:macos_secure_bookmarks/macos_secure_bookmarks.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'util.dart'; + class MacSecureUtil { static const _bookmarkKey = "bookmark:"; static Future saveBookmark(String dir) async { - if (!Platform.isMacOS) { + if (!Util.isMacos()) { return; } final secureBookmarks = SecureBookmarks(); @@ -17,7 +19,7 @@ class MacSecureUtil { } static Future loadBookmark() async { - if (!Platform.isMacOS) { + if (!Util.isMacos()) { return; } final prefs = await SharedPreferences.getInstance(); diff --git a/ui/flutter/lib/util/util.dart b/ui/flutter/lib/util/util.dart index cab00cf0..90baf6f5 100644 --- a/ui/flutter/lib/util/util.dart +++ b/ui/flutter/lib/util/util.dart @@ -1,5 +1,7 @@ import 'dart:io'; +import 'package:flutter/foundation.dart'; + class Util { static String fmtByte(int byte) { if (byte < 1024) { @@ -21,11 +23,29 @@ class Util { return (dir == "" ? "" : "$dir/") + buildPath(path, name); } + static isWeb() { + return kIsWeb; + } + + static isAndroid() { + return !kIsWeb && Platform.isAndroid; + } + + static isMacos() { + return !kIsWeb && Platform.isMacOS; + } + static isDesktop() { + if (kIsWeb) { + return false; + } return Platform.isWindows || Platform.isLinux || Platform.isMacOS; } static isUnix() { + if (kIsWeb) { + return false; + } return !Platform.isWindows; } } diff --git a/ui/flutter/macos/Runner/DebugProfile.entitlements b/ui/flutter/macos/Runner/DebugProfile.entitlements index e9a99e71..63f40e06 100644 --- a/ui/flutter/macos/Runner/DebugProfile.entitlements +++ b/ui/flutter/macos/Runner/DebugProfile.entitlements @@ -6,10 +6,12 @@ com.apple.security.cs.allow-jit + com.apple.security.files.bookmarks.app-scope + + com.apple.security.files.downloads.read-write + com.apple.security.files.user-selected.read-write - com.apple.security.files.bookmarks.app-scope - com.apple.security.network.client com.apple.security.network.server diff --git a/ui/flutter/macos/Runner/Release.entitlements b/ui/flutter/macos/Runner/Release.entitlements index 4af82039..ee03f688 100644 --- a/ui/flutter/macos/Runner/Release.entitlements +++ b/ui/flutter/macos/Runner/Release.entitlements @@ -4,10 +4,12 @@ com.apple.security.app-sandbox + com.apple.security.files.bookmarks.app-scope + + com.apple.security.files.downloads.read-write + com.apple.security.files.user-selected.read-write - com.apple.security.files.bookmarks.app-scope - com.apple.security.network.client com.apple.security.network.server