Skip to content

Commit 9a5680f

Browse files
authored
feat: refactor browser extension communication to use IPC (#1130)
1 parent 75a8a3a commit 9a5680f

File tree

17 files changed

+352
-115
lines changed

17 files changed

+352
-115
lines changed

_examples/basic/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"fmt"
5+
56
"github.com/GopeedLab/gopeed/pkg/base"
67
"github.com/GopeedLab/gopeed/pkg/download"
78
"github.com/GopeedLab/gopeed/pkg/protocol/http"

cmd/host/dail_other.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//go:build !windows
2+
// +build !windows
3+
4+
package main
5+
6+
import (
7+
"net"
8+
"os"
9+
"path/filepath"
10+
)
11+
12+
func Dial() (net.Conn, error) {
13+
// Get binary path
14+
exe, err := os.Executable()
15+
if err != nil {
16+
return nil, err
17+
}
18+
return net.Dial("unix", filepath.Join(filepath.Dir(exe), "gopeed_host.sock"))
19+
}

cmd/host/dail_windows.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
import (
4+
"net"
5+
6+
"github.com/Microsoft/go-winio"
7+
)
8+
9+
func Dial() (net.Conn, error) {
10+
return winio.DialPipe(`\\.\pipe\gopeed_host`, nil)
11+
}

cmd/host/main.go

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
package main
22

33
import (
4+
"bytes"
5+
"context"
46
"encoding/binary"
57
"encoding/json"
68
"errors"
79
"fmt"
810
"io"
11+
"net"
12+
"net/http"
913
"os"
10-
"strings"
14+
"time"
1115

1216
"github.com/pkg/browser"
13-
"github.com/shirou/gopsutil/v4/process"
1417
)
1518

16-
const identifier = "gopeed"
17-
1819
type Message struct {
1920
Method string `json:"method"`
21+
Meta map[string]any `json:"meta"`
2022
Params json.RawMessage `json:"params"`
2123
}
2224

@@ -26,31 +28,74 @@ type Response struct {
2628
Message string `json:"message,omitempty"`
2729
}
2830

29-
var apiMap = map[string]func(params json.RawMessage) (data any, err error){
30-
"ping": func(params json.RawMessage) (data any, err error) {
31-
processes, err := process.Processes()
31+
func check() (data bool, err error) {
32+
conn, err := Dial()
33+
if err != nil {
34+
return false, err
35+
}
36+
defer conn.Close()
37+
return true, nil
38+
}
39+
40+
func wakeup(hidden bool) error {
41+
running, _ := check()
42+
if running {
43+
return nil
44+
}
45+
46+
uri := "gopeed:"
47+
if hidden {
48+
uri = uri + "?hidden=true"
49+
}
50+
if err := browser.OpenURL(uri); err != nil {
51+
return err
52+
}
53+
54+
for i := 0; i < 10; i++ {
55+
if ok, _ := check(); ok {
56+
return nil
57+
}
58+
time.Sleep(1 * time.Second)
59+
}
60+
return fmt.Errorf("start gopeed failed")
61+
}
62+
63+
var apiMap = map[string]func(message *Message) (data any, err error){
64+
"ping": func(message *Message) (data any, err error) {
65+
return check()
66+
},
67+
"create": func(message *Message) (data any, err error) {
68+
buf, err := message.Params.MarshalJSON()
3269
if err != nil {
33-
return false, err
70+
return
3471
}
3572

36-
for _, p := range processes {
37-
name, err := p.Name()
38-
if err != nil {
39-
continue
40-
}
73+
silent := false
74+
if v, ok := message.Meta["silent"]; ok {
75+
silent, _ = v.(bool)
76+
}
4177

42-
if strings.Contains(strings.ToLower(name), strings.ToLower(identifier)) {
43-
return true, nil
44-
}
78+
if err := wakeup(silent); err != nil {
79+
return nil, err
4580
}
46-
return false, nil
47-
},
48-
"create": func(params json.RawMessage) (data any, err error) {
49-
var strParams string
50-
if err = json.Unmarshal(params, &strParams); err != nil {
81+
82+
client := &http.Client{
83+
Transport: &http.Transport{
84+
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
85+
return Dial()
86+
},
87+
},
88+
Timeout: 10 * time.Second,
89+
}
90+
req, err := http.NewRequest("POST", "http://127.0.0.1/create", bytes.NewBuffer(buf))
91+
if err != nil {
5192
return
5293
}
53-
err = browser.OpenURL(fmt.Sprintf("%s:///create?params=%s", identifier, strParams))
94+
if message.Meta != nil {
95+
metaJson, _ := json.Marshal(message.Meta)
96+
req.Header.Set("X-Gopeed-Host-Meta", string(metaJson))
97+
}
98+
_, err = client.Do(req)
5499
return
55100
},
56101
}
@@ -88,7 +133,7 @@ func main() {
88133
var data any
89134
var err error
90135
if handler, ok := apiMap[message.Method]; ok {
91-
data, err = handler(message.Params)
136+
data, err = handler(&message)
92137
} else {
93138
err = errors.New("Unknown method: " + message.Method)
94139
}

cmd/updater/main.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"log"
77
"os"
8-
"path/filepath"
98
"syscall"
109
"time"
1110

@@ -19,6 +18,7 @@ func main() {
1918
pid := flag.Int("pid", 0, "PID of the process to update")
2019
updateChannel := flag.String("channel", "", "Update channel")
2120
packagePath := flag.String("asset", "", "Path to the package asset")
21+
exeDir := flag.String("exeDir", "", "Directory of the entry executable")
2222
logPath := flag.String("log", "", "Log file path")
2323
flag.Parse()
2424

@@ -43,7 +43,7 @@ func main() {
4343
restart bool
4444
err error
4545
)
46-
if restart, err = update(*pid, *updateChannel, *packagePath); err != nil {
46+
if restart, err = update(*pid, *updateChannel, *packagePath, *exeDir); err != nil {
4747
log.Printf("Update failed: %v\n", err)
4848
os.Exit(1)
4949
}
@@ -61,7 +61,7 @@ func main() {
6161
os.Exit(0)
6262
}
6363

64-
func update(pid int, updateChannel, packagePath string) (restart bool, err error) {
64+
func update(pid int, updateChannel, packagePath, exeDir string) (restart bool, err error) {
6565
killSignalChan := make(chan any, 1)
6666

6767
go func() {
@@ -76,9 +76,8 @@ func update(pid int, updateChannel, packagePath string) (restart bool, err error
7676
}
7777
}()
7878

79-
appDir := filepath.Dir(os.Args[0])
80-
log.Printf("Updating process updateChannel=%s packagePath=%s appDir=%s\n", updateChannel, packagePath, appDir)
81-
if restart, err = install(killSignalChan, updateChannel, packagePath, appDir); err != nil {
79+
log.Printf("Updating process updateChannel=%s packagePath=%s exeDir=%s\n", updateChannel, packagePath, exeDir)
80+
if restart, err = install(killSignalChan, updateChannel, packagePath, exeDir); err != nil {
8281
return false, errors.Wrap(err, "failed to install package")
8382
}
8483

cmd/updater/updater_darwin.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,20 @@ func installByDmg(killSignalChan chan<- any, packagePath, destDir string) error
2525
mountPoint := ""
2626
for _, line := range strings.Split(string(output), "\n") {
2727
if strings.Contains(line, "/Volumes/") {
28-
fields := strings.Fields(line)
29-
if len(fields) > 2 {
30-
mountPoint = fields[len(fields)-1]
28+
// Find the /Volumes/ path in the line
29+
// hdiutil output format: /dev/disk4s1 Apple_HFS /Volumes/Gopeed
30+
// or with sequence number: /dev/disk4s1 Apple_HFS /Volumes/Gopeed 1
31+
idx := strings.Index(line, "/Volumes/")
32+
if idx != -1 {
33+
// Extract everything from /Volumes/ onwards and trim whitespace
34+
mountPoint = strings.TrimSpace(line[idx:])
3135
break
3236
}
3337
}
3438
}
3539

3640
if mountPoint == "" {
37-
return fmt.Errorf("failed to get mount point")
41+
return fmt.Errorf("failed to get mount point from hdiutil output: %s", string(output))
3842
}
3943

4044
// Detach the mounted DMG
@@ -45,7 +49,7 @@ func installByDmg(killSignalChan chan<- any, packagePath, destDir string) error
4549
return err
4650
}
4751
if len(matches) == 0 {
48-
return fmt.Errorf("no .app found in dmg")
52+
return fmt.Errorf("no .app found in dmg, mountPoint: %s", mountPoint)
4953
}
5054

5155
killSignalChan <- nil

ui/flutter/lib/app/modules/app/controllers/app_controller.dart

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:url_launcher/url_launcher.dart';
1616
import 'package:window_manager/window_manager.dart';
1717

1818
import '../../../../api/api.dart';
19+
import '../../../../api/model/create_task.dart';
1920
import '../../../../api/model/downloader_config.dart';
2021
import '../../../../api/model/request.dart';
2122
import '../../../../core/common/start_config.dart';
@@ -31,7 +32,7 @@ import '../../../../util/package_info.dart';
3132
import '../../../../util/updater.dart';
3233
import '../../../../util/util.dart';
3334
import '../../../routes/app_pages.dart';
34-
import '../../create/dto/create_router_params.dart';
35+
import '../../../rpc/rpc.dart';
3536
import '../../redirect/views/redirect_view.dart';
3637

3738
const unixSocketPath = 'gopeed.sock';
@@ -78,6 +79,9 @@ class AppController extends GetxController with WindowListener, TrayListener {
7879
_initTray().onError(
7980
(error, stackTrace) => logger.w("initTray error", error, stackTrace));
8081

82+
_initRpcServer().onError((error, stackTrace) =>
83+
logger.w("initRpcServer error", error, stackTrace));
84+
8185
_initForegroundTask().onError((error, stackTrace) =>
8286
logger.w("initForegroundTask error", error, stackTrace));
8387

@@ -271,6 +275,37 @@ class AppController extends GetxController with WindowListener, TrayListener {
271275
trayManager.addListener(this);
272276
}
273277

278+
Future<void> _initRpcServer() async {
279+
if (!Util.isDesktop()) {
280+
return;
281+
}
282+
try {
283+
await startRpcServer({
284+
"/create": (ctx) async {
285+
final meta =
286+
ctx.request.headers["X-Gopeed-Host-Meta"]?.firstOrNull ?? "{}";
287+
final jsonMeta = jsonDecode(meta);
288+
final silent = jsonMeta['silent'] as bool? ?? false;
289+
final params = await ctx.readText();
290+
final createTaskParams = _decodeToCreatTaskParams(params);
291+
if (!silent) {
292+
await windowManager.show();
293+
_handleToCreate0(createTaskParams);
294+
} else {
295+
try {
296+
await createTask(createTaskParams);
297+
} catch (e) {
298+
logger.w(
299+
"create task from extension fail", e, StackTrace.current);
300+
}
301+
}
302+
},
303+
});
304+
} catch (e) {
305+
logger.w("start rpc server fail", e, StackTrace.current);
306+
}
307+
}
308+
274309
Future<void> _initForegroundTask() async {
275310
if (!Util.isMobile()) {
276311
return;
@@ -317,13 +352,7 @@ class AppController extends GetxController with WindowListener, TrayListener {
317352
if (uri.path == "/create") {
318353
final params = uri.queryParameters["params"];
319354
if (params?.isNotEmpty == true) {
320-
final safeParams = params!.replaceAll(" ", "+");
321-
final paramsJson =
322-
String.fromCharCodes(base64Decode(base64.normalize(safeParams)));
323-
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
324-
arguments: RedirectArgs(Routes.CREATE,
325-
arguments:
326-
CreateRouterParams.fromJson(jsonDecode(paramsJson))));
355+
_handleToCreate(params!);
327356
return;
328357
}
329358
Get.rootDelegate.offAndToNamed(Routes.CREATE);
@@ -346,7 +375,7 @@ class AppController extends GetxController with WindowListener, TrayListener {
346375
}
347376
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
348377
arguments: RedirectArgs(Routes.CREATE,
349-
arguments: CreateRouterParams(req: Request(url: path))));
378+
arguments: CreateTask(req: Request(url: path))));
350379
}
351380

352381
String runningAddress() {
@@ -524,4 +553,21 @@ class AppController extends GetxController with WindowListener, TrayListener {
524553
apiToken: startConfig.value.apiToken));
525554
await putConfig(downloaderConfig.value);
526555
}
556+
557+
CreateTask _decodeToCreatTaskParams(String params) {
558+
final safeParams = params.replaceAll('"', "").replaceAll(" ", "+");
559+
final paramsJson =
560+
String.fromCharCodes(base64Decode(base64.normalize(safeParams)));
561+
return CreateTask.fromJson(jsonDecode(paramsJson));
562+
}
563+
564+
_handleToCreate(String params) {
565+
final createTaskParams = _decodeToCreatTaskParams(params);
566+
_handleToCreate0(createTaskParams);
567+
}
568+
569+
_handleToCreate0(CreateTask createTaskParams) {
570+
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
571+
arguments: RedirectArgs(Routes.CREATE, arguments: createTaskParams));
572+
}
527573
}

ui/flutter/lib/app/modules/create/dto/create_router_params.dart

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)