Repository: valyala/fasthttp
Pull request #428 (2018-09-29 09:30:23)

Add support for named profiles like heap, goroutine, threadcreate, etc.
Improve #243 to support all named profiles like heap, goroutine, threadcreate, mutex, block, allocs, and possibly other names in the future.

Tested with the following to also verify the web UI:
```go
func main() {
	router := fasthttprouter.New()
	router.GET("/debug/pprof/:name", pprofhandler.PprofHandler)
	router.GET("/debug/pprof/", pprofhandler.PprofHandler)
	log.Fatal(fasthttp.ListenAndServe(":8080", router.Handler))
}
```


Patch:
diff --git a/args.go b/args.go
index 2781593..4d9b35e 100644
--- a/args.go
+++ b/args.go
@@ -5,6 +5,8 @@ import (
 	"errors"
 	"io"
 	"sync"
+
+	"github.com/valyala/bytebufferpool"
 )
 
 // AcquireArgs returns an empty Args object from the pool.
@@ -243,10 +245,10 @@ func (a *Args) GetUint(key string) (int, error) {
 
 // SetUint sets uint value for the given key.
 func (a *Args) SetUint(key string, value int) {
-	bb := AcquireByteBuffer()
+	bb := bytebufferpool.Get()
 	bb.B = AppendUint(bb.B[:0], value)
 	a.SetBytesV(key, bb.B)
-	ReleaseByteBuffer(bb)
+	bytebufferpool.Put(bb)
 }
 
 // SetUintBytes sets uint value for the given key.
diff --git a/bytebuffer.go b/bytebuffer.go
deleted file mode 100644
index 2345e8a..0000000
--- a/bytebuffer.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package fasthttp
-
-import (
-	"github.com/valyala/bytebufferpool"
-)
-
-// ByteBuffer provides byte buffer, which can be used with fasthttp API
-// in order to minimize memory allocations.
-//
-// ByteBuffer may be used with functions appending data to the given []byte
-// slice. See example code for details.
-//
-// Use AcquireByteBuffer for obtaining an empty byte buffer.
-//
-// Deprecated: use github.com/valyala/bytebufferpool instead.
-//
-// WARNING: This type is going to be removed on 2018-10-01!!!
-// See https://github.com/valyala/fasthttp/pull/415 for more infomation.
-//
-type ByteBuffer bytebufferpool.ByteBuffer
-
-// Write implements io.Writer - it appends p to ByteBuffer.B
-func (b *ByteBuffer) Write(p []byte) (int, error) {
-	return bb(b).Write(p)
-}
-
-// WriteString appends s to ByteBuffer.B
-func (b *ByteBuffer) WriteString(s string) (int, error) {
-	return bb(b).WriteString(s)
-}
-
-// Set sets ByteBuffer.B to p
-func (b *ByteBuffer) Set(p []byte) {
-	bb(b).Set(p)
-}
-
-// SetString sets ByteBuffer.B to s
-func (b *ByteBuffer) SetString(s string) {
-	bb(b).SetString(s)
-}
-
-// Reset makes ByteBuffer.B empty.
-func (b *ByteBuffer) Reset() {
-	bb(b).Reset()
-}
-
-// AcquireByteBuffer returns an empty byte buffer from the pool.
-//
-// Acquired byte buffer may be returned to the pool via ReleaseByteBuffer call.
-// This reduces the number of memory allocations required for byte buffer
-// management.
-func AcquireByteBuffer() *ByteBuffer {
-	return (*ByteBuffer)(defaultByteBufferPool.Get())
-}
-
-// ReleaseByteBuffer returns byte buffer to the pool.
-//
-// ByteBuffer.B mustn't be touched after returning it to the pool.
-// Otherwise data races occur.
-func ReleaseByteBuffer(b *ByteBuffer) {
-	defaultByteBufferPool.Put(bb(b))
-}
-
-func bb(b *ByteBuffer) *bytebufferpool.ByteBuffer {
-	return (*bytebufferpool.ByteBuffer)(b)
-}
-
-var defaultByteBufferPool bytebufferpool.Pool
diff --git a/compress.go b/compress.go
index 221472a..73a40d3 100644
--- a/compress.go
+++ b/compress.go
@@ -152,7 +152,6 @@ func WriteGzipLevel(w io.Writer, p []byte, level int) (int, error) {
 	switch w.(type) {
 	case *byteSliceWriter,
 		*bytes.Buffer,
-		*ByteBuffer,
 		*bytebufferpool.ByteBuffer:
 		// These writers don't block, so we can just use stacklessWriteGzip
 		ctx := &compressCtx{
@@ -249,7 +248,6 @@ func WriteDeflateLevel(w io.Writer, p []byte, level int) (int, error) {
 	switch w.(type) {
 	case *byteSliceWriter,
 		*bytes.Buffer,
-		*ByteBuffer,
 		*bytebufferpool.ByteBuffer:
 		// These writers don't block, so we can just use stacklessWriteDeflate
 		ctx := &compressCtx{
@@ -409,7 +407,7 @@ func isFileCompressible(f *os.File, minCompressRatio float64) bool {
 	// Try compressing the first 4kb of of the file
 	// and see if it can be compressed by more than
 	// the given minCompressRatio.
-	b := AcquireByteBuffer()
+	b := bytebufferpool.Get()
 	zw := acquireStacklessGzipWriter(b, CompressDefaultCompression)
 	lr := &io.LimitedReader{
 		R: f,
@@ -424,7 +422,7 @@ func isFileCompressible(f *os.File, minCompressRatio float64) bool {
 
 	n := 4096 - lr.N
 	zn := len(b.B)
-	ReleaseByteBuffer(b)
+	bytebufferpool.Put(b)
 	return float64(zn) < float64(n)*minCompressRatio
 }
 
diff --git a/fs.go b/fs.go
index f0ea066..7a73a79 100644
--- a/fs.go
+++ b/fs.go
@@ -17,6 +17,7 @@ import (
 	"time"
 
 	"github.com/klauspost/compress/gzip"
+	"github.com/valyala/bytebufferpool"
 )
 
 // ServeFileBytesUncompressed returns HTTP response containing file contents
@@ -139,12 +140,12 @@ func NewVHostPathRewriter(slashesCount int) PathRewriteFunc {
 		if len(host) == 0 {
 			host = strInvalidHost
 		}
-		b := AcquireByteBuffer()
+		b := bytebufferpool.Get()
 		b.B = append(b.B, '/')
 		b.B = append(b.B, host...)
 		b.B = append(b.B, path...)
 		ctx.URI().SetPathBytes(b.B)
-		ReleaseByteBuffer(b)
+		bytebufferpool.Put(b)
 
 		return ctx.Path()
 	}
@@ -915,7 +916,7 @@ var (
 )
 
 func (h *fsHandler) createDirIndex(base *URI, dirPath string, mustCompress bool) (*fsFile, error) {
-	w := &ByteBuffer{}
+	w := &bytebufferpool.ByteBuffer{}
 
 	basePathEscaped := html.EscapeString(string(base.Path()))
 	fmt.Fprintf(w, "<html><head><title>%s</title><style>.dir { font-weight: bold }</style></head><body>", basePathEscaped)
@@ -975,7 +976,7 @@ func (h *fsHandler) createDirIndex(base *URI, dirPath string, mustCompress bool)
 	fmt.Fprintf(w, "</ul></body></html>")
 
 	if mustCompress {
-		var zbuf ByteBuffer
+		var zbuf bytebufferpool.ByteBuffer
 		zbuf.B = AppendGzipBytesLevel(zbuf.B, w.B, CompressDefaultCompression)
 		w = &zbuf
 	}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..8434ca1
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,9 @@
+module github.com/valyala/fasthttp
+
+require (
+	github.com/klauspost/compress v1.4.0
+	github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e // indirect
+	github.com/valyala/bytebufferpool v1.0.0
+	github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a
+	golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..93f38fc
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,10 @@
+github.com/klauspost/compress v1.4.0 h1:8nsMz3tWa9SWWPL60G1V6CUsf4lLjWLTNEtibhe8gh8=
+github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e h1:+lIPJOWl+jSiJOc70QXJ07+2eg2Jy2EC7Mi11BWujeM=
+github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
+github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
+golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3 h1:czFLhve3vsQetD6JOJ8NZZvGQIXlnN3/yXxbT6/awxI=
+golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
diff --git a/http.go b/http.go
index a585a63..f8c38fc 100644
--- a/http.go
+++ b/http.go
@@ -346,7 +346,7 @@ func (resp *Response) BodyGunzip() ([]byte, error) {
 }
 
 func gunzipData(p []byte) ([]byte, error) {
-	var bb ByteBuffer
+	var bb bytebufferpool.ByteBuffer
 	_, err := WriteGunzip(&bb, p)
 	if err != nil {
 		return nil, err
@@ -373,7 +373,7 @@ func (resp *Response) BodyInflate() ([]byte, error) {
 }
 
 func inflateData(p []byte) ([]byte, error) {
-	var bb ByteBuffer
+	var bb bytebufferpool.ByteBuffer
 	_, err := WriteInflate(&bb, p)
 	if err != nil {
 		return nil, err
@@ -711,7 +711,7 @@ func (req *Request) MultipartForm() (*multipart.Form, error) {
 }
 
 func marshalMultipartForm(f *multipart.Form, boundary string) ([]byte, error) {
-	var buf ByteBuffer
+	var buf bytebufferpool.ByteBuffer
 	if err := WriteMultipartForm(&buf, f, boundary); err != nil {
 		return nil, err
 	}
@@ -1457,7 +1457,7 @@ func (resp *Response) String() string {
 }
 
 func getHTTPString(hw httpWriter) string {
-	w := AcquireByteBuffer()
+	w := bytebufferpool.Get()
 	bw := bufio.NewWriter(w)
 	if err := hw.Write(bw); err != nil {
 		return err.Error()
@@ -1466,7 +1466,7 @@ func getHTTPString(hw httpWriter) string {
 		return err.Error()
 	}
 	s := string(w.B)
-	ReleaseByteBuffer(w)
+	bytebufferpool.Put(w)
 	return s
 }
 
diff --git a/pprofhandler/pprof.go b/pprofhandler/pprof.go
index 200ae51..c94d2c1 100644
--- a/pprofhandler/pprof.go
+++ b/pprofhandler/pprof.go
@@ -2,6 +2,7 @@ package pprofhandler
 
 import (
 	"net/http/pprof"
+	rtp "runtime/pprof"
 	"strings"
 
 	"github.com/valyala/fasthttp"
@@ -30,6 +31,14 @@ func PprofHandler(ctx *fasthttp.RequestCtx) {
 	} else if strings.HasPrefix(string(ctx.Path()), "/debug/pprof/trace") {
 		trace(ctx)
 	} else {
+		for _, v := range rtp.Profiles() {
+			ppName := v.Name()
+			if strings.HasPrefix(string(ctx.Path()), "/debug/pprof/"+ppName) {
+				namedHandler := fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler(ppName).ServeHTTP)
+				namedHandler(ctx)
+				return
+			}
+		}
 		index(ctx)
 	}
 }


Test patch:
diff --git a/args_test.go b/args_test.go
index d5b768f..217952a 100644
--- a/args_test.go
+++ b/args_test.go
@@ -6,6 +6,8 @@ import (
 	"strings"
 	"testing"
 	"time"
+
+	"github.com/valyala/bytebufferpool"
 )
 
 func TestDecodeArgAppend(t *testing.T) {
@@ -171,7 +173,7 @@ func TestArgsWriteTo(t *testing.T) {
 	var a Args
 	a.Parse(s)
 
-	var w ByteBuffer
+	var w bytebufferpool.ByteBuffer
 	n, err := a.WriteTo(&w)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
diff --git a/bytebuffer_example_test.go b/bytebuffer_example_test.go
deleted file mode 100644
index b89542b..0000000
--- a/bytebuffer_example_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package fasthttp_test
-
-import (
-	"fmt"
-
-	"github.com/valyala/fasthttp"
-)
-
-func ExampleByteBuffer() {
-	// This request handler sets 'Your-IP' response header
-	// to 'Your IP is <ip>'. It uses ByteBuffer for constructing response
-	// header value with zero memory allocations.
-	yourIPRequestHandler := func(ctx *fasthttp.RequestCtx) {
-		b := fasthttp.AcquireByteBuffer()
-		b.B = append(b.B, "Your IP is <"...)
-		b.B = fasthttp.AppendIPv4(b.B, ctx.RemoteIP())
-		b.B = append(b.B, ">"...)
-		ctx.Response.Header.SetBytesV("Your-IP", b.B)
-
-		fmt.Fprintf(ctx, "Check response headers - they must contain 'Your-IP: %s'", b.B)
-
-		// It is safe to release byte buffer now, since it is
-		// no longer used.
-		fasthttp.ReleaseByteBuffer(b)
-	}
-
-	// Start fasthttp server returning your ip in response headers.
-	fasthttp.ListenAndServe(":8080", yourIPRequestHandler)
-}
diff --git a/bytebuffer_test.go b/bytebuffer_test.go
deleted file mode 100644
index 2ecbd77..0000000
--- a/bytebuffer_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package fasthttp
-
-import (
-	"fmt"
-	"testing"
-	"time"
-)
-
-func TestByteBufferAcquireReleaseSerial(t *testing.T) {
-	testByteBufferAcquireRelease(t)
-}
-
-func TestByteBufferAcquireReleaseConcurrent(t *testing.T) {
-	concurrency := 10
-	ch := make(chan struct{}, concurrency)
-	for i := 0; i < concurrency; i++ {
-		go func() {
-			testByteBufferAcquireRelease(t)
-			ch <- struct{}{}
-		}()
-	}
-
-	for i := 0; i < concurrency; i++ {
-		select {
-		case <-ch:
-		case <-time.After(time.Second):
-			t.Fatalf("timeout!")
-		}
-	}
-}
-
-func testByteBufferAcquireRelease(t *testing.T) {
-	for i := 0; i < 10; i++ {
-		b := AcquireByteBuffer()
-		b.B = append(b.B, "num "...)
-		b.B = AppendUint(b.B, i)
-		expectedS := fmt.Sprintf("num %d", i)
-		if string(b.B) != expectedS {
-			t.Fatalf("unexpected result: %q. Expecting %q", b.B, expectedS)
-		}
-		ReleaseByteBuffer(b)
-	}
-}
diff --git a/bytebuffer_timing_test.go b/bytebuffer_timing_test.go
deleted file mode 100644
index 92bbafa..0000000
--- a/bytebuffer_timing_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package fasthttp
-
-import (
-	"bytes"
-	"testing"
-)
-
-func BenchmarkByteBufferWrite(b *testing.B) {
-	s := []byte("foobarbaz")
-	b.RunParallel(func(pb *testing.PB) {
-		var buf ByteBuffer
-		for pb.Next() {
-			for i := 0; i < 100; i++ {
-				buf.Write(s)
-			}
-			buf.Reset()
-		}
-	})
-}
-
-func BenchmarkBytesBufferWrite(b *testing.B) {
-	s := []byte("foobarbaz")
-	b.RunParallel(func(pb *testing.PB) {
-		var buf bytes.Buffer
-		for pb.Next() {
-			for i := 0; i < 100; i++ {
-				buf.Write(s)
-			}
-			buf.Reset()
-		}
-	})
-}
diff --git a/bytesconv_test.go b/bytesconv_test.go
index b5da44d..ea31aab 100644
--- a/bytesconv_test.go
+++ b/bytesconv_test.go
@@ -7,6 +7,8 @@ import (
 	"net"
 	"testing"
 	"time"
+
+	"github.com/valyala/bytebufferpool"
 )
 
 func TestAppendHTMLEscape(t *testing.T) {
@@ -92,7 +94,7 @@ func testAppendUint(t *testing.T, n int) {
 }
 
 func testWriteHexInt(t *testing.T, n int, expectedS string) {
-	var w ByteBuffer
+	var w bytebufferpool.ByteBuffer
 	bw := bufio.NewWriter(&w)
 	if err := writeHexInt(bw, n); err != nil {
 		t.Fatalf("unexpected error when writing hex %x: %s", n, err)
diff --git a/bytesconv_timing_test.go b/bytesconv_timing_test.go
index 24b6a49..f817cf4 100644
--- a/bytesconv_timing_test.go
+++ b/bytesconv_timing_test.go
@@ -5,6 +5,8 @@ import (
 	"html"
 	"net"
 	"testing"
+
+	"github.com/valyala/bytebufferpool"
 )
 
 func BenchmarkAppendHTMLEscape(b *testing.B) {
@@ -77,7 +79,7 @@ func BenchmarkInt2HexByte(b *testing.B) {
 
 func BenchmarkWriteHexInt(b *testing.B) {
 	b.RunParallel(func(pb *testing.PB) {
-		var w ByteBuffer
+		var w bytebufferpool.ByteBuffer
 		bw := bufio.NewWriter(&w)
 		i := 0
 		for pb.Next() {
diff --git a/header_timing_test.go b/header_timing_test.go
index a6f7a11..55f5df9 100644
--- a/header_timing_test.go
+++ b/header_timing_test.go
@@ -5,6 +5,8 @@ import (
 	"bytes"
 	"io"
 	"testing"
+
+	"github.com/valyala/bytebufferpool"
 )
 
 var strFoobar = []byte("foobar.com")
@@ -65,7 +67,7 @@ func BenchmarkRequestHeaderWrite(b *testing.B) {
 		h.SetHost("foobar.com")
 		h.SetUserAgent("aaa.bbb")
 		h.SetReferer("http://google.com/aaa/bbb")
-		var w ByteBuffer
+		var w bytebufferpool.ByteBuffer
 		for pb.Next() {
 			if _, err := h.WriteTo(&w); err != nil {
 				b.Fatalf("unexpected error when writing header: %s", err)
@@ -83,7 +85,7 @@ func BenchmarkResponseHeaderWrite(b *testing.B) {
 		h.SetContentLength(1256)
 		h.SetServer("aaa 1/2.3")
 		h.Set("Test", "1.2.3")
-		var w ByteBuffer
+		var w bytebufferpool.ByteBuffer
 		for pb.Next() {
 			if _, err := h.WriteTo(&w); err != nil {
 				b.Fatalf("unexpected error when writing header: %s", err)
diff --git a/http_test.go b/http_test.go
index 4ecab5b..4052c61 100644
--- a/http_test.go
+++ b/http_test.go
@@ -10,6 +10,8 @@ import (
 	"strings"
 	"testing"
 	"time"
+
+	"github.com/valyala/bytebufferpool"
 )
 
 func TestResponseBodyStreamDeflate(t *testing.T) {
@@ -490,7 +492,7 @@ type bodyWriterTo interface {
 }
 
 func testBodyWriteTo(t *testing.T, bw bodyWriterTo, expectedS string, isRetainedBody bool) {
-	var buf ByteBuffer
+	var buf bytebufferpool.ByteBuffer
 	if err := bw.BodyWriteTo(&buf); err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -564,7 +566,7 @@ func TestResponseWriteTo(t *testing.T) {
 	r.SetBodyString("foobar")
 
 	s := r.String()
-	var buf ByteBuffer
+	var buf bytebufferpool.ByteBuffer
 	n, err := r.WriteTo(&buf)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
@@ -583,7 +585,7 @@ func TestRequestWriteTo(t *testing.T) {
 	r.SetRequestURI("http://foobar.com/aaa/bbb")
 
 	s := r.String()
-	var buf ByteBuffer
+	var buf bytebufferpool.ByteBuffer
 	n, err := r.WriteTo(&buf)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
@@ -1457,7 +1459,7 @@ func testRequestWriteError(t *testing.T, method, requestURI, host, userAgent, bo
 	req.Header.Set("User-Agent", userAgent)
 	req.SetBody([]byte(body))
 
-	w := &ByteBuffer{}
+	w := &bytebufferpool.ByteBuffer{}
 	bw := bufio.NewWriter(w)
 	err := req.Write(bw)
 	if err == nil {