github.com/bazelbuild/bazel-watcher@v0.25.2/internal/ibazel/live_reload/server.go (about) 1 // Copyright 2017 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package live_reload 16 17 import ( 18 "bytes" 19 "flag" 20 "fmt" 21 "net" 22 "os" 23 "strconv" 24 25 "github.com/jaschaephraim/lrserver" 26 27 "github.com/bazelbuild/bazel-watcher/internal/ibazel/log" 28 "github.com/bazelbuild/bazel-watcher/third_party/bazel/master/src/main/protobuf/blaze_query" 29 30 golog "log" 31 ) 32 33 var noLiveReload = flag.Bool("nolive_reload", false, "Disable JavaScript live reload support") 34 35 type LiveReloadServer struct { 36 lrserver *lrserver.Server 37 eventListeners []Events 38 } 39 40 func New() *LiveReloadServer { 41 l := &LiveReloadServer{} 42 l.eventListeners = []Events{} 43 return l 44 } 45 46 func (l *LiveReloadServer) AddEventsListener(listener Events) { 47 l.eventListeners = append(l.eventListeners, listener) 48 } 49 50 func (l *LiveReloadServer) Initialize(info *map[string]string) {} 51 52 func (l *LiveReloadServer) Cleanup() { 53 if l.lrserver != nil { 54 l.lrserver.Close() 55 } 56 } 57 58 func (l *LiveReloadServer) TargetDecider(rule *blaze_query.Rule) { 59 for _, attr := range rule.Attribute { 60 if *attr.Name == "tags" && *attr.Type == blaze_query.Attribute_STRING_LIST { 61 if contains(attr.StringListValue, "ibazel_live_reload") { 62 if *noLiveReload { 63 log.Log("Target requests live_reload but liveReload has been disabled with the -nolive_reload flag.") 64 return 65 } 66 l.startLiveReloadServer() 67 return 68 } 69 } 70 } 71 } 72 73 func (l *LiveReloadServer) ChangeDetected(targets []string, changeType string, change string) { 74 } 75 76 func (l *LiveReloadServer) BeforeCommand(targets []string, command string) {} 77 78 func (l *LiveReloadServer) AfterCommand(targets []string, command string, success bool, output *bytes.Buffer) { 79 l.triggerReload(targets) 80 } 81 82 func (l *LiveReloadServer) ReloadTriggered(targets []string) {} 83 84 func (l *LiveReloadServer) startLiveReloadServer() { 85 if l.lrserver != nil { 86 return 87 } 88 89 port := lrserver.DefaultPort 90 for ; port < lrserver.DefaultPort+100; port++ { 91 if testPort(port) { 92 l.lrserver = lrserver.New("live reload", port) 93 // Live reload server shouldn't log. 94 l.lrserver.SetStatusLog(golog.New(os.Stderr, "", 0)) 95 go func() { 96 err := l.lrserver.ListenAndServe() 97 if err != nil { 98 log.Errorf("Live reload server failed to start: %v", err) 99 } 100 }() 101 url := fmt.Sprintf("http://localhost:%d/livereload.js?snipver=1", port) 102 os.Setenv("IBAZEL_LIVERELOAD_URL", url) 103 return 104 } 105 } 106 log.Errorf("Could not find open port for live reload server") 107 } 108 109 func (l *LiveReloadServer) triggerReload(targets []string) { 110 if l.lrserver != nil { 111 log.Log("Triggering live reload") 112 l.lrserver.Reload("reload") 113 for _, e := range l.eventListeners { 114 e.ReloadTriggered(targets) 115 } 116 } 117 } 118 119 func testPort(port uint16) bool { 120 ln, err := net.Listen("tcp", ":"+strconv.FormatInt(int64(port), 10)) 121 122 if err != nil { 123 log.Logf("Port %d: %v", port, err) 124 return false 125 } 126 127 ln.Close() 128 return true 129 } 130 131 func contains(l []string, e string) bool { 132 for _, i := range l { 133 if i == e { 134 return true 135 } 136 } 137 return false 138 }