diff --git a/brotli.go b/brotli.go index 815e4b3..c829c39 100644 --- a/brotli.go +++ b/brotli.go @@ -138,10 +138,7 @@ func nonblockingWriteBrotli(ctxv interface{}) { ctx := ctxv.(*compressCtx) zw := acquireRealBrotliWriter(ctx.w, ctx.level) - _, err := zw.Write(ctx.p) - if err != nil { - panic(fmt.Sprintf("BUG: brotli.Writer.Write for len(p)=%d returned unexpected error: %v", len(ctx.p), err)) - } + zw.Write(ctx.p) //nolint:errcheck // no way to handle this error anyway releaseRealBrotliWriter(zw, ctx.level) } diff --git a/bytesconv.go b/bytesconv.go index 9b2ffeb..809b490 100644 --- a/bytesconv.go +++ b/bytesconv.go @@ -79,6 +79,7 @@ func ParseIPv4(dst net.IP, ipStr []byte) (net.IP, error) { copy(dst, net.IPv4zero) dst = dst.To4() if dst == nil { + // developer sanity-check panic("BUG: dst must not be nil") } @@ -126,6 +127,7 @@ func ParseHTTPDate(date []byte) (time.Time, error) { // AppendUint appends n to dst and returns the extended dst. func AppendUint(dst []byte, n int) []byte { if n < 0 { + // developer sanity-check panic("BUG: int must be positive") } @@ -281,6 +283,7 @@ var hexIntBufPool sync.Pool func writeHexInt(w *bufio.Writer, n int) error { if n < 0 { + // developer sanity-check panic("BUG: int must be positive") } diff --git a/client.go b/client.go index 8546a12..9a2ef85 100644 --- a/client.go +++ b/client.go @@ -1294,26 +1294,23 @@ func isIdempotent(req *Request) bool { } func (c *HostClient) do(req *Request, resp *Response) (bool, error) { - nilResp := false if resp == nil { - nilResp = true resp = AcquireResponse() + defer ReleaseResponse(resp) } ok, err := c.doNonNilReqResp(req, resp) - if nilResp { - ReleaseResponse(resp) - } - return ok, err } func (c *HostClient) doNonNilReqResp(req *Request, resp *Response) (bool, error) { if req == nil { + // for debugging purposes panic("BUG: req cannot be nil") } if resp == nil { + // for debugging purposes panic("BUG: resp cannot be nil") } @@ -1994,7 +1991,7 @@ func dialAddr(addr string, dial DialFunc, dialDualStack, isTLS bool, tlsConfig * return nil, err } if conn == nil { - panic("BUG: DialFunc returned (nil, nil)") + return nil, errors.New("dialling unsuccessful. Please report this bug!") } // We assume that any conn that has the Handshake() method is a TLS conn already. diff --git a/compress.go b/compress.go index 5781c77..50d381b 100644 --- a/compress.go +++ b/compress.go @@ -66,6 +66,7 @@ func releaseFlateReader(zr io.ReadCloser) { func resetFlateReader(zr io.ReadCloser, r io.Reader) error { zrr, ok := zr.(zlib.Resetter) if !ok { + // sanity check. should only be called with a zlib.Reader panic("BUG: zlib.Reader doesn't implement zlib.Resetter???") } return zrr.Reset(r, nil) @@ -101,7 +102,14 @@ func acquireRealGzipWriter(w io.Writer, level int) *gzip.Writer { if v == nil { zw, err := gzip.NewWriterLevel(w, level) if err != nil { - panic(fmt.Sprintf("BUG: unexpected error from gzip.NewWriterLevel(%d): %v", level, err)) + // gzip.NewWriterLevel only errors for invalid + // compression levels. Clamp it to be min or max. + if level < gzip.HuffmanOnly { + level = gzip.HuffmanOnly + } else { + level = gzip.BestCompression + } + zw, _ = gzip.NewWriterLevel(w, level) } return zw } @@ -175,10 +183,7 @@ func nonblockingWriteGzip(ctxv interface{}) { ctx := ctxv.(*compressCtx) zw := acquireRealGzipWriter(ctx.w, ctx.level) - _, err := zw.Write(ctx.p) - if err != nil { - panic(fmt.Sprintf("BUG: gzip.Writer.Write for len(p)=%d returned unexpected error: %v", len(ctx.p), err)) - } + zw.Write(ctx.p) //nolint:errcheck // no way to handle this error anyway releaseRealGzipWriter(zw, ctx.level) } @@ -271,10 +276,7 @@ func nonblockingWriteDeflate(ctxv interface{}) { ctx := ctxv.(*compressCtx) zw := acquireRealDeflateWriter(ctx.w, ctx.level) - _, err := zw.Write(ctx.p) - if err != nil { - panic(fmt.Sprintf("BUG: zlib.Writer.Write for len(p)=%d returned unexpected error: %v", len(ctx.p), err)) - } + zw.Write(ctx.p) //nolint:errcheck // no way to handle this error anyway releaseRealDeflateWriter(zw, ctx.level) } @@ -379,7 +381,14 @@ func acquireRealDeflateWriter(w io.Writer, level int) *zlib.Writer { if v == nil { zw, err := zlib.NewWriterLevel(w, level) if err != nil { - panic(fmt.Sprintf("BUG: unexpected error from zlib.NewWriterLevel(%d): %v", level, err)) + // zlib.NewWriterLevel only errors for invalid + // compression levels. Clamp it to be min or max. + if level < zlib.HuffmanOnly { + level = zlib.HuffmanOnly + } else { + level = zlib.BestCompression + } + zw, _ = zlib.NewWriterLevel(w, level) } return zw } diff --git a/fs.go b/fs.go index d4ea8bd..1278298 100644 --- a/fs.go +++ b/fs.go @@ -602,7 +602,7 @@ func (ff *fsFile) decReadersCount() { ff.h.cacheLock.Lock() ff.readersCount-- if ff.readersCount < 0 { - panic("BUG: negative fsFile.readersCount!") + ff.readersCount = 0 } ff.h.cacheLock.Unlock() } @@ -1395,6 +1395,7 @@ func readFileHeader(f *os.File, compressed bool, fileEncoding string) ([]byte, e func stripLeadingSlashes(path []byte, stripSlashes int) []byte { for stripSlashes > 0 && len(path) > 0 { if path[0] != '/' { + // developer sanity-check panic("BUG: path must start with slash") } n := bytes.IndexByte(path[1:], '/') diff --git a/http.go b/http.go index ce38c1f..b20d665 100644 --- a/http.go +++ b/http.go @@ -963,7 +963,7 @@ func WriteMultipartForm(w io.Writer, f *multipart.Form, boundary string) error { // Do not care about memory allocations here, since multipart // form processing is slow. if len(boundary) == 0 { - panic("BUG: form boundary cannot be empty") + return errors.New("form boundary cannot be empty") } mw := multipart.NewWriter(w) @@ -2134,13 +2134,14 @@ func readBodyIdentity(r *bufio.Reader, maxBodySize int, dst []byte) ([]byte, err for { nn, err := r.Read(dst[offset:]) if nn <= 0 { - if err != nil { - if err == io.EOF { - return dst[:offset], nil - } + switch { + case errors.Is(err, io.EOF): + return dst[:offset], nil + case err != nil: return dst[:offset], err + default: + return dst[:offset], fmt.Errorf("bufio.Read() returned (%d, nil)", nn) } - panic(fmt.Sprintf("BUG: bufio.Read() returned (%d, nil)", nn)) } offset += nn if maxBodySize > 0 && offset > maxBodySize { @@ -2175,13 +2176,14 @@ func appendBodyFixedSize(r *bufio.Reader, dst []byte, n int) ([]byte, error) { for { nn, err := r.Read(dst[offset:]) if nn <= 0 { - if err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } + switch { + case errors.Is(err, io.EOF): + return dst[:offset], io.ErrUnexpectedEOF + case err != nil: return dst[:offset], err + default: + return dst[:offset], fmt.Errorf("bufio.Read() returned (%d, nil)", nn) } - panic(fmt.Sprintf("BUG: bufio.Read() returned (%d, nil)", nn)) } offset += nn if offset == dstLen { @@ -2197,6 +2199,8 @@ type ErrBrokenChunk struct { func readBodyChunked(r *bufio.Reader, maxBodySize int, dst []byte) ([]byte, error) { if len(dst) > 0 { + // data integrity might be in danger. No idea what we received, + // but nothing we should write to. panic("BUG: expected zero-length buffer") } diff --git a/lbclient.go b/lbclient.go index 56ac1bf..6be2dc9 100644 --- a/lbclient.go +++ b/lbclient.go @@ -84,6 +84,7 @@ func (cc *LBClient) init() { cc.mu.Lock() defer cc.mu.Unlock() if len(cc.Clients) == 0 { + // developer sanity-check panic("BUG: LBClient.Clients cannot be empty") } for _, c := range cc.Clients { diff --git a/peripconn.go b/peripconn.go index c2d1827..123c55e 100644 --- a/peripconn.go +++ b/peripconn.go @@ -1,7 +1,6 @@ package fasthttp import ( - "fmt" "net" "sync" ) @@ -25,17 +24,16 @@ func (cc *perIPConnCounter) Register(ip uint32) int { func (cc *perIPConnCounter) Unregister(ip uint32) { cc.lock.Lock() + defer cc.lock.Unlock() if cc.m == nil { - cc.lock.Unlock() + // developer safeguard panic("BUG: perIPConnCounter.Register() wasn't called") } n := cc.m[ip] - 1 if n < 0 { - cc.lock.Unlock() - panic(fmt.Sprintf("BUG: negative per-ip counter=%d for ip=%d", n, ip)) + n = 0 } cc.m[ip] = n - cc.lock.Unlock() } type perIPConn struct { diff --git a/peripconn_test.go b/peripconn_test.go index e2137c0..5571654 100644 --- a/peripconn_test.go +++ b/peripconn_test.go @@ -25,8 +25,6 @@ func TestPerIPConnCounter(t *testing.T) { var cc perIPConnCounter - expectPanic(t, func() { cc.Unregister(123) }) - for i := 1; i < 100; i++ { if n := cc.Register(123); n != i { t.Fatalf("Unexpected counter value=%d. Expected %d", n, i) @@ -43,21 +41,9 @@ func TestPerIPConnCounter(t *testing.T) { } cc.Unregister(456) - expectPanic(t, func() { cc.Unregister(123) }) - expectPanic(t, func() { cc.Unregister(456) }) - n = cc.Register(123) if n != 1 { t.Fatalf("Unexpected counter value=%d. Expected 1", n) } cc.Unregister(123) } - -func expectPanic(t *testing.T, f func()) { - defer func() { - if r := recover(); r == nil { - t.Fatalf("Expecting panic") - } - }() - f() -} diff --git a/server.go b/server.go index 1c1c39d..4ed82b1 100644 --- a/server.go +++ b/server.go @@ -1929,9 +1929,6 @@ func acceptConn(s *Server, ln net.Listener, lastPerIPErrorTime *time.Time) (net. for { c, err := ln.Accept() if err != nil { - if c != nil { - panic("BUG: net.Listener returned non-nil conn and non-nil error") - } if netErr, ok := err.(net.Error); ok && netErr.Timeout() { s.logger().Printf("Timeout error when accepting new connections: %v", netErr) time.Sleep(time.Second) @@ -1943,9 +1940,6 @@ func acceptConn(s *Server, ln net.Listener, lastPerIPErrorTime *time.Time) (net. } return nil, io.EOF } - if c == nil { - panic("BUG: net.Listener returned (nil, nil)") - } if tc, ok := c.(*net.TCPConn); ok && s.TCPKeepalive { if err := tc.SetKeepAlive(s.TCPKeepalive); err != nil { @@ -2578,7 +2572,7 @@ func (ctx *RequestCtx) LastTimeoutErrorResponse() *Response { func writeResponse(ctx *RequestCtx, w *bufio.Writer) error { if ctx.timeoutResponse != nil { - panic("BUG: cannot write timed out response") + return errors.New("cannot write timed out response") } err := ctx.Response.Write(w) @@ -2596,8 +2590,8 @@ func acquireByteReader(ctxP **RequestCtx) (*bufio.Reader, error) { c := ctx.c s.releaseCtx(ctx) - // Make GC happy, so it could garbage collect ctx - // while we waiting for the next request. + // Make GC happy, so it could garbage collect ctx while we wait for the + // next request. ctx = nil *ctxP = nil @@ -2612,6 +2606,7 @@ func acquireByteReader(ctxP **RequestCtx) (*bufio.Reader, error) { return nil, io.EOF } if n != 1 { + // developer sanity-check panic("BUG: Reader must return at least one byte") } @@ -2787,19 +2782,23 @@ func (fa *fakeAddrer) LocalAddr() net.Addr { } func (fa *fakeAddrer) Read(p []byte) (int, error) { + // developer sanity-check panic("BUG: unexpected Read call") } func (fa *fakeAddrer) Write(p []byte) (int, error) { + // developer sanity-check panic("BUG: unexpected Write call") } func (fa *fakeAddrer) Close() error { + // developer sanity-check panic("BUG: unexpected Close call") } func (s *Server) releaseCtx(ctx *RequestCtx) { if ctx.timeoutResponse != nil { + // developer sanity-check panic("BUG: cannot release timed out RequestCtx") } diff --git a/stackless/func.go b/stackless/func.go index a50b3eb..70521e1 100644 --- a/stackless/func.go +++ b/stackless/func.go @@ -20,6 +20,7 @@ import ( // at the moment due to high load. func NewFunc(f func(ctx interface{})) func(ctx interface{}) bool { if f == nil { + // developer sanity-check panic("BUG: f cannot be nil") } diff --git a/timer.go b/timer.go index 885aa69..6c06ba0 100644 --- a/timer.go +++ b/timer.go @@ -10,6 +10,7 @@ func initTimer(t *time.Timer, timeout time.Duration) *time.Timer { return time.NewTimer(timeout) } if t.Reset(timeout) { + // developer sanity-check panic("BUG: active timer trapped into initTimer()") } return t diff --git a/workerpool.go b/workerpool.go index 50a5c75..71da1f2 100644 --- a/workerpool.go +++ b/workerpool.go @@ -47,7 +47,7 @@ type workerChan struct { func (wp *workerPool) Start() { if wp.stopCh != nil { - panic("BUG: workerPool already started") + return } wp.stopCh = make(chan struct{}) stopCh := wp.stopCh @@ -72,7 +72,7 @@ func (wp *workerPool) Start() { func (wp *workerPool) Stop() { if wp.stopCh == nil { - panic("BUG: workerPool wasn't started") + return } close(wp.stopCh) wp.stopCh = nil