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 }