返回 DeepSeek-Reasonix
browser_broker_remote.go
根目录 / desktop / browser_broker_remote.go
1 package main
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "log"
8 "slices"
9 "strings"
10
11 "reasonix/internal/remote/bootstrap"
12 )
13
14 // serveCapabilityBrowser is the token a serve advertises on its token
15 // handshake when it was started with a browser broker (and accepts
16 // POST /browser/broker rebinds). Older serves omit the header entirely.
17 const serveCapabilityBrowser = "browser"
18
19 // browserBrokerCallback is the bootstrap BrowserBroker hook for one ensure
20 // round: it mints this connection generation's token and opens the reverse
21 // tunnel only when the desktop shell can host browser surfaces. A desktop
22 // without host mode returns nil, launching the serve without a broker.
23 func (m *desktopRemoteManager) browserBrokerCallback(c desktopSSHClient, hostID string, gen *managedHost) func(context.Context) (*bootstrap.BrowserBrokerOptions, error) {
24 return func(ctx context.Context) (*bootstrap.BrowserBrokerOptions, error) {
25 app, ok := m.sink.(*App)
26 if !ok || app == nil || !app.hostMode() {
27 return nil, nil
28 }
29 if !m.isCurrent(hostID, gen) {
30 return nil, fmt.Errorf("browser broker: host %q connection was replaced", hostID)
31 }
32 token, port, err := app.registerBrowserBrokerRoute(hostID, gen)
33 if err != nil {
34 return nil, err
35 }
36 remotePort, err := ensureBrowserBrokerForward(c, hostID, port)
37 if err != nil {
38 app.revokeBrowserBrokerRoutes(hostID)
39 return nil, fmt.Errorf("browser broker: reverse tunnel: %w", err)
40 }
41 return &bootstrap.BrowserBrokerOptions{
42 BaseURL: fmt.Sprintf("http://127.0.0.1:%d", remotePort),
43 Token: token,
44 }, nil
45 }
46 }
47
48 // rebindBrowserBroker re-points a REUSED serve at the new connection
49 // generation's broker: the process environment still carries the dead
50 // generation's endpoint and token, so the desktop rotates the route and posts
51 // the fresh pair. Serves without the browser capability keep every existing
52 // feature untouched.
53 func (m *desktopRemoteManager) rebindBrowserBroker(ctx context.Context, c desktopSSHClient, mh *managedHost, hostID string, view RemoteServerView, token string) error {
54 app, ok := m.sink.(*App)
55 if !ok || app == nil || !app.hostMode() {
56 return nil
57 }
58 if strings.TrimSpace(view.LocalURL) == "" || token == "" {
59 return nil
60 }
61 if !m.isCurrent(hostID, mh) {
62 return fmt.Errorf("browser broker: host %q connection was replaced", hostID)
63 }
64 client, err := newServeHTTPClient(view.LocalURL)
65 if err != nil {
66 return err
67 }
68 caps, err := serveHandshakeCapabilities(ctx, client, view.LocalURL, token)
69 if err != nil {
70 return fmt.Errorf("browser broker: handshake: %w", err)
71 }
72 if !slices.Contains(caps, serveCapabilityBrowser) {
73 return nil
74 }
75 brokerToken, port, err := app.registerBrowserBrokerRoute(hostID, mh)
76 if err != nil {
77 return err
78 }
79 remotePort, err := ensureBrowserBrokerForward(c, hostID, port)
80 if err != nil {
81 return fmt.Errorf("browser broker: reverse tunnel: %w", err)
82 }
83 body, err := json.Marshal(map[string]string{
84 "endpoint": fmt.Sprintf("http://127.0.0.1:%d", remotePort),
85 "token": brokerToken,
86 })
87 if err != nil {
88 return err
89 }
90 if err := servePost(ctx, client, serveURL(view.LocalURL, "/browser/broker"), body); err != nil {
91 return fmt.Errorf("browser broker: rebind: %w", err)
92 }
93 log.Printf("[remote] browser broker rebound host=%s url=%s", hostID, view.LocalURL)
94 return nil
95 }
96
97 // rebindBrowserBrokerBestEffort runs the rebind without failing the ensure
98 // round: browser access is additive, and a serve whose rebind failed reports
99 // itself unavailable through the broker health check instead.
100 func (m *desktopRemoteManager) rebindBrowserBrokerBestEffort(ctx context.Context, c desktopSSHClient, mh *managedHost, hostID string, view RemoteServerView, token string) {
101 if err := m.rebindBrowserBroker(ctx, c, mh, hostID, view, token); err != nil {
102 log.Printf("[remote] browser broker rebind FAILED host=%s err=%v", hostID, err)
103 }
104 }
105
106 // revokeBrowserBroker drops the host's broker route and reverse tunnel when
107 // its serve is stopped or the connection goes away.
108 func (m *desktopRemoteManager) revokeBrowserBroker(hostID string) {
109 if app, ok := m.sink.(*App); ok && app != nil {
110 app.revokeBrowserBrokerRoutes(hostID)
111 }
112 }
113
113 lines GO