_content/tour: fix links, fix CSP policy for tour

Some links were missing the /tour/ at the front.

The tour also has links like <a href="javascript:next()">, which require
us to set unsafe-inline (or else enumerate the SHA256 of every link)
in the CSP header.

Also make the local server a bit more tour-friendly.

Fixes golang/go#49880.

Change-Id: Ice746571db1a34e1c02b4b2a16c4e9f30dc164d1
Reviewed-on: https://go-review.googlesource.com/c/website/+/368034
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
Website-Publish: Russ Cox <rsc@golang.org>
diff --git a/_content/tour/basics.article b/_content/tour/basics.article
index 082a6ab..34a9b55 100644
--- a/_content/tour/basics.article
+++ b/_content/tour/basics.article
@@ -18,7 +18,7 @@
 #appengine: deterministic, so each time you run the example program
 #appengine: `rand.Intn` will return the same number.
 #appengine:
-#appengine: (To see a different number, seed the number generator; see [[https://golang.org/pkg/math/rand/#Seed][`rand.Seed`]].
+#appengine: (To see a different number, seed the number generator; see [[/pkg/math/rand/#Seed][`rand.Seed`]].
 #appengine: Time is constant in the playground, so you will need to use something else as the seed.)
 
 .play basics/packages.go
@@ -232,4 +232,4 @@
 
 You finished this lesson!
 
-You can go back to the list of [[/list][modules]] to find what to learn next, or continue with the [[javascript:click('.next-page')][next lesson]].
+You can go back to the list of [[/tour/list][modules]] to find what to learn next, or continue with the [[javascript:click('.next-page')][next lesson]].
diff --git a/_content/tour/concurrency.article b/_content/tour/concurrency.article
index 8d50e62..83efbbe 100644
--- a/_content/tour/concurrency.article
+++ b/_content/tour/concurrency.article
@@ -16,7 +16,7 @@
 
 The evaluation of `f`, `x`, `y`, and `z` happens in the current goroutine and the execution of `f` happens in the new goroutine.
 
