github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/livereload/livereload.go (about) 1 // Copyright 2015 The Hugo 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 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 // 14 // Contains an embedded version of livereload.js 15 // 16 // Copyright (c) 2010-2015 Andrey Tarantsov 17 // 18 // Permission is hereby granted, free of charge, to any person obtaining 19 // a copy of this software and associated documentation files (the 20 // "Software"), to deal in the Software without restriction, including 21 // without limitation the rights to use, copy, modify, merge, publish, 22 // distribute, sublicense, and/or sell copies of the Software, and to 23 // permit persons to whom the Software is furnished to do so, subject to 24 // the following conditions: 25 // 26 // The above copyright notice and this permission notice shall be 27 // included in all copies or substantial portions of the Software. 28 // 29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 31 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 32 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 33 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 34 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 35 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 37 package livereload 38 39 import ( 40 "fmt" 41 "net" 42 "net/http" 43 "net/url" 44 "path/filepath" 45 46 _ "embed" 47 48 "github.com/gohugoio/hugo/media" 49 "github.com/gorilla/websocket" 50 ) 51 52 // Prefix to signal to LiveReload that we need to navigate to another path. 53 const hugoNavigatePrefix = "__hugo_navigate" 54 55 var upgrader = &websocket.Upgrader{ 56 // Hugo may potentially spin up multiple HTTP servers, so we need to exclude the 57 // port when checking the origin. 58 CheckOrigin: func(r *http.Request) bool { 59 origin := r.Header["Origin"] 60 if len(origin) == 0 { 61 return true 62 } 63 u, err := url.Parse(origin[0]) 64 if err != nil { 65 return false 66 } 67 68 rHost := r.Host 69 // For Github codespace in browser #9936 70 if forwardedHost := r.Header.Get("X-Forwarded-Host"); forwardedHost != "" { 71 rHost = forwardedHost 72 } 73 74 if u.Host == rHost { 75 return true 76 } 77 78 h1, _, err := net.SplitHostPort(u.Host) 79 if err != nil { 80 return false 81 } 82 h2, _, err := net.SplitHostPort(r.Host) 83 if err != nil { 84 return false 85 } 86 87 return h1 == h2 88 }, 89 ReadBufferSize: 1024, WriteBufferSize: 1024, 90 } 91 92 // Handler is a HandlerFunc handling the livereload 93 // Websocket interaction. 94 func Handler(w http.ResponseWriter, r *http.Request) { 95 ws, err := upgrader.Upgrade(w, r, nil) 96 if err != nil { 97 return 98 } 99 c := &connection{send: make(chan []byte, 256), ws: ws} 100 wsHub.register <- c 101 defer func() { wsHub.unregister <- c }() 102 go c.writer() 103 c.reader() 104 } 105 106 // Initialize starts the Websocket Hub handling live reloads. 107 func Initialize() { 108 go wsHub.run() 109 } 110 111 // ForceRefresh tells livereload to force a hard refresh. 112 func ForceRefresh() { 113 RefreshPath("/x.js") 114 } 115 116 // NavigateToPath tells livereload to navigate to the given path. 117 // This translates to `window.location.href = path` in the client. 118 func NavigateToPath(path string) { 119 RefreshPath(hugoNavigatePrefix + path) 120 } 121 122 // NavigateToPathForPort is similar to NavigateToPath but will also 123 // set window.location.port to the given port value. 124 func NavigateToPathForPort(path string, port int) { 125 refreshPathForPort(hugoNavigatePrefix+path, port) 126 } 127 128 // RefreshPath tells livereload to refresh only the given path. 129 // If that path points to a CSS stylesheet or an image, only the changes 130 // will be updated in the browser, not the entire page. 131 func RefreshPath(s string) { 132 refreshPathForPort(s, -1) 133 } 134 135 func refreshPathForPort(s string, port int) { 136 // Tell livereload a file has changed - will force a hard refresh if not CSS or an image 137 urlPath := filepath.ToSlash(s) 138 portStr := "" 139 if port > 0 { 140 portStr = fmt.Sprintf(`, "overrideURL": %d`, port) 141 } 142 msg := fmt.Sprintf(`{"command":"reload","path":%q,"originalPath":"","liveCSS":true,"liveImg":true%s}`, urlPath, portStr) 143 wsHub.broadcast <- []byte(msg) 144 } 145 146 // ServeJS serves the livereload.js who's reference is injected into the page. 147 func ServeJS(w http.ResponseWriter, r *http.Request) { 148 w.Header().Set("Content-Type", media.Builtin.JavascriptType.Type) 149 w.Write(liveReloadJS()) 150 } 151 152 func liveReloadJS() []byte { 153 return []byte(livereloadJS + hugoLiveReloadPlugin) 154 } 155 156 var ( 157 // This is a patched version, see https://github.com/livereload/livereload-js/pull/84 158 //go:embed livereload.js 159 livereloadJS string 160 hugoLiveReloadPlugin = fmt.Sprintf(` 161 /* 162 Hugo adds a specific prefix, "__hugo_navigate", to the path in certain situations to signal 163 navigation to another content page. 164 */ 165 166 function HugoReload() {} 167 168 HugoReload.identifier = 'hugoReloader'; 169 HugoReload.version = '0.9'; 170 171 HugoReload.prototype.reload = function(path, options) { 172 var prefix = %q; 173 174 if (path.lastIndexOf(prefix, 0) !== 0) { 175 return false 176 } 177 178 path = path.substring(prefix.length); 179 180 var portChanged = options.overrideURL && options.overrideURL != window.location.port 181 182 if (!portChanged && window.location.pathname === path) { 183 window.location.reload(); 184 } else { 185 if (portChanged) { 186 window.location = location.protocol + "//" + location.hostname + ":" + options.overrideURL + path; 187 } else { 188 window.location.pathname = path; 189 } 190 } 191 192 return true; 193 }; 194 195 LiveReload.addPlugin(HugoReload) 196 `, hugoNavigatePrefix) 197 )