github.com/vicanso/pike@v1.0.1-0.20210630235453-9099e041f6ec/cache/http_response_test.go (about)

     1  package cache
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"regexp"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/golang/snappy"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/vicanso/elton"
    14  	"github.com/vicanso/pike/compress"
    15  )
    16  
    17  func TestCloneHeaderAndIgnore(t *testing.T) {
    18  	assert := assert.New(t)
    19  	assert.Equal("Content-Encoding,Content-Length,Connection,Date", strings.Join(ignoreHeaders, ","))
    20  	h := make(http.Header)
    21  	for _, key := range ignoreHeaders {
    22  		h.Add(key, "value")
    23  	}
    24  	newHeader := cloneHeaderAndIgnore(h)
    25  	for _, key := range ignoreHeaders {
    26  		assert.Empty(newHeader.Get(key))
    27  	}
    28  }
    29  
    30  func TestHTTPResponseMarshal(t *testing.T) {
    31  	assert := assert.New(t)
    32  	header := make(http.Header)
    33  	header.Add("a", "1")
    34  	header.Add("a", "2")
    35  	header.Add("b", "3")
    36  	resp := &HTTPResponse{
    37  		CompressSrv:               "compress",
    38  		CompressMinLength:         1000,
    39  		CompressContentTypeFilter: regexp.MustCompile(`a|b|c`),
    40  		Header:                    header,
    41  		StatusCode:                200,
    42  		GzipBody:                  []byte("gzip"),
    43  		BrBody:                    []byte("br"),
    44  		RawBody:                   []byte("raw"),
    45  	}
    46  	data, err := resp.Bytes()
    47  	assert.Nil(err)
    48  
    49  	newResp := &HTTPResponse{}
    50  	err = newResp.FromBytes(data)
    51  	assert.Nil(err)
    52  
    53  	assert.Equal(resp.CompressSrv, newResp.CompressSrv)
    54  	assert.Equal(resp.CompressMinLength, newResp.CompressMinLength)
    55  	assert.Equal(resp.CompressContentTypeFilter, newResp.CompressContentTypeFilter)
    56  	assert.Equal(resp.Header, newResp.Header)
    57  	assert.Equal(resp.StatusCode, newResp.StatusCode)
    58  	assert.Equal(resp.GzipBody, newResp.GzipBody)
    59  	assert.Equal(resp.BrBody, newResp.BrBody)
    60  	assert.Equal(resp.RawBody, newResp.RawBody)
    61  }
    62  
    63  func TestNewHTTPResponse(t *testing.T) {
    64  	assert := assert.New(t)
    65  	data := []byte("Hello world!")
    66  	compressSrv := compress.Get("")
    67  	tests := []struct {
    68  		statusCode int
    69  		header     http.Header
    70  		encoding   string
    71  		fn         func() ([]byte, error)
    72  	}{
    73  		{
    74  			statusCode: 200,
    75  			header:     http.Header{},
    76  			encoding:   compress.EncodingGzip,
    77  			fn: func() ([]byte, error) {
    78  				return compressSrv.Gzip(data)
    79  			},
    80  		},
    81  		{
    82  			statusCode: 201,
    83  			header:     http.Header{},
    84  			encoding:   compress.EncodingBrotli,
    85  			fn: func() ([]byte, error) {
    86  				return compressSrv.Brotli(data)
    87  			},
    88  		},
    89  		{
    90  			statusCode: 200,
    91  			header:     http.Header{},
    92  			encoding:   compress.EncodingSnappy,
    93  			fn: func() ([]byte, error) {
    94  				dst := []byte{}
    95  				dst = snappy.Encode(dst, data)
    96  				return dst, nil
    97  			},
    98  		},
    99  	}
   100  
   101  	for _, tt := range tests {
   102  		result, err := tt.fn()
   103  		assert.Nil(err)
   104  		resp, err := NewHTTPResponse(tt.statusCode, tt.header, tt.encoding, result)
   105  		assert.Nil(err)
   106  		assert.Equal(tt.statusCode, resp.StatusCode)
   107  		switch tt.encoding {
   108  		case compress.EncodingGzip:
   109  			assert.NotNil(resp.GzipBody)
   110  			assert.Nil(resp.RawBody)
   111  			assert.Nil(resp.BrBody)
   112  		case compress.EncodingBrotli:
   113  			assert.NotNil(resp.BrBody)
   114  			assert.Nil(resp.GzipBody)
   115  			assert.Nil(resp.RawBody)
   116  		default:
   117  			assert.NotNil(resp.RawBody)
   118  			assert.Nil(resp.GzipBody)
   119  			assert.Nil(resp.BrBody)
   120  		}
   121  	}
   122  }
   123  
   124  func TestShouldCompressed(t *testing.T) {
   125  	assert := assert.New(t)
   126  	data := []byte("Hello world!")
   127  	tests := []struct {
   128  		header           http.Header
   129  		rawBody          []byte
   130  		gzipBody         []byte
   131  		brBody           []byte
   132  		shouldCompressed bool
   133  	}{
   134  		{
   135  			shouldCompressed: false,
   136  		},
   137  		{
   138  			header: http.Header{
   139  				elton.HeaderContentType: []string{"image/png"},
   140  			},
   141  			rawBody:          data,
   142  			shouldCompressed: false,
   143  		},
   144  		{
   145  			header: http.Header{
   146  				elton.HeaderContentType: []string{"application/json"},
   147  			},
   148  			rawBody:          data,
   149  			shouldCompressed: true,
   150  		},
   151  		{
   152  			header: http.Header{
   153  				elton.HeaderContentType: []string{"application/json"},
   154  			},
   155  			gzipBody:         data,
   156  			shouldCompressed: true,
   157  		},
   158  		{
   159  			header: http.Header{
   160  				elton.HeaderContentType: []string{"application/json"},
   161  			},
   162  			brBody:           data,
   163  			shouldCompressed: true,
   164  		},
   165  	}
   166  	for _, tt := range tests {
   167  		resp := &HTTPResponse{
   168  			Header:            tt.header,
   169  			RawBody:           tt.rawBody,
   170  			GzipBody:          tt.gzipBody,
   171  			BrBody:            tt.brBody,
   172  			CompressMinLength: 1,
   173  		}
   174  		result := resp.shouldCompressed()
   175  		assert.Equal(tt.shouldCompressed, result)
   176  	}
   177  }
   178  
   179  func TestGetRawBody(t *testing.T) {
   180  	assert := assert.New(t)
   181  	compressSrv := compress.Get("")
   182  	data := []byte("Hello world!")
   183  	gzipData, err := compressSrv.Gzip(data)
   184  	assert.Nil(err)
   185  	brData, err := compressSrv.Brotli(data)
   186  	assert.Nil(err)
   187  
   188  	tests := []struct {
   189  		rawBody  []byte
   190  		gzipBody []byte
   191  		brBody   []byte
   192  	}{
   193  		{
   194  			rawBody: data,
   195  		},
   196  		{
   197  			gzipBody: gzipData,
   198  		},
   199  		{
   200  			brBody: brData,
   201  		},
   202  	}
   203  	for _, tt := range tests {
   204  		resp := &HTTPResponse{
   205  			RawBody:  tt.rawBody,
   206  			BrBody:   tt.brBody,
   207  			GzipBody: tt.gzipBody,
   208  		}
   209  		rawBody, err := resp.GetRawBody()
   210  		assert.Nil(err)
   211  		assert.Equal(data, rawBody)
   212  	}
   213  }
   214  
   215  func TestCompress(t *testing.T) {
   216  	assert := assert.New(t)
   217  	data := []byte("Hello world!")
   218  	compressSrv := compress.Get("")
   219  	gzipData, err := compressSrv.Gzip(data)
   220  	assert.Nil(err)
   221  	brData, err := compressSrv.Brotli(data)
   222  	assert.Nil(err)
   223  
   224  	tests := []struct {
   225  		rawBody  []byte
   226  		gzipBody []byte
   227  		brBody   []byte
   228  	}{
   229  		{
   230  			rawBody: data,
   231  		},
   232  		{
   233  			gzipBody: gzipData,
   234  			brBody:   brData,
   235  		},
   236  	}
   237  	for _, tt := range tests {
   238  		resp := &HTTPResponse{
   239  			Header: http.Header{
   240  				elton.HeaderContentType: []string{"application/json"},
   241  			},
   242  			RawBody:           tt.rawBody,
   243  			GzipBody:          tt.gzipBody,
   244  			BrBody:            tt.brBody,
   245  			CompressMinLength: 1,
   246  		}
   247  		err := resp.Compress()
   248  		assert.Nil(err)
   249  		assert.Nil(resp.RawBody)
   250  		assert.Equal(gzipData, resp.GzipBody)
   251  		assert.Equal(brData, resp.BrBody)
   252  	}
   253  }
   254  
   255  func TestGetBodyByAcceptEncoding(t *testing.T) {
   256  	assert := assert.New(t)
   257  	data := []byte("Hello world!")
   258  	compressSrv := compress.Get("")
   259  	gzipData, err := compressSrv.Gzip(data)
   260  	assert.Nil(err)
   261  	brData, err := compressSrv.Brotli(data)
   262  	assert.Nil(err)
   263  
   264  	tests := []struct {
   265  		rawBody        []byte
   266  		gzipBody       []byte
   267  		brBody         []byte
   268  		acceptEncoding string
   269  		minLength      int
   270  		resultEncoding string
   271  		result         []byte
   272  	}{
   273  		// 支持br且已存在br
   274  		{
   275  			brBody:         brData,
   276  			acceptEncoding: compress.EncodingBrotli,
   277  			resultEncoding: compress.EncodingBrotli,
   278  			result:         brData,
   279  		},
   280  		// 支持gzip且已存在gzip
   281  		{
   282  			gzipBody:       gzipData,
   283  			acceptEncoding: compress.EncodingGzip,
   284  			resultEncoding: compress.EncodingGzip,
   285  			result:         gzipData,
   286  		},
   287  		// 数据不应该被压缩,没有gzip,而且原始数据小于最小压缩长度
   288  		{
   289  			rawBody:        data,
   290  			minLength:      1000,
   291  			acceptEncoding: compress.EncodingGzip,
   292  			resultEncoding: "",
   293  			result:         data,
   294  		},
   295  		// 支持br但没有br,且数据不应该压缩
   296  		{
   297  			rawBody:        data,
   298  			minLength:      1000,
   299  			acceptEncoding: compress.EncodingBrotli,
   300  			resultEncoding: "",
   301  			result:         data,
   302  		},
   303  		// 支持br,而且原始数据大于最小压缩长度,压缩后返回
   304  		{
   305  			rawBody:        data,
   306  			acceptEncoding: compress.EncodingBrotli,
   307  			resultEncoding: compress.EncodingBrotli,
   308  			result:         brData,
   309  		},
   310  		// 支持gzip,而且压缩数据大于最小压缩长度,压缩后返回
   311  		{
   312  			rawBody:        data,
   313  			acceptEncoding: compress.EncodingGzip,
   314  			resultEncoding: compress.EncodingGzip,
   315  			result:         gzipData,
   316  		},
   317  		// 不支持压缩,从br中返回
   318  		{
   319  			brBody:         brData,
   320  			acceptEncoding: "",
   321  			resultEncoding: "",
   322  			result:         data,
   323  		},
   324  		// 不支持压缩,从gzip中返回
   325  		{
   326  			gzipBody:       gzipData,
   327  			acceptEncoding: "",
   328  			resultEncoding: "",
   329  			result:         data,
   330  		},
   331  	}
   332  	for _, tt := range tests {
   333  		resp := &HTTPResponse{
   334  			Header: http.Header{
   335  				elton.HeaderContentType: []string{"application/json"},
   336  			},
   337  			RawBody:           tt.rawBody,
   338  			GzipBody:          tt.gzipBody,
   339  			BrBody:            tt.brBody,
   340  			CompressMinLength: tt.minLength,
   341  		}
   342  		encoding, body, err := resp.getBodyByAcceptEncoding(tt.acceptEncoding)
   343  		assert.Nil(err)
   344  		assert.Equal(tt.resultEncoding, encoding)
   345  		assert.Equal(tt.result, body)
   346  	}
   347  }
   348  
   349  func TestFill(t *testing.T) {
   350  	assert := assert.New(t)
   351  	data := []byte("Hello world!")
   352  	compressSrv := compress.Get("")
   353  	gzipData, err := compressSrv.Gzip(data)
   354  	assert.Nil(err)
   355  	brData, err := compressSrv.Brotli(data)
   356  	assert.Nil(err)
   357  
   358  	tests := []struct {
   359  		rawBody        []byte
   360  		acceptEncoding string
   361  		resultEncoding string
   362  		result         []byte
   363  	}{
   364  		{
   365  			rawBody:        data,
   366  			acceptEncoding: compress.EncodingBrotli,
   367  			resultEncoding: compress.EncodingBrotli,
   368  			result:         brData,
   369  		},
   370  		{
   371  			rawBody:        data,
   372  			acceptEncoding: compress.EncodingGzip,
   373  			resultEncoding: compress.EncodingGzip,
   374  			result:         gzipData,
   375  		},
   376  		{
   377  			rawBody:        data,
   378  			acceptEncoding: "",
   379  			resultEncoding: "",
   380  			result:         data,
   381  		},
   382  	}
   383  	for _, tt := range tests {
   384  		resp := &HTTPResponse{
   385  			Header: http.Header{
   386  				elton.HeaderContentType: []string{"application/json"},
   387  			},
   388  			RawBody:    tt.rawBody,
   389  			StatusCode: 200,
   390  		}
   391  		req := httptest.NewRequest("GET", "/", nil)
   392  		c := elton.NewContext(httptest.NewRecorder(), req)
   393  		c.SetRequestHeader(elton.HeaderAcceptEncoding, tt.acceptEncoding)
   394  		err := resp.Fill(c)
   395  		assert.Nil(err)
   396  		assert.Equal(200, c.StatusCode)
   397  		assert.Equal(tt.resultEncoding, c.GetHeader(elton.HeaderContentEncoding))
   398  		assert.Equal(tt.result, c.BodyBuffer.Bytes())
   399  	}
   400  }
   401  
   402  func BenchmarkMarshalHTTPResponse(b *testing.B) {
   403  	resp := &HTTPResponse{
   404  		CompressSrv:       "compress",
   405  		CompressMinLength: 1024,
   406  		Header: http.Header{
   407  			"Content-Type": []string{
   408  				"application/json; charset=UTF-8",
   409  			},
   410  			"ETag": []string{
   411  				`"60-R79m3yeTgBMQWG5Ysx2j_T3gIsM="`,
   412  			},
   413  		},
   414  		GzipBody: make([]byte, 5*1024),
   415  		BrBody:   make([]byte, 5*1024),
   416  	}
   417  	for i := 0; i < b.N; i++ {
   418  		buf, err := json.Marshal(resp)
   419  		if err != nil || len(buf) == 0 {
   420  			panic("to bytes fail")
   421  		}
   422  	}
   423  }
   424  
   425  func BenchmarkHTTPResponseToBytes(b *testing.B) {
   426  	resp := &HTTPResponse{
   427  		CompressSrv:               "compress",
   428  		CompressMinLength:         1024,
   429  		CompressContentTypeFilter: regexp.MustCompile(`text|javascript|json|wasm|xml|font`),
   430  		Header: http.Header{
   431  			"Content-Type": []string{
   432  				"application/json; charset=UTF-8",
   433  			},
   434  			"ETag": []string{
   435  				`"60-R79m3yeTgBMQWG5Ysx2j_T3gIsM="`,
   436  			},
   437  		},
   438  		GzipBody: make([]byte, 5*1024),
   439  		BrBody:   make([]byte, 5*1024),
   440  	}
   441  	for i := 0; i < b.N; i++ {
   442  		buf, err := resp.Bytes()
   443  		if err != nil || len(buf) == 0 {
   444  			panic("to bytes fail")
   445  		}
   446  	}
   447  }