返回 DeepSeek-Reasonix
present_preview.go
根目录 / desktop / present_preview.go
1 package main
2
3 import (
4 "context"
5 "errors"
6 "log/slog"
7 "net"
8 "net/http"
9 "sync"
10 "time"
11 )
12
13 // workspacePreviewOrigin owns the unprivileged loopback resource origin used
14 // by deliverables opened in the built-in browser. Access is limited by the
15 // random, short-lived media capability embedded in each URL.
16 type workspacePreviewOrigin struct {
17 mu sync.Mutex
18 origin string
19 listener net.Listener
20 server *http.Server
21 }
22
23 func newWorkspacePreviewOrigin() *workspacePreviewOrigin { return &workspacePreviewOrigin{} }
24
25 func (a *App) ensureWorkspacePreviewOrigin() (string, error) {
26 a.mu.Lock()
27 if a.presentPreview == nil {
28 a.presentPreview = newWorkspacePreviewOrigin()
29 }
30 p := a.presentPreview
31 a.mu.Unlock()
32 p.mu.Lock()
33 defer p.mu.Unlock()
34 if p.server != nil {
35 return p.origin, nil
36 }
37 listener, err := net.Listen("tcp", "127.0.0.1:0")
38 if err != nil {
39 return "", err
40 }
41 handler := a.workspaceMediaMiddleware()(http.NotFoundHandler())
42 server := &http.Server{Handler: handler, ReadHeaderTimeout: 10 * time.Second}
43 p.listener = listener
44 p.server = server
45 p.origin = "http://" + listener.Addr().String()
46 go func() {
47 if serveErr := server.Serve(listener); serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) {
48 slog.Warn("desktop: workspace preview origin stopped", "err", serveErr)
49 }
50 }()
51 return p.origin, nil
52 }
53
54 func (a *App) stopWorkspacePreviewOrigin() {
55 if a == nil {
56 return
57 }
58 a.mu.Lock()
59 p := a.presentPreview
60 a.mu.Unlock()
61 if p == nil {
62 return
63 }
64 p.mu.Lock()
65 server := p.server
66 p.server = nil
67 p.listener = nil
68 p.origin = ""
69 p.mu.Unlock()
70 if server == nil {
71 return
72 }
73 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
74 defer cancel()
75 _ = server.Shutdown(ctx)
76 }
77
77 lines GO