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.
This commit is contained in:
greatroar 2020-05-07 11:47:23 +02:00 committed by GitHub
parent 2cdeb1bf70
commit e2febf246e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 21 additions and 25 deletions

View File

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

View File

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

View File

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

View File

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

View File

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