...

Source file src/github.com/gin-gonic/gin/context_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  	"bytes"
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"html/template"
    13  	"io"
    14  	"mime/multipart"
    15  	"net"
    16  	"net/http"
    17  	"net/http/httptest"
    18  	"net/url"
    19  	"os"
    20  	"reflect"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/gin-contrib/sse"
    27  	"github.com/gin-gonic/gin/binding"
    28  	testdata "github.com/gin-gonic/gin/testdata/protoexample"
    29  	"github.com/stretchr/testify/assert"
    30  	"google.golang.org/protobuf/proto"
    31  )
    32  
    33  var _ context.Context = (*Context)(nil)
    34  
    35  var errTestRender = errors.New("TestRender")
    36  
    37  // Unit tests TODO
    38  // func (c *Context) File(filepath string) {
    39  // func (c *Context) Negotiate(code int, config Negotiate) {
    40  // BAD case: func (c *Context) Render(code int, render render.Render, obj ...any) {
    41  // test that information is not leaked when reusing Contexts (using the Pool)
    42  
    43  func createMultipartRequest() *http.Request {
    44  	boundary := "--testboundary"
    45  	body := new(bytes.Buffer)
    46  	mw := multipart.NewWriter(body)
    47  	defer mw.Close()
    48  
    49  	must(mw.SetBoundary(boundary))
    50  	must(mw.WriteField("foo", "bar"))
    51  	must(mw.WriteField("bar", "10"))
    52  	must(mw.WriteField("bar", "foo2"))
    53  	must(mw.WriteField("array", "first"))
    54  	must(mw.WriteField("array", "second"))
    55  	must(mw.WriteField("id", ""))
    56  	must(mw.WriteField("time_local", "31/12/2016 14:55"))
    57  	must(mw.WriteField("time_utc", "31/12/2016 14:55"))
    58  	must(mw.WriteField("time_location", "31/12/2016 14:55"))
    59  	must(mw.WriteField("names[a]", "thinkerou"))
    60  	must(mw.WriteField("names[b]", "tianou"))
    61  	req, err := http.NewRequest("POST", "/", body)
    62  	must(err)
    63  	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
    64  	return req
    65  }
    66  
    67  func must(err error) {
    68  	if err != nil {
    69  		panic(err.Error())
    70  	}
    71  }
    72  
    73  func TestContextFormFile(t *testing.T) {
    74  	buf := new(bytes.Buffer)
    75  	mw := multipart.NewWriter(buf)
    76  	w, err := mw.CreateFormFile("file", "test")
    77  	if assert.NoError(t, err) {
    78  		_, err = w.Write([]byte("test"))
    79  		assert.NoError(t, err)
    80  	}
    81  	mw.Close()
    82  	c, _ := CreateTestContext(httptest.NewRecorder())
    83  	c.Request, _ = http.NewRequest("POST", "/", buf)
    84  	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
    85  	f, err := c.FormFile("file")
    86  	if assert.NoError(t, err) {
    87  		assert.Equal(t, "test", f.Filename)
    88  	}
    89  
    90  	assert.NoError(t, c.SaveUploadedFile(f, "test"))
    91  }
    92  
    93  func TestContextMultipartForm(t *testing.T) {
    94  	buf := new(bytes.Buffer)
    95  	mw := multipart.NewWriter(buf)
    96  	assert.NoError(t, mw.WriteField("foo", "bar"))
    97  	w, err := mw.CreateFormFile("file", "test")
    98  	if assert.NoError(t, err) {
    99  		_, err = w.Write([]byte("test"))
   100  		assert.NoError(t, err)
   101  	}
   102  	mw.Close()
   103  	c, _ := CreateTestContext(httptest.NewRecorder())
   104  	c.Request, _ = http.NewRequest("POST", "/", buf)
   105  	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
   106  	f, err := c.MultipartForm()
   107  	if assert.NoError(t, err) {
   108  		assert.NotNil(t, f)
   109  	}
   110  
   111  	assert.NoError(t, c.SaveUploadedFile(f.File["file"][0], "test"))
   112  }
   113  
   114  func TestSaveUploadedOpenFailed(t *testing.T) {
   115  	buf := new(bytes.Buffer)
   116  	mw := multipart.NewWriter(buf)
   117  	mw.Close()
   118  
   119  	c, _ := CreateTestContext(httptest.NewRecorder())
   120  	c.Request, _ = http.NewRequest("POST", "/", buf)
   121  	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
   122  
   123  	f := &multipart.FileHeader{
   124  		Filename: "file",
   125  	}
   126  	assert.Error(t, c.SaveUploadedFile(f, "test"))
   127  }
   128  
   129  func TestSaveUploadedCreateFailed(t *testing.T) {
   130  	buf := new(bytes.Buffer)
   131  	mw := multipart.NewWriter(buf)
   132  	w, err := mw.CreateFormFile("file", "test")
   133  	if assert.NoError(t, err) {
   134  		_, err = w.Write([]byte("test"))
   135  		assert.NoError(t, err)
   136  	}
   137  	mw.Close()
   138  	c, _ := CreateTestContext(httptest.NewRecorder())
   139  	c.Request, _ = http.NewRequest("POST", "/", buf)
   140  	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
   141  	f, err := c.FormFile("file")
   142  	if assert.NoError(t, err) {
   143  		assert.Equal(t, "test", f.Filename)
   144  	}
   145  
   146  	assert.Error(t, c.SaveUploadedFile(f, "/"))
   147  }
   148  
   149  func TestContextReset(t *testing.T) {
   150  	router := New()
   151  	c := router.allocateContext(0)
   152  	assert.Equal(t, c.engine, router)
   153  
   154  	c.index = 2
   155  	c.Writer = &responseWriter{ResponseWriter: httptest.NewRecorder()}
   156  	c.Params = Params{Param{}}
   157  	c.Error(errors.New("test")) //nolint: errcheck
   158  	c.Set("foo", "bar")
   159  	c.reset()
   160  
   161  	assert.False(t, c.IsAborted())
   162  	assert.Nil(t, c.Keys)
   163  	assert.Nil(t, c.Accepted)
   164  	assert.Len(t, c.Errors, 0)
   165  	assert.Empty(t, c.Errors.Errors())
   166  	assert.Empty(t, c.Errors.ByType(ErrorTypeAny))
   167  	assert.Len(t, c.Params, 0)
   168  	assert.EqualValues(t, c.index, -1)
   169  	assert.Equal(t, c.Writer.(*responseWriter), &c.writermem)
   170  }
   171  
   172  func TestContextHandlers(t *testing.T) {
   173  	c, _ := CreateTestContext(httptest.NewRecorder())
   174  	assert.Nil(t, c.handlers)
   175  	assert.Nil(t, c.handlers.Last())
   176  
   177  	c.handlers = HandlersChain{}
   178  	assert.NotNil(t, c.handlers)
   179  	assert.Nil(t, c.handlers.Last())
   180  
   181  	f := func(c *Context) {}
   182  	g := func(c *Context) {}
   183  
   184  	c.handlers = HandlersChain{f}
   185  	compareFunc(t, f, c.handlers.Last())
   186  
   187  	c.handlers = HandlersChain{f, g}
   188  	compareFunc(t, g, c.handlers.Last())
   189  }
   190  
   191  // TestContextSetGet tests that a parameter is set correctly on the
   192  // current context and can be retrieved using Get.
   193  func TestContextSetGet(t *testing.T) {
   194  	c, _ := CreateTestContext(httptest.NewRecorder())
   195  	c.Set("foo", "bar")
   196  
   197  	value, err := c.Get("foo")
   198  	assert.Equal(t, "bar", value)
   199  	assert.True(t, err)
   200  
   201  	value, err = c.Get("foo2")
   202  	assert.Nil(t, value)
   203  	assert.False(t, err)
   204  
   205  	assert.Equal(t, "bar", c.MustGet("foo"))
   206  	assert.Panics(t, func() { c.MustGet("no_exist") })
   207  }
   208  
   209  func TestContextSetGetValues(t *testing.T) {
   210  	c, _ := CreateTestContext(httptest.NewRecorder())
   211  	c.Set("string", "this is a string")
   212  	c.Set("int32", int32(-42))
   213  	c.Set("int64", int64(42424242424242))
   214  	c.Set("uint64", uint64(42))
   215  	c.Set("float32", float32(4.2))
   216  	c.Set("float64", 4.2)
   217  	var a any = 1
   218  	c.Set("intInterface", a)
   219  
   220  	assert.Exactly(t, c.MustGet("string").(string), "this is a string")
   221  	assert.Exactly(t, c.MustGet("int32").(int32), int32(-42))
   222  	assert.Exactly(t, c.MustGet("int64").(int64), int64(42424242424242))
   223  	assert.Exactly(t, c.MustGet("uint64").(uint64), uint64(42))
   224  	assert.Exactly(t, c.MustGet("float32").(float32), float32(4.2))
   225  	assert.Exactly(t, c.MustGet("float64").(float64), 4.2)
   226  	assert.Exactly(t, c.MustGet("intInterface").(int), 1)
   227  }
   228  
   229  func TestContextGetString(t *testing.T) {
   230  	c, _ := CreateTestContext(httptest.NewRecorder())
   231  	c.Set("string", "this is a string")
   232  	assert.Equal(t, "this is a string", c.GetString("string"))
   233  }
   234  
   235  func TestContextSetGetBool(t *testing.T) {
   236  	c, _ := CreateTestContext(httptest.NewRecorder())
   237  	c.Set("bool", true)
   238  	assert.True(t, c.GetBool("bool"))
   239  }
   240  
   241  func TestContextGetInt(t *testing.T) {
   242  	c, _ := CreateTestContext(httptest.NewRecorder())
   243  	c.Set("int", 1)
   244  	assert.Equal(t, 1, c.GetInt("int"))
   245  }
   246  
   247  func TestContextGetInt64(t *testing.T) {
   248  	c, _ := CreateTestContext(httptest.NewRecorder())
   249  	c.Set("int64", int64(42424242424242))
   250  	assert.Equal(t, int64(42424242424242), c.GetInt64("int64"))
   251  }
   252  
   253  func TestContextGetUint(t *testing.T) {
   254  	c, _ := CreateTestContext(httptest.NewRecorder())
   255  	c.Set("uint", uint(1))
   256  	assert.Equal(t, uint(1), c.GetUint("uint"))
   257  }
   258  
   259  func TestContextGetUint64(t *testing.T) {
   260  	c, _ := CreateTestContext(httptest.NewRecorder())
   261  	c.Set("uint64", uint64(18446744073709551615))
   262  	assert.Equal(t, uint64(18446744073709551615), c.GetUint64("uint64"))
   263  }
   264  
   265  func TestContextGetFloat64(t *testing.T) {
   266  	c, _ := CreateTestContext(httptest.NewRecorder())
   267  	c.Set("float64", 4.2)
   268  	assert.Equal(t, 4.2, c.GetFloat64("float64"))
   269  }
   270  
   271  func TestContextGetTime(t *testing.T) {
   272  	c, _ := CreateTestContext(httptest.NewRecorder())
   273  	t1, _ := time.Parse("1/2/2006 15:04:05", "01/01/2017 12:00:00")
   274  	c.Set("time", t1)
   275  	assert.Equal(t, t1, c.GetTime("time"))
   276  }
   277  
   278  func TestContextGetDuration(t *testing.T) {
   279  	c, _ := CreateTestContext(httptest.NewRecorder())
   280  	c.Set("duration", time.Second)
   281  	assert.Equal(t, time.Second, c.GetDuration("duration"))
   282  }
   283  
   284  func TestContextGetStringSlice(t *testing.T) {
   285  	c, _ := CreateTestContext(httptest.NewRecorder())
   286  	c.Set("slice", []string{"foo"})
   287  	assert.Equal(t, []string{"foo"}, c.GetStringSlice("slice"))
   288  }
   289  
   290  func TestContextGetStringMap(t *testing.T) {
   291  	c, _ := CreateTestContext(httptest.NewRecorder())
   292  	m := make(map[string]any)
   293  	m["foo"] = 1
   294  	c.Set("map", m)
   295  
   296  	assert.Equal(t, m, c.GetStringMap("map"))
   297  	assert.Equal(t, 1, c.GetStringMap("map")["foo"])
   298  }
   299  
   300  func TestContextGetStringMapString(t *testing.T) {
   301  	c, _ := CreateTestContext(httptest.NewRecorder())
   302  	m := make(map[string]string)
   303  	m["foo"] = "bar"
   304  	c.Set("map", m)
   305  
   306  	assert.Equal(t, m, c.GetStringMapString("map"))
   307  	assert.Equal(t, "bar", c.GetStringMapString("map")["foo"])
   308  }
   309  
   310  func TestContextGetStringMapStringSlice(t *testing.T) {
   311  	c, _ := CreateTestContext(httptest.NewRecorder())
   312  	m := make(map[string][]string)
   313  	m["foo"] = []string{"foo"}
   314  	c.Set("map", m)
   315  
   316  	assert.Equal(t, m, c.GetStringMapStringSlice("map"))
   317  	assert.Equal(t, []string{"foo"}, c.GetStringMapStringSlice("map")["foo"])
   318  }
   319  
   320  func TestContextCopy(t *testing.T) {
   321  	c, _ := CreateTestContext(httptest.NewRecorder())
   322  	c.index = 2
   323  	c.Request, _ = http.NewRequest("POST", "/hola", nil)
   324  	c.handlers = HandlersChain{func(c *Context) {}}
   325  	c.Params = Params{Param{Key: "foo", Value: "bar"}}
   326  	c.Set("foo", "bar")
   327  
   328  	cp := c.Copy()
   329  	assert.Nil(t, cp.handlers)
   330  	assert.Nil(t, cp.writermem.ResponseWriter)
   331  	assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter))
   332  	assert.Equal(t, cp.Request, c.Request)
   333  	assert.Equal(t, cp.index, abortIndex)
   334  	assert.Equal(t, cp.Keys, c.Keys)
   335  	assert.Equal(t, cp.engine, c.engine)
   336  	assert.Equal(t, cp.Params, c.Params)
   337  	cp.Set("foo", "notBar")
   338  	assert.False(t, cp.Keys["foo"] == c.Keys["foo"])
   339  }
   340  
   341  func TestContextHandlerName(t *testing.T) {
   342  	c, _ := CreateTestContext(httptest.NewRecorder())
   343  	c.handlers = HandlersChain{func(c *Context) {}, handlerNameTest}
   344  
   345  	assert.Regexp(t, "^(.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest$", c.HandlerName())
   346  }
   347  
   348  func TestContextHandlerNames(t *testing.T) {
   349  	c, _ := CreateTestContext(httptest.NewRecorder())
   350  	c.handlers = HandlersChain{func(c *Context) {}, handlerNameTest, func(c *Context) {}, handlerNameTest2}
   351  
   352  	names := c.HandlerNames()
   353  
   354  	assert.True(t, len(names) == 4)
   355  	for _, name := range names {
   356  		assert.Regexp(t, `^(.*/vendor/)?(github\.com/gin-gonic/gin\.){1}(TestContextHandlerNames\.func.*){0,1}(handlerNameTest.*){0,1}`, name)
   357  	}
   358  }
   359  
   360  func handlerNameTest(c *Context) {
   361  }
   362  
   363  func handlerNameTest2(c *Context) {
   364  }
   365  
   366  var handlerTest HandlerFunc = func(c *Context) {
   367  }
   368  
   369  func TestContextHandler(t *testing.T) {
   370  	c, _ := CreateTestContext(httptest.NewRecorder())
   371  	c.handlers = HandlersChain{func(c *Context) {}, handlerTest}
   372  
   373  	assert.Equal(t, reflect.ValueOf(handlerTest).Pointer(), reflect.ValueOf(c.Handler()).Pointer())
   374  }
   375  
   376  func TestContextQuery(t *testing.T) {
   377  	c, _ := CreateTestContext(httptest.NewRecorder())
   378  	c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&id=", nil)
   379  
   380  	value, ok := c.GetQuery("foo")
   381  	assert.True(t, ok)
   382  	assert.Equal(t, "bar", value)
   383  	assert.Equal(t, "bar", c.DefaultQuery("foo", "none"))
   384  	assert.Equal(t, "bar", c.Query("foo"))
   385  
   386  	value, ok = c.GetQuery("page")
   387  	assert.True(t, ok)
   388  	assert.Equal(t, "10", value)
   389  	assert.Equal(t, "10", c.DefaultQuery("page", "0"))
   390  	assert.Equal(t, "10", c.Query("page"))
   391  
   392  	value, ok = c.GetQuery("id")
   393  	assert.True(t, ok)
   394  	assert.Empty(t, value)
   395  	assert.Empty(t, c.DefaultQuery("id", "nada"))
   396  	assert.Empty(t, c.Query("id"))
   397  
   398  	value, ok = c.GetQuery("NoKey")
   399  	assert.False(t, ok)
   400  	assert.Empty(t, value)
   401  	assert.Equal(t, "nada", c.DefaultQuery("NoKey", "nada"))
   402  	assert.Empty(t, c.Query("NoKey"))
   403  
   404  	// postform should not mess
   405  	value, ok = c.GetPostForm("page")
   406  	assert.False(t, ok)
   407  	assert.Empty(t, value)
   408  	assert.Empty(t, c.PostForm("foo"))
   409  }
   410  
   411  func TestContextDefaultQueryOnEmptyRequest(t *testing.T) {
   412  	c, _ := CreateTestContext(httptest.NewRecorder()) // here c.Request == nil
   413  	assert.NotPanics(t, func() {
   414  		value, ok := c.GetQuery("NoKey")
   415  		assert.False(t, ok)
   416  		assert.Empty(t, value)
   417  	})
   418  	assert.NotPanics(t, func() {
   419  		assert.Equal(t, "nada", c.DefaultQuery("NoKey", "nada"))
   420  	})
   421  	assert.NotPanics(t, func() {
   422  		assert.Empty(t, c.Query("NoKey"))
   423  	})
   424  }
   425  
   426  func TestContextQueryAndPostForm(t *testing.T) {
   427  	c, _ := CreateTestContext(httptest.NewRecorder())
   428  	body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second")
   429  	c.Request, _ = http.NewRequest("POST",
   430  		"/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body)
   431  	c.Request.Header.Add("Content-Type", MIMEPOSTForm)
   432  
   433  	assert.Equal(t, "bar", c.DefaultPostForm("foo", "none"))
   434  	assert.Equal(t, "bar", c.PostForm("foo"))
   435  	assert.Empty(t, c.Query("foo"))
   436  
   437  	value, ok := c.GetPostForm("page")
   438  	assert.True(t, ok)
   439  	assert.Equal(t, "11", value)
   440  	assert.Equal(t, "11", c.DefaultPostForm("page", "0"))
   441  	assert.Equal(t, "11", c.PostForm("page"))
   442  	assert.Empty(t, c.Query("page"))
   443  
   444  	value, ok = c.GetPostForm("both")
   445  	assert.True(t, ok)
   446  	assert.Empty(t, value)
   447  	assert.Empty(t, c.PostForm("both"))
   448  	assert.Empty(t, c.DefaultPostForm("both", "nothing"))
   449  	assert.Equal(t, "GET", c.Query("both"), "GET")
   450  
   451  	value, ok = c.GetQuery("id")
   452  	assert.True(t, ok)
   453  	assert.Equal(t, "main", value)
   454  	assert.Equal(t, "000", c.DefaultPostForm("id", "000"))
   455  	assert.Equal(t, "main", c.Query("id"))
   456  	assert.Empty(t, c.PostForm("id"))
   457  
   458  	value, ok = c.GetQuery("NoKey")
   459  	assert.False(t, ok)
   460  	assert.Empty(t, value)
   461  	value, ok = c.GetPostForm("NoKey")
   462  	assert.False(t, ok)
   463  	assert.Empty(t, value)
   464  	assert.Equal(t, "nada", c.DefaultPostForm("NoKey", "nada"))
   465  	assert.Equal(t, "nothing", c.DefaultQuery("NoKey", "nothing"))
   466  	assert.Empty(t, c.PostForm("NoKey"))
   467  	assert.Empty(t, c.Query("NoKey"))
   468  
   469  	var obj struct {
   470  		Foo   string   `form:"foo"`
   471  		ID    string   `form:"id"`
   472  		Page  int      `form:"page"`
   473  		Both  string   `form:"both"`
   474  		Array []string `form:"array[]"`
   475  	}
   476  	assert.NoError(t, c.Bind(&obj))
   477  	assert.Equal(t, "bar", obj.Foo, "bar")
   478  	assert.Equal(t, "main", obj.ID, "main")
   479  	assert.Equal(t, 11, obj.Page, 11)
   480  	assert.Empty(t, obj.Both)
   481  	assert.Equal(t, []string{"first", "second"}, obj.Array)
   482  
   483  	values, ok := c.GetQueryArray("array[]")
   484  	assert.True(t, ok)
   485  	assert.Equal(t, "first", values[0])
   486  	assert.Equal(t, "second", values[1])
   487  
   488  	values = c.QueryArray("array[]")
   489  	assert.Equal(t, "first", values[0])
   490  	assert.Equal(t, "second", values[1])
   491  
   492  	values = c.QueryArray("nokey")
   493  	assert.Equal(t, 0, len(values))
   494  
   495  	values = c.QueryArray("both")
   496  	assert.Equal(t, 1, len(values))
   497  	assert.Equal(t, "GET", values[0])
   498  
   499  	dicts, ok := c.GetQueryMap("ids")
   500  	assert.True(t, ok)
   501  	assert.Equal(t, "hi", dicts["a"])
   502  	assert.Equal(t, "3.14", dicts["b"])
   503  
   504  	dicts, ok = c.GetQueryMap("nokey")
   505  	assert.False(t, ok)
   506  	assert.Equal(t, 0, len(dicts))
   507  
   508  	dicts, ok = c.GetQueryMap("both")
   509  	assert.False(t, ok)
   510  	assert.Equal(t, 0, len(dicts))
   511  
   512  	dicts, ok = c.GetQueryMap("array")
   513  	assert.False(t, ok)
   514  	assert.Equal(t, 0, len(dicts))
   515  
   516  	dicts = c.QueryMap("ids")
   517  	assert.Equal(t, "hi", dicts["a"])
   518  	assert.Equal(t, "3.14", dicts["b"])
   519  
   520  	dicts = c.QueryMap("nokey")
   521  	assert.Equal(t, 0, len(dicts))
   522  }
   523  
   524  func TestContextPostFormMultipart(t *testing.T) {
   525  	c, _ := CreateTestContext(httptest.NewRecorder())
   526  	c.Request = createMultipartRequest()
   527  
   528  	var obj struct {
   529  		Foo          string    `form:"foo"`
   530  		Bar          string    `form:"bar"`
   531  		BarAsInt     int       `form:"bar"`
   532  		Array        []string  `form:"array"`
   533  		ID           string    `form:"id"`
   534  		TimeLocal    time.Time `form:"time_local" time_format:"02/01/2006 15:04"`
   535  		TimeUTC      time.Time `form:"time_utc" time_format:"02/01/2006 15:04" time_utc:"1"`
   536  		TimeLocation time.Time `form:"time_location" time_format:"02/01/2006 15:04" time_location:"Asia/Tokyo"`
   537  		BlankTime    time.Time `form:"blank_time" time_format:"02/01/2006 15:04"`
   538  	}
   539  	assert.NoError(t, c.Bind(&obj))
   540  	assert.Equal(t, "bar", obj.Foo)
   541  	assert.Equal(t, "10", obj.Bar)
   542  	assert.Equal(t, 10, obj.BarAsInt)
   543  	assert.Equal(t, []string{"first", "second"}, obj.Array)
   544  	assert.Empty(t, obj.ID)
   545  	assert.Equal(t, "31/12/2016 14:55", obj.TimeLocal.Format("02/01/2006 15:04"))
   546  	assert.Equal(t, time.Local, obj.TimeLocal.Location())
   547  	assert.Equal(t, "31/12/2016 14:55", obj.TimeUTC.Format("02/01/2006 15:04"))
   548  	assert.Equal(t, time.UTC, obj.TimeUTC.Location())
   549  	loc, _ := time.LoadLocation("Asia/Tokyo")
   550  	assert.Equal(t, "31/12/2016 14:55", obj.TimeLocation.Format("02/01/2006 15:04"))
   551  	assert.Equal(t, loc, obj.TimeLocation.Location())
   552  	assert.True(t, obj.BlankTime.IsZero())
   553  
   554  	value, ok := c.GetQuery("foo")
   555  	assert.False(t, ok)
   556  	assert.Empty(t, value)
   557  	assert.Empty(t, c.Query("bar"))
   558  	assert.Equal(t, "nothing", c.DefaultQuery("id", "nothing"))
   559  
   560  	value, ok = c.GetPostForm("foo")
   561  	assert.True(t, ok)
   562  	assert.Equal(t, "bar", value)
   563  	assert.Equal(t, "bar", c.PostForm("foo"))
   564  
   565  	value, ok = c.GetPostForm("array")
   566  	assert.True(t, ok)
   567  	assert.Equal(t, "first", value)
   568  	assert.Equal(t, "first", c.PostForm("array"))
   569  
   570  	assert.Equal(t, "10", c.DefaultPostForm("bar", "nothing"))
   571  
   572  	value, ok = c.GetPostForm("id")
   573  	assert.True(t, ok)
   574  	assert.Empty(t, value)
   575  	assert.Empty(t, c.PostForm("id"))
   576  	assert.Empty(t, c.DefaultPostForm("id", "nothing"))
   577  
   578  	value, ok = c.GetPostForm("nokey")
   579  	assert.False(t, ok)
   580  	assert.Empty(t, value)
   581  	assert.Equal(t, "nothing", c.DefaultPostForm("nokey", "nothing"))
   582  
   583  	values, ok := c.GetPostFormArray("array")
   584  	assert.True(t, ok)
   585  	assert.Equal(t, "first", values[0])
   586  	assert.Equal(t, "second", values[1])
   587  
   588  	values = c.PostFormArray("array")
   589  	assert.Equal(t, "first", values[0])
   590  	assert.Equal(t, "second", values[1])
   591  
   592  	values = c.PostFormArray("nokey")
   593  	assert.Equal(t, 0, len(values))
   594  
   595  	values = c.PostFormArray("foo")
   596  	assert.Equal(t, 1, len(values))
   597  	assert.Equal(t, "bar", values[0])
   598  
   599  	dicts, ok := c.GetPostFormMap("names")
   600  	assert.True(t, ok)
   601  	assert.Equal(t, "thinkerou", dicts["a"])
   602  	assert.Equal(t, "tianou", dicts["b"])
   603  
   604  	dicts, ok = c.GetPostFormMap("nokey")
   605  	assert.False(t, ok)
   606  	assert.Equal(t, 0, len(dicts))
   607  
   608  	dicts = c.PostFormMap("names")
   609  	assert.Equal(t, "thinkerou", dicts["a"])
   610  	assert.Equal(t, "tianou", dicts["b"])
   611  
   612  	dicts = c.PostFormMap("nokey")
   613  	assert.Equal(t, 0, len(dicts))
   614  }
   615  
   616  func TestContextSetCookie(t *testing.T) {
   617  	c, _ := CreateTestContext(httptest.NewRecorder())
   618  	c.SetSameSite(http.SameSiteLaxMode)
   619  	c.SetCookie("user", "gin", 1, "/", "localhost", true, true)
   620  	assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie"))
   621  }
   622  
   623  func TestContextSetCookiePathEmpty(t *testing.T) {
   624  	c, _ := CreateTestContext(httptest.NewRecorder())
   625  	c.SetSameSite(http.SameSiteLaxMode)
   626  	c.SetCookie("user", "gin", 1, "", "localhost", true, true)
   627  	assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie"))
   628  }
   629  
   630  func TestContextGetCookie(t *testing.T) {
   631  	c, _ := CreateTestContext(httptest.NewRecorder())
   632  	c.Request, _ = http.NewRequest("GET", "/get", nil)
   633  	c.Request.Header.Set("Cookie", "user=gin")
   634  	cookie, _ := c.Cookie("user")
   635  	assert.Equal(t, "gin", cookie)
   636  
   637  	_, err := c.Cookie("nokey")
   638  	assert.Error(t, err)
   639  }
   640  
   641  func TestContextBodyAllowedForStatus(t *testing.T) {
   642  	assert.False(t, false, bodyAllowedForStatus(http.StatusProcessing))
   643  	assert.False(t, false, bodyAllowedForStatus(http.StatusNoContent))
   644  	assert.False(t, false, bodyAllowedForStatus(http.StatusNotModified))
   645  	assert.True(t, true, bodyAllowedForStatus(http.StatusInternalServerError))
   646  }
   647  
   648  type TestRender struct{}
   649  
   650  func (*TestRender) Render(http.ResponseWriter) error {
   651  	return errTestRender
   652  }
   653  
   654  func (*TestRender) WriteContentType(http.ResponseWriter) {}
   655  
   656  func TestContextRenderIfErr(t *testing.T) {
   657  	w := httptest.NewRecorder()
   658  	c, _ := CreateTestContext(w)
   659  
   660  	c.Render(http.StatusOK, &TestRender{})
   661  
   662  	assert.Equal(t, errorMsgs{&Error{Err: errTestRender, Type: 1}}, c.Errors)
   663  }
   664  
   665  // Tests that the response is serialized as JSON
   666  // and Content-Type is set to application/json
   667  // and special HTML characters are escaped
   668  func TestContextRenderJSON(t *testing.T) {
   669  	w := httptest.NewRecorder()
   670  	c, _ := CreateTestContext(w)
   671  
   672  	c.JSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
   673  
   674  	assert.Equal(t, http.StatusCreated, w.Code)
   675  	assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
   676  	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
   677  }
   678  
   679  // Tests that the response is serialized as JSONP
   680  // and Content-Type is set to application/javascript
   681  func TestContextRenderJSONP(t *testing.T) {
   682  	w := httptest.NewRecorder()
   683  	c, _ := CreateTestContext(w)
   684  	c.Request, _ = http.NewRequest("GET", "http://example.com/?callback=x", nil)
   685  
   686  	c.JSONP(http.StatusCreated, H{"foo": "bar"})
   687  
   688  	assert.Equal(t, http.StatusCreated, w.Code)
   689  	assert.Equal(t, "x({\"foo\":\"bar\"});", w.Body.String())
   690  	assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
   691  }
   692  
   693  // Tests that the response is serialized as JSONP
   694  // and Content-Type is set to application/json
   695  func TestContextRenderJSONPWithoutCallback(t *testing.T) {
   696  	w := httptest.NewRecorder()
   697  	c, _ := CreateTestContext(w)
   698  	c.Request, _ = http.NewRequest("GET", "http://example.com", nil)
   699  
   700  	c.JSONP(http.StatusCreated, H{"foo": "bar"})
   701  
   702  	assert.Equal(t, http.StatusCreated, w.Code)
   703  	assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
   704  	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
   705  }
   706  
   707  // Tests that no JSON is rendered if code is 204
   708  func TestContextRenderNoContentJSON(t *testing.T) {
   709  	w := httptest.NewRecorder()
   710  	c, _ := CreateTestContext(w)
   711  
   712  	c.JSON(http.StatusNoContent, H{"foo": "bar"})
   713  
   714  	assert.Equal(t, http.StatusNoContent, w.Code)
   715  	assert.Empty(t, w.Body.String())
   716  	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
   717  }
   718  
   719  // Tests that the response is serialized as JSON
   720  // we change the content-type before
   721  func TestContextRenderAPIJSON(t *testing.T) {
   722  	w := httptest.NewRecorder()
   723  	c, _ := CreateTestContext(w)
   724  
   725  	c.Header("Content-Type", "application/vnd.api+json")
   726  	c.JSON(http.StatusCreated, H{"foo": "bar"})
   727  
   728  	assert.Equal(t, http.StatusCreated, w.Code)
   729  	assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
   730  	assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type"))
   731  }
   732  
   733  // Tests that no Custom JSON is rendered if code is 204
   734  func TestContextRenderNoContentAPIJSON(t *testing.T) {
   735  	w := httptest.NewRecorder()
   736  	c, _ := CreateTestContext(w)
   737  
   738  	c.Header("Content-Type", "application/vnd.api+json")
   739  	c.JSON(http.StatusNoContent, H{"foo": "bar"})
   740  
   741  	assert.Equal(t, http.StatusNoContent, w.Code)
   742  	assert.Empty(t, w.Body.String())
   743  	assert.Equal(t, w.Header().Get("Content-Type"), "application/vnd.api+json")
   744  }
   745  
   746  // Tests that the response is serialized as JSON
   747  // and Content-Type is set to application/json
   748  func TestContextRenderIndentedJSON(t *testing.T) {
   749  	w := httptest.NewRecorder()
   750  	c, _ := CreateTestContext(w)
   751  
   752  	c.IndentedJSON(http.StatusCreated, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
   753  
   754  	assert.Equal(t, http.StatusCreated, w.Code)
   755  	assert.Equal(t, "{\n    \"bar\": \"foo\",\n    \"foo\": \"bar\",\n    \"nested\": {\n        \"foo\": \"bar\"\n    }\n}", w.Body.String())
   756  	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
   757  }
   758  
   759  // Tests that no Custom JSON is rendered if code is 204
   760  func TestContextRenderNoContentIndentedJSON(t *testing.T) {
   761  	w := httptest.NewRecorder()
   762  	c, _ := CreateTestContext(w)
   763  
   764  	c.IndentedJSON(http.StatusNoContent, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
   765  
   766  	assert.Equal(t, http.StatusNoContent, w.Code)
   767  	assert.Empty(t, w.Body.String())
   768  	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
   769  }
   770  
   771  // Tests that the response is serialized as Secure JSON
   772  // and Content-Type is set to application/json
   773  func TestContextRenderSecureJSON(t *testing.T) {
   774  	w := httptest.NewRecorder()
   775  	c, router := CreateTestContext(w)
   776  
   777  	router.SecureJsonPrefix("&&&START&&&")
   778  	c.SecureJSON(http.StatusCreated, []string{"foo", "bar"})
   779  
   780  	assert.Equal(t, http.StatusCreated, w.Code)
   781  	assert.Equal(t, "&&&START&&&[\"foo\",\"bar\"]", w.Body.String())
   782  	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
   783  }
   784  
   785  // Tests that no Custom JSON is rendered if code is 204
   786  func TestContextRenderNoContentSecureJSON(t *testing.T) {
   787  	w := httptest.NewRecorder()
   788  	c, _ := CreateTestContext(w)
   789  
   790  	c.SecureJSON(http.StatusNoContent, []string{"foo", "bar"})
   791  
   792  	assert.Equal(t, http.StatusNoContent, w.Code)
   793  	assert.Empty(t, w.Body.String())
   794  	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
   795  }
   796  
   797  func TestContextRenderNoContentAsciiJSON(t *testing.T) {
   798  	w := httptest.NewRecorder()
   799  	c, _ := CreateTestContext(w)
   800  
   801  	c.AsciiJSON(http.StatusNoContent, []string{"lang", "Go语言"})
   802  
   803  	assert.Equal(t, http.StatusNoContent, w.Code)
   804  	assert.Empty(t, w.Body.String())
   805  	assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
   806  }
   807  
   808  // Tests that the response is serialized as JSON
   809  // and Content-Type is set to application/json
   810  // and special HTML characters are preserved
   811  func TestContextRenderPureJSON(t *testing.T) {
   812  	w := httptest.NewRecorder()
   813  	c, _ := CreateTestContext(w)
   814  	c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
   815  	assert.Equal(t, http.StatusCreated, w.Code)
   816  	assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
   817  	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
   818  }
   819  
   820  // Tests that the response executes the templates
   821  // and responds with Content-Type set to text/html
   822  func TestContextRenderHTML(t *testing.T) {
   823  	w := httptest.NewRecorder()
   824  	c, router := CreateTestContext(w)
   825  
   826  	templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
   827  	router.SetHTMLTemplate(templ)
   828  
   829  	c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"})
   830  
   831  	assert.Equal(t, http.StatusCreated, w.Code)
   832  	assert.Equal(t, "Hello alexandernyquist", w.Body.String())
   833  	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
   834  }
   835  
   836  func TestContextRenderHTML2(t *testing.T) {
   837  	w := httptest.NewRecorder()
   838  	c, router := CreateTestContext(w)
   839  
   840  	// print debug warning log when Engine.trees > 0
   841  	router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
   842  	assert.Len(t, router.trees, 1)
   843  
   844  	templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
   845  	re := captureOutput(t, func() {
   846  		SetMode(DebugMode)
   847  		router.SetHTMLTemplate(templ)
   848  		SetMode(TestMode)
   849  	})
   850  
   851  	assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", re)
   852  
   853  	c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"})
   854  
   855  	assert.Equal(t, http.StatusCreated, w.Code)
   856  	assert.Equal(t, "Hello alexandernyquist", w.Body.String())
   857  	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
   858  }
   859  
   860  // Tests that no HTML is rendered if code is 204
   861  func TestContextRenderNoContentHTML(t *testing.T) {
   862  	w := httptest.NewRecorder()
   863  	c, router := CreateTestContext(w)
   864  	templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
   865  	router.SetHTMLTemplate(templ)
   866  
   867  	c.HTML(http.StatusNoContent, "t", H{"name": "alexandernyquist"})
   868  
   869  	assert.Equal(t, http.StatusNoContent, w.Code)
   870  	assert.Empty(t, w.Body.String())
   871  	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
   872  }
   873  
   874  // TestContextXML tests that the response is serialized as XML
   875  // and Content-Type is set to application/xml
   876  func TestContextRenderXML(t *testing.T) {
   877  	w := httptest.NewRecorder()
   878  	c, _ := CreateTestContext(w)
   879  
   880  	c.XML(http.StatusCreated, H{"foo": "bar"})
   881  
   882  	assert.Equal(t, http.StatusCreated, w.Code)
   883  	assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
   884  	assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
   885  }
   886  
   887  // Tests that no XML is rendered if code is 204
   888  func TestContextRenderNoContentXML(t *testing.T) {
   889  	w := httptest.NewRecorder()
   890  	c, _ := CreateTestContext(w)
   891  
   892  	c.XML(http.StatusNoContent, H{"foo": "bar"})
   893  
   894  	assert.Equal(t, http.StatusNoContent, w.Code)
   895  	assert.Empty(t, w.Body.String())
   896  	assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
   897  }
   898  
   899  // TestContextString tests that the response is returned
   900  // with Content-Type set to text/plain
   901  func TestContextRenderString(t *testing.T) {
   902  	w := httptest.NewRecorder()
   903  	c, _ := CreateTestContext(w)
   904  
   905  	c.String(http.StatusCreated, "test %s %d", "string", 2)
   906  
   907  	assert.Equal(t, http.StatusCreated, w.Code)
   908  	assert.Equal(t, "test string 2", w.Body.String())
   909  	assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
   910  }
   911  
   912  // Tests that no String is rendered if code is 204
   913  func TestContextRenderNoContentString(t *testing.T) {
   914  	w := httptest.NewRecorder()
   915  	c, _ := CreateTestContext(w)
   916  
   917  	c.String(http.StatusNoContent, "test %s %d", "string", 2)
   918  
   919  	assert.Equal(t, http.StatusNoContent, w.Code)
   920  	assert.Empty(t, w.Body.String())
   921  	assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
   922  }
   923  
   924  // TestContextString tests that the response is returned
   925  // with Content-Type set to text/html
   926  func TestContextRenderHTMLString(t *testing.T) {
   927  	w := httptest.NewRecorder()
   928  	c, _ := CreateTestContext(w)
   929  
   930  	c.Header("Content-Type", "text/html; charset=utf-8")
   931  	c.String(http.StatusCreated, "<html>%s %d</html>", "string", 3)
   932  
   933  	assert.Equal(t, http.StatusCreated, w.Code)
   934  	assert.Equal(t, "<html>string 3</html>", w.Body.String())
   935  	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
   936  }
   937  
   938  // Tests that no HTML String is rendered if code is 204
   939  func TestContextRenderNoContentHTMLString(t *testing.T) {
   940  	w := httptest.NewRecorder()
   941  	c, _ := CreateTestContext(w)
   942  
   943  	c.Header("Content-Type", "text/html; charset=utf-8")
   944  	c.String(http.StatusNoContent, "<html>%s %d</html>", "string", 3)
   945  
   946  	assert.Equal(t, http.StatusNoContent, w.Code)
   947  	assert.Empty(t, w.Body.String())
   948  	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
   949  }
   950  
   951  // TestContextData tests that the response can be written from `bytestring`
   952  // with specified MIME type
   953  func TestContextRenderData(t *testing.T) {
   954  	w := httptest.NewRecorder()
   955  	c, _ := CreateTestContext(w)
   956  
   957  	c.Data(http.StatusCreated, "text/csv", []byte(`foo,bar`))
   958  
   959  	assert.Equal(t, http.StatusCreated, w.Code)
   960  	assert.Equal(t, "foo,bar", w.Body.String())
   961  	assert.Equal(t, "text/csv", w.Header().Get("Content-Type"))
   962  }
   963  
   964  // Tests that no Custom Data is rendered if code is 204
   965  func TestContextRenderNoContentData(t *testing.T) {
   966  	w := httptest.NewRecorder()
   967  	c, _ := CreateTestContext(w)
   968  
   969  	c.Data(http.StatusNoContent, "text/csv", []byte(`foo,bar`))
   970  
   971  	assert.Equal(t, http.StatusNoContent, w.Code)
   972  	assert.Empty(t, w.Body.String())
   973  	assert.Equal(t, "text/csv", w.Header().Get("Content-Type"))
   974  }
   975  
   976  func TestContextRenderSSE(t *testing.T) {
   977  	w := httptest.NewRecorder()
   978  	c, _ := CreateTestContext(w)
   979  
   980  	c.SSEvent("float", 1.5)
   981  	c.Render(-1, sse.Event{
   982  		Id:   "123",
   983  		Data: "text",
   984  	})
   985  	c.SSEvent("chat", H{
   986  		"foo": "bar",
   987  		"bar": "foo",
   988  	})
   989  
   990  	assert.Equal(t, strings.Replace(w.Body.String(), " ", "", -1), strings.Replace("event:float\ndata:1.5\n\nid:123\ndata:text\n\nevent:chat\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\n", " ", "", -1))
   991  }
   992  
   993  func TestContextRenderFile(t *testing.T) {
   994  	w := httptest.NewRecorder()
   995  	c, _ := CreateTestContext(w)
   996  
   997  	c.Request, _ = http.NewRequest("GET", "/", nil)
   998  	c.File("./gin.go")
   999  
  1000  	assert.Equal(t, http.StatusOK, w.Code)
  1001  	assert.Contains(t, w.Body.String(), "func New() *Engine {")
  1002  	// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
  1003  	// else, Content-Type='text/x-go; charset=utf-8'
  1004  	assert.NotEqual(t, "", w.Header().Get("Content-Type"))
  1005  }
  1006  
  1007  func TestContextRenderFileFromFS(t *testing.T) {
  1008  	w := httptest.NewRecorder()
  1009  	c, _ := CreateTestContext(w)
  1010  
  1011  	c.Request, _ = http.NewRequest("GET", "/some/path", nil)
  1012  	c.FileFromFS("./gin.go", Dir(".", false))
  1013  
  1014  	assert.Equal(t, http.StatusOK, w.Code)
  1015  	assert.Contains(t, w.Body.String(), "func New() *Engine {")
  1016  	// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
  1017  	// else, Content-Type='text/x-go; charset=utf-8'
  1018  	assert.NotEqual(t, "", w.Header().Get("Content-Type"))
  1019  	assert.Equal(t, "/some/path", c.Request.URL.Path)
  1020  }
  1021  
  1022  func TestContextRenderAttachment(t *testing.T) {
  1023  	w := httptest.NewRecorder()
  1024  	c, _ := CreateTestContext(w)
  1025  	newFilename := "new_filename.go"
  1026  
  1027  	c.Request, _ = http.NewRequest("GET", "/", nil)
  1028  	c.FileAttachment("./gin.go", newFilename)
  1029  
  1030  	assert.Equal(t, 200, w.Code)
  1031  	assert.Contains(t, w.Body.String(), "func New() *Engine {")
  1032  	assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition"))
  1033  }
  1034  
  1035  func TestContextRenderAndEscapeAttachment(t *testing.T) {
  1036  	w := httptest.NewRecorder()
  1037  	c, _ := CreateTestContext(w)
  1038  	maliciousFilename := "tampering_field.sh\"; \\\"; dummy=.go"
  1039  	actualEscapedResponseFilename := "tampering_field.sh\\\"; \\\\\\\"; dummy=.go"
  1040  
  1041  	c.Request, _ = http.NewRequest("GET", "/", nil)
  1042  	c.FileAttachment("./gin.go", maliciousFilename)
  1043  
  1044  	assert.Equal(t, 200, w.Code)
  1045  	assert.Contains(t, w.Body.String(), "func New() *Engine {")
  1046  	assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", actualEscapedResponseFilename), w.Header().Get("Content-Disposition"))
  1047  }
  1048  
  1049  func TestContextRenderUTF8Attachment(t *testing.T) {
  1050  	w := httptest.NewRecorder()
  1051  	c, _ := CreateTestContext(w)
  1052  	newFilename := "new🧡_filename.go"
  1053  
  1054  	c.Request, _ = http.NewRequest("GET", "/", nil)
  1055  	c.FileAttachment("./gin.go", newFilename)
  1056  
  1057  	assert.Equal(t, 200, w.Code)
  1058  	assert.Contains(t, w.Body.String(), "func New() *Engine {")
  1059  	assert.Equal(t, `attachment; filename*=UTF-8''`+url.QueryEscape(newFilename), w.Header().Get("Content-Disposition"))
  1060  }
  1061  
  1062  // TestContextRenderYAML tests that the response is serialized as YAML
  1063  // and Content-Type is set to application/x-yaml
  1064  func TestContextRenderYAML(t *testing.T) {
  1065  	w := httptest.NewRecorder()
  1066  	c, _ := CreateTestContext(w)
  1067  
  1068  	c.YAML(http.StatusCreated, H{"foo": "bar"})
  1069  
  1070  	assert.Equal(t, http.StatusCreated, w.Code)
  1071  	assert.Equal(t, "foo: bar\n", w.Body.String())
  1072  	assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
  1073  }
  1074  
  1075  // TestContextRenderTOML tests that the response is serialized as TOML
  1076  // and Content-Type is set to application/toml
  1077  func TestContextRenderTOML(t *testing.T) {
  1078  	w := httptest.NewRecorder()
  1079  	c, _ := CreateTestContext(w)
  1080  
  1081  	c.TOML(http.StatusCreated, H{"foo": "bar"})
  1082  
  1083  	assert.Equal(t, http.StatusCreated, w.Code)
  1084  	assert.Equal(t, "foo = 'bar'\n", w.Body.String())
  1085  	assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type"))
  1086  }
  1087  
  1088  // TestContextRenderProtoBuf tests that the response is serialized as ProtoBuf
  1089  // and Content-Type is set to application/x-protobuf
  1090  // and we just use the example protobuf to check if the response is correct
  1091  func TestContextRenderProtoBuf(t *testing.T) {
  1092  	w := httptest.NewRecorder()
  1093  	c, _ := CreateTestContext(w)
  1094  
  1095  	reps := []int64{int64(1), int64(2)}
  1096  	label := "test"
  1097  	data := &testdata.Test{
  1098  		Label: &label,
  1099  		Reps:  reps,
  1100  	}
  1101  
  1102  	c.ProtoBuf(http.StatusCreated, data)
  1103  
  1104  	protoData, err := proto.Marshal(data)
  1105  	assert.NoError(t, err)
  1106  
  1107  	assert.Equal(t, http.StatusCreated, w.Code)
  1108  	assert.Equal(t, string(protoData), w.Body.String())
  1109  	assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
  1110  }
  1111  
  1112  func TestContextHeaders(t *testing.T) {
  1113  	c, _ := CreateTestContext(httptest.NewRecorder())
  1114  	c.Header("Content-Type", "text/plain")
  1115  	c.Header("X-Custom", "value")
  1116  
  1117  	assert.Equal(t, "text/plain", c.Writer.Header().Get("Content-Type"))
  1118  	assert.Equal(t, "value", c.Writer.Header().Get("X-Custom"))
  1119  
  1120  	c.Header("Content-Type", "text/html")
  1121  	c.Header("X-Custom", "")
  1122  
  1123  	assert.Equal(t, "text/html", c.Writer.Header().Get("Content-Type"))
  1124  	_, exist := c.Writer.Header()["X-Custom"]
  1125  	assert.False(t, exist)
  1126  }
  1127  
  1128  // TODO
  1129  func TestContextRenderRedirectWithRelativePath(t *testing.T) {
  1130  	w := httptest.NewRecorder()
  1131  	c, _ := CreateTestContext(w)
  1132  
  1133  	c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
  1134  	assert.Panics(t, func() { c.Redirect(299, "/new_path") })
  1135  	assert.Panics(t, func() { c.Redirect(309, "/new_path") })
  1136  
  1137  	c.Redirect(http.StatusMovedPermanently, "/path")
  1138  	c.Writer.WriteHeaderNow()
  1139  	assert.Equal(t, http.StatusMovedPermanently, w.Code)
  1140  	assert.Equal(t, "/path", w.Header().Get("Location"))
  1141  }
  1142  
  1143  func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
  1144  	w := httptest.NewRecorder()
  1145  	c, _ := CreateTestContext(w)
  1146  
  1147  	c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
  1148  	c.Redirect(http.StatusFound, "http://google.com")
  1149  	c.Writer.WriteHeaderNow()
  1150  
  1151  	assert.Equal(t, http.StatusFound, w.Code)
  1152  	assert.Equal(t, "http://google.com", w.Header().Get("Location"))
  1153  }
  1154  
  1155  func TestContextRenderRedirectWith201(t *testing.T) {
  1156  	w := httptest.NewRecorder()
  1157  	c, _ := CreateTestContext(w)
  1158  
  1159  	c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
  1160  	c.Redirect(http.StatusCreated, "/resource")
  1161  	c.Writer.WriteHeaderNow()
  1162  
  1163  	assert.Equal(t, http.StatusCreated, w.Code)
  1164  	assert.Equal(t, "/resource", w.Header().Get("Location"))
  1165  }
  1166  
  1167  func TestContextRenderRedirectAll(t *testing.T) {
  1168  	c, _ := CreateTestContext(httptest.NewRecorder())
  1169  	c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
  1170  	assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") })
  1171  	assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") })
  1172  	assert.Panics(t, func() { c.Redirect(299, "/resource") })
  1173  	assert.Panics(t, func() { c.Redirect(309, "/resource") })
  1174  	assert.NotPanics(t, func() { c.Redirect(http.StatusMultipleChoices, "/resource") })
  1175  	assert.NotPanics(t, func() { c.Redirect(http.StatusPermanentRedirect, "/resource") })
  1176  }
  1177  
  1178  func TestContextNegotiationWithJSON(t *testing.T) {
  1179  	w := httptest.NewRecorder()
  1180  	c, _ := CreateTestContext(w)
  1181  	c.Request, _ = http.NewRequest("POST", "", nil)
  1182  
  1183  	c.Negotiate(http.StatusOK, Negotiate{
  1184  		Offered: []string{MIMEJSON, MIMEXML, MIMEYAML},
  1185  		Data:    H{"foo": "bar"},
  1186  	})
  1187  
  1188  	assert.Equal(t, http.StatusOK, w.Code)
  1189  	assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
  1190  	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
  1191  }
  1192  
  1193  func TestContextNegotiationWithXML(t *testing.T) {
  1194  	w := httptest.NewRecorder()
  1195  	c, _ := CreateTestContext(w)
  1196  	c.Request, _ = http.NewRequest("POST", "", nil)
  1197  
  1198  	c.Negotiate(http.StatusOK, Negotiate{
  1199  		Offered: []string{MIMEXML, MIMEJSON, MIMEYAML},
  1200  		Data:    H{"foo": "bar"},
  1201  	})
  1202  
  1203  	assert.Equal(t, http.StatusOK, w.Code)
  1204  	assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
  1205  	assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
  1206  }
  1207  
  1208  func TestContextNegotiationWithYAML(t *testing.T) {
  1209  	w := httptest.NewRecorder()
  1210  	c, _ := CreateTestContext(w)
  1211  	c.Request, _ = http.NewRequest("POST", "", nil)
  1212  
  1213  	c.Negotiate(http.StatusOK, Negotiate{
  1214  		Offered: []string{MIMEYAML, MIMEXML, MIMEJSON, MIMETOML},
  1215  		Data:    H{"foo": "bar"},
  1216  	})
  1217  
  1218  	assert.Equal(t, http.StatusOK, w.Code)
  1219  	assert.Equal(t, "foo: bar\n", w.Body.String())
  1220  	assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
  1221  }
  1222  
  1223  func TestContextNegotiationWithTOML(t *testing.T) {
  1224  	w := httptest.NewRecorder()
  1225  	c, _ := CreateTestContext(w)
  1226  	c.Request, _ = http.NewRequest("POST", "", nil)
  1227  
  1228  	c.Negotiate(http.StatusOK, Negotiate{
  1229  		Offered: []string{MIMETOML, MIMEXML, MIMEJSON, MIMEYAML},
  1230  		Data:    H{"foo": "bar"},
  1231  	})
  1232  
  1233  	assert.Equal(t, http.StatusOK, w.Code)
  1234  	assert.Equal(t, "foo = 'bar'\n", w.Body.String())
  1235  	assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type"))
  1236  }
  1237  
  1238  func TestContextNegotiationWithHTML(t *testing.T) {
  1239  	w := httptest.NewRecorder()
  1240  	c, router := CreateTestContext(w)
  1241  	c.Request, _ = http.NewRequest("POST", "", nil)
  1242  	templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
  1243  	router.SetHTMLTemplate(templ)
  1244  
  1245  	c.Negotiate(http.StatusOK, Negotiate{
  1246  		Offered:  []string{MIMEHTML},
  1247  		Data:     H{"name": "gin"},
  1248  		HTMLName: "t",
  1249  	})
  1250  
  1251  	assert.Equal(t, http.StatusOK, w.Code)
  1252  	assert.Equal(t, "Hello gin", w.Body.String())
  1253  	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
  1254  }
  1255  
  1256  func TestContextNegotiationNotSupport(t *testing.T) {
  1257  	w := httptest.NewRecorder()
  1258  	c, _ := CreateTestContext(w)
  1259  	c.Request, _ = http.NewRequest("POST", "", nil)
  1260  
  1261  	c.Negotiate(http.StatusOK, Negotiate{
  1262  		Offered: []string{MIMEPOSTForm},
  1263  	})
  1264  
  1265  	assert.Equal(t, http.StatusNotAcceptable, w.Code)
  1266  	assert.Equal(t, c.index, abortIndex)
  1267  	assert.True(t, c.IsAborted())
  1268  }
  1269  
  1270  func TestContextNegotiationFormat(t *testing.T) {
  1271  	c, _ := CreateTestContext(httptest.NewRecorder())
  1272  	c.Request, _ = http.NewRequest("POST", "", nil)
  1273  
  1274  	assert.Panics(t, func() { c.NegotiateFormat() })
  1275  	assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
  1276  	assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML, MIMEJSON))
  1277  }
  1278  
  1279  func TestContextNegotiationFormatWithAccept(t *testing.T) {
  1280  	c, _ := CreateTestContext(httptest.NewRecorder())
  1281  	c.Request, _ = http.NewRequest("POST", "/", nil)
  1282  	c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
  1283  
  1284  	assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEJSON, MIMEXML))
  1285  	assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEXML, MIMEHTML))
  1286  	assert.Empty(t, c.NegotiateFormat(MIMEJSON))
  1287  }
  1288  
  1289  func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
  1290  	c, _ := CreateTestContext(httptest.NewRecorder())
  1291  	c.Request, _ = http.NewRequest("POST", "/", nil)
  1292  	c.Request.Header.Add("Accept", "*/*")
  1293  
  1294  	assert.Equal(t, c.NegotiateFormat("*/*"), "*/*")
  1295  	assert.Equal(t, c.NegotiateFormat("text/*"), "text/*")
  1296  	assert.Equal(t, c.NegotiateFormat("application/*"), "application/*")
  1297  	assert.Equal(t, c.NegotiateFormat(MIMEJSON), MIMEJSON)
  1298  	assert.Equal(t, c.NegotiateFormat(MIMEXML), MIMEXML)
  1299  	assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML)
  1300  
  1301  	c, _ = CreateTestContext(httptest.NewRecorder())
  1302  	c.Request, _ = http.NewRequest("POST", "/", nil)
  1303  	c.Request.Header.Add("Accept", "text/*")
  1304  
  1305  	assert.Equal(t, c.NegotiateFormat("*/*"), "*/*")
  1306  	assert.Equal(t, c.NegotiateFormat("text/*"), "text/*")
  1307  	assert.Equal(t, c.NegotiateFormat("application/*"), "")
  1308  	assert.Equal(t, c.NegotiateFormat(MIMEJSON), "")
  1309  	assert.Equal(t, c.NegotiateFormat(MIMEXML), "")
  1310  	assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML)
  1311  }
  1312  
  1313  func TestContextNegotiationFormatCustom(t *testing.T) {
  1314  	c, _ := CreateTestContext(httptest.NewRecorder())
  1315  	c.Request, _ = http.NewRequest("POST", "/", nil)
  1316  	c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
  1317  
  1318  	c.Accepted = nil
  1319  	c.SetAccepted(MIMEJSON, MIMEXML)
  1320  
  1321  	assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
  1322  	assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML, MIMEHTML))
  1323  	assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON))
  1324  }
  1325  
  1326  func TestContextNegotiationFormat2(t *testing.T) {
  1327  	c, _ := CreateTestContext(httptest.NewRecorder())
  1328  	c.Request, _ = http.NewRequest("POST", "/", nil)
  1329  	c.Request.Header.Add("Accept", "image/tiff-fx")
  1330  
  1331  	assert.Equal(t, "", c.NegotiateFormat("image/tiff"))
  1332  }
  1333  
  1334  func TestContextIsAborted(t *testing.T) {
  1335  	c, _ := CreateTestContext(httptest.NewRecorder())
  1336  	assert.False(t, c.IsAborted())
  1337  
  1338  	c.Abort()
  1339  	assert.True(t, c.IsAborted())
  1340  
  1341  	c.Next()
  1342  	assert.True(t, c.IsAborted())
  1343  
  1344  	c.index++
  1345  	assert.True(t, c.IsAborted())
  1346  }
  1347  
  1348  // TestContextData tests that the response can be written from `bytestring`
  1349  // with specified MIME type
  1350  func TestContextAbortWithStatus(t *testing.T) {
  1351  	w := httptest.NewRecorder()
  1352  	c, _ := CreateTestContext(w)
  1353  
  1354  	c.index = 4
  1355  	c.AbortWithStatus(http.StatusUnauthorized)
  1356  
  1357  	assert.Equal(t, abortIndex, c.index)
  1358  	assert.Equal(t, http.StatusUnauthorized, c.Writer.Status())
  1359  	assert.Equal(t, http.StatusUnauthorized, w.Code)
  1360  	assert.True(t, c.IsAborted())
  1361  }
  1362  
  1363  type testJSONAbortMsg struct {
  1364  	Foo string `json:"foo"`
  1365  	Bar string `json:"bar"`
  1366  }
  1367  
  1368  func TestContextAbortWithStatusJSON(t *testing.T) {
  1369  	w := httptest.NewRecorder()
  1370  	c, _ := CreateTestContext(w)
  1371  	c.index = 4
  1372  
  1373  	in := new(testJSONAbortMsg)
  1374  	in.Bar = "barValue"
  1375  	in.Foo = "fooValue"
  1376  
  1377  	c.AbortWithStatusJSON(http.StatusUnsupportedMediaType, in)
  1378  
  1379  	assert.Equal(t, abortIndex, c.index)
  1380  	assert.Equal(t, http.StatusUnsupportedMediaType, c.Writer.Status())
  1381  	assert.Equal(t, http.StatusUnsupportedMediaType, w.Code)
  1382  	assert.True(t, c.IsAborted())
  1383  
  1384  	contentType := w.Header().Get("Content-Type")
  1385  	assert.Equal(t, "application/json; charset=utf-8", contentType)
  1386  
  1387  	buf := new(bytes.Buffer)
  1388  	_, err := buf.ReadFrom(w.Body)
  1389  	assert.NoError(t, err)
  1390  	jsonStringBody := buf.String()
  1391  	assert.Equal(t, "{\"foo\":\"fooValue\",\"bar\":\"barValue\"}", jsonStringBody)
  1392  }
  1393  
  1394  func TestContextError(t *testing.T) {
  1395  	c, _ := CreateTestContext(httptest.NewRecorder())
  1396  	assert.Empty(t, c.Errors)
  1397  
  1398  	firstErr := errors.New("first error")
  1399  	c.Error(firstErr) //nolint: errcheck
  1400  	assert.Len(t, c.Errors, 1)
  1401  	assert.Equal(t, "Error #01: first error\n", c.Errors.String())
  1402  
  1403  	secondErr := errors.New("second error")
  1404  	c.Error(&Error{ //nolint: errcheck
  1405  		Err:  secondErr,
  1406  		Meta: "some data 2",
  1407  		Type: ErrorTypePublic,
  1408  	})
  1409  	assert.Len(t, c.Errors, 2)
  1410  
  1411  	assert.Equal(t, firstErr, c.Errors[0].Err)
  1412  	assert.Nil(t, c.Errors[0].Meta)
  1413  	assert.Equal(t, ErrorTypePrivate, c.Errors[0].Type)
  1414  
  1415  	assert.Equal(t, secondErr, c.Errors[1].Err)
  1416  	assert.Equal(t, "some data 2", c.Errors[1].Meta)
  1417  	assert.Equal(t, ErrorTypePublic, c.Errors[1].Type)
  1418  
  1419  	assert.Equal(t, c.Errors.Last(), c.Errors[1])
  1420  
  1421  	defer func() {
  1422  		if recover() == nil {
  1423  			t.Error("didn't panic")
  1424  		}
  1425  	}()
  1426  	c.Error(nil) //nolint: errcheck
  1427  }
  1428  
  1429  func TestContextTypedError(t *testing.T) {
  1430  	c, _ := CreateTestContext(httptest.NewRecorder())
  1431  	c.Error(errors.New("externo 0")).SetType(ErrorTypePublic)  //nolint: errcheck
  1432  	c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate) //nolint: errcheck
  1433  
  1434  	for _, err := range c.Errors.ByType(ErrorTypePublic) {
  1435  		assert.Equal(t, ErrorTypePublic, err.Type)
  1436  	}
  1437  	for _, err := range c.Errors.ByType(ErrorTypePrivate) {
  1438  		assert.Equal(t, ErrorTypePrivate, err.Type)
  1439  	}
  1440  	assert.Equal(t, []string{"externo 0", "interno 0"}, c.Errors.Errors())
  1441  }
  1442  
  1443  func TestContextAbortWithError(t *testing.T) {
  1444  	w := httptest.NewRecorder()
  1445  	c, _ := CreateTestContext(w)
  1446  
  1447  	c.AbortWithError(http.StatusUnauthorized, errors.New("bad input")).SetMeta("some input") //nolint: errcheck
  1448  
  1449  	assert.Equal(t, http.StatusUnauthorized, w.Code)
  1450  	assert.Equal(t, abortIndex, c.index)
  1451  	assert.True(t, c.IsAborted())
  1452  }
  1453  
  1454  func TestContextClientIP(t *testing.T) {
  1455  	c, _ := CreateTestContext(httptest.NewRecorder())
  1456  	c.Request, _ = http.NewRequest("POST", "/", nil)
  1457  	c.engine.trustedCIDRs, _ = c.engine.prepareTrustedCIDRs()
  1458  	resetContextForClientIPTests(c)
  1459  
  1460  	// Legacy tests (validating that the defaults don't break the
  1461  	// (insecure!) old behaviour)
  1462  	assert.Equal(t, "20.20.20.20", c.ClientIP())
  1463  
  1464  	c.Request.Header.Del("X-Forwarded-For")
  1465  	assert.Equal(t, "10.10.10.10", c.ClientIP())
  1466  
  1467  	c.Request.Header.Set("X-Forwarded-For", "30.30.30.30  ")
  1468  	assert.Equal(t, "30.30.30.30", c.ClientIP())
  1469  
  1470  	c.Request.Header.Del("X-Forwarded-For")
  1471  	c.Request.Header.Del("X-Real-IP")
  1472  	c.engine.TrustedPlatform = PlatformGoogleAppEngine
  1473  	assert.Equal(t, "50.50.50.50", c.ClientIP())
  1474  
  1475  	c.Request.Header.Del("X-Appengine-Remote-Addr")
  1476  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1477  
  1478  	// no port
  1479  	c.Request.RemoteAddr = "50.50.50.50"
  1480  	assert.Empty(t, c.ClientIP())
  1481  
  1482  	// Tests exercising the TrustedProxies functionality
  1483  	resetContextForClientIPTests(c)
  1484  
  1485  	// IPv6 support
  1486  	c.Request.RemoteAddr = "[::1]:12345"
  1487  	assert.Equal(t, "20.20.20.20", c.ClientIP())
  1488  
  1489  	resetContextForClientIPTests(c)
  1490  	// No trusted proxies
  1491  	_ = c.engine.SetTrustedProxies([]string{})
  1492  	c.engine.RemoteIPHeaders = []string{"X-Forwarded-For"}
  1493  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1494  
  1495  	// Disabled TrustedProxies feature
  1496  	_ = c.engine.SetTrustedProxies(nil)
  1497  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1498  
  1499  	// Last proxy is trusted, but the RemoteAddr is not
  1500  	_ = c.engine.SetTrustedProxies([]string{"30.30.30.30"})
  1501  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1502  
  1503  	// Only trust RemoteAddr
  1504  	_ = c.engine.SetTrustedProxies([]string{"40.40.40.40"})
  1505  	assert.Equal(t, "30.30.30.30", c.ClientIP())
  1506  
  1507  	// All steps are trusted
  1508  	_ = c.engine.SetTrustedProxies([]string{"40.40.40.40", "30.30.30.30", "20.20.20.20"})
  1509  	assert.Equal(t, "20.20.20.20", c.ClientIP())
  1510  
  1511  	// Use CIDR
  1512  	_ = c.engine.SetTrustedProxies([]string{"40.40.25.25/16", "30.30.30.30"})
  1513  	assert.Equal(t, "20.20.20.20", c.ClientIP())
  1514  
  1515  	// Use hostname that resolves to all the proxies
  1516  	_ = c.engine.SetTrustedProxies([]string{"foo"})
  1517  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1518  
  1519  	// Use hostname that returns an error
  1520  	_ = c.engine.SetTrustedProxies([]string{"bar"})
  1521  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1522  
  1523  	// X-Forwarded-For has a non-IP element
  1524  	_ = c.engine.SetTrustedProxies([]string{"40.40.40.40"})
  1525  	c.Request.Header.Set("X-Forwarded-For", " blah ")
  1526  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1527  
  1528  	// Result from LookupHost has non-IP element. This should never
  1529  	// happen, but we should test it to make sure we handle it
  1530  	// gracefully.
  1531  	_ = c.engine.SetTrustedProxies([]string{"baz"})
  1532  	c.Request.Header.Set("X-Forwarded-For", " 30.30.30.30 ")
  1533  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1534  
  1535  	_ = c.engine.SetTrustedProxies([]string{"40.40.40.40"})
  1536  	c.Request.Header.Del("X-Forwarded-For")
  1537  	c.engine.RemoteIPHeaders = []string{"X-Forwarded-For", "X-Real-IP"}
  1538  	assert.Equal(t, "10.10.10.10", c.ClientIP())
  1539  
  1540  	c.engine.RemoteIPHeaders = []string{}
  1541  	c.engine.TrustedPlatform = PlatformGoogleAppEngine
  1542  	assert.Equal(t, "50.50.50.50", c.ClientIP())
  1543  
  1544  	// Use custom TrustedPlatform header
  1545  	c.engine.TrustedPlatform = "X-CDN-IP"
  1546  	c.Request.Header.Set("X-CDN-IP", "80.80.80.80")
  1547  	assert.Equal(t, "80.80.80.80", c.ClientIP())
  1548  	// wrong header
  1549  	c.engine.TrustedPlatform = "X-Wrong-Header"
  1550  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1551  
  1552  	c.Request.Header.Del("X-CDN-IP")
  1553  	// TrustedPlatform is empty
  1554  	c.engine.TrustedPlatform = ""
  1555  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1556  
  1557  	// Test the legacy flag
  1558  	c.engine.AppEngine = true
  1559  	assert.Equal(t, "50.50.50.50", c.ClientIP())
  1560  	c.engine.AppEngine = false
  1561  	c.engine.TrustedPlatform = PlatformGoogleAppEngine
  1562  
  1563  	c.Request.Header.Del("X-Appengine-Remote-Addr")
  1564  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1565  
  1566  	c.engine.TrustedPlatform = PlatformCloudflare
  1567  	assert.Equal(t, "60.60.60.60", c.ClientIP())
  1568  
  1569  	c.Request.Header.Del("CF-Connecting-IP")
  1570  	assert.Equal(t, "40.40.40.40", c.ClientIP())
  1571  
  1572  	c.engine.TrustedPlatform = ""
  1573  
  1574  	// no port
  1575  	c.Request.RemoteAddr = "50.50.50.50"
  1576  	assert.Empty(t, c.ClientIP())
  1577  }
  1578  
  1579  func resetContextForClientIPTests(c *Context) {
  1580  	c.Request.Header.Set("X-Real-IP", " 10.10.10.10  ")
  1581  	c.Request.Header.Set("X-Forwarded-For", "  20.20.20.20, 30.30.30.30")
  1582  	c.Request.Header.Set("X-Appengine-Remote-Addr", "50.50.50.50")
  1583  	c.Request.Header.Set("CF-Connecting-IP", "60.60.60.60")
  1584  	c.Request.RemoteAddr = "  40.40.40.40:42123 "
  1585  	c.engine.TrustedPlatform = ""
  1586  	c.engine.trustedCIDRs = defaultTrustedCIDRs
  1587  	c.engine.AppEngine = false
  1588  }
  1589  
  1590  func TestContextContentType(t *testing.T) {
  1591  	c, _ := CreateTestContext(httptest.NewRecorder())
  1592  	c.Request, _ = http.NewRequest("POST", "/", nil)
  1593  	c.Request.Header.Set("Content-Type", "application/json; charset=utf-8")
  1594  
  1595  	assert.Equal(t, "application/json", c.ContentType())
  1596  }
  1597  
  1598  func TestContextAutoBindJSON(t *testing.T) {
  1599  	c, _ := CreateTestContext(httptest.NewRecorder())
  1600  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
  1601  	c.Request.Header.Add("Content-Type", MIMEJSON)
  1602  
  1603  	var obj struct {
  1604  		Foo string `json:"foo"`
  1605  		Bar string `json:"bar"`
  1606  	}
  1607  	assert.NoError(t, c.Bind(&obj))
  1608  	assert.Equal(t, "foo", obj.Bar)
  1609  	assert.Equal(t, "bar", obj.Foo)
  1610  	assert.Empty(t, c.Errors)
  1611  }
  1612  
  1613  func TestContextBindWithJSON(t *testing.T) {
  1614  	w := httptest.NewRecorder()
  1615  	c, _ := CreateTestContext(w)
  1616  
  1617  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
  1618  	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
  1619  
  1620  	var obj struct {
  1621  		Foo string `json:"foo"`
  1622  		Bar string `json:"bar"`
  1623  	}
  1624  	assert.NoError(t, c.BindJSON(&obj))
  1625  	assert.Equal(t, "foo", obj.Bar)
  1626  	assert.Equal(t, "bar", obj.Foo)
  1627  	assert.Equal(t, 0, w.Body.Len())
  1628  }
  1629  
  1630  func TestContextBindWithXML(t *testing.T) {
  1631  	w := httptest.NewRecorder()
  1632  	c, _ := CreateTestContext(w)
  1633  
  1634  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
  1635  		<root>
  1636  			<foo>FOO</foo>
  1637  		   	<bar>BAR</bar>
  1638  		</root>`))
  1639  	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
  1640  
  1641  	var obj struct {
  1642  		Foo string `xml:"foo"`
  1643  		Bar string `xml:"bar"`
  1644  	}
  1645  	assert.NoError(t, c.BindXML(&obj))
  1646  	assert.Equal(t, "FOO", obj.Foo)
  1647  	assert.Equal(t, "BAR", obj.Bar)
  1648  	assert.Equal(t, 0, w.Body.Len())
  1649  }
  1650  
  1651  func TestContextBindHeader(t *testing.T) {
  1652  	w := httptest.NewRecorder()
  1653  	c, _ := CreateTestContext(w)
  1654  
  1655  	c.Request, _ = http.NewRequest("POST", "/", nil)
  1656  	c.Request.Header.Add("rate", "8000")
  1657  	c.Request.Header.Add("domain", "music")
  1658  	c.Request.Header.Add("limit", "1000")
  1659  
  1660  	var testHeader struct {
  1661  		Rate   int    `header:"Rate"`
  1662  		Domain string `header:"Domain"`
  1663  		Limit  int    `header:"limit"`
  1664  	}
  1665  
  1666  	assert.NoError(t, c.BindHeader(&testHeader))
  1667  	assert.Equal(t, 8000, testHeader.Rate)
  1668  	assert.Equal(t, "music", testHeader.Domain)
  1669  	assert.Equal(t, 1000, testHeader.Limit)
  1670  	assert.Equal(t, 0, w.Body.Len())
  1671  }
  1672  
  1673  func TestContextBindWithQuery(t *testing.T) {
  1674  	w := httptest.NewRecorder()
  1675  	c, _ := CreateTestContext(w)
  1676  
  1677  	c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
  1678  
  1679  	var obj struct {
  1680  		Foo string `form:"foo"`
  1681  		Bar string `form:"bar"`
  1682  	}
  1683  	assert.NoError(t, c.BindQuery(&obj))
  1684  	assert.Equal(t, "foo", obj.Bar)
  1685  	assert.Equal(t, "bar", obj.Foo)
  1686  	assert.Equal(t, 0, w.Body.Len())
  1687  }
  1688  
  1689  func TestContextBindWithYAML(t *testing.T) {
  1690  	w := httptest.NewRecorder()
  1691  	c, _ := CreateTestContext(w)
  1692  
  1693  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
  1694  	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
  1695  
  1696  	var obj struct {
  1697  		Foo string `yaml:"foo"`
  1698  		Bar string `yaml:"bar"`
  1699  	}
  1700  	assert.NoError(t, c.BindYAML(&obj))
  1701  	assert.Equal(t, "foo", obj.Bar)
  1702  	assert.Equal(t, "bar", obj.Foo)
  1703  	assert.Equal(t, 0, w.Body.Len())
  1704  }
  1705  
  1706  func TestContextBindWithTOML(t *testing.T) {
  1707  	w := httptest.NewRecorder()
  1708  	c, _ := CreateTestContext(w)
  1709  
  1710  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo = 'bar'\nbar = 'foo'"))
  1711  	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
  1712  
  1713  	var obj struct {
  1714  		Foo string `toml:"foo"`
  1715  		Bar string `toml:"bar"`
  1716  	}
  1717  	assert.NoError(t, c.BindTOML(&obj))
  1718  	assert.Equal(t, "foo", obj.Bar)
  1719  	assert.Equal(t, "bar", obj.Foo)
  1720  	assert.Equal(t, 0, w.Body.Len())
  1721  }
  1722  
  1723  func TestContextBadAutoBind(t *testing.T) {
  1724  	w := httptest.NewRecorder()
  1725  	c, _ := CreateTestContext(w)
  1726  
  1727  	c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
  1728  	c.Request.Header.Add("Content-Type", MIMEJSON)
  1729  	var obj struct {
  1730  		Foo string `json:"foo"`
  1731  		Bar string `json:"bar"`
  1732  	}
  1733  
  1734  	assert.False(t, c.IsAborted())
  1735  	assert.Error(t, c.Bind(&obj))
  1736  	c.Writer.WriteHeaderNow()
  1737  
  1738  	assert.Empty(t, obj.Bar)
  1739  	assert.Empty(t, obj.Foo)
  1740  	assert.Equal(t, http.StatusBadRequest, w.Code)
  1741  	assert.True(t, c.IsAborted())
  1742  }
  1743  
  1744  func TestContextAutoShouldBindJSON(t *testing.T) {
  1745  	c, _ := CreateTestContext(httptest.NewRecorder())
  1746  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
  1747  	c.Request.Header.Add("Content-Type", MIMEJSON)
  1748  
  1749  	var obj struct {
  1750  		Foo string `json:"foo"`
  1751  		Bar string `json:"bar"`
  1752  	}
  1753  	assert.NoError(t, c.ShouldBind(&obj))
  1754  	assert.Equal(t, "foo", obj.Bar)
  1755  	assert.Equal(t, "bar", obj.Foo)
  1756  	assert.Empty(t, c.Errors)
  1757  }
  1758  
  1759  func TestContextShouldBindWithJSON(t *testing.T) {
  1760  	w := httptest.NewRecorder()
  1761  	c, _ := CreateTestContext(w)
  1762  
  1763  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
  1764  	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
  1765  
  1766  	var obj struct {
  1767  		Foo string `json:"foo"`
  1768  		Bar string `json:"bar"`
  1769  	}
  1770  	assert.NoError(t, c.ShouldBindJSON(&obj))
  1771  	assert.Equal(t, "foo", obj.Bar)
  1772  	assert.Equal(t, "bar", obj.Foo)
  1773  	assert.Equal(t, 0, w.Body.Len())
  1774  }
  1775  
  1776  func TestContextShouldBindWithXML(t *testing.T) {
  1777  	w := httptest.NewRecorder()
  1778  	c, _ := CreateTestContext(w)
  1779  
  1780  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
  1781  		<root>
  1782  			<foo>FOO</foo>
  1783  			<bar>BAR</bar>
  1784  		</root>`))
  1785  	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
  1786  
  1787  	var obj struct {
  1788  		Foo string `xml:"foo"`
  1789  		Bar string `xml:"bar"`
  1790  	}
  1791  	assert.NoError(t, c.ShouldBindXML(&obj))
  1792  	assert.Equal(t, "FOO", obj.Foo)
  1793  	assert.Equal(t, "BAR", obj.Bar)
  1794  	assert.Equal(t, 0, w.Body.Len())
  1795  }
  1796  
  1797  func TestContextShouldBindHeader(t *testing.T) {
  1798  	w := httptest.NewRecorder()
  1799  	c, _ := CreateTestContext(w)
  1800  
  1801  	c.Request, _ = http.NewRequest("POST", "/", nil)
  1802  	c.Request.Header.Add("rate", "8000")
  1803  	c.Request.Header.Add("domain", "music")
  1804  	c.Request.Header.Add("limit", "1000")
  1805  
  1806  	var testHeader struct {
  1807  		Rate   int    `header:"Rate"`
  1808  		Domain string `header:"Domain"`
  1809  		Limit  int    `header:"limit"`
  1810  	}
  1811  
  1812  	assert.NoError(t, c.ShouldBindHeader(&testHeader))
  1813  	assert.Equal(t, 8000, testHeader.Rate)
  1814  	assert.Equal(t, "music", testHeader.Domain)
  1815  	assert.Equal(t, 1000, testHeader.Limit)
  1816  	assert.Equal(t, 0, w.Body.Len())
  1817  }
  1818  
  1819  func TestContextShouldBindWithQuery(t *testing.T) {
  1820  	w := httptest.NewRecorder()
  1821  	c, _ := CreateTestContext(w)
  1822  
  1823  	c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", bytes.NewBufferString("foo=unused"))
  1824  
  1825  	var obj struct {
  1826  		Foo  string `form:"foo"`
  1827  		Bar  string `form:"bar"`
  1828  		Foo1 string `form:"Foo"`
  1829  		Bar1 string `form:"Bar"`
  1830  	}
  1831  	assert.NoError(t, c.ShouldBindQuery(&obj))
  1832  	assert.Equal(t, "foo", obj.Bar)
  1833  	assert.Equal(t, "bar", obj.Foo)
  1834  	assert.Equal(t, "foo1", obj.Bar1)
  1835  	assert.Equal(t, "bar1", obj.Foo1)
  1836  	assert.Equal(t, 0, w.Body.Len())
  1837  }
  1838  
  1839  func TestContextShouldBindWithYAML(t *testing.T) {
  1840  	w := httptest.NewRecorder()
  1841  	c, _ := CreateTestContext(w)
  1842  
  1843  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
  1844  	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
  1845  
  1846  	var obj struct {
  1847  		Foo string `yaml:"foo"`
  1848  		Bar string `yaml:"bar"`
  1849  	}
  1850  	assert.NoError(t, c.ShouldBindYAML(&obj))
  1851  	assert.Equal(t, "foo", obj.Bar)
  1852  	assert.Equal(t, "bar", obj.Foo)
  1853  	assert.Equal(t, 0, w.Body.Len())
  1854  }
  1855  
  1856  func TestContextShouldBindWithTOML(t *testing.T) {
  1857  	w := httptest.NewRecorder()
  1858  	c, _ := CreateTestContext(w)
  1859  
  1860  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'"))
  1861  	c.Request.Header.Add("Content-Type", MIMETOML) // set fake content-type
  1862  
  1863  	var obj struct {
  1864  		Foo string `toml:"foo"`
  1865  		Bar string `toml:"bar"`
  1866  	}
  1867  	assert.NoError(t, c.ShouldBindTOML(&obj))
  1868  	assert.Equal(t, "foo", obj.Bar)
  1869  	assert.Equal(t, "bar", obj.Foo)
  1870  	assert.Equal(t, 0, w.Body.Len())
  1871  }
  1872  
  1873  func TestContextBadAutoShouldBind(t *testing.T) {
  1874  	w := httptest.NewRecorder()
  1875  	c, _ := CreateTestContext(w)
  1876  
  1877  	c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
  1878  	c.Request.Header.Add("Content-Type", MIMEJSON)
  1879  	var obj struct {
  1880  		Foo string `json:"foo"`
  1881  		Bar string `json:"bar"`
  1882  	}
  1883  
  1884  	assert.False(t, c.IsAborted())
  1885  	assert.Error(t, c.ShouldBind(&obj))
  1886  
  1887  	assert.Empty(t, obj.Bar)
  1888  	assert.Empty(t, obj.Foo)
  1889  	assert.False(t, c.IsAborted())
  1890  }
  1891  
  1892  func TestContextShouldBindBodyWith(t *testing.T) {
  1893  	type typeA struct {
  1894  		Foo string `json:"foo" xml:"foo" binding:"required"`
  1895  	}
  1896  	type typeB struct {
  1897  		Bar string `json:"bar" xml:"bar" binding:"required"`
  1898  	}
  1899  	for _, tt := range []struct {
  1900  		name               string
  1901  		bindingA, bindingB binding.BindingBody
  1902  		bodyA, bodyB       string
  1903  	}{
  1904  		{
  1905  			name:     "JSON & JSON",
  1906  			bindingA: binding.JSON,
  1907  			bindingB: binding.JSON,
  1908  			bodyA:    `{"foo":"FOO"}`,
  1909  			bodyB:    `{"bar":"BAR"}`,
  1910  		},
  1911  		{
  1912  			name:     "JSON & XML",
  1913  			bindingA: binding.JSON,
  1914  			bindingB: binding.XML,
  1915  			bodyA:    `{"foo":"FOO"}`,
  1916  			bodyB: `<?xml version="1.0" encoding="UTF-8"?>
  1917  <root>
  1918     <bar>BAR</bar>
  1919  </root>`,
  1920  		},
  1921  		{
  1922  			name:     "XML & XML",
  1923  			bindingA: binding.XML,
  1924  			bindingB: binding.XML,
  1925  			bodyA: `<?xml version="1.0" encoding="UTF-8"?>
  1926  <root>
  1927     <foo>FOO</foo>
  1928  </root>`,
  1929  			bodyB: `<?xml version="1.0" encoding="UTF-8"?>
  1930  <root>
  1931     <bar>BAR</bar>
  1932  </root>`,
  1933  		},
  1934  	} {
  1935  		t.Logf("testing: %s", tt.name)
  1936  		// bodyA to typeA and typeB
  1937  		{
  1938  			w := httptest.NewRecorder()
  1939  			c, _ := CreateTestContext(w)
  1940  			c.Request, _ = http.NewRequest(
  1941  				"POST", "http://example.com", bytes.NewBufferString(tt.bodyA),
  1942  			)
  1943  			// When it binds to typeA and typeB, it finds the body is
  1944  			// not typeB but typeA.
  1945  			objA := typeA{}
  1946  			assert.NoError(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
  1947  			assert.Equal(t, typeA{"FOO"}, objA)
  1948  			objB := typeB{}
  1949  			assert.Error(t, c.ShouldBindBodyWith(&objB, tt.bindingB))
  1950  			assert.NotEqual(t, typeB{"BAR"}, objB)
  1951  		}
  1952  		// bodyB to typeA and typeB
  1953  		{
  1954  			// When it binds to typeA and typeB, it finds the body is
  1955  			// not typeA but typeB.
  1956  			w := httptest.NewRecorder()
  1957  			c, _ := CreateTestContext(w)
  1958  			c.Request, _ = http.NewRequest(
  1959  				"POST", "http://example.com", bytes.NewBufferString(tt.bodyB),
  1960  			)
  1961  			objA := typeA{}
  1962  			assert.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
  1963  			assert.NotEqual(t, typeA{"FOO"}, objA)
  1964  			objB := typeB{}
  1965  			assert.NoError(t, c.ShouldBindBodyWith(&objB, tt.bindingB))
  1966  			assert.Equal(t, typeB{"BAR"}, objB)
  1967  		}
  1968  	}
  1969  }
  1970  
  1971  func TestContextGolangContext(t *testing.T) {
  1972  	c, _ := CreateTestContext(httptest.NewRecorder())
  1973  	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
  1974  	assert.NoError(t, c.Err())
  1975  	assert.Nil(t, c.Done())
  1976  	ti, ok := c.Deadline()
  1977  	assert.Equal(t, ti, time.Time{})
  1978  	assert.False(t, ok)
  1979  	assert.Equal(t, c.Value(0), c.Request)
  1980  	assert.Equal(t, c.Value(ContextKey), c)
  1981  	assert.Nil(t, c.Value("foo"))
  1982  
  1983  	c.Set("foo", "bar")
  1984  	assert.Equal(t, "bar", c.Value("foo"))
  1985  	assert.Nil(t, c.Value(1))
  1986  }
  1987  
  1988  func TestWebsocketsRequired(t *testing.T) {
  1989  	// Example request from spec: https://tools.ietf.org/html/rfc6455#section-1.2
  1990  	c, _ := CreateTestContext(httptest.NewRecorder())
  1991  	c.Request, _ = http.NewRequest("GET", "/chat", nil)
  1992  	c.Request.Header.Set("Host", "server.example.com")
  1993  	c.Request.Header.Set("Upgrade", "websocket")
  1994  	c.Request.Header.Set("Connection", "Upgrade")
  1995  	c.Request.Header.Set("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
  1996  	c.Request.Header.Set("Origin", "http://example.com")
  1997  	c.Request.Header.Set("Sec-WebSocket-Protocol", "chat, superchat")
  1998  	c.Request.Header.Set("Sec-WebSocket-Version", "13")
  1999  
  2000  	assert.True(t, c.IsWebsocket())
  2001  
  2002  	// Normal request, no websocket required.
  2003  	c, _ = CreateTestContext(httptest.NewRecorder())
  2004  	c.Request, _ = http.NewRequest("GET", "/chat", nil)
  2005  	c.Request.Header.Set("Host", "server.example.com")
  2006  
  2007  	assert.False(t, c.IsWebsocket())
  2008  }
  2009  
  2010  func TestGetRequestHeaderValue(t *testing.T) {
  2011  	c, _ := CreateTestContext(httptest.NewRecorder())
  2012  	c.Request, _ = http.NewRequest("GET", "/chat", nil)
  2013  	c.Request.Header.Set("Gin-Version", "1.0.0")
  2014  
  2015  	assert.Equal(t, "1.0.0", c.GetHeader("Gin-Version"))
  2016  	assert.Empty(t, c.GetHeader("Connection"))
  2017  }
  2018  
  2019  func TestContextGetRawData(t *testing.T) {
  2020  	c, _ := CreateTestContext(httptest.NewRecorder())
  2021  	body := bytes.NewBufferString("Fetch binary post data")
  2022  	c.Request, _ = http.NewRequest("POST", "/", body)
  2023  	c.Request.Header.Add("Content-Type", MIMEPOSTForm)
  2024  
  2025  	data, err := c.GetRawData()
  2026  	assert.Nil(t, err)
  2027  	assert.Equal(t, "Fetch binary post data", string(data))
  2028  }
  2029  
  2030  func TestContextRenderDataFromReader(t *testing.T) {
  2031  	w := httptest.NewRecorder()
  2032  	c, _ := CreateTestContext(w)
  2033  
  2034  	body := "#!PNG some raw data"
  2035  	reader := strings.NewReader(body)
  2036  	contentLength := int64(len(body))
  2037  	contentType := "image/png"
  2038  	extraHeaders := map[string]string{"Content-Disposition": `attachment; filename="gopher.png"`}
  2039  
  2040  	c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
  2041  
  2042  	assert.Equal(t, http.StatusOK, w.Code)
  2043  	assert.Equal(t, body, w.Body.String())
  2044  	assert.Equal(t, contentType, w.Header().Get("Content-Type"))
  2045  	assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length"))
  2046  	assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition"))
  2047  }
  2048  
  2049  func TestContextRenderDataFromReaderNoHeaders(t *testing.T) {
  2050  	w := httptest.NewRecorder()
  2051  	c, _ := CreateTestContext(w)
  2052  
  2053  	body := "#!PNG some raw data"
  2054  	reader := strings.NewReader(body)
  2055  	contentLength := int64(len(body))
  2056  	contentType := "image/png"
  2057  
  2058  	c.DataFromReader(http.StatusOK, contentLength, contentType, reader, nil)
  2059  
  2060  	assert.Equal(t, http.StatusOK, w.Code)
  2061  	assert.Equal(t, body, w.Body.String())
  2062  	assert.Equal(t, contentType, w.Header().Get("Content-Type"))
  2063  	assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length"))
  2064  }
  2065  
  2066  type TestResponseRecorder struct {
  2067  	*httptest.ResponseRecorder
  2068  	closeChannel chan bool
  2069  }
  2070  
  2071  func (r *TestResponseRecorder) CloseNotify() <-chan bool {
  2072  	return r.closeChannel
  2073  }
  2074  
  2075  func (r *TestResponseRecorder) closeClient() {
  2076  	r.closeChannel <- true
  2077  }
  2078  
  2079  func CreateTestResponseRecorder() *TestResponseRecorder {
  2080  	return &TestResponseRecorder{
  2081  		httptest.NewRecorder(),
  2082  		make(chan bool, 1),
  2083  	}
  2084  }
  2085  
  2086  func TestContextStream(t *testing.T) {
  2087  	w := CreateTestResponseRecorder()
  2088  	c, _ := CreateTestContext(w)
  2089  
  2090  	stopStream := true
  2091  	c.Stream(func(w io.Writer) bool {
  2092  		defer func() {
  2093  			stopStream = false
  2094  		}()
  2095  
  2096  		_, err := w.Write([]byte("test"))
  2097  		assert.NoError(t, err)
  2098  
  2099  		return stopStream
  2100  	})
  2101  
  2102  	assert.Equal(t, "testtest", w.Body.String())
  2103  }
  2104  
  2105  func TestContextStreamWithClientGone(t *testing.T) {
  2106  	w := CreateTestResponseRecorder()
  2107  	c, _ := CreateTestContext(w)
  2108  
  2109  	c.Stream(func(writer io.Writer) bool {
  2110  		defer func() {
  2111  			w.closeClient()
  2112  		}()
  2113  
  2114  		_, err := writer.Write([]byte("test"))
  2115  		assert.NoError(t, err)
  2116  
  2117  		return true
  2118  	})
  2119  
  2120  	assert.Equal(t, "test", w.Body.String())
  2121  }
  2122  
  2123  func TestContextResetInHandler(t *testing.T) {
  2124  	w := CreateTestResponseRecorder()
  2125  	c, _ := CreateTestContext(w)
  2126  
  2127  	c.handlers = []HandlerFunc{
  2128  		func(c *Context) { c.reset() },
  2129  	}
  2130  	assert.NotPanics(t, func() {
  2131  		c.Next()
  2132  	})
  2133  }
  2134  
  2135  func TestRaceParamsContextCopy(t *testing.T) {
  2136  	DefaultWriter = os.Stdout
  2137  	router := Default()
  2138  	nameGroup := router.Group("/:name")
  2139  	var wg sync.WaitGroup
  2140  	wg.Add(2)
  2141  	{
  2142  		nameGroup.GET("/api", func(c *Context) {
  2143  			go func(c *Context, param string) {
  2144  				defer wg.Done()
  2145  				// First assert must be executed after the second request
  2146  				time.Sleep(50 * time.Millisecond)
  2147  				assert.Equal(t, c.Param("name"), param)
  2148  			}(c.Copy(), c.Param("name"))
  2149  		})
  2150  	}
  2151  	PerformRequest(router, "GET", "/name1/api")
  2152  	PerformRequest(router, "GET", "/name2/api")
  2153  	wg.Wait()
  2154  }
  2155  
  2156  func TestContextWithKeysMutex(t *testing.T) {
  2157  	c := &Context{}
  2158  	c.Set("foo", "bar")
  2159  
  2160  	value, err := c.Get("foo")
  2161  	assert.Equal(t, "bar", value)
  2162  	assert.True(t, err)
  2163  
  2164  	value, err = c.Get("foo2")
  2165  	assert.Nil(t, value)
  2166  	assert.False(t, err)
  2167  }
  2168  
  2169  func TestRemoteIPFail(t *testing.T) {
  2170  	c, _ := CreateTestContext(httptest.NewRecorder())
  2171  	c.Request, _ = http.NewRequest("POST", "/", nil)
  2172  	c.Request.RemoteAddr = "[:::]:80"
  2173  	ip := net.ParseIP(c.RemoteIP())
  2174  	trust := c.engine.isTrustedProxy(ip)
  2175  	assert.Nil(t, ip)
  2176  	assert.False(t, trust)
  2177  }
  2178  
  2179  func TestHasRequestContext(t *testing.T) {
  2180  	c, _ := CreateTestContext(httptest.NewRecorder())
  2181  	assert.False(t, c.hasRequestContext(), "no request, no fallback")
  2182  	c.engine.ContextWithFallback = true
  2183  	assert.False(t, c.hasRequestContext(), "no request, has fallback")
  2184  	c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
  2185  	assert.True(t, c.hasRequestContext(), "has request, has fallback")
  2186  	c.Request, _ = http.NewRequestWithContext(nil, "", "", nil) //nolint:staticcheck
  2187  	assert.False(t, c.hasRequestContext(), "has request with nil ctx, has fallback")
  2188  	c.engine.ContextWithFallback = false
  2189  	assert.False(t, c.hasRequestContext(), "has request, no fallback")
  2190  
  2191  	c = &Context{}
  2192  	assert.False(t, c.hasRequestContext(), "no request, no engine")
  2193  	c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
  2194  	assert.False(t, c.hasRequestContext(), "has request, no engine")
  2195  }
  2196  
  2197  func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) {
  2198  	c, _ := CreateTestContext(httptest.NewRecorder())
  2199  	// enable ContextWithFallback feature flag
  2200  	c.engine.ContextWithFallback = true
  2201  
  2202  	deadline, ok := c.Deadline()
  2203  	assert.Zero(t, deadline)
  2204  	assert.False(t, ok)
  2205  
  2206  	c2, _ := CreateTestContext(httptest.NewRecorder())
  2207  	// enable ContextWithFallback feature flag
  2208  	c2.engine.ContextWithFallback = true
  2209  
  2210  	c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
  2211  	d := time.Now().Add(time.Second)
  2212  	ctx, cancel := context.WithDeadline(context.Background(), d)
  2213  	defer cancel()
  2214  	c2.Request = c2.Request.WithContext(ctx)
  2215  	deadline, ok = c2.Deadline()
  2216  	assert.Equal(t, d, deadline)
  2217  	assert.True(t, ok)
  2218  }
  2219  
  2220  func TestContextWithFallbackDoneFromRequestContext(t *testing.T) {
  2221  	c, _ := CreateTestContext(httptest.NewRecorder())
  2222  	// enable ContextWithFallback feature flag
  2223  	c.engine.ContextWithFallback = true
  2224  
  2225  	assert.Nil(t, c.Done())
  2226  
  2227  	c2, _ := CreateTestContext(httptest.NewRecorder())
  2228  	// enable ContextWithFallback feature flag
  2229  	c2.engine.ContextWithFallback = true
  2230  
  2231  	c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
  2232  	ctx, cancel := context.WithCancel(context.Background())
  2233  	c2.Request = c2.Request.WithContext(ctx)
  2234  	cancel()
  2235  	assert.NotNil(t, <-c2.Done())
  2236  }
  2237  
  2238  func TestContextWithFallbackErrFromRequestContext(t *testing.T) {
  2239  	c, _ := CreateTestContext(httptest.NewRecorder())
  2240  	// enable ContextWithFallback feature flag
  2241  	c.engine.ContextWithFallback = true
  2242  
  2243  	assert.Nil(t, c.Err())
  2244  
  2245  	c2, _ := CreateTestContext(httptest.NewRecorder())
  2246  	// enable ContextWithFallback feature flag
  2247  	c2.engine.ContextWithFallback = true
  2248  
  2249  	c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
  2250  	ctx, cancel := context.WithCancel(context.Background())
  2251  	c2.Request = c2.Request.WithContext(ctx)
  2252  	cancel()
  2253  
  2254  	assert.EqualError(t, c2.Err(), context.Canceled.Error())
  2255  }
  2256  
  2257  func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
  2258  	type contextKey string
  2259  
  2260  	tests := []struct {
  2261  		name             string
  2262  		getContextAndKey func() (*Context, any)
  2263  		value            any
  2264  	}{
  2265  		{
  2266  			name: "c with struct context key",
  2267  			getContextAndKey: func() (*Context, any) {
  2268  				var key struct{}
  2269  				c, _ := CreateTestContext(httptest.NewRecorder())
  2270  				// enable ContextWithFallback feature flag
  2271  				c.engine.ContextWithFallback = true
  2272  				c.Request, _ = http.NewRequest("POST", "/", nil)
  2273  				c.Request = c.Request.WithContext(context.WithValue(context.TODO(), key, "value"))
  2274  				return c, key
  2275  			},
  2276  			value: "value",
  2277  		},
  2278  		{
  2279  			name: "c with string context key",
  2280  			getContextAndKey: func() (*Context, any) {
  2281  				c, _ := CreateTestContext(httptest.NewRecorder())
  2282  				// enable ContextWithFallback feature flag
  2283  				c.engine.ContextWithFallback = true
  2284  				c.Request, _ = http.NewRequest("POST", "/", nil)
  2285  				c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value"))
  2286  				return c, contextKey("key")
  2287  			},
  2288  			value: "value",
  2289  		},
  2290  		{
  2291  			name: "c with nil http.Request",
  2292  			getContextAndKey: func() (*Context, any) {
  2293  				c, _ := CreateTestContext(httptest.NewRecorder())
  2294  				// enable ContextWithFallback feature flag
  2295  				c.engine.ContextWithFallback = true
  2296  				c.Request = nil
  2297  				return c, "key"
  2298  			},
  2299  			value: nil,
  2300  		},
  2301  		{
  2302  			name: "c with nil http.Request.Context()",
  2303  			getContextAndKey: func() (*Context, any) {
  2304  				c, _ := CreateTestContext(httptest.NewRecorder())
  2305  				// enable ContextWithFallback feature flag
  2306  				c.engine.ContextWithFallback = true
  2307  				c.Request, _ = http.NewRequest("POST", "/", nil)
  2308  				return c, "key"
  2309  			},
  2310  			value: nil,
  2311  		},
  2312  	}
  2313  	for _, tt := range tests {
  2314  		t.Run(tt.name, func(t *testing.T) {
  2315  			c, key := tt.getContextAndKey()
  2316  			assert.Equal(t, tt.value, c.Value(key))
  2317  		})
  2318  	}
  2319  }
  2320  
  2321  func TestContextCopyShouldNotCancel(t *testing.T) {
  2322  	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
  2323  		w.WriteHeader(http.StatusOK)
  2324  	}))
  2325  	defer srv.Close()
  2326  
  2327  	ensureRequestIsOver := make(chan struct{})
  2328  
  2329  	wg := &sync.WaitGroup{}
  2330  
  2331  	r := New()
  2332  	r.GET("/", func(ginctx *Context) {
  2333  		wg.Add(1)
  2334  
  2335  		ginctx = ginctx.Copy()
  2336  
  2337  		// start async goroutine for calling srv
  2338  		go func() {
  2339  			defer wg.Done()
  2340  
  2341  			<-ensureRequestIsOver // ensure request is done
  2342  
  2343  			req, err := http.NewRequestWithContext(ginctx, http.MethodGet, srv.URL, nil)
  2344  			must(err)
  2345  
  2346  			res, err := http.DefaultClient.Do(req)
  2347  			if err != nil {
  2348  				t.Error(fmt.Errorf("request error: %w", err))
  2349  				return
  2350  			}
  2351  
  2352  			if res.StatusCode != http.StatusOK {
  2353  				t.Error(fmt.Errorf("unexpected status code: %s", res.Status))
  2354  			}
  2355  		}()
  2356  	})
  2357  
  2358  	l, err := net.Listen("tcp", ":0")
  2359  	must(err)
  2360  	go func() {
  2361  		s := &http.Server{
  2362  			Handler: r,
  2363  		}
  2364  
  2365  		must(s.Serve(l))
  2366  	}()
  2367  
  2368  	addr := strings.Split(l.Addr().String(), ":")
  2369  	res, err := http.Get(fmt.Sprintf("http://127.0.0.1:%s/", addr[len(addr)-1]))
  2370  	if err != nil {
  2371  		t.Error(fmt.Errorf("request error: %w", err))
  2372  		return
  2373  	}
  2374  
  2375  	close(ensureRequestIsOver)
  2376  
  2377  	if res.StatusCode != http.StatusOK {
  2378  		t.Error(fmt.Errorf("unexpected status code: %s", res.Status))
  2379  		return
  2380  	}
  2381  
  2382  	wg.Wait()
  2383  }
  2384  
  2385  func TestContextAddParam(t *testing.T) {
  2386  	c := &Context{}
  2387  	id := "id"
  2388  	value := "1"
  2389  	c.AddParam(id, value)
  2390  
  2391  	v, ok := c.Params.Get(id)
  2392  	assert.Equal(t, ok, true)
  2393  	assert.Equal(t, value, v)
  2394  }
  2395  
  2396  func TestCreateTestContextWithRouteParams(t *testing.T) {
  2397  	w := httptest.NewRecorder()
  2398  	engine := New()
  2399  	engine.GET("/:action/:name", func(ctx *Context) {
  2400  		ctx.String(http.StatusOK, "%s %s", ctx.Param("action"), ctx.Param("name"))
  2401  	})
  2402  	c := CreateTestContextOnly(w, engine)
  2403  	c.Request, _ = http.NewRequest(http.MethodGet, "/hello/gin", nil)
  2404  	engine.HandleContext(c)
  2405  
  2406  	assert.Equal(t, http.StatusOK, w.Code)
  2407  	assert.Equal(t, "hello gin", w.Body.String())
  2408  }
  2409  
  2410  type interceptedWriter struct {
  2411  	ResponseWriter
  2412  	b *bytes.Buffer
  2413  }
  2414  
  2415  func (i interceptedWriter) WriteHeader(code int) {
  2416  	i.Header().Del("X-Test")
  2417  	i.ResponseWriter.WriteHeader(code)
  2418  }
  2419  
  2420  func TestInterceptedHeader(t *testing.T) {
  2421  	w := httptest.NewRecorder()
  2422  	c, r := CreateTestContext(w)
  2423  
  2424  	r.Use(func(c *Context) {
  2425  		i := interceptedWriter{
  2426  			ResponseWriter: c.Writer,
  2427  			b:              bytes.NewBuffer(nil),
  2428  		}
  2429  		c.Writer = i
  2430  		c.Next()
  2431  		c.Header("X-Test", "overridden")
  2432  		c.Writer = i.ResponseWriter
  2433  	})
  2434  	r.GET("/", func(c *Context) {
  2435  		c.Header("X-Test", "original")
  2436  		c.Header("X-Test-2", "present")
  2437  		c.String(http.StatusOK, "hello world")
  2438  	})
  2439  	c.Request = httptest.NewRequest("GET", "/", nil)
  2440  	r.HandleContext(c)
  2441  	// Result() has headers frozen when WriteHeaderNow() has been called
  2442  	// Compared to this time, this is when the response headers will be flushed
  2443  	// As response is flushed on c.String, the Header cannot be set by the first
  2444  	// middleware. Assert this
  2445  	assert.Equal(t, "", w.Result().Header.Get("X-Test"))
  2446  	assert.Equal(t, "present", w.Result().Header.Get("X-Test-2"))
  2447  }
  2448  

View as plain text