Browse Source

Allow serving web ui at arbitrary root (#348)

Previously, the web UI was hard-coded to be served at `/` but
applications which are *embedding* the ui are unlikely to host it there.

This change makes the links between the various ui pages relative,
addressing this problem.

To make sure this doesn't rot, the internal http server now serves the
ui at /ui/ (redirecting there from the root).

I think we can all agree that the ui code and JavaScript is kind of a
haphazard affair. I performed a great deal of clicking around to make
sure the semantics didn't change and interestingly spent some time
trying to fix semantics I later realized weren't actually there: I
thought that when navigating to another page from a focused search (say
`flamegraph?f=x`) the focus would be carried over. This doesn't seem to
be the case, however.

The point being, there's no test coverage and setting it up is way
beyond what I can do here. I'm happy to manually verify whatever
sequence of clicks is suggested, though.

Touches https://github.com/cockroachdb/cockroach/pull/24145.
Tobias Schottdorf 7 years ago
parent
commit
c7bd8844d7
3 changed files with 34 additions and 28 deletions
  1. 1
    1
      internal/driver/flamegraph.go
  2. 18
    18
      internal/driver/webhtml.go
  3. 15
    9
      internal/driver/webui.go

+ 1
- 1
internal/driver/flamegraph.go View File

92
 		return
92
 		return
93
 	}
93
 	}
94
 
94
 
95
-	ui.render(w, "/flamegraph", "flamegraph", rpt, errList, config.Labels, webArgs{
95
+	ui.render(w, "flamegraph", rpt, errList, config.Labels, webArgs{
96
 		FlameGraph: template.JS(b),
96
 		FlameGraph: template.JS(b),
97
 		Nodes:      nodeArr,
97
 		Nodes:      nodeArr,
98
 	})
98
 	})

+ 18
- 18
internal/driver/webhtml.go View File

233
 {{define "header"}}
233
 {{define "header"}}
234
 <div class="header">
234
 <div class="header">
235
   <div class="title">
235
   <div class="title">
236
-    <h1><a href="/">pprof</a></h1>
236
+    <h1><a href="./">pprof</a></h1>
237
   </div>
237
   </div>
238
 
238
 
239
   <div id="view" class="menu-item">
239
   <div id="view" class="menu-item">
242
       <i class="downArrow"></i>
242
       <i class="downArrow"></i>
243
     </div>
243
     </div>
244
     <div class="submenu">
244
     <div class="submenu">
245
-      <a title="{{.Help.top}}"  href="/top" id="topbtn">Top</a>
246
-      <a title="{{.Help.graph}}" href="/" id="graphbtn">Graph</a>
247
-      <a title="{{.Help.flamegraph}}" href="/flamegraph" id="flamegraph">Flame Graph</a>
248
-      <a title="{{.Help.peek}}" href="/peek" id="peek">Peek</a>
249
-      <a title="{{.Help.list}}" href="/source" id="list">Source</a>
250
-      <a title="{{.Help.disasm}}" href="/disasm" id="disasm">Disassemble</a>
245
+      <a title="{{.Help.top}}"  href="./top" id="topbtn">Top</a>
246
+      <a title="{{.Help.graph}}" href="./" id="graphbtn">Graph</a>
247
+      <a title="{{.Help.flamegraph}}" href="./flamegraph" id="flamegraph">Flame Graph</a>
248
+      <a title="{{.Help.peek}}" href="./peek" id="peek">Peek</a>
249
+      <a title="{{.Help.list}}" href="./source" id="list">Source</a>
250
+      <a title="{{.Help.disasm}}" href="./disasm" id="disasm">Disassemble</a>
251
     </div>
251
     </div>
252
   </div>
252
   </div>
253
 
253
 
257
       <i class="downArrow"></i>
257
       <i class="downArrow"></i>
258
     </div>
258
     </div>
259
     <div class="submenu">
259
     <div class="submenu">
