|
|
@@ -0,0 +1,351 @@
|
|
|
+// Copyright 2018 Frédéric Guillot. All rights reserved.
|
|
|
+// Use of this source code is governed by the Apache 2.0
|
|
|
+// license that can be found in the LICENSE file.
|
|
|
+
|
|
|
+package response // import "miniflux.app/http/response"
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "net/http"
|
|
|
+ "net/http/httptest"
|
|
|
+ "strings"
|
|
|
+ "testing"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+func TestResponseHasCommonHeaders(t *testing.T) {
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ headers := map[string]string{
|
|
|
+ "X-XSS-Protection": "1; mode=block",
|
|
|
+ "X-Content-Type-Options": "nosniff",
|
|
|
+ "X-Frame-Options": "DENY",
|
|
|
+ "Content-Security-Policy": "default-src 'self'; img-src *; media-src *; frame-src *; child-src *",
|
|
|
+ }
|
|
|
+
|
|
|
+ for header, expected := range headers {
|
|
|
+ actual := resp.Header.Get(header)
|
|
|
+ if actual != expected {
|
|
|
+ t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithCustomStatusCode(t *testing.T) {
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithStatus(http.StatusNotAcceptable).Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expectedStatusCode := http.StatusNotAcceptable
|
|
|
+ if resp.StatusCode != expectedStatusCode {
|
|
|
+ t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithCustomHeader(t *testing.T) {
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithHeader("X-My-Header", "Value").Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expected := "Value"
|
|
|
+ actual := resp.Header.Get("X-My-Header")
|
|
|
+ if actual != expected {
|
|
|
+ t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithAttachment(t *testing.T) {
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithAttachment("my_file.pdf").Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expected := "attachment; filename=my_file.pdf"
|
|
|
+ actual := resp.Header.Get("Content-Disposition")
|
|
|
+ if actual != expected {
|
|
|
+ t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithError(t *testing.T) {
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithBody(errors.New("Some error")).Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+
|
|
|
+ expectedBody := `Some error`
|
|
|
+ actualBody := w.Body.String()
|
|
|
+ if actualBody != expectedBody {
|
|
|
+ t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithByteBody(t *testing.T) {
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithBody([]byte("body")).Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+
|
|
|
+ expectedBody := `body`
|
|
|
+ actualBody := w.Body.String()
|
|
|
+ if actualBody != expectedBody {
|
|
|
+ t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithCachingEnabled(t *testing.T) {
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
|
|
|
+ b.WithBody("cached body")
|
|
|
+ b.Write()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expectedStatusCode := http.StatusOK
|
|
|
+ if resp.StatusCode != expectedStatusCode {
|
|
|
+ t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
|
|
|
+ }
|
|
|
+
|
|
|
+ expectedBody := `cached body`
|
|
|
+ actualBody := w.Body.String()
|
|
|
+ if actualBody != expectedBody {
|
|
|
+ t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
|
|
|
+ }
|
|
|
+
|
|
|
+ expectedHeader := "public"
|
|
|
+ actualHeader := resp.Header.Get("Cache-Control")
|
|
|
+ if actualHeader != expectedHeader {
|
|
|
+ t.Fatalf(`Unexpected cache control header, got %q instead of %q`, actualHeader, expectedHeader)
|
|
|
+ }
|
|
|
+
|
|
|
+ if resp.Header.Get("Expires") == "" {
|
|
|
+ t.Fatalf(`Expires header should not be empty`)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithCachingAndEtag(t *testing.T) {
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ r.Header.Set("If-None-Match", "etag")
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
|
|
|
+ b.WithBody("cached body")
|
|
|
+ b.Write()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expectedStatusCode := http.StatusNotModified
|
|
|
+ if resp.StatusCode != expectedStatusCode {
|
|
|
+ t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
|
|
|
+ }
|
|
|
+
|
|
|
+ expectedBody := ``
|
|
|
+ actualBody := w.Body.String()
|
|
|
+ if actualBody != expectedBody {
|
|
|
+ t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
|
|
|
+ }
|
|
|
+
|
|
|
+ expectedHeader := "public"
|
|
|
+ actualHeader := resp.Header.Get("Cache-Control")
|
|
|
+ if actualHeader != expectedHeader {
|
|
|
+ t.Fatalf(`Unexpected cache control header, got %q instead of %q`, actualHeader, expectedHeader)
|
|
|
+ }
|
|
|
+
|
|
|
+ if resp.Header.Get("Expires") == "" {
|
|
|
+ t.Fatalf(`Expires header should not be empty`)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithGzipCompression(t *testing.T) {
|
|
|
+ body := strings.Repeat("a", compressionThreshold+1)
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ r.Header.Set("Accept-Encoding", "gzip, deflate, br")
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithBody(body).Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expected := "gzip"
|
|
|
+ actual := resp.Header.Get("Content-Encoding")
|
|
|
+ if actual != expected {
|
|
|
+ t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithDeflateCompression(t *testing.T) {
|
|
|
+ body := strings.Repeat("a", compressionThreshold+1)
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ r.Header.Set("Accept-Encoding", "deflate")
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithBody(body).Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expected := "deflate"
|
|
|
+ actual := resp.Header.Get("Content-Encoding")
|
|
|
+ if actual != expected {
|
|
|
+ t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithCompressionDisabled(t *testing.T) {
|
|
|
+ body := strings.Repeat("a", compressionThreshold+1)
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ r.Header.Set("Accept-Encoding", "deflate")
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithBody(body).WithoutCompression().Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expected := ""
|
|
|
+ actual := resp.Header.Get("Content-Encoding")
|
|
|
+ if actual != expected {
|
|
|
+ t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithDeflateCompressionAndSmallPayload(t *testing.T) {
|
|
|
+ body := strings.Repeat("a", compressionThreshold)
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ r.Header.Set("Accept-Encoding", "deflate")
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithBody(body).Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expected := ""
|
|
|
+ actual := resp.Header.Get("Content-Encoding")
|
|
|
+ if actual != expected {
|
|
|
+ t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBuildResponseWithoutCompressionHeader(t *testing.T) {
|
|
|
+ body := strings.Repeat("a", compressionThreshold+1)
|
|
|
+ r, err := http.NewRequest("GET", "/", nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ w := httptest.NewRecorder()
|
|
|
+
|
|
|
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ New(w, r).WithBody(body).Write()
|
|
|
+ })
|
|
|
+
|
|
|
+ handler.ServeHTTP(w, r)
|
|
|
+ resp := w.Result()
|
|
|
+
|
|
|
+ expected := ""
|
|
|
+ actual := resp.Header.Get("Content-Encoding")
|
|
|
+ if actual != expected {
|
|
|
+ t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
|
|
|
+ }
|
|
|
+}
|