132 lines
4.6 KiB
Go
132 lines
4.6 KiB
Go
package api
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"io"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/gofiber/fiber/v2/middleware/compress"
|
|
)
|
|
|
|
// buildTinyXLSX returns a minimal but structurally valid .xlsx (ZIP) payload.
|
|
func buildTinyXLSX(t *testing.T) []byte {
|
|
t.Helper()
|
|
var buf bytes.Buffer
|
|
zw := zip.NewWriter(&buf)
|
|
entries := map[string]string{
|
|
"[Content_Types].xml": `<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/></Types>`,
|
|
"_rels/.rels": `<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`,
|
|
"xl/workbook.xml": `<?xml version="1.0"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheets><sheet name="Sheet1" sheetId="1" r:id="rId1"/></sheets></workbook>`,
|
|
}
|
|
for name, content := range entries {
|
|
w, err := zw.Create(name)
|
|
if err != nil {
|
|
t.Fatalf("zip create %s: %v", name, err)
|
|
}
|
|
if _, err := io.WriteString(w, content); err != nil {
|
|
t.Fatalf("zip write %s: %v", name, err)
|
|
}
|
|
}
|
|
if err := zw.Close(); err != nil {
|
|
t.Fatalf("zip close: %v", err)
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func assertValidZip(t *testing.T, b []byte) {
|
|
t.Helper()
|
|
if _, err := zip.NewReader(bytes.NewReader(b), int64(len(b))); err != nil {
|
|
t.Fatalf("payload is not a valid zip: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestIsWOPIRequest(t *testing.T) {
|
|
app := fiber.New()
|
|
cases := []struct {
|
|
path string
|
|
want bool
|
|
}{
|
|
{"/wopi/files/123/contents", true},
|
|
{"/wopi/files/123", true},
|
|
{"/api/file-manager/files", false},
|
|
{"/wopimirror/x", false},
|
|
{"/", false},
|
|
}
|
|
for _, tc := range cases {
|
|
app.Get(tc.path, func(c *fiber.Ctx) error {
|
|
if got := isWOPIRequest(c); got != tc.want {
|
|
t.Errorf("isWOPIRequest(%q) = %v, want %v", tc.path, got, tc.want)
|
|
}
|
|
return c.SendStatus(fiber.StatusOK)
|
|
})
|
|
resp, err := app.Test(httptest.NewRequest("GET", tc.path, nil), -1)
|
|
if err != nil {
|
|
t.Fatalf("app.Test(%q): %v", tc.path, err)
|
|
}
|
|
_ = resp.Body.Close()
|
|
}
|
|
}
|
|
|
|
// Regression for the "template .xlsx always corrupt via WOPI" bug: the global
|
|
// gzip compress middleware must NOT compress WOPI file-content responses.
|
|
// Collabora saves the response body verbatim as the document, so a gzip stream
|
|
// there is a guaranteed-corrupt file.
|
|
func TestCompressMiddleware_SkipsWOPIContents(t *testing.T) {
|
|
src := buildTinyXLSX(t)
|
|
assertValidZip(t, src)
|
|
|
|
app := fiber.New()
|
|
// Mirror the production compress wiring (UseDefaultMiddlewares).
|
|
app.Use(compress.New(compress.Config{
|
|
Level: compress.LevelBestSpeed,
|
|
Next: func(c *fiber.Ctx) bool {
|
|
return isEventStreamRequest(c) || isWOPIRequest(c)
|
|
},
|
|
}))
|
|
|
|
streamXLSX := func(c *fiber.Ctx) error {
|
|
c.Set(fiber.HeaderContentType, "application/octet-stream")
|
|
c.Status(fiber.StatusOK)
|
|
c.Context().Response.SetBodyStream(io.NopCloser(bytes.NewReader(src)), len(src))
|
|
return nil
|
|
}
|
|
app.Get("/wopi/files/:id/contents", streamXLSX)
|
|
// Negative control: a non-WOPI route with a compressible body must still gzip.
|
|
app.Get("/api/echo", func(c *fiber.Ctx) error {
|
|
c.Set(fiber.HeaderContentType, "application/octet-stream")
|
|
return c.Send(bytes.Repeat([]byte("A"), 4096))
|
|
})
|
|
|
|
// WOPI content download with gzip offered must come back raw + valid.
|
|
req := httptest.NewRequest("GET", "/wopi/files/abc/contents", nil)
|
|
req.Header.Set("Accept-Encoding", "gzip")
|
|
resp, err := app.Test(req, -1)
|
|
if err != nil {
|
|
t.Fatalf("app.Test wopi: %v", err)
|
|
}
|
|
if enc := resp.Header.Get("Content-Encoding"); enc != "" {
|
|
t.Fatalf("WOPI content unexpectedly encoded: Content-Encoding=%q", enc)
|
|
}
|
|
got, _ := io.ReadAll(resp.Body)
|
|
_ = resp.Body.Close()
|
|
assertValidZip(t, got)
|
|
if !bytes.Equal(got, src) {
|
|
t.Fatalf("WOPI content body mutated: got %d bytes, want %d", len(got), len(src))
|
|
}
|
|
|
|
// Sanity: prove compression is actually active for non-excluded routes.
|
|
req2 := httptest.NewRequest("GET", "/api/echo", nil)
|
|
req2.Header.Set("Accept-Encoding", "gzip")
|
|
resp2, err := app.Test(req2, -1)
|
|
if err != nil {
|
|
t.Fatalf("app.Test echo: %v", err)
|
|
}
|
|
if enc := resp2.Header.Get("Content-Encoding"); enc != "gzip" {
|
|
t.Fatalf("expected gzip on non-WOPI route, got Content-Encoding=%q (compress not active - test invalid)", enc)
|
|
}
|
|
_ = resp2.Body.Close()
|
|
}
|