From 13c771c84e6b86cebe6df14d0e92001f940581ba Mon Sep 17 00:00:00 2001 From: JP Appel Date: Tue, 8 Oct 2024 13:17:03 -0400 Subject: Move ansi stripper to util package --- Makefile | 2 +- api/api.go | 7 ++++++- api/docker.go | 33 --------------------------------- api/docker_test.go | 50 -------------------------------------------------- util/util.go | 38 ++++++++++++++++++++++++++++++++++++++ util/util_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 95 insertions(+), 85 deletions(-) delete mode 100644 api/docker_test.go create mode 100644 util/util.go create mode 100644 util/util_test.go diff --git a/Makefile b/Makefile index 6867614..45282a3 100644 --- a/Makefile +++ b/Makefile @@ -20,5 +20,5 @@ info: @echo "BIN: $(BIN)" @echo "SERVICE: $(SERVICE)" -nonsense-time: nonsense-time.go $(wildcard api/*.go) $(wildcard dashboard/*.go) +nonsense-time: nonsense-time.go $(wildcard api/*.go) $(wildcard dashboard/*.go) $(wildcard util/*.go) go build . diff --git a/api/api.go b/api/api.go index eb801eb..650e3d2 100644 --- a/api/api.go +++ b/api/api.go @@ -5,6 +5,7 @@ import ( "encoding/json" "log/slog" "net/http" + "nonsense-time/util" "os" "strconv" "time" @@ -84,10 +85,14 @@ func VttLogs(w http.ResponseWriter, req *http.Request) { http.Error(w, "Error occured while getting logs", http.StatusInternalServerError) return } + logReader = util.NewAnsiStripper(logReader) defer logReader.Close() - if _, err = stdcopy.StdCopy(w, w, logReader); err != nil { + // FIXME: erroring here with "Unrecognized input header: 112" + n, err := stdcopy.StdCopy(w, nil, logReader) + if err != nil { Logger.Error("Error occured while writting logs to response", slog.Any("err", err), slog.Any("request", req)) + Logger.Error("", slog.Int64("written", n)) http.Error(w, "Error occured while writting logs", http.StatusInternalServerError) return } diff --git a/api/docker.go b/api/docker.go index c79b76d..551c818 100644 --- a/api/docker.go +++ b/api/docker.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "log/slog" - "regexp" "time" "github.com/docker/docker/api/types/container" @@ -14,38 +13,6 @@ import ( const vttContainerId string = "foundry-foundry-1" -// Strip ansi control characters from source -// -// The source reader is automatically closed when stripper reader is -type ansiStripper struct { - regex regexp.Regexp - source io.ReadCloser -} - -func NewAnsiStripper(source io.ReadCloser) *ansiStripper { - striper := new(ansiStripper) - striper.regex = *regexp.MustCompile(`\x1b[[0-9;]*m`) - striper.source = source - - return striper -} - -func (rc ansiStripper) Read(p []byte) (int, error) { - n, err := rc.source.Read(p) - if err != io.EOF && err != nil { - return n, err - } - - stripped := rc.regex.ReplaceAll(p[:n], []byte("")) - n = copy(p, stripped) - - return n, err -} - -func (rc ansiStripper) Close() error { - return rc.source.Close() -} - func stopVtt(ctx context.Context) error { apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { diff --git a/api/docker_test.go b/api/docker_test.go deleted file mode 100644 index 7d85803..0000000 --- a/api/docker_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package api_test - -import ( - "io" - "nonsense-time/api" - "strings" - "testing" -) - -type stringReadCloser struct { - *strings.Reader -} - -func newStringReadCloser(s string) stringReadCloser { - rc := stringReadCloser{} - rc.Reader = strings.NewReader(s) - - return rc -} - -func (stringReadCloser) Close() error { - return nil -} - -func testAnsiStripper(t *testing.T, input string, expected string) { - reader := newStringReadCloser(input) - cleanReader := api.NewAnsiStripper(reader) - defer cleanReader.Close() - - buf := new(strings.Builder) - n, err := io.Copy(buf, cleanReader) - if err != nil { - t.Fatal("Error while copying cleaned text to output", err) - } - - result := buf.String() - - if n != int64(len(expected)) { - t.Errorf("Expected to write %d characters but wrote %d\n", 3, n) - } - - if result != expected { - t.Errorf("Expected string `%s` but wrote `%s`", "abc", result) - } -} - -func TestStripAnsiColors(t *testing.T) { - testAnsiStripper(t, "a\x1b[31mbc", "abc") - testAnsiStripper(t, "[\x1b[32minfo\x1b[39m]", "[info]") -} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..deb528b --- /dev/null +++ b/util/util.go @@ -0,0 +1,38 @@ +package util + +import ( + "io" + "regexp" +) + +// Strip ansi control characters from source +// +// The source reader is automatically closed when stripper reader is +type AnsiStripper struct { + regex regexp.Regexp + source io.ReadCloser +} + +func NewAnsiStripper(source io.ReadCloser) *AnsiStripper { + striper := new(AnsiStripper) + striper.regex = *regexp.MustCompile(`\x1b[[0-9;]*m`) + striper.source = source + + return striper +} + +func (rc AnsiStripper) Read(p []byte) (int, error) { + n, err := rc.source.Read(p) + if err != io.EOF && err != nil { + return n, err + } + + stripped := rc.regex.ReplaceAll(p[:n], []byte("")) + n = copy(p, stripped) + + return n, err +} + +func (rc AnsiStripper) Close() error { + return rc.source.Close() +} diff --git a/util/util_test.go b/util/util_test.go new file mode 100644 index 0000000..1c4ae39 --- /dev/null +++ b/util/util_test.go @@ -0,0 +1,50 @@ +package util_test; + +import ( + "io" + "nonsense-time/util" + "strings" + "testing" +) + +type stringReadCloser struct { + *strings.Reader +} + +func newStringReadCloser(s string) stringReadCloser { + rc := stringReadCloser{} + rc.Reader = strings.NewReader(s) + + return rc +} + +func (stringReadCloser) Close() error { + return nil +} + +func testAnsiStripper(t *testing.T, input string, expected string) { + reader := newStringReadCloser(input) + cleanReader := util.NewAnsiStripper(reader) + defer cleanReader.Close() + + buf := new(strings.Builder) + n, err := io.Copy(buf, cleanReader) + if err != nil { + t.Fatal("Error while copying cleaned text to output", err) + } + + result := buf.String() + + if n != int64(len(expected)) { + t.Errorf("Expected to write %d characters but wrote %d\n", 3, n) + } + + if result != expected { + t.Errorf("Expected string `%s` but wrote `%s`", "abc", result) + } +} + +func TestStripAnsiColors(t *testing.T) { + testAnsiStripper(t, "a\x1b[31mbc", "abc") + testAnsiStripper(t, "[\x1b[32minfo\x1b[39m]", "[info]") +} -- cgit v1.2.3