| // Copyright 2020 Marc-Antoine Ruel. All rights reserved. | |
| // Use of this source code is governed under the Apache License, Version 2.0 | |
| // that can be found in the LICENSE file. | |
| // Examples exposing goroutine snapshots through an HTTP handler. | |
| package webstack_test | |
| import ( | |
| "log" | |
| "net" | |
| "net/http" | |
| "strings" | |
| "sync" | |
| "time" | |
| "github.com/maruel/panicparse/v2/stack/webstack" | |
| ) | |
| func ExampleSnapshotHandler() { | |
| http.HandleFunc("/debug/panicparse", webstack.SnapshotHandler) | |
| // Access as http://localhost:6060/debug/panicparse | |
| log.Println(http.ListenAndServe("localhost:6060", nil)) | |
| } | |
| func ExampleSnapshotHandler_complex() { | |
| // This example does a few things: | |
| // - Diables "augment" by default, can be enabled manually with "?augment=1". | |
| // - Forces the "maxmem" value to reduce memory pressure in worst case. | |
| // - Serializes handler to one at a time. | |
| // - Throttles requests to once per second. | |
| // - Limit request source IP to localhost and 100.64.x.x/10. (e.g. | |
| // Tailscale). | |
| const delay = time.Second | |
| mu := sync.Mutex{} | |
| var last time.Time | |
| http.HandleFunc("/debug/panicparse", func(w http.ResponseWriter, req *http.Request) { | |
| // Only allow requests from localhost or in the 100.64.x.x/10 IPv4 range. | |
| ok := false | |
| if i := strings.LastIndexByte(req.RemoteAddr, ':'); i != -1 { | |
| switch ip := req.RemoteAddr[:i]; ip { | |
| case "localhost", "127.0.0.1", "[::1]", "::1": | |
| ok = true | |
| default: | |
| p := net.ParseIP(ip).To4() | |
| ok = p != nil && p[0] == 100 && p[1] >= 64 && p[1] < 128 | |
| } | |
| } | |
| if !ok { | |
| http.Error(w, "forbidden", http.StatusForbidden) | |
| return | |
| } | |
| // Serialize the handler. | |
| mu.Lock() | |
| defer mu.Unlock() | |
| // Throttle requests. | |
| if time.Since(last) < delay { | |
| http.Error(w, "retry later", http.StatusTooManyRequests) | |
| return | |
| } | |
| // Must be called before touching req.Form. | |
| req.ParseForm() | |
| // Disables source scanning by default since it's heavy. | |
| if req.FormValue("augment") == "" { | |
| req.Form.Set("augment", "0") | |
| } | |
| // Reduces maximum memory usage to 32MiB (from 64MiB) for the goroutines | |
| // snapshot. | |
| req.Form.Set("maxmem", "33554432") | |
| webstack.SnapshotHandler(w, req) | |
| last = time.Now() | |
| }) | |
| // Access as http://localhost:6060/debug/panicparse | |
| log.Println(http.ListenAndServe("localhost:6060", nil)) | |
| } |