github.com/zak-blake/goa@v1.4.1/middleware/gzip/middleware_test.go (about) 1 package gzip_test 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "context" 7 "io" 8 "net/http" 9 "strconv" 10 "strings" 11 12 "github.com/goadesign/goa" 13 gzm "github.com/goadesign/goa/middleware/gzip" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 ) 17 18 type TestResponseWriter struct { 19 ParentHeader http.Header 20 Body []byte 21 Status int 22 } 23 24 func (t *TestResponseWriter) Header() http.Header { 25 return t.ParentHeader 26 } 27 28 func (t *TestResponseWriter) Write(b []byte) (int, error) { 29 t.Body = append(t.Body, b...) 30 return len(b), nil 31 } 32 33 func (t *TestResponseWriter) WriteHeader(s int) { 34 t.Status = s 35 } 36 37 var _ = Describe("Gzip", func() { 38 var ctx context.Context 39 var req *http.Request 40 var rw *TestResponseWriter 41 payload := map[string]interface{}{"payload": 42} 42 43 BeforeEach(func() { 44 var err error 45 req, err = http.NewRequest("POST", "/foo/bar", strings.NewReader(`{"payload":42}`)) 46 req.Header.Set("Accept-Encoding", "gzip") 47 req.Header.Set("Range", "bytes=0-1023") 48 Ω(err).ShouldNot(HaveOccurred()) 49 rw = &TestResponseWriter{ParentHeader: make(http.Header)} 50 51 ctx = goa.NewContext(nil, rw, req, nil) 52 goa.ContextRequest(ctx).Payload = payload 53 }) 54 55 It("encodes response using gzip", func() { 56 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 57 resp := goa.ContextResponse(ctx) 58 resp.WriteHeader(http.StatusOK) 59 resp.Write([]byte("gzip me!")) 60 return nil 61 } 62 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0))(h) 63 err := t(ctx, rw, req) 64 Ω(err).ShouldNot(HaveOccurred()) 65 resp := goa.ContextResponse(ctx) 66 Ω(resp.Status).Should(Equal(http.StatusOK)) 67 Ω(resp.Header().Get("Content-Encoding")).Should(Equal("gzip")) 68 69 gzr, err := gzip.NewReader(bytes.NewReader(rw.Body)) 70 Ω(err).ShouldNot(HaveOccurred()) 71 var buf bytes.Buffer 72 _, err = io.Copy(&buf, gzr) 73 Ω(err).ShouldNot(HaveOccurred()) 74 Ω(buf.String()).Should(Equal("gzip me!")) 75 Ω(resp.Header().Get("Content-Length")).Should(Equal("")) 76 }) 77 78 It("encodes response using gzip (custom status)", func() { 79 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 80 resp := goa.ContextResponse(ctx) 81 resp.WriteHeader(http.StatusBadRequest) 82 resp.Write([]byte("gzip me!")) 83 return nil 84 } 85 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0), gzm.AddStatusCodes(http.StatusBadRequest))(h) 86 err := t(ctx, rw, req) 87 Ω(err).ShouldNot(HaveOccurred()) 88 resp := goa.ContextResponse(ctx) 89 Ω(resp.Status).Should(Equal(http.StatusBadRequest)) 90 Ω(resp.Header().Get("Content-Encoding")).Should(Equal("gzip")) 91 92 gzr, err := gzip.NewReader(bytes.NewReader(rw.Body)) 93 Ω(err).ShouldNot(HaveOccurred()) 94 var buf bytes.Buffer 95 _, err = io.Copy(&buf, gzr) 96 Ω(err).ShouldNot(HaveOccurred()) 97 Ω(buf.String()).Should(Equal("gzip me!")) 98 }) 99 100 It("encodes response using gzip (all status)", func() { 101 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 102 resp := goa.ContextResponse(ctx) 103 resp.WriteHeader(http.StatusBadRequest) 104 resp.Write([]byte("gzip me!")) 105 return nil 106 } 107 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0), gzm.OnlyStatusCodes())(h) 108 err := t(ctx, rw, req) 109 Ω(err).ShouldNot(HaveOccurred()) 110 resp := goa.ContextResponse(ctx) 111 Ω(resp.Status).Should(Equal(http.StatusBadRequest)) 112 Ω(resp.Header().Get("Content-Encoding")).Should(Equal("gzip")) 113 114 gzr, err := gzip.NewReader(bytes.NewReader(rw.Body)) 115 Ω(err).ShouldNot(HaveOccurred()) 116 var buf bytes.Buffer 117 _, err = io.Copy(&buf, gzr) 118 Ω(err).ShouldNot(HaveOccurred()) 119 Ω(buf.String()).Should(Equal("gzip me!")) 120 }) 121 122 It("encodes response using gzip (custom type)", func() { 123 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 124 resp := goa.ContextResponse(ctx) 125 resp.Header().Add("Content-Type", "custom/type") 126 resp.WriteHeader(http.StatusOK) 127 resp.Write([]byte("gzip me!")) 128 return nil 129 } 130 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0), gzm.AddContentTypes("custom/type"))(h) 131 err := t(ctx, rw, req) 132 Ω(err).ShouldNot(HaveOccurred()) 133 resp := goa.ContextResponse(ctx) 134 Ω(resp.Status).Should(Equal(http.StatusOK)) 135 Ω(resp.Header().Get("Content-Encoding")).Should(Equal("gzip")) 136 137 gzr, err := gzip.NewReader(bytes.NewReader(rw.Body)) 138 Ω(err).ShouldNot(HaveOccurred()) 139 var buf bytes.Buffer 140 _, err = io.Copy(&buf, gzr) 141 Ω(err).ShouldNot(HaveOccurred()) 142 Ω(buf.String()).Should(Equal("gzip me!")) 143 }) 144 145 It("encodes response using gzip (length check)", func() { 146 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 147 resp := goa.ContextResponse(ctx) 148 resp.WriteHeader(http.StatusOK) 149 // Use multiple writes. 150 for i := 0; i < 128; i++ { 151 _, err := resp.Write([]byte("gzip me!")) 152 if err != nil { 153 return err 154 } 155 } 156 return nil 157 } 158 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(512))(h) 159 err := t(ctx, rw, req) 160 Ω(err).ShouldNot(HaveOccurred()) 161 resp := goa.ContextResponse(ctx) 162 Ω(resp.Status).Should(Equal(http.StatusOK)) 163 Ω(resp.Header().Get("Content-Encoding")).Should(Equal("gzip")) 164 165 gzr, err := gzip.NewReader(bytes.NewReader(rw.Body)) 166 Ω(err).ShouldNot(HaveOccurred()) 167 var buf bytes.Buffer 168 _, err = io.Copy(&buf, gzr) 169 Ω(err).ShouldNot(HaveOccurred()) 170 Ω(buf.String()).Should(Equal(strings.Repeat("gzip me!", 128))) 171 }) 172 173 It("removes Accept-Ranges header", func() { 174 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 175 resp := goa.ContextResponse(ctx) 176 resp.Header().Add("Accept-Ranges", "some value") 177 resp.WriteHeader(http.StatusOK) 178 // Use multiple writes. 179 for i := 0; i < 128; i++ { 180 _, err := resp.Write([]byte("gzip me!")) 181 if err != nil { 182 return err 183 } 184 } 185 return nil 186 } 187 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(512))(h) 188 err := t(ctx, rw, req) 189 Ω(err).ShouldNot(HaveOccurred()) 190 resp := goa.ContextResponse(ctx) 191 Ω(resp.Status).Should(Equal(http.StatusOK)) 192 Ω(resp.Header().Get("Content-Encoding")).Should(Equal("gzip")) 193 Ω(resp.Header().Get("Accept-Ranges")).Should(Equal("")) 194 195 gzr, err := gzip.NewReader(bytes.NewReader(rw.Body)) 196 Ω(err).ShouldNot(HaveOccurred()) 197 var buf bytes.Buffer 198 _, err = io.Copy(&buf, gzr) 199 Ω(err).ShouldNot(HaveOccurred()) 200 Ω(buf.String()).Should(Equal(strings.Repeat("gzip me!", 128))) 201 }) 202 203 It("should preserve status code", func() { 204 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 205 resp := goa.ContextResponse(ctx) 206 resp.WriteHeader(http.StatusConflict) 207 // Use multiple writes. 208 for i := 0; i < 128; i++ { 209 _, err := resp.Write([]byte("gzip me!")) 210 if err != nil { 211 return err 212 } 213 } 214 return nil 215 } 216 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(512), gzm.AddStatusCodes(http.StatusConflict))(h) 217 err := t(ctx, rw, req) 218 Ω(err).ShouldNot(HaveOccurred()) 219 resp := goa.ContextResponse(ctx) 220 Ω(resp.Status).Should(Equal(http.StatusConflict)) 221 Ω(resp.Header().Get("Content-Encoding")).Should(Equal("gzip")) 222 Ω(resp.Header().Get("Accept-Ranges")).Should(Equal("")) 223 224 gzr, err := gzip.NewReader(bytes.NewReader(rw.Body)) 225 Ω(err).ShouldNot(HaveOccurred()) 226 var buf bytes.Buffer 227 _, err = io.Copy(&buf, gzr) 228 Ω(err).ShouldNot(HaveOccurred()) 229 Ω(buf.String()).Should(Equal(strings.Repeat("gzip me!", 128))) 230 }) 231 }) 232 233 var _ = Describe("NotGzip", func() { 234 var ctx context.Context 235 var req *http.Request 236 var rw *TestResponseWriter 237 payload := map[string]interface{}{"payload": 42} 238 239 BeforeEach(func() { 240 var err error 241 req, err = http.NewRequest("POST", "/foo/bar", strings.NewReader(`{"payload":42}`)) 242 req.Header.Set("Accept-Encoding", "gzip") 243 req.Header.Set("Range", "bytes=0-10") 244 Ω(err).ShouldNot(HaveOccurred()) 245 rw = &TestResponseWriter{ParentHeader: make(http.Header)} 246 247 ctx = goa.NewContext(nil, rw, req, nil) 248 goa.ContextRequest(ctx).Payload = payload 249 }) 250 251 It("does not encode response (already gzipped)", func() { 252 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 253 resp := goa.ContextResponse(ctx) 254 resp.Header().Set("Content-Type", "gzip") 255 resp.WriteHeader(http.StatusOK) 256 resp.Write([]byte("gzip data")) 257 return nil 258 } 259 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0))(h) 260 err := t(ctx, rw, req) 261 Ω(err).ShouldNot(HaveOccurred()) 262 resp := goa.ContextResponse(ctx) 263 Ω(resp.Status).Should(Equal(http.StatusOK)) 264 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 265 266 var buf bytes.Buffer 267 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 268 Ω(err).ShouldNot(HaveOccurred()) 269 Ω(buf.String()).Should(Equal("gzip data")) 270 }) 271 272 It("does not encode response (too small)", func() { 273 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 274 resp := goa.ContextResponse(ctx) 275 resp.WriteHeader(http.StatusOK) 276 resp.Write([]byte("gzip me!")) 277 return nil 278 } 279 t := gzm.Middleware(gzip.BestCompression)(h) 280 err := t(ctx, rw, req) 281 Ω(err).ShouldNot(HaveOccurred()) 282 resp := goa.ContextResponse(ctx) 283 Ω(resp.Status).Should(Equal(http.StatusOK)) 284 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 285 286 var buf bytes.Buffer 287 n, err := io.Copy(&buf, bytes.NewBuffer(rw.Body)) 288 Ω(err).ShouldNot(HaveOccurred()) 289 Ω(buf.String()).Should(Equal("gzip me!")) 290 Ω(resp.Header().Get("Content-Length")).Should(Equal(strconv.Itoa(int(n)))) 291 Ω(resp.Header().Get("Content-Length")).Should(Equal(strconv.Itoa(len("gzip me!")))) 292 }) 293 294 It("does not encode response (wrong status code)", func() { 295 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 296 resp := goa.ContextResponse(ctx) 297 resp.WriteHeader(http.StatusBadRequest) 298 resp.Write([]byte("gzip me!")) 299 return nil 300 } 301 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0))(h) 302 err := t(ctx, rw, req) 303 Ω(err).ShouldNot(HaveOccurred()) 304 resp := goa.ContextResponse(ctx) 305 Ω(resp.Status).Should(Equal(http.StatusBadRequest)) 306 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 307 308 var buf bytes.Buffer 309 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 310 Ω(err).ShouldNot(HaveOccurred()) 311 Ω(buf.String()).Should(Equal("gzip me!")) 312 }) 313 314 It("does not encode response (removed status code)", func() { 315 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 316 resp := goa.ContextResponse(ctx) 317 resp.WriteHeader(http.StatusOK) 318 resp.Write([]byte("gzip me!")) 319 return nil 320 } 321 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0), gzm.OnlyStatusCodes(http.StatusBadRequest))(h) 322 err := t(ctx, rw, req) 323 Ω(err).ShouldNot(HaveOccurred()) 324 resp := goa.ContextResponse(ctx) 325 Ω(resp.Status).Should(Equal(http.StatusOK)) 326 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 327 328 var buf bytes.Buffer 329 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 330 Ω(err).ShouldNot(HaveOccurred()) 331 Ω(buf.String()).Should(Equal("gzip me!")) 332 }) 333 334 It("does not encode response (unknown content type)", func() { 335 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 336 resp := goa.ContextResponse(ctx) 337 resp.Header().Add("Content-Type", "unknown/contenttype") 338 resp.WriteHeader(http.StatusOK) 339 resp.Write([]byte("gzip me!")) 340 return nil 341 } 342 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0))(h) 343 err := t(ctx, rw, req) 344 Ω(err).ShouldNot(HaveOccurred()) 345 resp := goa.ContextResponse(ctx) 346 Ω(resp.Status).Should(Equal(http.StatusOK)) 347 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 348 349 var buf bytes.Buffer 350 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 351 Ω(err).ShouldNot(HaveOccurred()) 352 Ω(buf.String()).Should(Equal("gzip me!")) 353 }) 354 355 It("does not encode response (removed type)", func() { 356 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 357 resp := goa.ContextResponse(ctx) 358 resp.WriteHeader(http.StatusOK) 359 resp.Write([]byte("gzip me!")) 360 return nil 361 } 362 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0), gzm.OnlyContentTypes("some/type"))(h) 363 err := t(ctx, rw, req) 364 Ω(err).ShouldNot(HaveOccurred()) 365 resp := goa.ContextResponse(ctx) 366 Ω(resp.Status).Should(Equal(http.StatusOK)) 367 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 368 369 var buf bytes.Buffer 370 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 371 Ω(err).ShouldNot(HaveOccurred()) 372 Ω(buf.String()).Should(Equal("gzip me!")) 373 }) 374 375 It("does not encode response (has Range header)", func() { 376 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 377 resp := goa.ContextResponse(ctx) 378 resp.WriteHeader(http.StatusOK) 379 resp.Write([]byte("gzip me!")) 380 return nil 381 } 382 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0), gzm.IgnoreRange(false))(h) 383 err := t(ctx, rw, req) 384 Ω(err).ShouldNot(HaveOccurred()) 385 resp := goa.ContextResponse(ctx) 386 Ω(resp.Status).Should(Equal(http.StatusOK)) 387 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 388 389 var buf bytes.Buffer 390 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 391 Ω(err).ShouldNot(HaveOccurred()) 392 Ω(buf.String()).Should(Equal("gzip me!")) 393 }) 394 395 It("should preserve status code", func() { 396 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 397 resp := goa.ContextResponse(ctx) 398 resp.WriteHeader(http.StatusConflict) 399 resp.Write([]byte("gzip me!")) 400 return nil 401 } 402 t := gzm.Middleware(gzip.BestCompression)(h) 403 err := t(ctx, rw, req) 404 Ω(err).ShouldNot(HaveOccurred()) 405 resp := goa.ContextResponse(ctx) 406 Ω(resp.Status).Should(Equal(http.StatusConflict)) 407 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 408 409 var buf bytes.Buffer 410 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 411 Ω(err).ShouldNot(HaveOccurred()) 412 Ω(buf.String()).Should(Equal("gzip me!")) 413 }) 414 415 It("should preserve status code with no body", func() { 416 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 417 resp := goa.ContextResponse(ctx) 418 resp.WriteHeader(http.StatusConflict) 419 return nil 420 } 421 t := gzm.Middleware(gzip.BestCompression)(h) 422 err := t(ctx, rw, req) 423 Ω(err).ShouldNot(HaveOccurred()) 424 resp := goa.ContextResponse(ctx) 425 Ω(resp.Status).Should(Equal(http.StatusConflict)) 426 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 427 428 var buf bytes.Buffer 429 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 430 Ω(err).ShouldNot(HaveOccurred()) 431 Ω(buf.String()).Should(Equal("")) 432 }) 433 434 It("should default to OK with no code set", func() { 435 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 436 resp := goa.ContextResponse(ctx) 437 resp.Write([]byte("gzip me!")) 438 return nil 439 } 440 t := gzm.Middleware(gzip.BestCompression)(h) 441 err := t(ctx, rw, req) 442 Ω(err).ShouldNot(HaveOccurred()) 443 resp := goa.ContextResponse(ctx) 444 Ω(resp.Status).Should(Equal(http.StatusOK)) 445 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 446 447 var buf bytes.Buffer 448 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 449 Ω(err).ShouldNot(HaveOccurred()) 450 Ω(buf.String()).Should(Equal("gzip me!")) 451 }) 452 453 }) 454 455 var _ = Describe("NotGzip", func() { 456 var ctx context.Context 457 var req *http.Request 458 var rw *TestResponseWriter 459 payload := map[string]interface{}{"payload": 42} 460 461 BeforeEach(func() { 462 var err error 463 req, err = http.NewRequest("POST", "/foo/bar", strings.NewReader(`{"payload":42}`)) 464 req.Header.Set("Accept-Encoding", "nothing") 465 Ω(err).ShouldNot(HaveOccurred()) 466 rw = &TestResponseWriter{ParentHeader: make(http.Header)} 467 468 ctx = goa.NewContext(nil, rw, req, nil) 469 goa.ContextRequest(ctx).Payload = payload 470 }) 471 472 It("does not encode response (wrong accept-encoding)", func() { 473 h := func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { 474 resp := goa.ContextResponse(ctx) 475 resp.WriteHeader(http.StatusOK) 476 resp.Write([]byte("gzip me!")) 477 return nil 478 } 479 t := gzm.Middleware(gzip.BestCompression, gzm.MinSize(0))(h) 480 err := t(ctx, rw, req) 481 Ω(err).ShouldNot(HaveOccurred()) 482 resp := goa.ContextResponse(ctx) 483 Ω(resp.Status).Should(Equal(http.StatusOK)) 484 Ω(resp.Header().Get("Content-Encoding")).ShouldNot(Equal("gzip")) 485 486 var buf bytes.Buffer 487 _, err = io.Copy(&buf, bytes.NewBuffer(rw.Body)) 488 Ω(err).ShouldNot(HaveOccurred()) 489 Ω(buf.String()).Should(Equal("gzip me!")) 490 }) 491 })