gopls/internal/golang: view pkg doc: display when "disconnected"

This CL causes the "View package doc" pages to display a
banner when the gopls server dies, at which point the page
is no longer operative. (A new server will have a different
port.)

Change-Id: Icde10a7b4c25a28177d043c573dfdab4ce7c7964
Reviewed-on: https://go-review.googlesource.com/c/tools/+/575536
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gopls/internal/golang/pkgdoc.go b/gopls/internal/golang/pkgdoc.go
index 5596661..ff76e4a 100644
--- a/gopls/internal/golang/pkgdoc.go
+++ b/gopls/internal/golang/pkgdoc.go
@@ -19,9 +19,7 @@
 // - add option for doc.AllDecls: show non-exported symbols too.
 // - abbreviate long signatures by replacing parameters 4 onwards with "...".
 // - style the <li> bullets in the index as invisible.
-// - add push notifications (using hanging GET, server-side events,
-//   or polling) like didChange (=> auto reload)
-//   and server death (=> display "server disconnected" banner).
+// - add push notifications such as didChange -> reload.
 
 import (
 	"bytes"
@@ -131,9 +129,21 @@
 	xhttp.send();
 	return false; // disable usual <a href=...> behavior
 }
+
+// Start a GET /hang request. If it ever completes, the server
+// has disconnected. Show a banner in that case.
+{
+	var x = new XMLHttpRequest();
+	x.open("GET", "/hang", true);
+	x.onloadend = () => {
+		document.getElementById("disconnected").style.display = 'block';
+	};
+	x.send();
+};
   </script>
 </head>
 <body>
+<div id='disconnected'>Gopls server has terminated. Page is inactive.</div>
 `)
 
 	escape := html.EscapeString
@@ -541,4 +551,14 @@
 .lit { color: darkgreen; }
 
 #pkgsite { height: 1.5em; }
+
+#disconnected {
+  position: fixed;
+  top: 1em;
+  left: 1em;
+  display: none; /* initially */
+  background-color: white;
+  border: thick solid red;
+  padding: 2em;
+}
 `
diff --git a/gopls/internal/server/server.go b/gopls/internal/server/server.go
index 5639bab..ae670e7 100644
--- a/gopls/internal/server/server.go
+++ b/gopls/internal/server/server.go
@@ -254,11 +254,16 @@
 	rootMux.HandleFunc("/favicon.ico", func(w http.ResponseWriter, req *http.Request) {
 		http.Redirect(w, req, "/assets/favicon.ico", http.StatusMovedPermanently)
 	})
+	rootMux.HandleFunc("/hang", func(w http.ResponseWriter, req *http.Request) {
+		// This endpoint hangs until cancelled.
+		// It is used by JS to detect server disconnect.
+		<-req.Context().Done()
+	})
+	rootMux.Handle("/assets/", http.FileServer(http.FS(assets)))
 
 	secret := "/gopls/" + base64.RawURLEncoding.EncodeToString(token)
 	webMux := http.NewServeMux()
 	rootMux.Handle(secret+"/", http.StripPrefix(secret, webMux))
-	rootMux.Handle("/assets/", http.FileServer(http.FS(assets)))
 
 	webServer := &http.Server{Addr: listener.Addr().String(), Handler: rootMux}
 	go func() {