-Goroutines run in the same address space, so access to shared memory must be synchronized. The [[https://golang.org/pkg/sync/][`sync`]] package provides useful primitives, although you won't need them much in Go as there are other primitives. (See the next slide.)
+Goroutines run in the same address space, so access to shared memory must be synchronized. The [[/pkg/sync/][`sync`]] package provides useful primitives, although you won't need them much in Go as there are other primitives. (See the next slide.)
 
 .play concurrency/goroutines.go
 
@@ -145,7 +145,7 @@
 This concept is called _mutual_exclusion_, and the conventional name for the data structure that provides it is _mutex_.
 
 Go's standard library provides mutual exclusion with
-[[https://golang.org/pkg/sync/#Mutex][`sync.Mutex`]] and its two methods:
+[[/pkg/sync/#Mutex][`sync.Mutex`]] and its two methods:
 
 - `Lock`
 - `Unlock`
@@ -171,18 +171,18 @@
 * Where to Go from here...
 
 #appengine: You can get started by
-#appengine: [[https://golang.org/dl/][installing Go]].
+#appengine: [[/dl/][installing Go]].
 
 #appengine: Once you have Go installed, the
 The
-[[https://golang.org/doc/][Go Documentation]] is a great place to
+[[/doc/][Go Documentation]] is a great place to
 #appengine: continue.
 start.
 It contains references, tutorials, videos, and more.
 
-To learn how to organize and work with Go code, read [[https://golang.org/doc/code][How to Write Go Code]].
+To learn how to organize and work with Go code, read [[/doc/code][How to Write Go Code]].
 
-If you need help with the standard library, see the [[https://golang.org/pkg/][package reference]]. For help with the language itself, you might be surprised to find the [[https://golang.org/ref/spec][Language Spec]] is quite readable.
+If you need help with the standard library, see the [[/pkg/][package reference]]. For help with the language itself, you might be surprised to find the [[/ref/spec][Language Spec]] is quite readable.
 
 To further explore Go's concurrency model, watch
 [[https://www.youtube.com/watch?v=f6kdp27TYZs][Go Concurrency Patterns]]
@@ -191,18 +191,18 @@
 [[https://www.youtube.com/watch?v=QDDwwePbDtw][Advanced Go Concurrency Patterns]]
 ([[https://go.dev/talks/2013/advconc.slide][slides]])
 and read the
-[[https://golang.org/doc/codewalk/sharemem/][Share Memory by Communicating]]
+[[/doc/codewalk/sharemem/][Share Memory by Communicating]]
 codewalk.
 
 To get started writing web applications, watch
 [[https://vimeo.com/53221558][A simple programming environment]]
 ([[https://go.dev/talks/2012/simple.slide][slides]])
 and read the
-[[https://golang.org/doc/articles/wiki/][Writing Web Applications]] tutorial.
+[[/doc/articles/wiki/][Writing Web Applications]] tutorial.
 
-The [[https://golang.org/doc/codewalk/functions/][First Class Functions in Go]] codewalk gives an interesting perspective on Go's function types.
+The [[/doc/codewalk/functions/][First Class Functions in Go]] codewalk gives an interesting perspective on Go's function types.
 
-The [[https://blog.golang.org/][Go Blog]] has a large archive of informative Go articles.
+The [[/blog/][Go Blog]] has a large archive of informative Go articles.
 
-Visit [[https://golang.org][golang.org]] for more.
+Visit [[/][the Go home page]] for more.
 
diff --git a/_content/tour/flowcontrol.article b/_content/tour/flowcontrol.article
index 0f25ab4..448cb76 100644
--- a/_content/tour/flowcontrol.article
+++ b/_content/tour/flowcontrol.article
@@ -100,7 +100,7 @@
 changing (or only changes by a very small amount).
 See if that's more or fewer than 10 iterations.
 Try other initial guesses for z, like x, or x/2.
-How close are your function's results to the [[https://golang.org/pkg/math/#Sqrt][math.Sqrt]] in the standard library?
+How close are your function's results to the [[/pkg/math/#Sqrt][math.Sqrt]] in the standard library?
 
 (*Note:* If you are interested in the details of the algorithm, the z² − x above
 is how far away z² is from where it needs to be (x), and the division by 2z is the derivative
@@ -167,7 +167,7 @@
 deferred calls are executed in last-in-first-out order.
 
 To learn more about defer statements read this
-[[https://blog.golang.org/defer-panic-and-recover][blog post]].
+[[/blog/defer-panic-and-recover][blog post]].
 
 .play flowcontrol/defer-multi.go
 
@@ -175,4 +175,4 @@
 
 You finished this lesson!
 
-You can go back to the list of [[/list][modules]] to find what to learn next, or continue with the [[javascript:click('.next-page')][next lesson]].
+You can go back to the list of [[/tour/list][modules]] to find what to learn next, or continue with the [[javascript:click('.next-page')][next lesson]].
diff --git a/_content/tour/methods.article b/_content/tour/methods.article
index 4a956c2..610ce49 100644
--- a/_content/tour/methods.article
+++ b/_content/tour/methods.article
@@ -269,7 +269,7 @@
 
 * Stringers
 
-One of the most ubiquitous interfaces is [[//golang.org/pkg/fmt/#Stringer][`Stringer`]] defined by the [[//golang.org/pkg/fmt/][`fmt`]] package.
+One of the most ubiquitous interfaces is [[/pkg/fmt/#Stringer][`Stringer`]] defined by the [[/pkg/fmt/][`fmt`]] package.
 
 	type Stringer interface {
 		String() string
@@ -318,7 +318,7 @@
 
 * Exercise: Errors
 
-Copy your `Sqrt` function from the [[/flowcontrol/8][earlier exercise]] and modify it to return an `error` value.
+Copy your `Sqrt` function from the [[/tour/flowcontrol/8][earlier exercise]] and modify it to return an `error` value.
 
 `Sqrt` should return a non-nil error value when given a negative number, as it doesn't support complex numbers.
 
@@ -354,7 +354,7 @@
 ends.
 
 The example code creates a
-[[//golang.org/pkg/strings/#Reader][`strings.Reader`]]
+[[/pkg/strings/#Reader][`strings.Reader`]]
 and consumes its output 8 bytes at a time.
 
 .play methods/reader.go
@@ -368,9 +368,9 @@
 
 * Exercise: rot13Reader
 
-A common pattern is an [[https://golang.org/pkg/io/#Reader][io.Reader]] that wraps another `io.Reader`, modifying the stream in some way.
+A common pattern is an [[/pkg/io/#Reader][io.Reader]] that wraps another `io.Reader`, modifying the stream in some way.
 
-For example, the [[https://golang.org/pkg/compress/gzip/#NewReader][gzip.NewReader]] function takes an `io.Reader` (a stream of compressed data) and returns a `*gzip.Reader` that also implements `io.Reader` (a stream of the decompressed data).
+For example, the [[/pkg/compress/gzip/#NewReader][gzip.NewReader]] function takes an `io.Reader` (a stream of compressed data) and returns a `*gzip.Reader` that also implements `io.Reader` (a stream of the decompressed data).
 
 Implement a `rot13Reader` that implements `io.Reader` and reads from an `io.Reader`, modifying the stream by applying the [[https://en.wikipedia.org/wiki/ROT13][rot13]] substitution cipher to all alphabetical characters.
 
@@ -381,7 +381,7 @@
 
 * Images
 
-[[https://golang.org/pkg/image/#Image][Package image]] defines the `Image` interface:
+[[/pkg/image/#Image][Package image]] defines the `Image` interface:
 
 	package image
 
@@ -392,20 +392,20 @@
 	}
 
 *Note*: the `Rectangle` return value of the `Bounds` method is actually an
-[[https://golang.org/pkg/image/#Rectangle][`image.Rectangle`]], as the
+[[/pkg/image/#Rectangle][`image.Rectangle`]], as the
 declaration is inside package `image`.
 
-(See [[https://golang.org/pkg/image/#Image][the documentation]] for all the details.)
+(See [[/pkg/image/#Image][the documentation]] for all the details.)
 
-The `color.Color` and `color.Model` types are also interfaces, but we'll ignore that by using the predefined implementations `color.RGBA` and `color.RGBAModel`. These interfaces and types are specified by the [[https://golang.org/pkg/image/color/][image/color package]]
+The `color.Color` and `color.Model` types are also interfaces, but we'll ignore that by using the predefined implementations `color.RGBA` and `color.RGBAModel`. These interfaces and types are specified by the [[/pkg/image/color/][image/color package]]
 
 .play methods/images.go
 
 * Exercise: Images
 
-Remember the [[/moretypes/18][picture generator]] you wrote earlier? Let's write another one, but this time it will return an implementation of `image.Image` instead of a slice of data.
+Remember the [[/tour/moretypes/18][picture generator]] you wrote earlier? Let's write another one, but this time it will return an implementation of `image.Image` instead of a slice of data.
 
-Define your own `Image` type, implement [[https://golang.org/pkg/image/#Image][the necessary methods]], and call `pic.ShowImage`.
+Define your own `Image` type, implement [[/pkg/image/#Image][the necessary methods]], and call `pic.ShowImage`.
 
 `Bounds` should return a `image.Rectangle`, like `image.Rect(0,`0,`w,`h)`.
 
@@ -419,4 +419,4 @@
 
 You finished this lesson!
 
-You can go back to the list of [[/list][modules]] to find what to learn next, or continue with the [[javascript:click('.next-page')][next lesson]].
+You can go back to the list of [[/tour/list][modules]] to find what to learn next, or continue with the [[javascript:click('.next-page')][next lesson]].
diff --git a/_content/tour/moretypes.article b/_content/tour/moretypes.article
index 5fe9401..be118a2 100644
--- a/_content/tour/moretypes.article
+++ b/_content/tour/moretypes.article
@@ -214,7 +214,7 @@
 * Appending to a slice
 
 It is common to append new elements to a slice, and so Go provides a built-in
-`append` function. The [[https://golang.org/pkg/builtin/#append][documentation]]
+`append` function. The [[/pkg/builtin/#append][documentation]]
 of the built-in package describes `append`.
 
 	func append(s []T, vs ...T) []T
@@ -229,7 +229,7 @@
 array will be allocated. The returned slice will point to the newly allocated
 array.
 
-(To learn more about slices, read the [[https://blog.golang.org/go-slices-usage-and-internals][Slices: usage and internals]] article.)
+(To learn more about slices, read the [[/blog/go-slices-usage-and-internals][Slices: usage and internals]] article.)
 
 .play moretypes/append.go
 
@@ -324,7 +324,7 @@
 
 Implement `WordCount`.  It should return a map of the counts of each “word” in the string `s`. The `wc.Test` function runs a test suite against the provided function and prints success or failure.
 
-You might find [[https://golang.org/pkg/strings/#Fields][strings.Fields]] helpful.
+You might find [[/pkg/strings/#Fields][strings.Fields]] helpful.
 
 .play moretypes/exercise-maps.go
 
@@ -358,4 +358,4 @@
 
 You finished this lesson!
 
-You can go back to the list of [[/list][modules]] to find what to learn next, or continue with the [[javascript:click('.next-page')][next lesson]].
+You can go back to the list of [[/tour/list][modules]] to find what to learn next, or continue with the [[javascript:click('.next-page')][next lesson]].
diff --git a/_content/tour/welcome.article b/_content/tour/welcome.article
index d9cfd88..22a38ad 100644
--- a/_content/tour/welcome.article
+++ b/_content/tour/welcome.article
@@ -6,7 +6,7 @@
 
 * Hello, 世界
 
-Welcome to a tour of the [[https://golang.org/][Go programming language]].
+Welcome to a tour of the [[/][Go programming language]].
 
 The tour is divided into a list of modules that you can
 access by clicking on
@@ -36,7 +36,7 @@
 
 When you click on [[javascript:highlightAndClick("#format")][Format]]
 (shortcut: `Ctrl` + `Enter`), the text in the editor is formatted using the
-[[https://golang.org/cmd/gofmt/][gofmt]] tool. You can switch syntax highlighting on and off
+[[/cmd/gofmt/][gofmt]] tool. You can switch syntax highlighting on and off
 by clicking on the [[javascript:highlightAndClick(".syntax-checkbox")][syntax]] button.
 
 When you're ready to move on, click the [[javascript:highlightAndClick(".next-page")][right arrow]] below or type the `PageDown` key.
@@ -68,12 +68,12 @@
 #appengine: your own machine.
 #appengine:
 #appengine: To run the tour locally, you'll need to first
-#appengine: [[https://golang.org/doc/install][install Go]] and then run:
+#appengine: [[/doc/install][install Go]] and then run:
 #appengine:
 #appengine:   go install golang.org/x/website/tour@latest
 #appengine:
 #appengine: This will place a `tour` binary in your
-#appengine: [[https://golang.org/cmd/go/#hdr-GOPATH_and_Modules][GOPATH]]'s `bin` directory.
+#appengine: [[/cmd/go/#hdr-GOPATH_and_Modules][GOPATH]]'s `bin` directory.
 #appengine: When you run the tour program, it will open a web browser displaying
 #appengine: your local version of the tour.
 #appengine:
@@ -82,7 +82,7 @@
 #appengine: * The Go Playground
 #appengine:
 #appengine: This tour is built atop the [[https://play.golang.org/][Go Playground]], a
-#appengine: web service that runs on [[https://golang.org/][golang.org]]'s servers.
+#appengine: web service that runs on [[/][golang.org]]'s servers.
 #appengine:
 #appengine: The service receives a Go program, compiles, links, and runs the program inside
 #appengine: a sandbox, then returns the output.
@@ -95,7 +95,7 @@
 #appengine:
 #appengine: The playground uses the latest stable release of Go.
 #appengine:
-#appengine: Read "[[https://blog.golang.org/playground][Inside the Go Playground]]" to learn more.
+#appengine: Read "[[/blog/playground][Inside the Go Playground]]" to learn more.
 #appengine:
 #appengine: .play welcome/sandbox.go
 
diff --git a/cmd/golangorg/csp.go b/cmd/golangorg/csp.go
index affed28..e8c0ec1 100644
--- a/cmd/golangorg/csp.go
+++ b/cmd/golangorg/csp.go
@@ -22,12 +22,19 @@
 	for _, k := range ks {
 		sb.WriteString(k)
 		sb.WriteString(" ")
-		sb.WriteString(strings.Join(csp[k], " "))
+		for _, v := range csp[k] {
+			if (kind == "tour" || kind == "talks") && strings.HasPrefix(v, "'sha256-") {
+				// Must drop sha256 entries to use unsafe-inline.
+				continue
+			}
+			sb.WriteString(v)
+			sb.WriteString(" ")
+		}
 		if kind == "tour" && k == "script-src" {
 			sb.WriteString(" ")
 			sb.WriteString(unsafeEval)
 		}
-		if kind == "talks" && k == "script-src" {
+		if (kind == "talks" || kind == "tour") && k == "script-src" {
 			sb.WriteString(" ")
 			sb.WriteString(unsafeInline)
 		}
diff --git a/cmd/golangorg/server.go b/cmd/golangorg/server.go
index eda65ef..1d0f3e4 100644
--- a/cmd/golangorg/server.go
+++ b/cmd/golangorg/server.go
@@ -443,7 +443,13 @@
 
 		elem, rest := strings.TrimPrefix(r.URL.Path, "/"), ""
 		if i := strings.Index(elem, "/"); i >= 0 {
-			elem, rest = elem[:i], elem[i+1:]
+			if elem[:i] == "tour" {
+				// The Angular router serving /tour/ fails badly when it sees /go.dev/tour/.
+				// Just take http://localhost/tour/ as meaning /go.dev/tour/ instead of redirecting.
+				elem, rest = "go.dev", elem
+			} else {
+				elem, rest = elem[:i], elem[i+1:]
+			}
 		}
 		if !validHosts[elem] {
 			u := "/go.dev" + r.URL.EscapedPath()
@@ -461,7 +467,7 @@
 
 		log.Print(r.URL.String())
 
-		lw := &linkRewriter{ResponseWriter: w, host: r.Host}
+		lw := &linkRewriter{ResponseWriter: w, host: r.Host, tour: strings.HasPrefix(r.URL.Path, "/tour/")}
 		h.ServeHTTP(lw, r)
 		lw.Flush()
 	})
@@ -474,6 +480,7 @@
 type linkRewriter struct {
 	http.ResponseWriter
 	host string
+	tour bool // is this go.dev/tour/?
 	buf  []byte
 	ct   string // content-type
 }
@@ -481,7 +488,7 @@
 func (r *linkRewriter) WriteHeader(code int) {
 	loc := r.Header().Get("Location")
 	delete(r.Header(), "Content-Length") // we might change the content
-	if strings.HasPrefix(loc, "/") {
+	if strings.HasPrefix(loc, "/") && !strings.HasPrefix(loc, "/tour/") {
 		r.Header().Set("Location", "/"+r.host+loc)
 	} else if u, _ := url.Parse(loc); u != nil && validHosts[u.Host] {
 		r.Header().Set("Location", "/"+u.Host+"/"+strings.TrimPrefix(u.Path, "/")+u.RawQuery)
@@ -506,9 +513,12 @@
 }
 
 func (r *linkRewriter) Flush() {
-	repl := []string{
-		`href="/`, `href="/` + r.host + `/`,
-		`src="/`, `src="/` + r.host + `/`,
+	var repl []string
+	if !r.tour {
+		repl = []string{
+			`href="/`, `href="/` + r.host + `/`,
+			`src="/`, `src="/` + r.host + `/`,
+		}
 	}
 	for host := range validHosts {
 		repl = append(repl, `href="https://`+host, `href="/`+host)