260
-      <a title="{{.Help.focus}}" href="{{.BaseURL}}" id="focus">Focus</a>
261
-      <a title="{{.Help.ignore}}" href="{{.BaseURL}}" id="ignore">Ignore</a>
262
-      <a title="{{.Help.hide}}" href="{{.BaseURL}}" id="hide">Hide</a>
263
-      <a title="{{.Help.show}}" href="{{.BaseURL}}" id="show">Show</a>
260
+      <a title="{{.Help.focus}}" href="?" id="focus">Focus</a>
261
+      <a title="{{.Help.ignore}}" href="?" id="ignore">Ignore</a>
262
+      <a title="{{.Help.hide}}" href="?" id="hide">Hide</a>
263
+      <a title="{{.Help.show}}" href="?" id="show">Show</a>
264
       <hr>
264
       <hr>
265
-      <a title="{{.Help.reset}}" href="{{.BaseURL}}">Reset</a>
265
+      <a title="{{.Help.reset}}" href="?">Reset</a>
266
     </div>
266
     </div>
267
   </div>
267
   </div>
268
 
268
 
295
     {{.HTMLBody}}
295
     {{.HTMLBody}}
296
   </div>
296
   </div>
297
   {{template "script" .}}
297
   {{template "script" .}}
298
-  <script>viewer({{.BaseURL}}, {{.Nodes}});</script>
298
+  <script>viewer(new URL(window.location.href), {{.Nodes}});</script>
299
 </body>
299
 </body>
300
 </html>
300
 </html>
301
 {{end}}
301
 {{end}}
597
   function handleKey(e) {
597
   function handleKey(e) {
598
     if (e.keyCode != 13) return;
598
     if (e.keyCode != 13) return;
599
     window.location.href =
599
     window.location.href =
600
-        updateUrl(new URL({{.BaseURL}}, window.location.href), 'f');
600
+        updateUrl(new URL(window.location.href), 'f');
601
     e.preventDefault();
601
     e.preventDefault();
602
   }
602
   }
603
 
603
 
963
       bindSort('namehdr', 'Name');
963
       bindSort('namehdr', 'Name');
964
     }
964
     }
965
 
965
 
966
-    viewer({{.BaseURL}}, {{.Nodes}});
966
+    viewer(new URL(window.location.href), {{.Nodes}});
967
     makeTopTable({{.Total}}, {{.Top}});
967
     makeTopTable({{.Total}}, {{.Top}});
968
   </script>
968
   </script>
969
 </body>
969
 </body>
986
     {{.HTMLBody}}
986
     {{.HTMLBody}}
987
   </div>
987
   </div>
988
   {{template "script" .}}
988
   {{template "script" .}}
989
-  <script>viewer({{.BaseURL}}, null);</script>
989
+  <script>viewer(new URL(window.location.href), null);</script>
990
 </body>
990
 </body>
991
 </html>
991
 </html>
992
 {{end}}
992
 {{end}}
1007
     </pre>
1007
     </pre>
1008
   </div>
1008
   </div>
1009
   {{template "script" .}}
1009
   {{template "script" .}}
1010
-  <script>viewer({{.BaseURL}}, null);</script>
1010
+  <script>viewer(new URL(window.location.href), null);</script>
1011
 </body>
1011
 </body>
1012
 </html>
1012
 </html>
1013
 {{end}}
1013
 {{end}}
1044
     <div id="flamegraphdetails" class="flamegraph-details"></div>
1044
     <div id="flamegraphdetails" class="flamegraph-details"></div>
1045
   </div>
1045
   </div>
1046
   {{template "script" .}}
1046
   {{template "script" .}}
1047
-  <script>viewer({{.BaseURL}}, {{.Nodes}});</script>
1047
+  <script>viewer(new URL(window.location.href), {{.Nodes}});</script>
1048
   <script>{{template "d3script" .}}</script>
1048
   <script>{{template "d3script" .}}</script>
1049
   <script>{{template "d3tipscript" .}}</script>
1049
   <script>{{template "d3tipscript" .}}</script>
1050
   <script>{{template "d3flamegraphscript" .}}</script>
1050
   <script>{{template "d3flamegraphscript" .}}</script>

+ 15
- 9
internal/driver/webui.go View File

69
 
69
 
70
 // webArgs contains arguments passed to templates in webhtml.go.
70
 // webArgs contains arguments passed to templates in webhtml.go.
71
 type webArgs struct {
71
 type webArgs struct {
72
-	BaseURL    string
73
 	Title      string
72
 	Title      string
74
 	Errors     []string
73
 	Errors     []string
75
 	Total      int64
74
 	Total      int64
172
 		}
171
 		}
