From c937063ca31ad4d43debd0e2ecd9293a09a34377 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 30 Nov 2021 16:16:35 -0500 Subject: [PATCH] _content/tour: fix links, fix CSP policy for tour Some links were missing the /tour/ at the front. The tour also has links like , 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 Run-TryBot: Russ Cox TryBot-Result: Go Bot Reviewed-by: Jamal Carvalho Website-Publish: Russ Cox --- _content/tour/basics.article | 4 ++-- _content/tour/concurrency.article | 22 +++++++++++----------- _content/tour/flowcontrol.article | 6 +++--- _content/tour/methods.article | 24 ++++++++++++------------ _content/tour/moretypes.article | 8 ++++---- _content/tour/welcome.article | 12 ++++++------ cmd/golangorg/csp.go | 11 +++++++++-- cmd/golangorg/server.go | 22 ++++++++++++++++------ 8 files changed, 63 insertions(+), 46 deletions(-) diff --git a/_content/tour/basics.article b/_content/tour/basics.article index 082a6ab2c4..34a9b55095 100644 --- a/_content/tour/basics.article +++ b/_content/tour/basics.article @@ -18,7 +18,7 @@ By convention, the package name is the same as the last element of the import pa #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 @@ Try printing `needInt(Big)` too. 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 8d50e62f44..83efbbe2a0 100644 --- a/_content/tour/concurrency.article +++ b/_content/tour/concurrency.article @@ -16,7 +16,7 @@ starts a new goroutine running 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 @@ one goroutine can access a variable at a time to avoid conflicts? 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 @@ safe for concurrent use! * 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 @@ and [[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 0f25ab40bb..448cb76765 100644 --- a/_content/tour/flowcontrol.article +++ b/_content/tour/flowcontrol.article @@ -100,7 +100,7 @@ Next, change the loop condition to stop once the value has stopped 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 function calls are pushed onto a stack. When a function returns, its 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 @@ To learn more about defer statements read this 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 4a956c210c..610ce49c23 100644 --- a/_content/tour/methods.article +++ b/_content/tour/methods.article @@ -269,7 +269,7 @@ of the same interface type and value as `i`. * 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 @@ A nil `error` denotes success; a non-nil `error` denotes failure. * 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 @@ populated and an error value. It returns an `io.EOF` error when the stream 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 @@ Implement a `Reader` type that emits an infinite stream of the ASCII character * 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 @@ Make it an `io.Reader` by implementing its `Read` method. * 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 @@ Make it an `io.Reader` by implementing its `Read` method. } *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 @@ Define your own `Image` type, implement [[https://golang.org/pkg/image/#Image][t 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 5fe940142f..be118a2616 100644 --- a/_content/tour/moretypes.article +++ b/_content/tour/moretypes.article @@ -214,7 +214,7 @@ Slices can contain any type, including other slices. * 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 @@ If the backing array of `s` is too small to fit all the given values a bigger 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 @@ If `key` is not in the map, then `elem` is the zero value for the map's element 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 @@ returns successive [[https://en.wikipedia.org/wiki/Fibonacci_number][fibonacci n 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 d9cfd88dc4..22a38adfad 100644 --- a/_content/tour/welcome.article +++ b/_content/tour/welcome.article @@ -6,7 +6,7 @@ https://golang.org * 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 @@ Edit the program and run it again. 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 @@ Click the [[javascript:highlightAndClick(".next-page")]["next"]] button or type #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 @@ Click the [[javascript:highlightAndClick(".next-page")]["next"]] button or type #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 @@ Click the [[javascript:highlightAndClick(".next-page")]["next"]] button or type #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 affed2882a..e8c0ec1cfc 100644 --- a/cmd/golangorg/csp.go +++ b/cmd/golangorg/csp.go @@ -22,12 +22,19 @@ func buildCSP(kind string) string { 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 eda65ef195..1d0f3e4a82 100644 --- a/cmd/golangorg/server.go +++ b/cmd/golangorg/server.go @@ -443,7 +443,13 @@ func hostPathHandler(h http.Handler) http.Handler { 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 @@ func hostPathHandler(h http.Handler) http.Handler { 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 @@ func hostPathHandler(h http.Handler) http.Handler { type linkRewriter struct { http.ResponseWriter host string + tour bool // is this go.dev/tour/? buf []byte ct string // content-type } @@ -481,7 +488,7 @@ type linkRewriter struct { 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) Write(data []byte) (int, error) { } 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)