builder_test.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // Copyright 2018 Frédéric Guillot. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package response // import "miniflux.app/http/response"
  5. import (
  6. "errors"
  7. "net/http"
  8. "net/http/httptest"
  9. "strings"
  10. "testing"
  11. "time"
  12. )
  13. func TestResponseHasCommonHeaders(t *testing.T) {
  14. r, err := http.NewRequest("GET", "/", nil)
  15. if err != nil {
  16. t.Fatal(err)
  17. }
  18. w := httptest.NewRecorder()
  19. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  20. New(w, r).Write()
  21. })
  22. handler.ServeHTTP(w, r)
  23. resp := w.Result()
  24. headers := map[string]string{
  25. "X-XSS-Protection": "1; mode=block",
  26. "X-Content-Type-Options": "nosniff",
  27. "X-Frame-Options": "DENY",
  28. "Content-Security-Policy": "default-src 'self'; img-src * data:; media-src *; frame-src *",
  29. }
  30. for header, expected := range headers {
  31. actual := resp.Header.Get(header)
  32. if actual != expected {
  33. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  34. }
  35. }
  36. }
  37. func TestBuildResponseWithCustomStatusCode(t *testing.T) {
  38. r, err := http.NewRequest("GET", "/", nil)
  39. if err != nil {
  40. t.Fatal(err)
  41. }
  42. w := httptest.NewRecorder()
  43. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  44. New(w, r).WithStatus(http.StatusNotAcceptable).Write()
  45. })
  46. handler.ServeHTTP(w, r)
  47. resp := w.Result()
  48. expectedStatusCode := http.StatusNotAcceptable
  49. if resp.StatusCode != expectedStatusCode {
  50. t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
  51. }
  52. }
  53. func TestBuildResponseWithCustomHeader(t *testing.T) {
  54. r, err := http.NewRequest("GET", "/", nil)
  55. if err != nil {
  56. t.Fatal(err)
  57. }
  58. w := httptest.NewRecorder()
  59. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  60. New(w, r).WithHeader("X-My-Header", "Value").Write()
  61. })
  62. handler.ServeHTTP(w, r)
  63. resp := w.Result()
  64. expected := "Value"
  65. actual := resp.Header.Get("X-My-Header")
  66. if actual != expected {
  67. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  68. }
  69. }
  70. func TestBuildResponseWithAttachment(t *testing.T) {
  71. r, err := http.NewRequest("GET", "/", nil)
  72. if err != nil {
  73. t.Fatal(err)
  74. }
  75. w := httptest.NewRecorder()
  76. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  77. New(w, r).WithAttachment("my_file.pdf").Write()
  78. })
  79. handler.ServeHTTP(w, r)
  80. resp := w.Result()
  81. expected := "attachment; filename=my_file.pdf"
  82. actual := resp.Header.Get("Content-Disposition")
  83. if actual != expected {
  84. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  85. }
  86. }
  87. func TestBuildResponseWithError(t *testing.T) {
  88. r, err := http.NewRequest("GET", "/", nil)
  89. if err != nil {
  90. t.Fatal(err)
  91. }
  92. w := httptest.NewRecorder()
  93. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  94. New(w, r).WithBody(errors.New("Some error")).Write()
  95. })
  96. handler.ServeHTTP(w, r)
  97. expectedBody := `Some error`
  98. actualBody := w.Body.String()
  99. if actualBody != expectedBody {
  100. t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
  101. }
  102. }
  103. func TestBuildResponseWithByteBody(t *testing.T) {
  104. r, err := http.NewRequest("GET", "/", nil)
  105. if err != nil {
  106. t.Fatal(err)
  107. }
  108. w := httptest.NewRecorder()
  109. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  110. New(w, r).WithBody([]byte("body")).Write()
  111. })
  112. handler.ServeHTTP(w, r)
  113. expectedBody := `body`
  114. actualBody := w.Body.String()
  115. if actualBody != expectedBody {
  116. t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
  117. }
  118. }
  119. func TestBuildResponseWithCachingEnabled(t *testing.T) {
  120. r, err := http.NewRequest("GET", "/", nil)
  121. if err != nil {
  122. t.Fatal(err)
  123. }
  124. w := httptest.NewRecorder()
  125. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  126. New(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
  127. b.WithBody("cached body")
  128. b.Write()
  129. })
  130. })
  131. handler.ServeHTTP(w, r)
  132. resp := w.Result()
  133. expectedStatusCode := http.StatusOK
  134. if resp.StatusCode != expectedStatusCode {
  135. t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
  136. }
  137. expectedBody := `cached body`
  138. actualBody := w.Body.String()
  139. if actualBody != expectedBody {
  140. t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
  141. }
  142. expectedHeader := "public"
  143. actualHeader := resp.Header.Get("Cache-Control")
  144. if actualHeader != expectedHeader {
  145. t.Fatalf(`Unexpected cache control header, got %q instead of %q`, actualHeader, expectedHeader)
  146. }
  147. if resp.Header.Get("Expires") == "" {
  148. t.Fatalf(`Expires header should not be empty`)
  149. }
  150. }
  151. func TestBuildResponseWithCachingAndEtag(t *testing.T) {
  152. r, err := http.NewRequest("GET", "/", nil)
  153. r.Header.Set("If-None-Match", "etag")
  154. if err != nil {
  155. t.Fatal(err)
  156. }
  157. w := httptest.NewRecorder()
  158. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  159. New(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
  160. b.WithBody("cached body")
  161. b.Write()
  162. })
  163. })
  164. handler.ServeHTTP(w, r)
  165. resp := w.Result()
  166. expectedStatusCode := http.StatusNotModified
  167. if resp.StatusCode != expectedStatusCode {
  168. t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
  169. }
  170. expectedBody := ``
  171. actualBody := w.Body.String()
  172. if actualBody != expectedBody {
  173. t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
  174. }
  175. expectedHeader := "public"
  176. actualHeader := resp.Header.Get("Cache-Control")
  177. if actualHeader != expectedHeader {
  178. t.Fatalf(`Unexpected cache control header, got %q instead of %q`, actualHeader, expectedHeader)
  179. }
  180. if resp.Header.Get("Expires") == "" {
  181. t.Fatalf(`Expires header should not be empty`)
  182. }
  183. }
  184. func TestBuildResponseWithGzipCompression(t *testing.T) {
  185. body := strings.Repeat("a", compressionThreshold+1)
  186. r, err := http.NewRequest("GET", "/", nil)
  187. r.Header.Set("Accept-Encoding", "gzip, deflate, br")
  188. if err != nil {
  189. t.Fatal(err)
  190. }
  191. w := httptest.NewRecorder()
  192. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  193. New(w, r).WithBody(body).Write()
  194. })
  195. handler.ServeHTTP(w, r)
  196. resp := w.Result()
  197. expected := "gzip"
  198. actual := resp.Header.Get("Content-Encoding")
  199. if actual != expected {
  200. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  201. }
  202. }
  203. func TestBuildResponseWithDeflateCompression(t *testing.T) {
  204. body := strings.Repeat("a", compressionThreshold+1)
  205. r, err := http.NewRequest("GET", "/", nil)
  206. r.Header.Set("Accept-Encoding", "deflate")
  207. if err != nil {
  208. t.Fatal(err)
  209. }
  210. w := httptest.NewRecorder()
  211. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  212. New(w, r).WithBody(body).Write()
  213. })
  214. handler.ServeHTTP(w, r)
  215. resp := w.Result()
  216. expected := "deflate"
  217. actual := resp.Header.Get("Content-Encoding")
  218. if actual != expected {
  219. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  220. }
  221. }
  222. func TestBuildResponseWithCompressionDisabled(t *testing.T) {
  223. body := strings.Repeat("a", compressionThreshold+1)
  224. r, err := http.NewRequest("GET", "/", nil)
  225. r.Header.Set("Accept-Encoding", "deflate")
  226. if err != nil {
  227. t.Fatal(err)
  228. }
  229. w := httptest.NewRecorder()
  230. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  231. New(w, r).WithBody(body).WithoutCompression().Write()
  232. })
  233. handler.ServeHTTP(w, r)
  234. resp := w.Result()
  235. expected := ""
  236. actual := resp.Header.Get("Content-Encoding")
  237. if actual != expected {
  238. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  239. }
  240. }
  241. func TestBuildResponseWithDeflateCompressionAndSmallPayload(t *testing.T) {
  242. body := strings.Repeat("a", compressionThreshold)
  243. r, err := http.NewRequest("GET", "/", nil)
  244. r.Header.Set("Accept-Encoding", "deflate")
  245. if err != nil {
  246. t.Fatal(err)
  247. }
  248. w := httptest.NewRecorder()
  249. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  250. New(w, r).WithBody(body).Write()
  251. })
  252. handler.ServeHTTP(w, r)
  253. resp := w.Result()
  254. expected := ""
  255. actual := resp.Header.Get("Content-Encoding")
  256. if actual != expected {
  257. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  258. }
  259. }
  260. func TestBuildResponseWithoutCompressionHeader(t *testing.T) {
  261. body := strings.Repeat("a", compressionThreshold+1)
  262. r, err := http.NewRequest("GET", "/", nil)
  263. if err != nil {
  264. t.Fatal(err)
  265. }
  266. w := httptest.NewRecorder()
  267. handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  268. New(w, r).WithBody(body).Write()
  269. })
  270. handler.ServeHTTP(w, r)
  271. resp := w.Result()
  272. expected := ""
  273. actual := resp.Header.Get("Content-Encoding")
  274. if actual != expected {
  275. t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
  276. }
  277. }