Skip to content

Commit

Permalink
Merge branch 'release-2.8'
Browse files Browse the repository at this point in the history
Removed old "X-Requested" and "X-Result" headers
Record stats for Skins and Downloads as "Skin" being served
Output some useful information on start (listening address, Redis DB info)
Remove the last references of "Minotar" and replace with "Imgd"
Allow configuration of the redirect address
Remove statisticsEnabled an unused config option
Added a statistic for the number of cached skins (and differentiate more from the mem usage)
Allow selecting a different Database when using Redis
Removed old constants
Change some references of size to width
Fixed the estimation for the memory usage of the Memory cache
Fixed gcfg repository and tests
  • Loading branch information
LukeHandle committed Oct 6, 2015
2 parents 37cf7bd + 5ef4d34 commit 99e0c1f
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 58 deletions.
1 change: 1 addition & 0 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Cache interface {
has(username string) bool
pull(username string) minecraft.Skin
add(username string, skin minecraft.Skin)
size() uint
memory() uint64
}

Expand Down
10 changes: 8 additions & 2 deletions cache_memory.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package main

import (
"github.com/minotar/minecraft"
"time"

"github.com/minotar/minecraft"
)

const (
Expand Down Expand Up @@ -96,8 +97,13 @@ func (c *CacheMemory) add(username string, skin minecraft.Skin) {
})
}

// The exact number of usernames in the map
func (c *CacheMemory) size() uint {
return uint(len(c.Skins))
}

