Skip to content

Commit

Permalink
fix: serve http using zip file (#7)
Browse files Browse the repository at this point in the history
* fix: serve http using a zip file
  • Loading branch information
clement2026 authored Sep 5, 2024
1 parent 4c64856 commit 64e1dc3
Show file tree
Hide file tree
Showing 16 changed files with 307 additions and 22 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ build-for-release:
$(call print_step, Build for releasing)
$(MAKE) -C ts build
$(MAKE) copy
$(MAKE) zip
$(MAKE) -f go.mk build-for-release

clean:
Expand All @@ -52,3 +53,6 @@ copy:
fi
$(call print_step, Copying static files to go)
cp -r ts/out assets/web/html

zip:
zip -r assets/web/html.zip ts/out/*
Binary file added assets/web/html.zip
Binary file not shown.
19 changes: 19 additions & 0 deletions assets/web_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package assets

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestZipContent(t *testing.T) {
z, err := Web.Open("web/html.zip")
defer z.Close()
assert.NoError(t, err)

stat, err := z.Stat()
assert.NoError(t, err)

assert.Greater(t, stat.Size(), int64(0))
assert.Equal(t, stat.Name(), "html.zip")
}
6 changes: 4 additions & 2 deletions cmd/live-pprof/live-pprof.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package main

import "github.com/moderato-app/live-pprof/pkg"
import (
"github.com/moderato-app/live-pprof/internal"
)

func main() {
pkg.LivePprof()
internal.LivePprof()
}
2 changes: 1 addition & 1 deletion go.mk
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ test:

clean:
@echo "Cleaning go"
go clean; rm -rf build
go clean; rm -rf build; rm -rf assets/web/html
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/moderato-app/live-pprof

go 1.22.6
go 1.23.0

//replace github.com/moderato-app/pprof => ../../google-pprof

Expand All @@ -13,20 +13,24 @@ require (
github.com/moderato-app/pprof v0.0.0-20240823224210-78ccd2f4d170
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
)

require (
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca // indirect
github.com/klauspost/compress v1.11.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e // indirect
github.com/rs/cors v1.7.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
Expand All @@ -37,5 +41,6 @@ require (
golang.org/x/text v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
nhooyr.io/websocket v1.8.6 // indirect
)
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down Expand Up @@ -190,9 +191,12 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.1.10/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
Expand Down Expand Up @@ -297,6 +301,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e h1:51xcRlSMBU5rhM9KahnJGfEsBPVPz3182TgFRowA8yY=
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e/go.mod h1:tcaRap0jS3eifrEEllL6ZMd9dg8IlDpi2S1oARrQ+NI=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
Expand Down Expand Up @@ -520,6 +526,7 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
Expand Down
61 changes: 50 additions & 11 deletions internal/chi/chi_web_server.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
package chi

import (
"errors"
"fmt"
"io/fs"
"net/http"
"net/http/pprof"
_ "net/http/pprof"

"github.com/moderato-app/live-pprof/assets"
"github.com/moderato-app/live-pprof/internal/logging"

chi "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5"
chiMiddleware "github.com/go-chi/chi/v5/middleware"
"github.com/improbable-eng/grpc-web/go/grpcweb"
"github.com/moderato-app/live-pprof/assets"
"github.com/moderato-app/live-pprof/pkg"
)

func WebServer(g *grpcweb.WrappedGrpcServer) *chi.Mux {
func WebServer(middlewares ...func(http.Handler) http.Handler) (*chi.Mux, error) {
router := chi.NewRouter()
router.Use(
chiMiddleware.Logger,
chiMiddleware.Recoverer,
grpcMiddleware(g),
)
w, err := fs.Sub(assets.Web, "web/html")

for _, middleware := range middlewares {
router.Use(middleware)
}

dir, err := staticFS()
if err != nil {
logging.Sugar.Panic(err)
return nil, err
}

router.Handle("/debug/pprof", http.RedirectHandler("/debug/pprof/", http.StatusTemporaryRedirect))
Expand All @@ -33,7 +37,42 @@ func WebServer(g *grpcweb.WrappedGrpcServer) *chi.Mux {
router.HandleFunc("/debug/pprof/trace", pprof.Trace)
router.HandleFunc("/debug/pprof/*", pprof.Index)

router.Handle("/*", http.FileServer(http.FS(w)))
router.Handle("/*", http.FileServer(http.FS(dir)))

return router, nil
}

return router
// staticFS use web/html as static files. If web/html does not exist,
// extract web/html.zip into a memory FS
func staticFS() (fs.FS, error) {
_, err := assets.Web.Open("web/html")

if err != nil {
// There are 2 cases when web/html doesn't exist:
// 1. During development when web/html isn't generated yet.
// 2. When live-pprof is installed using `go install github.com/moderato-app/live-pprof`.
// In these cases, we can use the html.zip files as static content. They may not have the latest code,
// but they'll still work.
if errors.Is(err, fs.ErrNotExist) {
z, err := assets.Web.Open("web/html.zip")
if err != nil {
return nil, fmt.Errorf("failed to open web/html.zip: %w", err)
}
defer z.Close()

mf, err := pkg.Unzip(z)
if err != nil {
return nil, fmt.Errorf("failed to unzip web/html.zip: %w", err)
}
return mf, nil
} else {
return nil, fmt.Errorf("failed to open web/html: %w", err)
}
}

dir, err := fs.Sub(assets.Web, "web/html")
if err != nil {
return nil, fmt.Errorf("failed to open web/html: %w", err)
}
return dir, nil
}
67 changes: 67 additions & 0 deletions internal/chi/chi_web_server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package chi

import (
"io"
"io/fs"
"net/http"
"net/http/httptest"
"testing"

"github.com/moderato-app/live-pprof/assets"
"github.com/moderato-app/live-pprof/pkg"

"github.com/stretchr/testify/assert"
)

func TestWebServer(t *testing.T) {
chi, err := WebServer()
assert.NoError(t, err)

ts := httptest.NewServer(chi)

resp, err := http.Get(ts.URL + "/")
assert.NoError(t, err)
defer resp.Body.Close()

assert.Equal(t, resp.StatusCode, http.StatusOK)
assert.Contains(t, resp.Header.Get("Content-Type"), "text/html")

data, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
bodyStr := string(data)
t.Log("body:", bodyStr)

assert.Contains(t, bodyStr, "Live pprof")
}

func TestMemFSHTTPServer(t *testing.T) {
z, err := assets.Web.Open("web/html.zip")
assert.NoError(t, err)
defer z.Close()

assert.NoError(t, err)

mf, err := pkg.Unzip(z)
assert.NoError(t, err)

ts := httptest.NewServer(http.FileServer(http.FS(mf)))

resp, err := http.Get(ts.URL + "/")
assert.NoError(t, err)
defer resp.Body.Close()

assert.Equal(t, resp.StatusCode, http.StatusOK)
assert.Contains(t, resp.Header.Get("Content-Type"), "text/html")

data, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
bodyStr := string(data)
t.Log("body:", bodyStr)

assert.Contains(t, bodyStr, "Live pprof")
}

func TestFileNotExists(t *testing.T) {
_, err := assets.Web.Open("web/html2")
assert.ErrorIs(t, err, fs.ErrNotExist)
}
2 changes: 1 addition & 1 deletion internal/chi/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/improbable-eng/grpc-web/go/grpcweb"
)

func grpcMiddleware(grpcWeb *grpcweb.WrappedGrpcServer) func(next http.Handler) http.Handler {
func GrpcMiddleware(grpcWeb *grpcweb.WrappedGrpcServer) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if grpcWeb.IsAcceptableGrpcCorsRequest(r) || grpcWeb.IsGrpcWebRequest(r) {
Expand Down
5 changes: 2 additions & 3 deletions pkg/live-pprof.go → internal/live-pprof.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package pkg
package internal

import (
"net/url"
Expand All @@ -7,7 +7,6 @@ import (
"time"

"github.com/moderato-app/live-pprof/api"
"github.com/moderato-app/live-pprof/internal"
"github.com/moderato-app/live-pprof/internal/config"
"github.com/moderato-app/live-pprof/internal/general"
"github.com/moderato-app/live-pprof/internal/logging"
Expand All @@ -33,7 +32,7 @@ func LivePprof() {
maybeOpenURL(conf)
}()

internal.StartServeGrpc(grpcServer, conf)
StartServeGrpc(grpcServer, conf)
}

func maybeOpenURL(conf *config.LivePprofConfig) {
Expand Down
5 changes: 4 additions & 1 deletion internal/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ func StartServeGrpc(gs *grpc.Server, conf *config.LivePprofConfig) {
wrappedGrpc := grpcweb.WrapServer(gs, grpcweb.WithOriginFunc(func(origin string) bool {
return true
}))
s, err := chi2.WebServer(chi2.GrpcMiddleware(wrappedGrpc))

s := chi2.WebServer(wrappedGrpc)
if err != nil {
logging.Sugar.Panic("failed to init WebServer", err)
}

go func() {
if err := http.Serve(l, s); err != nil {
Expand Down
6 changes: 4 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package main

import "github.com/moderato-app/live-pprof/pkg"
import (
"github.com/moderato-app/live-pprof/internal"
)

func main() {
pkg.LivePprof()
internal.LivePprof()
}
Binary file added pkg/test_data/html.zip
Binary file not shown.
Loading

0 comments on commit 64e1dc3

Please sign in to comment.