aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vendor/github.com/google/pprof/internal/driver
diff options
context:
space:
mode:
authorDmitri Shuralyov <dmitshur@golang.org>2024-02-15 23:59:52 -0500
committerGopher Robot <gobot@golang.org>2024-02-16 15:19:53 +0000
commit63dd79c07b0026b58f421a5273c41e705ccb73d1 (patch)
tree78ae03458e1b1f1ea8e3dde24533f6b4eb1af94e /src/cmd/vendor/github.com/google/pprof/internal/driver
parent3b51581261704bfc3e6feeb29b6d2588b163777d (diff)
downloadgo-63dd79c07b0026b58f421a5273c41e705ccb73d1.tar.xz
cmd/pprof: update vendored github.com/google/pprof
Pull in the latest published version of github.com/google/pprof as part of the continuous process of keeping Go's dependencies up to date. Done with: go get github.com/google/pprof go mod tidy go mod vendor For #36905. Fixes #65741. Change-Id: Ice7b085c03ff69be97929cbe47bfd91954907529 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Reviewed-on: https://go-review.googlesource.com/c/go/+/564636 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/cmd/vendor/github.com/google/pprof/internal/driver')
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/config.go2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go24
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go106
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.js14
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/html/flamegraph.html103
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html1
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js18
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/svg.go2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go10
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go16
13 files changed, 54 insertions, 248 deletions
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
index c9edf10bb4..f990780d75 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
@@ -247,6 +247,8 @@ var configHelp = map[string]string{
"noinlines": helpText(
"Ignore inlines.",
"Attributes inlined functions to their first out-of-line caller."),
+ "showcolumns": helpText(
+ "Show column numbers at the source code line level."),
}
func helpText(s ...string) string {
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
index 9fcdd459b2..f7d227416e 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
@@ -51,6 +51,7 @@ type config struct {
TagShow string `json:"tagshow,omitempty"`
TagHide string `json:"taghide,omitempty"`
NoInlines bool `json:"noinlines,omitempty"`
+ ShowColumns bool `json:"showcolumns,omitempty"`
// Output granularity
Granularity string `json:"granularity,omitempty"`
@@ -157,6 +158,7 @@ func init() {
"sort": "sort",
"granularity": "g",
"noinlines": "noinlines",
+ "showcolumns": "showcolumns",
}
def := defaultConfig()
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
index 27681c540f..74ce8cb422 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
@@ -256,7 +256,7 @@ func aggregate(prof *profile.Profile, cfg config) error {
default:
return fmt.Errorf("unexpected granularity")
}
- return prof.Aggregate(inlines, function, filename, linenumber, address)
+ return prof.Aggregate(inlines, function, filename, linenumber, cfg.ShowColumns, address)
}
func reportOptions(p *profile.Profile, numLabelUnits map[string]string, cfg config) (*report.Options, error) {
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go
index 584c5d85e0..95204a394f 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go
@@ -492,17 +492,23 @@ mapping:
func fetch(source string, duration, timeout time.Duration, ui plugin.UI, tr http.RoundTripper) (p *profile.Profile, src string, err error) {
var f io.ReadCloser
- if sourceURL, timeout := adjustURL(source, duration, timeout); sourceURL != "" {
- ui.Print("Fetching profile over HTTP from " + sourceURL)
- if duration > 0 {
- ui.Print(fmt.Sprintf("Please wait... (%v)", duration))
+ // First determine whether the source is a file, if not, it will be treated as a URL.
+ if _, openErr := os.Stat(source); openErr == nil {
+ if isPerfFile(source) {
+ f, err = convertPerfData(source, ui)
+ } else {
+ f, err = os.Open(source)
}
- f, err = fetchURL(sourceURL, timeout, tr)
- src = sourceURL
- } else if isPerfFile(source) {
- f, err = convertPerfData(source, ui)
} else {
- f, err = os.Open(source)
+ sourceURL, timeout := adjustURL(source, duration, timeout)
+ if sourceURL != "" {
+ ui.Print("Fetching profile over HTTP from " + sourceURL)
+ if duration > 0 {
+ ui.Print(fmt.Sprintf("Please wait... (%v)", duration))
+ }
+ f, err = fetchURL(sourceURL, timeout, tr)
+ src = sourceURL
+ }
}
if err == nil {
defer f.Close()
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
deleted file mode 100644
index fbeb765dbc..0000000000
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package driver
-
-import (
- "encoding/json"
- "html/template"
- "net/http"
- "strings"
-
- "github.com/google/pprof/internal/graph"
- "github.com/google/pprof/internal/measurement"
- "github.com/google/pprof/internal/report"
-)
-
-type treeNode struct {
- Name string `json:"n"`
- FullName string `json:"f"`
- Cum int64 `json:"v"`
- CumFormat string `json:"l"`
- Percent string `json:"p"`
- Children []*treeNode `json:"c"`
-}
-
-// flamegraph generates a web page containing a flamegraph.
-func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) {
- // Force the call tree so that the graph is a tree.
- // Also do not trim the tree so that the flame graph contains all functions.
- rpt, errList := ui.makeReport(w, req, []string{"svg"}, func(cfg *config) {
- cfg.CallTree = true
- cfg.Trim = false
- })
- if rpt == nil {
- return // error already reported
- }
-
- // Generate dot graph.
- g, config := report.GetDOT(rpt)
- var nodes []*treeNode
- nroots := 0
- rootValue := int64(0)
- nodeArr := []string{}
- nodeMap := map[*graph.Node]*treeNode{}
- // Make all nodes and the map, collect the roots.
- for _, n := range g.Nodes {
- v := n.CumValue()
- fullName := n.Info.PrintableName()
- node := &treeNode{
- Name: graph.ShortenFunctionName(fullName),
- FullName: fullName,
- Cum: v,
- CumFormat: config.FormatValue(v),
- Percent: strings.TrimSpace(measurement.Percentage(v, config.Total)),
- }
- nodes = append(nodes, node)
- if len(n.In) == 0 {
- nodes[nroots], nodes[len(nodes)-1] = nodes[len(nodes)-1], nodes[nroots]
- nroots++
- rootValue += v
- }
- nodeMap[n] = node
- // Get all node names into an array.
- nodeArr = append(nodeArr, n.Info.Name)
- }
- // Populate the child links.
- for _, n := range g.Nodes {
- node := nodeMap[n]
- for child := range n.Out {
- node.Children = append(node.Children, nodeMap[child])
- }
- }
-
- rootNode := &treeNode{
- Name: "root",
- FullName: "root",
- Cum: rootValue,
- CumFormat: config.FormatValue(rootValue),
- Percent: strings.TrimSpace(measurement.Percentage(rootValue, config.Total)),
- Children: nodes[0:nroots],
- }
-
- // JSON marshalling flame graph
- b, err := json.Marshal(rootNode)
- if err != nil {
- http.Error(w, "error serializing flame graph", http.StatusInternalServerError)
- ui.options.UI.PrintErr(err)
- return
- }
-
- ui.render(w, req, "flamegraph", rpt, errList, config.Labels, webArgs{
- FlameGraph: template.JS(b),
- Nodes: nodeArr,
- })
-}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.js b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.js
index ff980f66de..4a2067eb68 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.js
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.js
@@ -558,11 +558,6 @@ function viewer(baseUrl, nodes, options) {
return null;
}
- // convert a string to a regexp that matches that string.
- function quotemeta(str) {
- return str.replace(/([\\\.?+*\[\](){}|^$])/g, '\\$1');
- }
-
function setSampleIndexLink(si) {
const elem = document.getElementById('sampletype-' + si);
if (elem != null) {
@@ -595,7 +590,7 @@ function viewer(baseUrl, nodes, options) {
// list-based. Construct regular expression depending on mode.
let re = regexpActive
? search.value
- : Array.from(getSelection().keys()).map(key => quotemeta(nodes[key])).join('|');
+ : Array.from(getSelection().keys()).map(key => pprofQuoteMeta(nodes[key])).join('|');
setHrefParams(elem, function (params) {
if (re != '') {
@@ -683,7 +678,7 @@ function viewer(baseUrl, nodes, options) {
}
const ids = ['topbtn', 'graphbtn',
- 'flamegraph', 'flamegraph2', 'flamegraphold',
+ 'flamegraph',
'peek', 'list',
'disasm', 'focus', 'ignore', 'hide', 'show', 'show-from'];
ids.forEach(makeSearchLinkDynamic);
@@ -712,3 +707,8 @@ function viewer(baseUrl, nodes, options) {
main.focus();
}
}
+
+// convert a string to a regexp that matches exactly that string.
+function pprofQuoteMeta(str) {
+ return '^' + str.replace(/([\\\.?+*\[\](){}|^$])/g, '\\$1') + '$';
+}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/flamegraph.html b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/flamegraph.html
deleted file mode 100644
index 9866755bcd..0000000000
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/flamegraph.html
+++ /dev/null
@@ -1,103 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <meta charset="utf-8">
- <title>{{.Title}}</title>
- {{template "css" .}}
- <style type="text/css">{{template "d3flamegraphcss" .}}</style>
- <style type="text/css">
- .flamegraph-content {
- width: 90%;
- min-width: 80%;
- margin-left: 5%;
- }
- .flamegraph-details {
- height: 1.2em;
- width: 90%;
- min-width: 90%;
- margin-left: 5%;
- padding: 15px 0 35px;
- }
- </style>
-</head>
-<body>
- {{template "header" .}}
- <div id="bodycontainer">
- <div id="flamegraphdetails" class="flamegraph-details"></div>
- <div class="flamegraph-content">
- <div id="chart"></div>
- </div>
- </div>
- {{template "script" .}}
- <script>viewer(new URL(window.location.href), {{.Nodes}});</script>
- <script>{{template "d3flamegraphscript" .}}</script>
- <script>
- {{- /* Deserialize as JSON instead of a JS object literal because the browser's
- JSON parser can handle larger payloads than its JS parser. */ -}}
- var data = JSON.parse("{{.FlameGraph}}");
-
- var width = document.getElementById('chart').clientWidth;
-
- var flameGraph = flamegraph()
- .width(width)
- .cellHeight(18)
- .minFrameSize(1)
- .transitionDuration(750)
- .inverted(true)
- .sort(true)
- .title('')
- .tooltip(false)
- .setDetailsElement(document.getElementById('flamegraphdetails'));
-
- // <full name> (percentage, value)
- flameGraph.label((d) => d.data.f + ' (' + d.data.p + ', ' + d.data.l + ')');
-
- flameGraph.setColorHue('warm');
-
- select('#chart')
- .datum(data)
- .call(flameGraph);
-
- function clear() {
- flameGraph.clear();
- }
-
- function resetZoom() {
- flameGraph.resetZoom();
- }
-
- window.addEventListener('resize', function() {
- var width = document.getElementById('chart').clientWidth;
- var graphs = document.getElementsByClassName('d3-flame-graph');
- if (graphs.length > 0) {
- graphs[0].setAttribute('width', width);
- }
- flameGraph.width(width);
- flameGraph.resetZoom();
- }, true);
-
- var search = document.getElementById('search');
- var searchAlarm = null;
-
- function selectMatching() {
- searchAlarm = null;
-
- if (search.value != '') {
- flameGraph.search(search.value);
- } else {
- flameGraph.clear();
- }
- }
-
- function handleSearch() {
- // Delay expensive processing so a flurry of key strokes is handled once.
- if (searchAlarm != null) {
- clearTimeout(searchAlarm);
- }
- searchAlarm = setTimeout(selectMatching, 300);
- }
-
- search.addEventListener('input', handleSearch);
- </script>
-</body>
-</html>
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html
index 42cb7960e6..e946e6b882 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html
@@ -12,7 +12,6 @@
<a title="{{.Help.top}}" href="./top" id="topbtn">Top</a>
<a title="{{.Help.graph}}" href="./" id="graphbtn">Graph</a>
<a title="{{.Help.flamegraph}}" href="./flamegraph" id="flamegraph">Flame Graph</a>
- <a title="{{.Help.flamegraphold}}" href="./flamegraphold" id="flamegraphold">Flame Graph (old)</a>
<a title="{{.Help.peek}}" href="./peek" id="peek">Peek</a>
<a title="{{.Help.list}}" href="./source" id="list">Source</a>
<a title="{{.Help.disasm}}" href="./disasm" id="disasm">Disassemble</a>
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js
index be78edd553..c8059fe6bf 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js
@@ -75,8 +75,12 @@ function stackViewer(stacks, nodes) {
hiliter: (n, on) => { return hilite(n, on); },
current: () => {
let r = new Map();
- for (let p of pivots) {
- r.set(p, true);
+ if (pivots.length == 1 && pivots[0] == 0) {
+ // Not pivoting
+ } else {
+ for (let p of pivots) {
+ r.set(p, true);
+ }
}
return r;
}});
@@ -145,7 +149,7 @@ function stackViewer(stacks, nodes) {
}
// Update params to include src.
- let v = stacks.Sources[src].RE;
+ let v = pprofQuoteMeta(stacks.Sources[src].FullName);
if (param != 'f' && param != 'sf') { // old f,sf values are overwritten
// Add new source to current parameter value.
const old = params.get(param);
@@ -174,7 +178,11 @@ function stackViewer(stacks, nodes) {
function switchPivots(regexp) {
// Switch URL without hitting the server.
const url = new URL(document.URL);
- url.searchParams.set('p', regexp);
+ if (regexp === '' || regexp === '^$') {
+ url.searchParams.delete('p'); // Not pivoting
+ } else {
+ url.searchParams.set('p', regexp);
+ }
history.pushState('', '', url.toString()); // Makes back-button work
matches = new Set();
search.value = '';
@@ -445,7 +453,7 @@ function stackViewer(stacks, nodes) {
r.appendChild(t);
}
- r.addEventListener('click', () => { switchPivots(src.RE); });
+ r.addEventListener('click', () => { switchPivots(pprofQuoteMeta(src.UniqueName)); });
r.addEventListener('mouseenter', () => { handleEnter(box, r); });
r.addEventListener('mouseleave', () => { handleLeave(box); });
r.addEventListener('contextmenu', (e) => { showActionMenu(e, box); });
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go
index 249dfe0742..6a61613344 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go
@@ -22,7 +22,7 @@ import (
"github.com/google/pprof/internal/report"
)
-// stackView generates the new flamegraph view.
+// stackView generates the flamegraph view.
func (ui *webInterface) stackView(w http.ResponseWriter, req *http.Request) {
// Get all data in a report.
rpt, errList := ui.makeReport(w, req, []string{"svg"}, func(cfg *config) {
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/svg.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/svg.go
index 62767e726d..9cbef4d787 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/svg.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/svg.go
@@ -65,7 +65,7 @@ func massageSVG(svg string) string {
if loc := graphID.FindStringIndex(svg); loc != nil {
svg = svg[:loc[0]] +
- `<script type="text/ecmascript"><![CDATA[` + string(svgpan.JSSource) + `]]></script>` +
+ `<script type="text/ecmascript"><![CDATA[` + svgpan.JSSource + `]]></script>` +
`<g id="viewport" transform="scale(0.5,0.5) translate(0,0)">` +
svg[loc[0]:]
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
index 55973ffb9f..984936a9d6 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
@@ -19,8 +19,6 @@ import (
"fmt"
"html/template"
"os"
-
- "github.com/google/pprof/third_party/d3flamegraph"
)
//go:embed html
@@ -52,11 +50,7 @@ func addTemplates(templates *template.Template) {
template.Must(templates.AddParseTree(name, sub.Tree))
}
- // Pre-packaged third-party files.
- def("d3flamegraphscript", d3flamegraph.JSSource)
- def("d3flamegraphcss", d3flamegraph.CSSSource)
-
- // Embeded files.
+ // Embedded files.
def("css", loadCSS("html/common.css"))
def("header", loadFile("html/header.html"))
def("graph", loadFile("html/graph.html"))
@@ -64,7 +58,7 @@ func addTemplates(templates *template.Template) {
def("top", loadFile("html/top.html"))
def("sourcelisting", loadFile("html/source.html"))
def("plaintext", loadFile("html/plaintext.html"))
- def("flamegraph", loadFile("html/flamegraph.html"))
+ // TODO: Rename "stacks" to "flamegraph" to seal moving off d3 flamegraph.
def("stacks", loadFile("html/stacks.html"))
def("stacks_css", loadCSS("html/stacks.css"))
def("stacks_js", loadJS("html/stacks.js"))
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
index 41b30021f5..476e1d2cdf 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
@@ -112,7 +112,6 @@ func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Options, d
ui.help["details"] = "Show information about the profile and this view"
ui.help["graph"] = "Display profile as a directed graph"
ui.help["flamegraph"] = "Display profile as a flame graph"
- ui.help["flamegraphold"] = "Display profile as a flame graph (old version; slated for removal)"
ui.help["reset"] = "Show the entire profile"
ui.help["save_config"] = "Save current settings"
@@ -130,9 +129,9 @@ func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Options, d
"/disasm": http.HandlerFunc(ui.disasm),
"/source": http.HandlerFunc(ui.source),
"/peek": http.HandlerFunc(ui.peek),
- "/flamegraphold": http.HandlerFunc(ui.flamegraph),
"/flamegraph": http.HandlerFunc(ui.stackView),
- "/flamegraph2": http.HandlerFunc(ui.stackView), // Support older URL
+ "/flamegraph2": redirectWithQuery("flamegraph", http.StatusMovedPermanently), // Keep legacy URL working.
+ "/flamegraphold": redirectWithQuery("flamegraph", http.StatusMovedPermanently), // Keep legacy URL working.
"/saveconfig": http.HandlerFunc(ui.saveConfig),
"/deleteconfig": http.HandlerFunc(ui.deleteConfig),
"/download": http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
@@ -209,15 +208,20 @@ func defaultWebServer(args *plugin.HTTPServerArgs) error {
// https://github.com/google/pprof/pull/348
mux := http.NewServeMux()
mux.Handle("/ui/", http.StripPrefix("/ui", handler))
- mux.Handle("/", redirectWithQuery("/ui"))
+ mux.Handle("/", redirectWithQuery("/ui", http.StatusTemporaryRedirect))
s := &http.Server{Handler: mux}
return s.Serve(ln)
}
-func redirectWithQuery(path string) http.HandlerFunc {
+// redirectWithQuery responds with a given redirect code, preserving query
+// parameters in the redirect URL. It does not convert relative paths to
+// absolute paths like http.Redirect does, so that HTTPServerArgs.Handlers can
+// generate relative redirects that work with the external prefixing.
+func redirectWithQuery(path string, code int) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
pathWithQuery := &gourl.URL{Path: path, RawQuery: r.URL.RawQuery}
- http.Redirect(w, r, pathWithQuery.String(), http.StatusTemporaryRedirect)
+ w.Header().Set("Location", pathWithQuery.String())
+ w.WriteHeader(code)
}
}