-
Notifications
You must be signed in to change notification settings - Fork 288
/
controller.go
139 lines (115 loc) · 3.38 KB
/
controller.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package server
import (
"context"
"net/http"
"sync/atomic"
"time"
"github.com/pkg/browser"
"github.com/pkg/errors"
"github.com/windmilleng/tilt/internal/network"
"github.com/windmilleng/tilt/internal/store"
"github.com/windmilleng/tilt/pkg/assets"
"github.com/windmilleng/tilt/pkg/model"
)
// The amount of time to wait for a reconnection before restarting the browser
// window.
const reconnectDur = 2 * time.Second
type HeadsUpServerController struct {
host model.WebHost
port model.WebPort
hudServer *HeadsUpServer
assetServer assets.Server
webURL model.WebURL
webLoadDone bool
initDone bool
noBrowser model.NoBrowser
}
func ProvideHeadsUpServerController(host model.WebHost, port model.WebPort, hudServer *HeadsUpServer, assetServer assets.Server, webURL model.WebURL, noBrowser model.NoBrowser) *HeadsUpServerController {
return &HeadsUpServerController{
host: host,
port: port,
hudServer: hudServer,
assetServer: assetServer,
webURL: webURL,
noBrowser: noBrowser,
}
}
func (s *HeadsUpServerController) TearDown(ctx context.Context) {
s.assetServer.TearDown(ctx)
}
func (s *HeadsUpServerController) isWebsocketConnected() bool {
connCount := atomic.LoadInt32(&(s.hudServer.numWebsocketConns))
return connCount > 0
}
func (s *HeadsUpServerController) maybeOpenBrowser(st store.RStore) {
if s.webURL.Empty() || s.webLoadDone || (bool)(s.noBrowser) {
return
}
if s.isWebsocketConnected() {
// Don't auto-open the web view. It's already opened.
s.webLoadDone = true
return
}
state := st.RLockState()
tiltfileCompleted := !state.TiltfileState.LastBuild().Empty()
startTime := state.TiltStartTime
st.RUnlockState()
// Only open the webview if the Tiltfile has completed.
if tiltfileCompleted {
s.webLoadDone = true
// Make sure we wait at least `reconnectDur` before opening the browser, to
// give any open pages time to reconnect. Do this on a goroutine so we don't
// hold the lock.
go func() {
runDur := time.Since(startTime)
if runDur < reconnectDur {
time.Sleep(reconnectDur - runDur)
}
if s.isWebsocketConnected() {
return
}
// We should probably dependency-inject a browser opener.
//
// It might also make sense to wait until the asset server is ready?
_ = browser.OpenURL(s.webURL.String())
}()
}
}
func (s *HeadsUpServerController) OnChange(ctx context.Context, st store.RStore) {
s.maybeOpenBrowser(st)
defer func() {
s.initDone = true
}()
if s.initDone || s.port == 0 {
return
}
err := network.IsBindAddrFree(network.BindAddr(string(s.host), int(s.port)))
if err != nil {
st.Dispatch(
store.NewErrorAction(
errors.Wrapf(err, "Cannot start Tilt. Maybe another process is already running on port %d? Use --port to set a custom port", s.port)))
return
}
httpServer := &http.Server{
Addr: network.BindAddr(string(s.host), int(s.port)),
Handler: http.DefaultServeMux,
}
http.Handle("/", s.hudServer.Router())
go func() {
<-ctx.Done()
_ = httpServer.Shutdown(context.Background())
}()
go func() {
err := s.assetServer.Serve(ctx)
if err != nil && ctx.Err() == nil {
st.Dispatch(store.NewErrorAction(err))
}
}()
go func() {
err := httpServer.ListenAndServe()
if err != nil && err != http.ErrServerClosed && ctx.Err() == nil {
st.Dispatch(store.NewErrorAction(err))
}
}()
}
var _ store.TearDowner = &HeadsUpServerController{}