-
-
Save jacobsa/44eec7b4998e760a1f6d8aa6df3d1b2d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Run an http2 server, and make client requests to it that are closed early. | |
// | |
// The program starts a server on port 8080 that returns very large responses | |
// on a handler for /foo. In another goroutine it repeatedly makes GET requests | |
// to /foo, closing the response bodies early. The client optionally sleeps | |
// between reads to simulate a slow connection. | |
// | |
// Afterward, a memory profile is written to /tmp/mem.pprof. | |
// | |
// Expects to be run in a directory containing cert.pem and key.pem files. Use | |
// the following to generate them: | |
// | |
// go run $GOROOT/src/crypto/tls/generate_cert.go --host=localhost | |
// | |
package main | |
import ( | |
"crypto/rand" | |
"crypto/tls" | |
"flag" | |
"fmt" | |
"io" | |
"log" | |
"net/http" | |
"os" | |
"runtime" | |
"runtime/pprof" | |
"time" | |
"golang.org/x/net/http2" | |
) | |
var ( | |
useHTTP2 = flag.Bool("http2", true, "Use HTTP/2 connections?") | |
slowClient = flag.Bool("slow_client", false, "Sleep between client reads.") | |
) | |
// Some random bytes initialized at program start. | |
var randBytes []byte | |
func init() { | |
randBytes = make([]byte, 1<<20) | |
_, err := io.ReadFull(rand.Reader, randBytes) | |
if err != nil { | |
log.Fatal(err) | |
} | |
} | |
func main() { | |
flag.Parse() | |
// Don't check certificates in the client. | |
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{ | |
InsecureSkipVerify: true, | |
} | |
// Turn on HTTP/2 for the client if requested. | |
if *useHTTP2 { | |
http2.ConfigureTransport(http.DefaultTransport.(*http.Transport)) | |
} | |
// Wait a moment for the server to initialize, then repeatedly make requests | |
// in the client. Afterward dump a profile and exit. | |
go func() { | |
time.Sleep(250 * time.Millisecond) | |
for i := 0; i < 32; i++ { | |
makeRequest() | |
} | |
dumpMemProfile() | |
os.Exit(0) | |
}() | |
// Start the server. | |
http.HandleFunc("/foo", fooHandler) | |
log.Fatal(http.ListenAndServeTLS(":8080", "cert.pem", "key.pem", nil)) | |
} | |
func fooHandler(rw http.ResponseWriter, req *http.Request) { | |
// Return a large number of random bytes. | |
const responseSize = 1 << 30 | |
for n := 0; n < responseSize; n += len(randBytes) { | |
_, err := rw.Write(randBytes) | |
if err != nil { | |
break | |
} | |
} | |
} | |
func makeRequest() { | |
// Make a GET request to the handler. | |
resp, err := http.Get("https://localhost:8080/foo") | |
if err != nil { | |
log.Fatal(err) | |
} | |
// Don't forget to clean up when we're done. | |
defer resp.Body.Close() | |
// Repeatedly read the request body, optionally sleeping between reads. Stop | |
// after reading 10 MiB. | |
const toRead = 10 << 20 | |
buf := make([]byte, 1<<20) | |
before := time.Now() | |
var n int | |
for n < toRead { | |
nn, err := resp.Body.Read(buf) | |
n += nn | |
if err == io.EOF { | |
break | |
} | |
if err != nil { | |
log.Fatal(err) | |
} | |
if *slowClient { | |
time.Sleep(50 * time.Millisecond) | |
} | |
} | |
duration := time.Since(before) | |
// Print stats. | |
log.Printf( | |
"Read %d bytes in %v (%.2f MiB/s)", | |
n, | |
duration, | |
float64(n>>20)/(float64(duration)/float64(time.Second))) | |
} | |
func dumpMemProfile() (err error) { | |
const path = "/tmp/mem.pprof" | |
// Trigger a garbage collection to get up to date information (cf. | |
// https://goo.gl/aXVQfL). | |
runtime.GC() | |
// Open the file. | |
var f *os.File | |
f, err = os.Create(path) | |
if err != nil { | |
err = fmt.Errorf("Create: %v", err) | |
return | |
} | |
defer func() { | |
closeErr := f.Close() | |
if err == nil { | |
err = closeErr | |
} | |
}() | |
// Dump to the file. | |
err = pprof.Lookup("heap").WriteTo(f, 0) | |
if err != nil { | |
err = fmt.Errorf("WriteTo: %v", err) | |
return | |
} | |
return | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment