diff --git a/mvc/controllers/controllers_pages/main.go b/mvc/controllers/controllers_pages/main.go index 4ae6b51..4423156 100644 --- a/mvc/controllers/controllers_pages/main.go +++ b/mvc/controllers/controllers_pages/main.go @@ -38,25 +38,35 @@ func MainPageHandler(app *models.App) http.HandlerFunc { } // Страничка рендерится только если ее нет в кэше - pageData, ok := app.PagesCache.Get(models_pages.MainPageTmplName) + page, ok := app.PagesCache.Get(models_pages.MainPageTmplName) if !ok { - pageData, err = models_pages.RenderMainPage(app.Templates, app.Version) + pageData, err := models_pages.RenderMainPage(app.Templates, app.Version) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - app.PagesCache.Set(models_pages.MainPageTmplName, pageData) + ETag := models.GenerateETag(pageData) + page = &models.Page{Data: pageData, ETag: ETag} + app.PagesCache.Set(models_pages.MainPageTmplName, page) } - sendMainPage(w, pageData) + sendMainPage(r, w, page) }) } // Отправляет страницу -func sendMainPage(w http.ResponseWriter, data []byte) { +func sendMainPage(r *http.Request, w http.ResponseWriter, page *models.Page) { + + w.Header().Set("Cache-Control", "public, max-age=31536000") + w.Header().Set("ETag", page.ETag) w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(data) + + if match := r.Header.Get("If-None-Match"); match == page.ETag { + w.WriteHeader(http.StatusNotModified) + return + } + + w.Write(page.Data) } // Ответ на метод COUNT diff --git a/mvc/controllers/controllers_pages/post.go b/mvc/controllers/controllers_pages/post.go index 3fa0491..8907b3e 100644 --- a/mvc/controllers/controllers_pages/post.go +++ b/mvc/controllers/controllers_pages/post.go @@ -10,8 +10,6 @@ import ( // Обработчик главной страницы func PostPageHandler(app *models.App) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var err error - postLink := r.URL.Path // Ссылки на посты имеют вид postLink = /link/, а если прилетело что то типо /link/123123, @@ -22,26 +20,33 @@ func PostPageHandler(app *models.App) http.HandlerFunc { } // Страничка рендерится только если ее нет в кэше - pageData, ok := app.PagesCache.Get(postLink) + page, ok := app.PagesCache.Get(postLink) if !ok { - post := app.Posts[models_pages.PostLink(postLink)] - - pageData, err = models_pages.RenderPostPage(app.Templates, app.Version, post.Data) + pageData, err := models_pages.RenderPostPage(app.Templates, app.Version, post.Data) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - app.PagesCache.Set(postLink, pageData) + ETag := models.GenerateETag(pageData) + page = &models.Page{Data: pageData, ETag: ETag} + app.PagesCache.Set(postLink, page) } - sendPostPage(w, pageData) + sendPostPage(r, w, page) }) } // Отправляет страницу -func sendPostPage(w http.ResponseWriter, data []byte) { +func sendPostPage(r *http.Request, w http.ResponseWriter, page *models.Page) { + w.Header().Set("Cache-Control", "public, max-age=31536000") + w.Header().Set("ETag", page.ETag) w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(data) + + if match := r.Header.Get("If-None-Match"); match == page.ETag { + w.WriteHeader(http.StatusNotModified) + return + } + + w.Write(page.Data) } diff --git a/mvc/controllers/controllers_pages/posts.go b/mvc/controllers/controllers_pages/posts.go index 76d98ec..4859cbe 100644 --- a/mvc/controllers/controllers_pages/posts.go +++ b/mvc/controllers/controllers_pages/posts.go @@ -9,26 +9,33 @@ import ( // Обработчик главной страницы func PostsPageHandler(app *models.App) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var err error - // Страничка рендерится только если ее нет в кэше - pageData, ok := app.PagesCache.Get(models_pages.PostsPageTmplName) + page, ok := app.PagesCache.Get(models_pages.PostsPageTmplName) if !ok { - pageData, err = app.Posts.RenderPostsPage(app.Templates, app.Version) + pageData, err := app.Posts.RenderPostsPage(app.Templates, app.Version) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - app.PagesCache.Set(models_pages.PostsPageTmplName, pageData) + ETag := models.GenerateETag(pageData) + page = &models.Page{Data: pageData, ETag: ETag} + app.PagesCache.Set(models_pages.PostsPageTmplName, page) } - sendPostsPage(w, pageData) + sendPostsPage(r, w, page) }) } // Отправляет страницу -func sendPostsPage(w http.ResponseWriter, data []byte) { +func sendPostsPage(r *http.Request, w http.ResponseWriter, page *models.Page) { + w.Header().Set("Cache-Control", "public, max-age=31536000") + w.Header().Set("ETag", page.ETag) w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(data) + + if match := r.Header.Get("If-None-Match"); match == page.ETag { + w.WriteHeader(http.StatusNotModified) + return + } + + w.Write(page.Data) } diff --git a/mvc/models/cache.go b/mvc/models/cache.go index e7cb4e5..46a09ec 100644 --- a/mvc/models/cache.go +++ b/mvc/models/cache.go @@ -1,25 +1,40 @@ package models -import "sync" +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "sync" +) + +type Page struct { + Data []byte + ETag string +} type Cache struct { - Data map[string][]byte + Data map[string]*Page Mu sync.RWMutex } func initCache() *Cache { - return &Cache{Data: make(map[string][]byte)} + return &Cache{Data: make(map[string]*Page)} } -func (c *Cache) Get(key string) ([]byte, bool) { +func (c *Cache) Get(key string) (*Page, bool) { c.Mu.RLock() - pageData, ok := c.Data[key] + page, ok := c.Data[key] c.Mu.RUnlock() - return pageData, ok + return page, ok } -func (c *Cache) Set(key string, data []byte) { +func (c *Cache) Set(key string, page *Page) { c.Mu.Lock() - c.Data[key] = data + c.Data[key] = page c.Mu.Unlock() } + +func GenerateETag(data []byte) string { + hash := md5.Sum(data) + return fmt.Sprintf(`"%s"`, hex.EncodeToString(hash[:])) +} diff --git a/mvc/views/blocks/footer.gohtml b/mvc/views/blocks/footer.gohtml index 7a436bb..a4cebe7 100644 --- a/mvc/views/blocks/footer.gohtml +++ b/mvc/views/blocks/footer.gohtml @@ -16,14 +16,8 @@
  • unix timestamp of page rendering: {{ .renderingTimestamp }}
  • curl -X LIMINAL https://hikan.ru - what do you know about liminal spaces?
  • this site code repository - git.hikan.ru/serr/hikan.ru
  • -
  • 24-hour server request count (curl -X COUNT https://hikan.ru):
  • +
  • curl -X COUNT https://hikan.ru - 24-hour server request count
  • -

    - - - - -

    @@ -34,16 +28,4 @@

    - {{ end }}