173
 		h.ServeHTTP(w, req)
172
 		h.ServeHTTP(w, req)
174
 	})
173
 	})
175
-	s := &http.Server{Handler: handler}
174
+
175
+	// We serve the ui at /ui/ and redirect there from the root. This is done
176
+	// to surface any problems with serving the ui at a non-root early. See:
177
+	//
178
+	// https://github.com/google/pprof/pull/348
179
+	mux := http.NewServeMux()
180
+	mux.Handle("/ui/", http.StripPrefix("/ui", handler))
181
+	mux.Handle("/", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect))
182
+	s := &http.Server{Handler: mux}
176
 	return s.Serve(ln)
183
 	return s.Serve(ln)
177
 }
184
 }
178
 
185
 
248
 }
255
 }
249
 
256
 
250
 // render generates html using the named template based on the contents of data.
257
 // render generates html using the named template based on the contents of data.
251
-func (ui *webInterface) render(w http.ResponseWriter, baseURL, tmpl string,
258
+func (ui *webInterface) render(w http.ResponseWriter, tmpl string,
252
 	rpt *report.Report, errList, legend []string, data webArgs) {
259
 	rpt *report.Report, errList, legend []string, data webArgs) {
253
 	file := getFromLegend(legend, "File: ", "unknown")
260
 	file := getFromLegend(legend, "File: ", "unknown")
254
 	profile := getFromLegend(legend, "Type: ", "unknown")
261
 	profile := getFromLegend(legend, "Type: ", "unknown")
255
-	data.BaseURL = baseURL
256
 	data.Title = file + " " + profile
262
 	data.Title = file + " " + profile
257
 	data.Errors = errList
263
 	data.Errors = errList
258
 	data.Total = rpt.Total()
264
 	data.Total = rpt.Total()
297
 		nodes = append(nodes, n.Info.Name)
303
 		nodes = append(nodes, n.Info.Name)
298
 	}
304
 	}
299
 
305
 
300
-	ui.render(w, "/", "graph", rpt, errList, legend, webArgs{
306
+	ui.render(w, "graph", rpt, errList, legend, webArgs{
301
 		HTMLBody: template.HTML(string(svg)),
307
 		HTMLBody: template.HTML(string(svg)),
302
 		Nodes:    nodes,
308
 		Nodes:    nodes,
303
 	})
309
 	})
332
 		nodes = append(nodes, item.Name)
338
 		nodes = append(nodes, item.Name)
333
 	}
339
 	}
334
 
340
 
335
-	ui.render(w, "/top", "top", rpt, errList, legend, webArgs{
341
+	ui.render(w, "top", rpt, errList, legend, webArgs{
336
 		Top:   top,
342
 		Top:   top,
337
 		Nodes: nodes,
343
 		Nodes: nodes,
338
 	})
344
 	})
354
 	}
360
 	}
355
 
361
 
356
 	legend := report.ProfileLabels(rpt)
362
 	legend := report.ProfileLabels(rpt)
357
-	ui.render(w, "/disasm", "plaintext", rpt, errList, legend, webArgs{
363
+	ui.render(w, "plaintext", rpt, errList, legend, webArgs{
358
 		TextBody: out.String(),
364
 		TextBody: out.String(),
359
 	})
365
 	})
360
 
366
 
378
 	}
384
 	}
379
 
385
 
380
 	legend := report.ProfileLabels(rpt)
386
 	legend := report.ProfileLabels(rpt)
381
-	ui.render(w, "/source", "sourcelisting", rpt, errList, legend, webArgs{
387
+	ui.render(w, "sourcelisting", rpt, errList, legend, webArgs{
382
 		HTMLBody: template.HTML(body.String()),
388
 		HTMLBody: template.HTML(body.String()),
383
 	})
389
 	})
384
 }
390
 }
399
 	}
405
 	}
400
 
406
 
401
 	legend := report.ProfileLabels(rpt)
407
 	legend := report.ProfileLabels(rpt)
402
-	ui.render(w, "/peek", "plaintext", rpt, errList, legend, webArgs{
408
+	ui.render(w, "plaintext", rpt, errList, legend, webArgs{
403
 		TextBody: out.String(),
409
 		TextBody: out.String(),
404
 	})
410
 	})
405
 }
411
 }