2020-05-10 02:44:34 -07:00
|
|
|
// Copyright (C) 2020 The Syncthing Authors.
|
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
|
package assets
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"compress/gzip"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2020-05-24 23:51:27 -07:00
|
|
|
"strconv"
|
2020-05-10 02:44:34 -07:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func compress(s string) string {
|
|
|
|
var sb strings.Builder
|
|
|
|
gz := gzip.NewWriter(&sb)
|
|
|
|
|
|
|
|
io.WriteString(gz, s)
|
|
|
|
gz.Close()
|
|
|
|
return sb.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func decompress(p []byte) (out []byte) {
|
|
|
|
r, err := gzip.NewReader(bytes.NewBuffer(p))
|
|
|
|
if err == nil {
|
|
|
|
out, err = ioutil.ReadAll(r)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2020-05-24 23:51:27 -07:00
|
|
|
func TestServe(t *testing.T) { testServe(t, false) }
|
|
|
|
func TestServeGzip(t *testing.T) { testServe(t, true) }
|
|
|
|
|
|
|
|
func testServe(t *testing.T, gzip bool) {
|
|
|
|
const indexHTML = `<html>Hello, world!</html>`
|
|
|
|
content := indexHTML
|
|
|
|
if gzip {
|
|
|
|
content = compress(indexHTML)
|
|
|
|
}
|
2020-05-10 02:44:34 -07:00
|
|
|
|
|
|
|
handler := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
Serve(w, r, Asset{
|
2020-05-24 23:51:27 -07:00
|
|
|
Content: content,
|
|
|
|
Gzipped: gzip,
|
|
|
|
Length: len(indexHTML),
|
|
|
|
Filename: r.URL.Path[1:],
|
|
|
|
Modified: time.Unix(0, 0),
|
2020-05-10 02:44:34 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, acceptGzip := range []bool{true, false} {
|
|
|
|
r := httptest.NewRequest("GET", "http://localhost/index.html", nil)
|
|
|
|
if acceptGzip {
|
|
|
|
r.Header.Set("accept-encoding", "gzip, deflate")
|
|
|
|
}
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
handler(w, r)
|
|
|
|
res := w.Result()
|
|
|
|
|
|
|
|
if res.StatusCode != http.StatusOK {
|
|
|
|
t.Fatalf("wanted OK, got status %d", res.StatusCode)
|
|
|
|
}
|
|
|
|
if ctype := res.Header.Get("Content-Type"); ctype != "text/html; charset=utf-8" {
|
|
|
|
t.Errorf("unexpected Content-Type %q", ctype)
|
|
|
|
}
|
|
|
|
// ETags must be quoted ASCII strings:
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
|
|
|
|
if etag := res.Header.Get("ETag"); etag != `"0"` {
|
|
|
|
t.Errorf("unexpected ETag %q", etag)
|
|
|
|
}
|
|
|
|
|
|
|
|
body, _ := ioutil.ReadAll(res.Body)
|
2020-05-24 23:51:27 -07:00
|
|
|
|
|
|
|
// Content-Length is the number of bytes in the encoded (compressed) body
|
|
|
|
// (https://stackoverflow.com/a/3819303).
|
|
|
|
n, err := strconv.Atoi(res.Header.Get("Content-Length"))
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("malformed Content-Length %q", res.Header.Get("Content-Length"))
|
|
|
|
} else if n != len(body) {
|
|
|
|
t.Errorf("wrong Content-Length %d, should be %d", n, len(body))
|
|
|
|
}
|
|
|
|
|
|
|
|
if gzip && acceptGzip {
|
2020-05-10 02:44:34 -07:00
|
|
|
body = decompress(body)
|
|
|
|
}
|
|
|
|
if string(body) != indexHTML {
|
|
|
|
t.Fatalf("unexpected content %q", body)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r := httptest.NewRequest("GET", "http://localhost/index.html", nil)
|
|
|
|
r.Header.Set("if-none-match", `"0"`)
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
handler(w, r)
|
|
|
|
res := w.Result()
|
|
|
|
|
|
|
|
if res.StatusCode != http.StatusNotModified {
|
|
|
|
t.Fatalf("wanted NotModified, got status %d", res.StatusCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
r = httptest.NewRequest("GET", "http://localhost/index.html", nil)
|
|
|
|
r.Header.Set("if-modified-since", time.Now().Format(http.TimeFormat))
|
|
|
|
w = httptest.NewRecorder()
|
|
|
|
handler(w, r)
|
|
|
|
res = w.Result()
|
|
|
|
|
|
|
|
if res.StatusCode != http.StatusNotModified {
|
|
|
|
t.Fatalf("wanted NotModified, got status %d", res.StatusCode)
|
|
|
|
}
|
|
|
|
}
|