mirror of
https://github.com/nkanaev/yarr.git
synced 2025-05-24 00:33:14 +00:00
cache feed icons
This commit is contained in:
parent
214c7aacfc
commit
f38dcfba3b
@ -1,12 +1,15 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/nkanaev/yarr/src/assets"
|
"github.com/nkanaev/yarr/src/assets"
|
||||||
@ -143,20 +146,51 @@ func (s *Server) handleFeedErrors(c *router.Context) {
|
|||||||
c.JSON(http.StatusOK, errors)
|
c.JSON(http.StatusOK, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type feedicon struct {
|
||||||
|
ctype string
|
||||||
|
bytes []byte
|
||||||
|
etag string
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) handleFeedIcon(c *router.Context) {
|
func (s *Server) handleFeedIcon(c *router.Context) {
|
||||||
// TODO: caching
|
|
||||||
id, err := c.VarInt64("id")
|
id, err := c.VarInt64("id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Out.WriteHeader(http.StatusBadRequest)
|
c.Out.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
feed := s.db.GetFeed(id)
|
|
||||||
if feed != nil && feed.Icon != nil {
|
cachekey := "icon:" + strconv.FormatInt(id, 10)
|
||||||
c.Out.Header().Set("Content-Type", http.DetectContentType(*feed.Icon))
|
cachedat := s.cache[cachekey]
|
||||||
c.Out.Write(*feed.Icon)
|
if cachedat == nil {
|
||||||
} else {
|
feed := s.db.GetFeed(id)
|
||||||
c.Out.WriteHeader(http.StatusNotFound)
|
if feed == nil || feed.Icon == nil {
|
||||||
|
c.Out.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := md5.New()
|
||||||
|
hash.Write(*feed.Icon)
|
||||||
|
|
||||||
|
etag := fmt.Sprintf("%x", hash.Sum(nil))[:16]
|
||||||
|
|
||||||
|
cachedat = feedicon{
|
||||||
|
ctype: http.DetectContentType(*feed.Icon),
|
||||||
|
bytes: *(*feed).Icon,
|
||||||
|
etag: etag,
|
||||||
|
}
|
||||||
|
s.cache[cachekey] = cachedat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
icon := cachedat.(feedicon)
|
||||||
|
|
||||||
|
if c.Req.Header.Get("If-None-Match") == icon.etag {
|
||||||
|
c.Out.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Out.Header().Set("Content-Type", icon.ctype)
|
||||||
|
c.Out.Header().Set("Etag", icon.etag)
|
||||||
|
c.Out.Write(icon.bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleFeedList(c *router.Context) {
|
func (s *Server) handleFeedList(c *router.Context) {
|
||||||
@ -191,7 +225,7 @@ func (s *Server) handleFeedList(c *router.Context) {
|
|||||||
|
|
||||||
c.JSON(http.StatusOK, map[string]interface{}{
|
c.JSON(http.StatusOK, map[string]interface{}{
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"feed": feed,
|
"feed": feed,
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
c.JSON(http.StatusOK, map[string]string{"status": "notfound"})
|
c.JSON(http.StatusOK, map[string]string{"status": "notfound"})
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nkanaev/yarr/src/storage"
|
"github.com/nkanaev/yarr/src/storage"
|
||||||
@ -71,3 +74,41 @@ func TestIndexGzipped(t *testing.T) {
|
|||||||
t.Errorf("invalid content-type header: %#v", response.Header.Get("content-type"))
|
t.Errorf("invalid content-type header: %#v", response.Header.Get("content-type"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFeedIcons(t *testing.T) {
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
db, _ := storage.New(":memory:")
|
||||||
|
icon := []byte("test")
|
||||||
|
feed := db.CreateFeed("", "", "", "", nil)
|
||||||
|
db.UpdateFeedIcon(feed.Id, &icon)
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
url := fmt.Sprintf("/api/feeds/%d/icon", feed.Id)
|
||||||
|
request := httptest.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
handler := NewServer(db, "127.0.0.1:8000").handler()
|
||||||
|
handler.ServeHTTP(recorder, request)
|
||||||
|
response := recorder.Result()
|
||||||
|
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
body, _ := io.ReadAll(response.Body)
|
||||||
|
if !reflect.DeepEqual(body, icon) {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
if response.Header.Get("Etag") == "" {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
recorder2 := httptest.NewRecorder()
|
||||||
|
request2 := httptest.NewRequest("GET", url, nil)
|
||||||
|
request2.Header.Set("If-None-Match", response.Header.Get("Etag"))
|
||||||
|
handler.ServeHTTP(recorder2, request2)
|
||||||
|
response2 := recorder2.Result()
|
||||||
|
|
||||||
|
if response2.StatusCode != http.StatusNotModified {
|
||||||
|
t.Fatal("got", response2.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,6 +12,7 @@ type Server struct {
|
|||||||
Addr string
|
Addr string
|
||||||
db *storage.Storage
|
db *storage.Storage
|
||||||
worker *worker.Worker
|
worker *worker.Worker
|
||||||
|
cache map[string]interface{}
|
||||||
|
|
||||||
BasePath string
|
BasePath string
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ func NewServer(db *storage.Storage, addr string) *Server {
|
|||||||
db: db,
|
db: db,
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
worker: worker.NewWorker(db),
|
worker: worker.NewWorker(db),
|
||||||
|
cache: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user