Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix panic stack trace being printed during recovery of broken pipe (#1089) #1259

Merged
merged 40 commits into from Nov 6, 2018
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
80916dc
Fix panic stack trace being printed during recovery of broken pipe (#…
Feb 27, 2018
133a62d
Add test for recovery of broken pipe (#1089)
Feb 28, 2018
d3bcb45
Fix spelling in test for recovery of broken pipe (#1089)
justinfx Feb 28, 2018
e7078e9
Merge branch 'master' into 1089_broken_pipe
justinfx Sep 17, 2018
97c4c98
Fix stack function not being called with params (merge conflict) (ref…
justinfx Sep 17, 2018
d3a05e4
gofmt recovery changes (refs #1259)
justinfx Sep 17, 2018
076e182
chore: add a version file includes gin version (#1549)
thinkerou Sep 17, 2018
10ba502
feat: replace debug log with fmt package. (#1560)
appleboy Sep 19, 2018
8a7ccc3
chore: recover go master build, partial revert #1514 (#1561)
javierprovecho Sep 20, 2018
500ad23
chore: fix typo and add a little anotation (#1562)
thinkerou Sep 20, 2018
d02ae1e
improve panic information when a catch-all wildcard conflict occurs (…
thinkerou Sep 21, 2018
1e326a1
add release badge for readme (#1533)
thinkerou Sep 22, 2018
5548cc3
Don't log requests (#1370)
dustin-decker Sep 23, 2018
946eba9
ci: fast finish when build failed (#1568)
thinkerou Sep 25, 2018
425f80b
Make logger use a yellow background and a darkgray text for legibilit…
Sep 26, 2018
d1b243d
feat: add go version judge when print debug warning log (#1572)
thinkerou Sep 26, 2018
331ad88
fix: fmt output log to os.Stderr (#1571)
appleboy Sep 27, 2018
1ded27a
chore: fix debug test error (#1574)
thinkerou Sep 27, 2018
e4e4270
remove allow fail flag (#1573)
appleboy Sep 27, 2018
5cc05e9
removed use of sync.pool from HandleContext and added test coverage (…
japettyjohn Oct 1, 2018
9a0fc0f
Update README.md (#1583)
Oct 8, 2018
bf40076
replace deprecated HeaderMap with Header() (#1585)
andriikushch Oct 11, 2018
cd693b4
fix(Makefile): golint to new URL (#1592)
appleboy Oct 14, 2018
2bcc84a
refactor(Makefile): allow overriding default go program (#1593)
appleboy Oct 14, 2018
e3a3b89
attempt to support go module (#1569)
thinkerou Oct 15, 2018
d369746
document: add docs dir and middleware document (#1521)
thinkerou Oct 15, 2018
37c19a7
vendor: upgrade some dependency package version (#1596)
thinkerou Oct 15, 2018
a5395d5
Fix LoadHTML* tests (#1559)
sponomarev Oct 16, 2018
de4743e
add example to set and get cookies (#1599)
Clivern Oct 17, 2018
64ea6f1
add gin user - photoprism (#1601)
thinkerou Oct 19, 2018
940d1f5
Pass MaxMultipartMemory when FormFile is called (#1600)
Oct 22, 2018
c03447e
Expose HandlerFunc in RouteInfos (#1272)
loopfz Oct 23, 2018
4940983
FIX r.LoadHTMLGlob("/path/to/templates") (#1616)
forging2012 Oct 31, 2018
58e2fbc
Added some comments to avoid having golint warnings (#1619)
Nov 1, 2018
6c0566e
Change HTML input tags to use HTML5 syntax. (#1617)
jbampton Nov 1, 2018
7f6ddf2
Change HTML link tags to use HTML5 syntax. (#1621)
jbampton Nov 1, 2018
17b319b
Update README.md (#1620)
chenyang929 Nov 1, 2018
a3804b6
Only build recovery tests for go >=1.7
justinfx Nov 3, 2018
92abfec
Merge branch 'master' into 1089_broken_pipe
justinfx Nov 3, 2018
9cbf651
Merge branch 'master' into 1089_broken_pipe
thinkerou Nov 6, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 29 additions & 5 deletions recovery.go
Expand Up @@ -10,9 +10,12 @@ import (
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httputil"
"os"
"runtime"
"syscall"
"time"
)

Expand All @@ -37,16 +40,37 @@ func RecoveryWithWriter(out io.Writer) HandlerFunc {
return func(c *Context) {
defer func() {
if err := recover(); err != nil {
// Check for a broken connection, as it is not really a
// condition that warrants a panic stack trace.
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if se.Err == syscall.EPIPE || se.Err == syscall.ECONNRESET {
brokenPipe = true
}
}
}
if logger != nil {
stack := stack(3)
if IsDebugging() {
httprequest, _ := httputil.DumpRequest(c.Request, false)
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset)
httprequest, _ := httputil.DumpRequest(c.Request, false)
if brokenPipe {
logger.Printf("%s\n%s%s", err, string(httprequest), reset)
} else if IsDebugging() {
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s",
timeFormat(time.Now()), string(httprequest), err, stack, reset)
} else {
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s", timeFormat(time.Now()), err, stack, reset)
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s",
timeFormat(time.Now()), err, stack, reset)
}
}
c.AbortWithStatus(http.StatusInternalServerError)

// If the connection is dead, we can't write a status to it.
if brokenPipe {
c.Error(err.(error))
c.Abort()
} else {
c.AbortWithStatus(http.StatusInternalServerError)
}
}
}()
c.Next()
Expand Down
40 changes: 40 additions & 0 deletions recovery_test.go
Expand Up @@ -2,11 +2,16 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

// +build go1.7

package gin

import (
"bytes"
"net"
"net/http"
"os"
"syscall"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -72,3 +77,38 @@ func TestFunction(t *testing.T) {
bs := function(1)
assert.Equal(t, []byte("???"), bs)
}

// TestPanicWithBrokenPipe asserts that recovery specifically handles
// writing responses to broken pipes
func TestPanicWithBrokenPipe(t *testing.T) {
const expectCode = 204

expectMsgs := map[syscall.Errno]string{
syscall.EPIPE: "broken pipe",
syscall.ECONNRESET: "connection reset",
}

for errno, expectMsg := range expectMsgs {
t.Run(expectMsg, func(t *testing.T) {

var buf bytes.Buffer

router := New()
router.Use(RecoveryWithWriter(&buf))
router.GET("/recovery", func(c *Context) {
// Start writing response
c.Header("X-Test", "Value")
c.Status(expectCode)

// Oops. Client connection closed
e := &net.OpError{Err: &os.SyscallError{Err: errno}}
panic(e)
})
// RUN
w := performRequest(router, "GET", "/recovery")
// TEST
assert.Equal(t, expectCode, w.Code)
assert.Contains(t, buf.String(), expectMsg)
})
}
}