96 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			96 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package assets
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"compress/gzip"
 | 
						|
	"crypto/md5"
 | 
						|
	"embed"
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
//go:embed *.ico *.html *.css
 | 
						|
var assets embed.FS
 | 
						|
 | 
						|
func ReadFile(name string) ([]byte, error) {
 | 
						|
	return assets.ReadFile(name)
 | 
						|
}
 | 
						|
 | 
						|
type StaticContent struct {
 | 
						|
	Type        string
 | 
						|
	Content     []byte
 | 
						|
	ETag        string
 | 
						|
	GZIPContent []byte
 | 
						|
	GZIPETag    string
 | 
						|
}
 | 
						|
 | 
						|
func NewStaticContent(name string, mime string) (*StaticContent, error) {
 | 
						|
	// Load data
 | 
						|
	data, err := assets.ReadFile(name)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	sc := &StaticContent{Type: mime, Content: data}
 | 
						|
 | 
						|
	// Set a default Content-Type, if needed
 | 
						|
	if sc.Type == "" {
 | 
						|
		sc.Type = "application/octet-stream"
 | 
						|
	}
 | 
						|
 | 
						|
	// Populate ETag
 | 
						|
	sc.ETag = fmt.Sprintf("%x", md5.Sum(sc.Content))
 | 
						|
 | 
						|
	var buf bytes.Buffer
 | 
						|
	gz, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
 | 
						|
	defer gz.Close()
 | 
						|
 | 
						|
	if _, err := gz.Write(sc.Content); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if err := gz.Flush(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// Using gzip encoding adds a minimum of 24 characters to the HTTP
 | 
						|
	// header, so only accept gzip encoding if we save that much or more
 | 
						|
	if (buf.Len() + 24) < len(sc.Content) {
 | 
						|
		sc.GZIPContent = buf.Bytes()
 | 
						|
		sc.GZIPETag = fmt.Sprintf("%x", md5.Sum(sc.GZIPContent))
 | 
						|
	}
 | 
						|
	return sc, nil
 | 
						|
}
 | 
						|
 | 
						|
func (sc *StaticContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
						|
	gzok := strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
 | 
						|
	gzlen := len(sc.GZIPContent)
 | 
						|
 | 
						|
	// Use the correct etag
 | 
						|
	var localETag string
 | 
						|
	if gzok && gzlen > 0 {
 | 
						|
		localETag = sc.GZIPETag
 | 
						|
	} else {
 | 
						|
		localETag = sc.ETag
 | 
						|
	}
 | 
						|
 | 
						|
	// Check the etag, maybe we don't need to send content
 | 
						|
	remoteETag := r.Header.Get("If-None-Match")
 | 
						|
	if localETag == remoteETag {
 | 
						|
		w.WriteHeader(http.StatusNotModified)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	w.Header().Set("ETag", localETag)
 | 
						|
	w.Header().Set("Content-Type", sc.Type)
 | 
						|
 | 
						|
	// Finally, write our content
 | 
						|
	if gzok && gzlen > 0 {
 | 
						|
		w.Header().Set("Content-Encoding", "gzip")
 | 
						|
		w.Header().Set("Content-Length", fmt.Sprintf("%d", gzlen))
 | 
						|
		w.Write(sc.GZIPContent)
 | 
						|
	} else {
 | 
						|
		w.Header().Set("Content-Length", fmt.Sprintf("%d", len(sc.Content)))
 | 
						|
		w.Write(sc.Content)
 | 
						|
	}
 | 
						|
}
 |