...

Source file src/github.com/gin-gonic/gin/recovery_test.go

Documentation: github.com/gin-gonic/gin

     1  // Copyright 2014 Manu Martinez-Almeida. All rights reserved.
     2  // Use of this source code is governed by a MIT style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gin
     6  
     7  import (
     8  	"fmt"
     9  	"net"
    10  	"net/http"
    11  	"os"
    12  	"strings"
    13  	"syscall"
    14  	"testing"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  func TestPanicClean(t *testing.T) {
    20  	buffer := new(strings.Builder)
    21  	router := New()
    22  	password := "my-super-secret-password"
    23  	router.Use(RecoveryWithWriter(buffer))
    24  	router.GET("/recovery", func(c *Context) {
    25  		c.AbortWithStatus(http.StatusBadRequest)
    26  		panic("Oupps, Houston, we have a problem")
    27  	})
    28  	// RUN
    29  	w := PerformRequest(router, "GET", "/recovery",
    30  		header{
    31  			Key:   "Host",
    32  			Value: "www.google.com",
    33  		},
    34  		header{
    35  			Key:   "Authorization",
    36  			Value: fmt.Sprintf("Bearer %s", password),
    37  		},
    38  		header{
    39  			Key:   "Content-Type",
    40  			Value: "application/json",
    41  		},
    42  	)
    43  	// TEST
    44  	assert.Equal(t, http.StatusBadRequest, w.Code)
    45  
    46  	// Check the buffer does not have the secret key
    47  	assert.NotContains(t, buffer.String(), password)
    48  }
    49  
    50  // TestPanicInHandler assert that panic has been recovered.
    51  func TestPanicInHandler(t *testing.T) {
    52  	buffer := new(strings.Builder)
    53  	router := New()
    54  	router.Use(RecoveryWithWriter(buffer))
    55  	router.GET("/recovery", func(_ *Context) {
    56  		panic("Oupps, Houston, we have a problem")
    57  	})
    58  	// RUN
    59  	w := PerformRequest(router, "GET", "/recovery")
    60  	// TEST
    61  	assert.Equal(t, http.StatusInternalServerError, w.Code)
    62  	assert.Contains(t, buffer.String(), "panic recovered")
    63  	assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem")
    64  	assert.Contains(t, buffer.String(), t.Name())
    65  	assert.NotContains(t, buffer.String(), "GET /recovery")
    66  
    67  	// Debug mode prints the request
    68  	SetMode(DebugMode)
    69  	// RUN
    70  	w = PerformRequest(router, "GET", "/recovery")
    71  	// TEST
    72  	assert.Equal(t, http.StatusInternalServerError, w.Code)
    73  	assert.Contains(t, buffer.String(), "GET /recovery")
    74  
    75  	SetMode(TestMode)
    76  }
    77  
    78  // TestPanicWithAbort assert that panic has been recovered even if context.Abort was used.
    79  func TestPanicWithAbort(t *testing.T) {
    80  	router := New()
    81  	router.Use(RecoveryWithWriter(nil))
    82  	router.GET("/recovery", func(c *Context) {
    83  		c.AbortWithStatus(http.StatusBadRequest)
    84  		panic("Oupps, Houston, we have a problem")
    85  	})
    86  	// RUN
    87  	w := PerformRequest(router, "GET", "/recovery")
    88  	// TEST
    89  	assert.Equal(t, http.StatusBadRequest, w.Code)
    90  }
    91  
    92  func TestSource(t *testing.T) {
    93  	bs := source(nil, 0)
    94  	assert.Equal(t, dunno, bs)
    95  
    96  	in := [][]byte{
    97  		[]byte("Hello world."),
    98  		[]byte("Hi, gin.."),
    99  	}
   100  	bs = source(in, 10)
   101  	assert.Equal(t, dunno, bs)
   102  
   103  	bs = source(in, 1)
   104  	assert.Equal(t, []byte("Hello world."), bs)
   105  }
   106  
   107  func TestFunction(t *testing.T) {
   108  	bs := function(1)
   109  	assert.Equal(t, dunno, bs)
   110  }
   111  
   112  // TestPanicWithBrokenPipe asserts that recovery specifically handles
   113  // writing responses to broken pipes
   114  func TestPanicWithBrokenPipe(t *testing.T) {
   115  	const expectCode = 204
   116  
   117  	expectMsgs := map[syscall.Errno]string{
   118  		syscall.EPIPE:      "broken pipe",
   119  		syscall.ECONNRESET: "connection reset by peer",
   120  	}
   121  
   122  	for errno, expectMsg := range expectMsgs {
   123  		t.Run(expectMsg, func(t *testing.T) {
   124  			var buf strings.Builder
   125  
   126  			router := New()
   127  			router.Use(RecoveryWithWriter(&buf))
   128  			router.GET("/recovery", func(c *Context) {
   129  				// Start writing response
   130  				c.Header("X-Test", "Value")
   131  				c.Status(expectCode)
   132  
   133  				// Oops. Client connection closed
   134  				e := &net.OpError{Err: &os.SyscallError{Err: errno}}
   135  				panic(e)
   136  			})
   137  			// RUN
   138  			w := PerformRequest(router, "GET", "/recovery")
   139  			// TEST
   140  			assert.Equal(t, expectCode, w.Code)
   141  			assert.Contains(t, strings.ToLower(buf.String()), expectMsg)
   142  		})
   143  	}
   144  }
   145  
   146  func TestCustomRecoveryWithWriter(t *testing.T) {
   147  	errBuffer := new(strings.Builder)
   148  	buffer := new(strings.Builder)
   149  	router := New()
   150  	handleRecovery := func(c *Context, err any) {
   151  		errBuffer.WriteString(err.(string))
   152  		c.AbortWithStatus(http.StatusBadRequest)
   153  	}
   154  	router.Use(CustomRecoveryWithWriter(buffer, handleRecovery))
   155  	router.GET("/recovery", func(_ *Context) {
   156  		panic("Oupps, Houston, we have a problem")
   157  	})
   158  	// RUN
   159  	w := PerformRequest(router, "GET", "/recovery")
   160  	// TEST
   161  	assert.Equal(t, http.StatusBadRequest, w.Code)
   162  	assert.Contains(t, buffer.String(), "panic recovered")
   163  	assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem")
   164  	assert.Contains(t, buffer.String(), t.Name())
   165  	assert.NotContains(t, buffer.String(), "GET /recovery")
   166  
   167  	// Debug mode prints the request
   168  	SetMode(DebugMode)
   169  	// RUN
   170  	w = PerformRequest(router, "GET", "/recovery")
   171  	// TEST
   172  	assert.Equal(t, http.StatusBadRequest, w.Code)
   173  	assert.Contains(t, buffer.String(), "GET /recovery")
   174  
   175  	assert.Equal(t, strings.Repeat("Oupps, Houston, we have a problem", 2), errBuffer.String())
   176  
   177  	SetMode(TestMode)
   178  }
   179  
   180  func TestCustomRecovery(t *testing.T) {
   181  	errBuffer := new(strings.Builder)
   182  	buffer := new(strings.Builder)
   183  	router := New()
   184  	DefaultErrorWriter = buffer
   185  	handleRecovery := func(c *Context, err any) {
   186  		errBuffer.WriteString(err.(string))
   187  		c.AbortWithStatus(http.StatusBadRequest)
   188  	}
   189  	router.Use(CustomRecovery(handleRecovery))
   190  	router.GET("/recovery", func(_ *Context) {
   191  		panic("Oupps, Houston, we have a problem")
   192  	})
   193  	// RUN
   194  	w := PerformRequest(router, "GET", "/recovery")
   195  	// TEST
   196  	assert.Equal(t, http.StatusBadRequest, w.Code)
   197  	assert.Contains(t, buffer.String(), "panic recovered")
   198  	assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem")
   199  	assert.Contains(t, buffer.String(), t.Name())
   200  	assert.NotContains(t, buffer.String(), "GET /recovery")
   201  
   202  	// Debug mode prints the request
   203  	SetMode(DebugMode)
   204  	// RUN
   205  	w = PerformRequest(router, "GET", "/recovery")
   206  	// TEST
   207  	assert.Equal(t, http.StatusBadRequest, w.Code)
   208  	assert.Contains(t, buffer.String(), "GET /recovery")
   209  
   210  	assert.Equal(t, strings.Repeat("Oupps, Houston, we have a problem", 2), errBuffer.String())
   211  
   212  	SetMode(TestMode)
   213  }
   214  
   215  func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) {
   216  	errBuffer := new(strings.Builder)
   217  	buffer := new(strings.Builder)
   218  	router := New()
   219  	DefaultErrorWriter = buffer
   220  	handleRecovery := func(c *Context, err any) {
   221  		errBuffer.WriteString(err.(string))
   222  		c.AbortWithStatus(http.StatusBadRequest)
   223  	}
   224  	router.Use(RecoveryWithWriter(DefaultErrorWriter, handleRecovery))
   225  	router.GET("/recovery", func(_ *Context) {
   226  		panic("Oupps, Houston, we have a problem")
   227  	})
   228  	// RUN
   229  	w := PerformRequest(router, "GET", "/recovery")
   230  	// TEST
   231  	assert.Equal(t, http.StatusBadRequest, w.Code)
   232  	assert.Contains(t, buffer.String(), "panic recovered")
   233  	assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem")
   234  	assert.Contains(t, buffer.String(), t.Name())
   235  	assert.NotContains(t, buffer.String(), "GET /recovery")
   236  
   237  	// Debug mode prints the request
   238  	SetMode(DebugMode)
   239  	// RUN
   240  	w = PerformRequest(router, "GET", "/recovery")
   241  	// TEST
   242  	assert.Equal(t, http.StatusBadRequest, w.Code)
   243  	assert.Contains(t, buffer.String(), "GET /recovery")
   244  
   245  	assert.Equal(t, strings.Repeat("Oupps, Houston, we have a problem", 2), errBuffer.String())
   246  
   247  	SetMode(TestMode)
   248  }
   249  

View as plain text