Repository: labstack/echo (go)

Issue:
Panic in Timeout middleware is causing application to exit
I enable recently timeout middleware: https://echo.labstack.com/middleware/timeout for my testing server. I've noticed once it triggered application is going down. 

### Configuration

Version 4.2.0
```
e := echo.New()
	renderer := &Template{
		templates: template.Must(template.New("").Funcs(template.FuncMap{
			"safe": func(s string) template.HTMLAttr {
				return template.HTMLAttr(s)
			},
			"meta": func(s api.Tag) template.HTML {
				return template.HTML("<meta " + s.Key + "=\"" + s.Name + "\"  " + s.Type + "=\"" + s.Content + "\" data-vue-router-controlled />")
			},
		}).ParseGlob("web/dist/*.html")),
	}
	e.Renderer = renderer

	e.Pre(middleware.HTTPSRedirect())
	e.Pre(middleware.HTTPSNonWWWRedirect())
	e.Use(middleware.RemoveTrailingSlashWithConfig(middleware.TrailingSlashConfig{
		RedirectCode: http.StatusMovedPermanently,
	}))

	e.Use(middleware.Recover())
	e.Use(middleware.SecureWithConfig(middleware.SecureConfig{
		ReferrerPolicy: "no-referrer",
	}))
	e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
		Format: "method=${method}, uri=${uri}, status=${status}\n",
	}))
	e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{
		Timeout: 60 * time.Second,
	}))
	e.Logger.Fatal(e.StartTLS(":1323", "fullchain.pem", "privkey.pem"))

```

### Stacktrace
```
panic: Write called after Handler finished

goroutine 12657 [running]:
net/http.(*http2responseWriter).write(0xc00056c840, 0x8000, 0xc00070e000, 0x8000, 0x8000, 0x0, 0x0, 0x8000, 0x8000, 0x0)
	/snap/go/7013/src/net/http/h2_bundle.go:6244 +0x1f7
net/http.(*http2responseWriter).Write(0xc00056c840, 0xc00070e000, 0x8000, 0x8000, 0xc00072f958, 0x4acf63, 0xc00056c858)
	/snap/go/7013/src/net/http/h2_bundle.go:6233 +0x56
github.com/labstack/echo/v4.(*Response).Write(0xc0005d0540, 0xc00070e000, 0x8000, 0x8000, 0x8000, 0x0, 0x0)
	/home/maciej/go/pkg/mod/github.com/labstack/echo/v4@v4.2.0/response.go:75 +0x69
io.copyBuffer(0xb63320, 0xc0005d0540, 0xb63580, 0xc0001b6ba0, 0xc00070e000, 0x8000, 0x8000, 0xa5c440, 0x0, 0xb63320)
	/snap/go/7013/src/io/io.go:411 +0x206
io.Copy(...)
	/snap/go/7013/src/io/io.go:368
io.CopyN(0xb63320, 0xc0005d0540, 0xb63b20, 0xc00056c858, 0x94104, 0x0, 0x0, 0x0)
	/snap/go/7013/src/io/io.go:344 +0x9a
net/http.serveContent(0xb6e860, 0xc0005d0540, 0xc0000b1a00, 0xc0001701ac, 0x10, 0x2d97886f, 0xed794cad9, 0xeca460, 0xc0004accb8, 0xb69780, ...)
	/snap/go/7013/src/net/http/fs.go:300 +0x2af
net/http.ServeContent(0xb6e860, 0xc0005d0540, 0xc0000b1a00, 0xc0001701ac, 0x10, 0x2d97886f, 0xed794cad9, 0xeca460, 0xb69780, 0xc00056c858)
	/snap/go/7013/src/net/http/fs.go:167 +0xd8
github.com/labstack/echo/v4.(*context).File(0xc0001cd360, 0xc000170180, 0x3c, 0x0, 0x0)
	/home/maciej/go/pkg/mod/github.com/labstack/echo/v4@v4.2.0/context.go:590 +0x1f4
webkomiksy.pl/m/api.pageImage(0xc00038e000, 0xb79e40, 0xc0001cd360, 0xc000673f50, 0x10, 0x0, 0x0)
	/home/maciej/projects/website/api/image.go:56 +0x219
webkomiksy.pl/m/api.(*Env).Image(0xc00038e000, 0xb79e40, 0xc0001cd360, 0xc0004c3000, 0xc00003ff30)
	/home/maciej/projects/website/api/image.go:21 +0x1a9
github.com/labstack/echo/v4.(*Echo).add.func1(0xb79e40, 0xc0001cd360, 0x5, 0xc00056c801)
	/home/maciej/go/pkg/mod/github.com/labstack/echo/v4@v4.2.0/echo.go:536 +0x62
main.main.func5.1(0xb79e40, 0xc0001cd360, 0xc00056c840, 0xadac68)
	/home/maciej/projects/website/server.go:105 +0x1ad
github.com/labstack/echo/v4/middleware.TimeoutWithConfig.func1.1.1(0xc0005d0780, 0xc000021ba0, 0xb79e40, 0xc0001cd360)
	/home/maciej/go/pkg/mod/github.com/labstack/echo/v4@v4.2.0/middleware/timeout.go:67 +0x3a
created by github.com/labstack/echo/v4/middleware.TimeoutWithConfig.func1.1
	/home/maciej/go/pkg/mod/github.com/labstack/echo/v4@v4.2.0/middleware/timeout.go:64 +0x230

```

### Expected behavior

The server will continue to work after a timeout on the request

### Actual behavior

Server is going down




Pull request:
panics in timeout middleware are not recovered and cause application to crash
Fix #1794: panics in timeout middleware are not recovered and cause application to crash (as those happen in different goroutine outside of recover middleware)

Patch:
diff --git a/middleware/timeout.go b/middleware/timeout.go
index d146541..4be557f 100644
--- a/middleware/timeout.go
+++ b/middleware/timeout.go
@@ -4,6 +4,7 @@ package middleware
 
 import (
 	"context"
+	"fmt"
 	"github.com/labstack/echo/v4"
 	"time"
 )
@@ -62,6 +63,17 @@ func TimeoutWithConfig(config TimeoutConfig) echo.MiddlewareFunc {
 
 			done := make(chan error, 1)
 			go func() {
+				defer func() {
+					if r := recover(); r != nil {
+						err, ok := r.(error)
+						if !ok {
+							err = fmt.Errorf("panic recovered in timeout middleware: %v", r)
+						}
+						c.Logger().Error(err)
+						done <- err
+					}
+				}()
+
 				// This goroutine will keep running even if this middleware times out and
 				// will be stopped when ctx.Done() is called down the next(c) call chain
 				done <- next(c)


Test patch:
diff --git a/middleware/timeout_test.go b/middleware/timeout_test.go
index c0e9459..faecc4c 100644
--- a/middleware/timeout_test.go
+++ b/middleware/timeout_test.go
@@ -175,3 +175,22 @@ func TestTimeoutTestRequestClone(t *testing.T) {
 	assert.NoError(t, err)
 
 }
+
+func TestTimeoutRecoversPanic(t *testing.T) {
+	t.Parallel()
+	m := TimeoutWithConfig(TimeoutConfig{
+		Timeout: 25 * time.Millisecond,
+	})
+
+	req := httptest.NewRequest(http.MethodGet, "/", nil)
+	rec := httptest.NewRecorder()
+
+	e := echo.New()
+	c := e.NewContext(req, rec)
+
+	err := m(func(c echo.Context) error {
+		panic("panic in handler")
+	})(c)
+
+	assert.Error(t, err, "panic recovered in timeout middleware: panic in handler")
+}