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  )