_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)