| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- // Copyright (C) 2014-2020 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- // Package assets hold utilities for serving static assets.
- //
- // The actual assets live in auto subpackages instead of here,
- // because the set of assets varies per program.
- package assets
- import (
- "compress/gzip"
- "fmt"
- "io"
- "mime"
- "net/http"
- "path/filepath"
- "strconv"
- "strings"
- "time"
- )
- // Asset is the type of arguments to Serve.
- type Asset struct {
- ContentGz string // gzipped contents of asset.
- Filename string // Original filename, determines Content-Type.
- Modified time.Time // Determines ETag and Last-Modified.
- }
- // Serve writes a gzipped asset to w.
- func Serve(w http.ResponseWriter, r *http.Request, asset Asset) {
- header := w.Header()
- mtype := MimeTypeForFile(asset.Filename)
- if mtype != "" {
- header.Set("Content-Type", mtype)
- }
- etag := fmt.Sprintf(`"%x"`, asset.Modified.Unix())
- header.Set("ETag", etag)
- header.Set("Last-Modified", asset.Modified.Format(http.TimeFormat))
- t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since"))
- if err == nil && !asset.Modified.After(t) {
- w.WriteHeader(http.StatusNotModified)
- return
- }
- if r.Header.Get("If-None-Match") == etag {
- w.WriteHeader(http.StatusNotModified)
- return
- }
- if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
- header.Set("Content-Encoding", "gzip")
- header.Set("Content-Length", strconv.Itoa(len(asset.ContentGz)))
- io.WriteString(w, asset.ContentGz)
- } else {
- // gunzip for browsers that don't want gzip.
- var gr *gzip.Reader
- gr, _ = gzip.NewReader(strings.NewReader(asset.ContentGz))
- io.Copy(w, gr)
- gr.Close()
- }
- }
- // MimeTypeForFile returns the appropriate MIME type for an asset,
- // based on the filename.
- //
- // We use a built in table of the common types since the system
- // TypeByExtension might be unreliable. But if we don't know, we delegate
- // to the system. All our text files are in UTF-8.
- func MimeTypeForFile(file string) string {
- ext := filepath.Ext(file)
- switch ext {
- case ".htm", ".html":
- return "text/html; charset=utf-8"
- case ".css":
- return "text/css; charset=utf-8"
- case ".js":
- return "application/javascript; charset=utf-8"
- case ".json":
- return "application/json; charset=utf-8"
- case ".png":
- return "image/png"
- case ".ttf":
- return "application/x-font-ttf"
- case ".woff":
- return "application/x-font-woff"
- case ".svg":
- return "image/svg+xml; charset=utf-8"
- default:
- return mime.TypeByExtension(ext)
- }
- }
|