a simple url shortener in Go (check it out at qurl.org)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

95 lines
2.1 KiB

3 years ago
3 years ago
3 years ago
  1. package assets
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "crypto/md5"
  6. "embed"
  7. "fmt"
  8. "net/http"
  9. "strings"
  10. )
  11. //go:embed *.ico *.html *.css
  12. var assets embed.FS
  13. func ReadFile(name string) ([]byte, error) {
  14. return assets.ReadFile(name)
  15. }
  16. type StaticContent struct {
  17. Type string
  18. Content []byte
  19. ETag string
  20. GZIPContent []byte
  21. GZIPETag string
  22. }
  23. func NewStaticContent(name string, mime string) (*StaticContent, error) {
  24. // Load data
  25. data, err := assets.ReadFile(name)
  26. if err != nil {
  27. return nil, err
  28. }
  29. sc := &StaticContent{Type: mime, Content: data}
  30. // Set a default Content-Type, if needed
  31. if sc.Type == "" {
  32. sc.Type = "application/octet-stream"
  33. }
  34. // Populate ETag
  35. sc.ETag = fmt.Sprintf("%x", md5.Sum(sc.Content))
  36. var buf bytes.Buffer
  37. gz, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
  38. defer gz.Close()
  39. if _, err := gz.Write(sc.Content); err != nil {
  40. return nil, err
  41. }
  42. if err := gz.Flush(); err != nil {
  43. return nil, err
  44. }
  45. // Using gzip encoding adds a minimum of 24 characters to the HTTP
  46. // header, so only accept gzip encoding if we save that much or more
  47. if (buf.Len() + 24) < len(sc.Content) {
  48. sc.GZIPContent = buf.Bytes()
  49. sc.GZIPETag = fmt.Sprintf("%x", md5.Sum(sc.GZIPContent))
  50. }
  51. return sc, nil
  52. }
  53. func (sc *StaticContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  54. gzok := strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
  55. gzlen := len(sc.GZIPContent)
  56. // Use the correct etag
  57. var localETag string
  58. if gzok && gzlen > 0 {
  59. localETag = sc.GZIPETag
  60. } else {
  61. localETag = sc.ETag
  62. }
  63. // Check the etag, maybe we don't need to send content
  64. remoteETag := r.Header.Get("If-None-Match")
  65. if localETag == remoteETag {
  66. w.WriteHeader(http.StatusNotModified)
  67. return
  68. }
  69. w.Header().Set("ETag", localETag)
  70. w.Header().Set("Content-Type", sc.Type)
  71. // Finally, write our content
  72. if gzok && gzlen > 0 {
  73. w.Header().Set("Content-Encoding", "gzip")
  74. w.Header().Set("Content-Length", fmt.Sprintf("%d", gzlen))
  75. w.Write(sc.GZIPContent)
  76. } else {
  77. w.Header().Set("Content-Length", fmt.Sprintf("%d", len(sc.Content)))
  78. w.Write(sc.Content)
  79. }
  80. }