Browse Source

all: Store assets as strings (#6611)

Storing assets as []byte requires every compiled-in asset to be copied
into writable memory at program startup. That currently takes up 1.6MB
per syncthing process. Strings stay in the RODATA section and should be
shared between processes running the same binary.
greatroar 5 years ago
parent
commit
e2febf246e
5 changed files with 21 additions and 25 deletions
  1. 4 8
      cmd/strelaypoolsrv/main.go
  2. 7 8
      lib/api/api_statics.go
  3. 3 3
      lib/api/api_test.go
  4. 4 3
      lib/auto/auto_test.go
  5. 3 3
      script/genassets.go

+ 4 - 8
cmd/strelaypoolsrv/main.go

@@ -1,11 +1,8 @@
 // Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).
 
-//go:generate go run ../../script/genassets.go gui >auto/gui.go
-
 package main
 
 import (
-	"bytes"
 	"compress/gzip"
 	"context"
 	"crypto/tls"
@@ -302,16 +299,15 @@ func handleAssets(w http.ResponseWriter, r *http.Request) {
 	}
 	if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
 		w.Header().Set("Content-Encoding", "gzip")
+		w.Header().Set("Content-Length", strconv.Itoa(len(bs)))
+		io.WriteString(w, bs)
 	} else {
 		// ungzip if browser not send gzip accepted header
 		var gr *gzip.Reader
-		gr, _ = gzip.NewReader(bytes.NewReader(bs))
-		bs, _ = ioutil.ReadAll(gr)
+		gr, _ = gzip.NewReader(strings.NewReader(bs))
+		io.Copy(w, gr)
 		gr.Close()
 	}
-	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(bs)))
-
-	w.Write(bs)
 }
 
 func mimeTypeForFile(file string) string {

+ 7 - 8
lib/api/api_statics.go

@@ -7,14 +7,14 @@
 package api
 
 import (
-	"bytes"
 	"compress/gzip"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"mime"
 	"net/http"
 	"os"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"time"
 
@@ -27,7 +27,7 @@ const themePrefix = "theme-assets/"
 
 type staticsServer struct {
 	assetDir        string
-	assets          map[string][]byte
+	assets          map[string]string
 	availableThemes []string
 
 	mut             sync.RWMutex
@@ -168,16 +168,15 @@ func (s *staticsServer) serveAsset(w http.ResponseWriter, r *http.Request) {
 	}
 	if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
 		w.Header().Set("Content-Encoding", "gzip")
+		w.Header().Set("Content-Length", strconv.Itoa(len(bs)))
+		io.WriteString(w, bs)
 	} else {
 		// ungzip if browser not send gzip accepted header
 		var gr *gzip.Reader
-		gr, _ = gzip.NewReader(bytes.NewReader(bs))
-		bs, _ = ioutil.ReadAll(gr)
+		gr, _ = gzip.NewReader(strings.NewReader(bs))
+		io.Copy(w, gr)
 		gr.Close()
 	}
-	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(bs)))
-
-	w.Write(bs)
 }
 
 func (s *staticsServer) serveThemes(w http.ResponseWriter, r *http.Request) {

+ 3 - 3
lib/api/api_test.go

@@ -152,19 +152,19 @@ func TestAssetsDir(t *testing.T) {
 	gw := gzip.NewWriter(buf)
 	gw.Write([]byte("default"))
 	gw.Close()
-	def := buf.Bytes()
+	def := buf.String()
 
 	buf = new(bytes.Buffer)
 	gw = gzip.NewWriter(buf)
 	gw.Write([]byte("foo"))
 	gw.Close()
-	foo := buf.Bytes()
+	foo := buf.String()
 
 	e := &staticsServer{
 		theme:    "foo",
 		mut:      sync.NewRWMutex(),
 		assetDir: "testdata",
-		assets: map[string][]byte{
+		assets: map[string]string{
 			"foo/a":     foo, // overridden in foo/a
 			"foo/b":     foo,
 			"default/a": def, // overridden in default/a (but foo/a takes precedence)

+ 4 - 3
lib/auto/auto_test.go

@@ -10,6 +10,7 @@ import (
 	"bytes"
 	"compress/gzip"
 	"io/ioutil"
+	"strings"
 	"testing"
 
 	"github.com/syncthing/syncthing/lib/auto"
@@ -23,10 +24,10 @@ func TestAssets(t *testing.T) {
 	}
 
 	var gr *gzip.Reader
-	gr, _ = gzip.NewReader(bytes.NewReader(idx))
-	idx, _ = ioutil.ReadAll(gr)
+	gr, _ = gzip.NewReader(strings.NewReader(idx))
+	html, _ := ioutil.ReadAll(gr)
 
-	if !bytes.Contains(idx, []byte("<html")) {
+	if !bytes.Contains(html, []byte("<html")) {
 		t.Fatal("No html in index.html")
 	}
 }

+ 3 - 3
script/genassets.go

@@ -29,8 +29,8 @@ package auto
 
 const Generated int64 = {{.Generated}}
 
-func Assets() map[string][]byte {
-	var assets = make(map[string][]byte, {{.Assets | len}})
+func Assets() map[string]string {
+	var assets = make(map[string]string, {{.Assets | len}})
 {{range $asset := .Assets}}
 	assets["{{$asset.Name}}"] = {{$asset.Data}}{{end}}
 	return assets
@@ -72,7 +72,7 @@ func walkerFor(basePath string) filepath.WalkFunc {
 			name, _ = filepath.Rel(basePath, name)
 			assets = append(assets, asset{
 				Name: filepath.ToSlash(name),
-				Data: fmt.Sprintf("[]byte(%q)", buf.String()),
+				Data: fmt.Sprintf("%q", buf.String()),
 			})
 		}