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.
 
 
 

72 lines
2.0 KiB

  1. package handlers
  2. import (
  3. "crypto/md5"
  4. "io/ioutil"
  5. "net/http"
  6. "strconv"
  7. "time"
  8. "github.com/PuerkitoBio/ghost"
  9. )
  10. // FaviconHandlerFunc is the same as FaviconHandler, it is just a convenience
  11. // signature that accepts a func(http.ResponseWriter, *http.Request) instead of
  12. // a http.Handler interface. It saves the boilerplate http.HandlerFunc() cast.
  13. func FaviconHandlerFunc(h http.HandlerFunc, path string, maxAge time.Duration) http.HandlerFunc {
  14. return FaviconHandler(h, path, maxAge)
  15. }
  16. // Efficient favicon handler, mostly a port of node's Connect library implementation
  17. // of the favicon middleware.
  18. // https://github.com/senchalabs/connect
  19. func FaviconHandler(h http.Handler, path string, maxAge time.Duration) http.HandlerFunc {
  20. var buf []byte
  21. var hash string
  22. return func(w http.ResponseWriter, r *http.Request) {
  23. var err error
  24. if r.URL.Path == "/favicon.ico" {
  25. if buf == nil {
  26. // Read from file and cache
  27. ghost.LogFn("ghost.favicon : serving from %s", path)
  28. buf, err = ioutil.ReadFile(path)
  29. if err != nil {
  30. ghost.LogFn("ghost.favicon : error reading file : %s", err)
  31. http.NotFound(w, r)
  32. return
  33. }
  34. hash = hashContent(buf)
  35. }
  36. writeHeaders(w.Header(), buf, maxAge, hash)
  37. writeBody(w, r, buf)
  38. } else {
  39. h.ServeHTTP(w, r)
  40. }
  41. }
  42. }
  43. // Write the content of the favicon, or respond with a 404 not found
  44. // in case of error (hardly a critical error).
  45. func writeBody(w http.ResponseWriter, r *http.Request, buf []byte) {
  46. _, err := w.Write(buf)
  47. if err != nil {
  48. ghost.LogFn("ghost.favicon : error writing response : %s", err)
  49. http.NotFound(w, r)
  50. }
  51. }
  52. // Correctly set the http headers.
  53. func writeHeaders(hdr http.Header, buf []byte, maxAge time.Duration, hash string) {
  54. hdr.Set("Content-Type", "image/x-icon")
  55. hdr.Set("Content-Length", strconv.Itoa(len(buf)))
  56. hdr.Set("Etag", hash)
  57. hdr.Set("Cache-Control", "public, max-age="+strconv.Itoa(int(maxAge.Seconds())))
  58. }
  59. // Get the MD5 hash of the content.
  60. func hashContent(buf []byte) string {
  61. h := md5.New()
  62. return string(h.Sum(buf))
  63. }