mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
fix(replay): a truncated index is a 500, not a partial 200
readAll reimplemented io.ReadAll and swallowed every error but fs.ErrInvalid, returning the bytes it had managed to read with a nil error, so a half-written index.html was indistinguishable from a whole one. io.ReadAll surfaces the directory-handle case fs.ErrInvalid covered as well.
This commit is contained in:
1 parent
dbd57ef989
commit
30febb78b8
2 files changed
+51
-20
No files matched your search
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
@@ -233,7 +234,7 @@ func serveIndex(responseWriter http.ResponseWriter, assets fs.FS) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
body, err := readAll(file)
|
body, err := io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
|
http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@@ -242,25 +243,6 @@ func serveIndex(responseWriter http.ResponseWriter, assets fs.FS) {
|
|||||||
_, _ = responseWriter.Write(body)
|
_, _ = responseWriter.Write(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readAll(file fs.File) ([]byte, error) {
|
|
||||||
const initialCapacity = 4 * 1024
|
|
||||||
buffer := make([]byte, 0, initialCapacity)
|
|
||||||
chunk := make([]byte, 4*1024)
|
|
||||||
for {
|
|
||||||
read, err := file.Read(chunk)
|
|
||||||
if read > 0 {
|
|
||||||
buffer = append(buffer, chunk[:read]...)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, fs.ErrInvalid) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buffer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeJSON(responseWriter http.ResponseWriter, status int, payload any) {
|
func writeJSON(responseWriter http.ResponseWriter, status int, payload any) {
|
||||||
responseWriter.Header().Set("Content-Type", "application/json")
|
responseWriter.Header().Set("Content-Type", "application/json")
|
||||||
responseWriter.WriteHeader(status)
|
responseWriter.WriteHeader(status)
|
||||||
|
|||||||
@@ -371,6 +371,55 @@ func TestAssets_FallbackToIndexHTML(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type truncatedIndexFS struct {
|
||||||
|
prefix []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assets truncatedIndexFS) Open(name string) (fs.File, error) {
|
||||||
|
if name != "index.html" {
|
||||||
|
return nil, fs.ErrNotExist
|
||||||
|
}
|
||||||
|
return &truncatedIndexFile{prefix: assets.prefix}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type truncatedIndexFile struct {
|
||||||
|
prefix []byte
|
||||||
|
consumed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (file *truncatedIndexFile) Stat() (fs.FileInfo, error) { return nil, fs.ErrInvalid }
|
||||||
|
|
||||||
|
func (file *truncatedIndexFile) Read(destination []byte) (int, error) {
|
||||||
|
if file.consumed {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
file.consumed = true
|
||||||
|
return copy(destination, file.prefix), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (file *truncatedIndexFile) Close() error { return nil }
|
||||||
|
|
||||||
|
func TestAssets_TruncatedIndexIsNotServedAsOK(t *testing.T) {
|
||||||
|
prefix := []byte(`<!doctype html><html><body><div id="root">`)
|
||||||
|
server, err := NewServer(ServerOptions{
|
||||||
|
RunsDirectory: t.TempDir(),
|
||||||
|
AssetsFS: truncatedIndexFS{prefix: prefix},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
server.Handler().ServeHTTP(recorder, request)
|
||||||
|
|
||||||
|
if recorder.Code != http.StatusInternalServerError {
|
||||||
|
t.Errorf("status = %d, want 500", recorder.Code)
|
||||||
|
}
|
||||||
|
if strings.Contains(recorder.Body.String(), string(prefix)) {
|
||||||
|
t.Errorf("served the partial index body: %q", recorder.Body.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAssets_API404DoesNotFallThrough(t *testing.T) {
|
func TestAssets_API404DoesNotFallThrough(t *testing.T) {
|
||||||
server, _ := newFixtureServer(t)
|
server, _ := newFixtureServer(t)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|||||||
Reference in new issue
Block a user