diff --git a/blog/blog.go b/blog/blog.go
index 4055b1b7c6..633014d31f 100644
--- a/blog/blog.go
+++ b/blog/blog.go
@@ -6,7 +6,6 @@
package blog // import "golang.org/x/tools/blog"
import (
- "bytes"
"encoding/json"
"encoding/xml"
"fmt"
@@ -24,7 +23,17 @@ import (
"golang.org/x/tools/present"
)
-var validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`)
+var (
+ validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`)
+ // used to serve relative paths when ServeLocalLinks is enabled.
+ // TODO(agnivade): change blog article links to all have https.
+ golangOrgAbsLinkReplacer = strings.NewReplacer(
+ `href="http://golang.org/pkg`, `href="/pkg`,
+ `href="https://golang.org/pkg`, `href="/pkg`,
+ `href="http://golang.org/cmd`, `href="/cmd`,
+ `href="https://golang.org/cmd`, `href="/cmd`,
+ )
+)
// Config specifies Server configuration values.
type Config struct {
@@ -40,7 +49,8 @@ type Config struct {
FeedArticles int // Articles to include in Atom and JSON feeds.
FeedTitle string // The title of the Atom XML feed
- PlayEnabled bool
+ PlayEnabled bool
+ ServeLocalLinks bool // rewrite golang.org/{pkg,cmd} links to host-less, relative paths.
}
// Doc represents an article adorned with presentation data.
@@ -143,7 +153,7 @@ func sectioned(d *present.Doc) bool {
// authors returns a comma-separated list of author names.
func authors(authors []present.Author) string {
- var b bytes.Buffer
+ var b strings.Builder
last := len(authors) - 1
for i, a := range authors {
if i > 0 {
@@ -191,8 +201,8 @@ func (s *Server) loadDocs(root string) error {
if err != nil {
return err
}
- html := new(bytes.Buffer)
- err = d.Render(html, s.template.doc)
+ var html strings.Builder
+ err = d.Render(&html, s.template.doc)
if err != nil {
return err
}
@@ -359,7 +369,7 @@ func summary(d *Doc) string {
// skip everything but non-text elements
continue
}
- var buf bytes.Buffer
+ var buf strings.Builder
for _, s := range text.Lines {
buf.WriteString(string(present.Style(s)))
buf.WriteByte('\n')
@@ -417,7 +427,18 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
d.Doc = doc
t = s.template.article
}
- err := t.ExecuteTemplate(w, "root", d)
+ var err error
+ if s.cfg.ServeLocalLinks {
+ var buf strings.Builder
+ err = t.ExecuteTemplate(&buf, "root", d)
+ if err != nil {
+ log.Println(err)
+ return
+ }
+ _, err = golangOrgAbsLinkReplacer.WriteString(w, buf.String())
+ } else {
+ err = t.ExecuteTemplate(w, "root", d)
+ }
if err != nil {
log.Println(err)
}
diff --git a/blog/blog_test.go b/blog/blog_test.go
new file mode 100644
index 0000000000..6e9f6411ce
--- /dev/null
+++ b/blog/blog_test.go
@@ -0,0 +1,44 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package blog
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestLinkRewrite(t *testing.T) {
+ tests := []struct {
+ input string
+ output string
+ }{
+ {
+ `For instance, the bytes package from the standard library exports the Buffer
type.`,
+ `For instance, the bytes package from the standard library exports the Buffer
type.`},
+ {
+ `(The gofmt command has a -r
flag that provides a syntax-aware search and replace, making large-scale refactoring easier.)`,
+ `(The gofmt command has a -r
flag that provides a syntax-aware search and replace, making large-scale refactoring easier.)`,
+ },
+ {
+ `BSD license.
Terms of Service `,
+ `BSD license.
Terms of Service `,
+ },
+ {
+ `For instance, the websocket
package from the go.net
sub-repository has an import path of "golang.org/x/net/websocket"
.`,
+ `For instance, the websocket
package from the go.net
sub-repository has an import path of "golang.org/x/net/websocket"
.`,
+ },
+ }
+ for _, test := range tests {
+ var buf strings.Builder
+ _, err := golangOrgAbsLinkReplacer.WriteString(&buf, test.input)
+ if err != nil {
+ t.Errorf("unexpected error during replacing links. Got: %#v, Want: nil.\n", err)
+ continue
+ }
+ if got, want := buf.String(), test.output; got != want {
+ t.Errorf("WriteString(%q) = %q. Expected: %q", test.input, got, want)
+ }
+ }
+}
diff --git a/cmd/godoc/blog.go b/cmd/godoc/blog.go
index dec4732fc7..e47b73eb09 100644
--- a/cmd/godoc/blog.go
+++ b/cmd/godoc/blog.go
@@ -34,12 +34,14 @@ var (
func init() {
// Initialize blog only when first accessed.
http.HandleFunc(blogPath, func(w http.ResponseWriter, r *http.Request) {
- blogInitOnce.Do(blogInit)
+ blogInitOnce.Do(func() {
+ blogInit(r.Host)
+ })
blogServer.ServeHTTP(w, r)
})
}
-func blogInit() {
+func blogInit(host string) {
// Binary distributions will include the blog content in "/blog".
root := filepath.Join(runtime.GOROOT(), "blog")
@@ -57,12 +59,13 @@ func blogInit() {
}
s, err := blog.NewServer(blog.Config{
- BaseURL: blogPath,
- BasePath: strings.TrimSuffix(blogPath, "/"),
- ContentPath: filepath.Join(root, "content"),
- TemplatePath: filepath.Join(root, "template"),
- HomeArticles: 5,
- PlayEnabled: playEnabled,
+ BaseURL: blogPath,
+ BasePath: strings.TrimSuffix(blogPath, "/"),
+ ContentPath: filepath.Join(root, "content"),
+ TemplatePath: filepath.Join(root, "template"),
+ HomeArticles: 5,
+ PlayEnabled: playEnabled,
+ ServeLocalLinks: strings.HasPrefix(host, "localhost"),
})
if err != nil {
log.Fatal(err)