// The byte size of the cache. Fairly rough... don't really want to venture
// into the land of manual memory management, because there be dragons.
func (c *CacheMemory) memory() uint64 {
return uint64(len(c.Usernames) * SKIN_SIZE)
return uint64(len(c.Skins) * SKIN_SIZE)
}
4 changes: 4 additions & 0 deletions cache_off.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func (c *CacheOff) pull(username string) minecraft.Skin {
func (c *CacheOff) add(username string, skin minecraft.Skin) {
}

func (c *CacheOff) size() uint {
return 0
}

func (c *CacheOff) memory() uint64 {
return 0
}
40 changes: 34 additions & 6 deletions cache_redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package main
import (
"bytes"
"errors"
"fmt"
"github.com/fzzy/radix/extra/pool"
"github.com/fzzy/radix/redis"
"github.com/minotar/minecraft"
"image/png"
"strconv"
"strings"

"github.com/fzzy/radix/extra/pool"
"github.com/fzzy/radix/redis"
"github.com/minotar/minecraft"
)

type CacheRedis struct {
Expand All @@ -22,12 +22,22 @@ func dialFunc(network, addr string) (*redis.Client, error) {
if err != nil {
return nil, err
}

if config.Redis.Auth != "" {
if err := client.Cmd("AUTH", config.Redis.Auth).Err; err != nil {
r := client.Cmd("AUTH", config.Redis.Auth)
if r.Err != nil {
client.Close()
return nil, err
}
}

// Select the DB within Redis
r := client.Cmd("SELECT", config.Redis.DB)
if r.Err != nil {
client.Close()
return nil, err
}

return client, nil
}

Expand All @@ -45,7 +55,7 @@ func (c *CacheRedis) setup() error {

c.Pool = pool

log.Info("Loaded Redis cache (pool: " + fmt.Sprintf("%v", config.Redis.PoolSize) + ")")
log.Info("Loaded Redis cache (address: %s, db: %v, prefix: \"%s\", pool: %v)", config.Redis.Address, config.Redis.DB, config.Redis.Prefix, config.Redis.PoolSize)
return nil
}

Expand Down Expand Up @@ -128,6 +138,24 @@ func (c *CacheRedis) remove(username string) {
err = client.Cmd("DEL", config.Redis.Prefix+username).Err
}

func (c *CacheRedis) size() uint {
var err error
client := c.getFromPool()
if client == nil {
return 0
}
defer c.Pool.CarefullyPut(client, &err)

resp := client.Cmd("DBSIZE")
size, err := resp.Int()
if err != nil {
log.Error(err.Error())
return 0
}

return uint(size)
}

func (c *CacheRedis) memory() uint64 {
var err error
client := c.getFromPool()
Expand Down
7 changes: 4 additions & 3 deletions config.example.gcfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ address = 0.0.0.0:8000
# Cache you want to use for skins. May be "redis", "memory", or "off".
# If it's Redis, you should fill out the [redis] section below.
cache = memory
# Whether to store statistics about API usage. This requires a
# redis connection if true (fill the config below).
statisticsEnabled = false
# Address to redirect users to upon browsing /
url = https://minotar.net/
# The duration, in seconds we should store item in our cache. Default: 48 hrs
ttl = 172800

Expand All @@ -16,6 +15,8 @@ ttl = 172800
address = 127.0.0.1:6379
# "auth" is optional, it can be left blank if you don't need authentication.
auth =
# If you use your Redis for other data it may be useful to further separate it with databases.
db = 0
# We'll place this before skin caches in Redis to prevent conflicts.
prefix = skins:
# The number of Redis connections to use. 10 is a good number.
Expand Down
12 changes: 7 additions & 5 deletions configuration.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package main

import (
"code.google.com/p/gcfg"
"io"
"os"

"gopkg.in/gcfg.v1"
)

const (
Expand All @@ -16,15 +17,16 @@ const (

type Configuration struct {
Server struct {
Address string
Cache string
StatisticsEnabled bool
Ttl int
Address string
Cache string
URL string
Ttl int
}

Redis struct {
Address string
Auth string
DB int
Prefix string
PoolSize int
}
Expand Down
39 changes: 18 additions & 21 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package main

import (
"fmt"
"github.com/gorilla/mux"
"github.com/minotar/minecraft"
"net/http"
"strconv"
"strings"

"github.com/gorilla/mux"
"github.com/minotar/minecraft"
)

type Router struct {
Expand All @@ -17,37 +18,33 @@ type NotFoundHandler struct{}

// Handles 404 errors
func (h NotFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "404 not found")
}

// Converts and sanitizes the string for the avatar size.
func (r *Router) GetSize(inp string) uint {
// GetWidth converts and sanitizes the string for the avatar width.
func (r *Router) GetWidth(inp string) uint {
out64, err := strconv.ParseUint(inp, 10, 0)
out := uint(out64)
if err != nil {
return DefaultSize
} else if out > MaxSize {
return MaxSize
} else if out < MinSize {
return MinSize
return DefaultWidth
} else if out > MaxWidth {
return MaxWidth
} else if out < MinWidth {
return MinWidth
}
return out

}

// Shows only the user's skin.
func (router *Router) SkinPage(w http.ResponseWriter, r *http.Request) {
stats.Served("Skin")
vars := mux.Vars(r)

username := vars["username"]

skin := fetchSkin(username)

w.Header().Add("Content-Type", "image/png")
w.Header().Add("X-Requested", "skin")
w.Header().Add("X-Result", "ok")

skin.WriteSkin(w)
}

Expand Down Expand Up @@ -112,7 +109,7 @@ func (router *Router) writeType(ext string, skin *mcSkin, w http.ResponseWriter)
func (router *Router) Serve(resource string) {
fn := func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
size := router.GetSize(vars["size"])
width := router.GetWidth(vars["width"])
skin := fetchSkin(vars["username"])
skin.Mode = router.getResizeMode(vars["extension"])

Expand All @@ -121,17 +118,17 @@ func (router *Router) Serve(resource string) {
return
}

err := router.ResolveMethod(skin, resource)(int(size))
err := router.ResolveMethod(skin, resource)(int(width))
if err != nil {
w.WriteHeader(500)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "500 internal server error")
return
}
router.writeType(vars["extension"], skin, w)
}

router.Mux.HandleFunc("/"+strings.ToLower(resource)+"/{username:"+minecraft.ValidUsernameRegex+"}{extension:(\\..*)?}", fn)
router.Mux.HandleFunc("/"+strings.ToLower(resource)+"/{username:"+minecraft.ValidUsernameRegex+"}/{size:[0-9]+}{extension:(\\..*)?}", fn)
router.Mux.HandleFunc("/"+strings.ToLower(resource)+"/{username:"+minecraft.ValidUsernameRegex+"}/{width:[0-9]+}{extension:(\\..*)?}", fn)
}

// Binds routes to the ServerMux.
Expand All @@ -153,7 +150,7 @@ func (router *Router) Bind() {
router.Mux.HandleFunc("/skin/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", router.SkinPage)

router.Mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s", MinotarVersion)
fmt.Fprintf(w, "%s", ImgdVersion)
})

router.Mux.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) {
Expand All @@ -162,7 +159,7 @@ func (router *Router) Bind() {
})

router.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://minotar.net/", 302)
http.Redirect(w, r, config.Server.URL, http.StatusFound)
})
}

Expand Down
28 changes: 13 additions & 15 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,21 @@ package main

import (
"fmt"
"github.com/gorilla/mux"
"github.com/op/go-logging"
"net/http"
"os"
"runtime"

"github.com/gorilla/mux"
"github.com/op/go-logging"
)

// Set the default, min and max width to resize processed images to.
const (
DefaultSize = uint(180)
MaxSize = uint(300)
MinSize = uint(8)
DefaultWidth = uint(180)
MinWidth = uint(8)
MaxWidth = uint(300)

SkinCache

Minutes uint = 60
Hours = 60 * Minutes
Days = 24 * Hours
TimeoutActualSkin = 2 * Days
TimeoutFailedFetch = 15 * Minutes

MinotarVersion = "2.7"
ImgdVersion = "2.8"
)

var (
Expand Down Expand Up @@ -61,8 +55,12 @@ func startServer() {
r := Router{Mux: mux.NewRouter()}
r.Bind()
http.Handle("/", r.Mux)
log.Info("imgd %s starting on %s", ImgdVersion, config.Server.Address)
err := http.ListenAndServe(config.Server.Address, nil)
log.Critical(err.Error())
if err != nil {
log.Critical("ListenAndServe: \"%s\"", err.Error())
os.Exit(1)
}
}

func main() {
Expand Down
2 changes: 1 addition & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestRenders(t *testing.T) {
So(err, ShouldBeNil)

hash := hashRender(skin.Processed)
So(hash, ShouldEqual, "a579af057377c35cbd4e53ed6bc5f03e")
So(hash, ShouldEqual, "a253bb68f5ed938eb235ff1e3807940c")
})

Convey("GetBust should return a valid image", t, func() {
Expand Down
13 changes: 8 additions & 5 deletions status.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type statusCollectorMessage struct {
type StatusCollector struct {
info struct {
// Number of bytes allocated to the process.
Memory uint64
ImgdMem uint64
// Time in seconds the process has been running for
Uptime int64
// Number of times a request type has been served.
Expand All @@ -34,8 +34,10 @@ type StatusCollector struct {
CacheHits uint
// Number of times skins have failed to be served from the cache.
CacheMisses uint
// Size of cached skins
Cached uint64
// Number of skins in cache.
CacheSize uint
// Size of cache memory.
CacheMem uint64
}

// Unix timestamp the process was booted at.
Expand Down Expand Up @@ -96,9 +98,10 @@ func (s *StatusCollector) Collect() {
memstats := &runtime.MemStats{}
runtime.ReadMemStats(memstats)

s.info.Memory = memstats.Alloc
s.info.ImgdMem = memstats.Alloc
s.info.Uptime = time.Now().Unix() - s.StartedAt
s.info.Cached = cache.memory()
s.info.CacheSize = cache.size()
s.info.CacheMem = cache.memory()
}

// Increments the request counter for the specific type of request.
Expand Down

0 comments on commit 99e0c1f

Please sign in to comment.