replace zxq.co/ripple/hanayo

This commit is contained in:
Alicia
2019-02-23 13:29:15 +00:00
commit c3d206c173
5871 changed files with 1353715 additions and 0 deletions

2
vendor/github.com/johnniedoe/contrib/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
*/Godeps/*
!*/Godeps/Godeps.json

8
vendor/github.com/johnniedoe/contrib/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,8 @@
language: go
go:
- 1.4
- 1.4.2
- tip
services:
- memcache
- redis-server

17
vendor/github.com/johnniedoe/contrib/README.md generated vendored Normal file
View File

@@ -0,0 +1,17 @@
# gin-gonic/contrib [![Build Status](https://travis-ci.org/gin-gonic/contrib.svg)](https://travis-ci.org/gin-gonic/contrib)
=======
Here you'll find middleware ready to use with [Gin Framework](https://github.com/gin-gonic/gin). Submit your pull request, either with the package in a folder, or by adding a link to this `README.md`.
If adding a package directly, don't forget to create a `README.md` inside with author name.
If adding a link to your own repository, please follow this example:
```
+ nameOfMiddleware (https://github.com/yourusername/yourrepo)
```
Each author is responsible of maintaining his own code, although if you submit as a package, you allow the community to fix it. You can also submit a pull request to fix an existing package.
======
## List of external middleware
+ [staticbin](https://github.com/olebedev/staticbin) - middleware/handler for serving static files from binary data

View File

@@ -0,0 +1,22 @@
{
"ImportPath": "github.com/gin-gonic/contrib/cache",
"GoVersion": "go1.3",
"Deps": [
{
"ImportPath": "github.com/bradfitz/gomemcache/memcache",
"Rev": "72a68649ba712ee7c4b5b4a943a626bcd7d90eb8"
},
{
"ImportPath": "github.com/garyburd/redigo/redis",
"Rev": "535138d7bcd717d6531c701ef5933d98b1866257"
},
{
"ImportPath": "github.com/gin-gonic/gin",
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
},
{
"ImportPath": "github.com/robfig/go-cache",
"Rev": "9fc39e0dbf62c034ec4e45e6120fc69433a3ec51"
}
]
}

153
vendor/github.com/johnniedoe/contrib/cache/cache.go generated vendored Normal file
View File

@@ -0,0 +1,153 @@
package cache
import (
"bytes"
"crypto/sha1"
"errors"
"github.com/gin-gonic/gin"
"io"
"net/http"
"net/url"
"time"
)
const (
DEFAULT = time.Duration(0)
FOREVER = time.Duration(-1)
CACHE_MIDDLEWARE_KEY = "gincontrib.cache"
)
var (
PageCachePrefix = "gincontrib.page.cache"
ErrCacheMiss = errors.New("cache: key not found.")
ErrNotStored = errors.New("cache: not stored.")
ErrNotSupport = errors.New("cache: not support.")
)
type CacheStore interface {
Get(key string, value interface{}) error
Set(key string, value interface{}, expire time.Duration) error
Add(key string, value interface{}, expire time.Duration) error
Replace(key string, data interface{}, expire time.Duration) error
Delete(key string) error
Increment(key string, data uint64) (uint64, error)
Decrement(key string, data uint64) (uint64, error)
Flush() error
}
type responseCache struct {
status int
header http.Header
data []byte
}
type cachedWriter struct {
gin.ResponseWriter
status int
written bool
store CacheStore
expire time.Duration
key string
}
func urlEscape(prefix string, u string) string {
key := url.QueryEscape(u)
if len(key) > 200 {
h := sha1.New()
io.WriteString(h, u)
key = string(h.Sum(nil))
}
var buffer bytes.Buffer
buffer.WriteString(prefix)
buffer.WriteString(":")
buffer.WriteString(key)
return buffer.String()
}
func newCachedWriter(store CacheStore, expire time.Duration, writer gin.ResponseWriter, key string) *cachedWriter {
return &cachedWriter{writer, 0, false, store, expire, key}
}
func (w *cachedWriter) WriteHeader(code int) {
w.status = code
w.written = true
w.ResponseWriter.WriteHeader(code)
}
func (w *cachedWriter) Status() int {
return w.status
}
func (w *cachedWriter) Written() bool {
return w.written
}
func (w *cachedWriter) Write(data []byte) (int, error) {
ret, err := w.ResponseWriter.Write(data)
if err == nil {
//cache response
store := w.store
val := responseCache{
w.status,
w.Header(),
data,
}
err = store.Set(w.key, val, w.expire)
if err != nil {
// need logger
}
}
return ret, err
}
// Cache Middleware
func Cache(store *CacheStore) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set(CACHE_MIDDLEWARE_KEY, store)
c.Next()
}
}
func SiteCache(store CacheStore, expire time.Duration) gin.HandlerFunc {
return func(c *gin.Context) {
var cache responseCache
url := c.Request.URL
key := urlEscape(PageCachePrefix, url.RequestURI())
if err := store.Get(key, &cache); err != nil {
c.Next()
} else {
c.Writer.WriteHeader(cache.status)
for k, vals := range cache.header {
for _, v := range vals {
c.Writer.Header().Add(k, v)
}
}
c.Writer.Write(cache.data)
}
}
}
// Cache Decorator
func CachePage(store CacheStore, expire time.Duration, handle gin.HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) {
var cache responseCache
url := c.Request.URL
key := urlEscape(PageCachePrefix, url.RequestURI())
if err := store.Get(key, &cache); err != nil {
// replace writer
writer := newCachedWriter(store, expire, c.Writer, key)
c.Writer = writer
handle(c)
} else {
c.Writer.WriteHeader(cache.status)
for k, vals := range cache.header {
for _, v := range vals {
c.Writer.Header().Add(k, v)
}
}
c.Writer.Write(cache.data)
}
}
}

View File

@@ -0,0 +1,202 @@
package cache
import (
"math"
"testing"
"time"
)
type cacheFactory func(*testing.T, time.Duration) CacheStore
// Test typical cache interactions
func typicalGetSet(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
value := "foo"
if err = cache.Set("value", value, DEFAULT); err != nil {
t.Errorf("Error setting a value: %s", err)
}
value = ""
err = cache.Get("value", &value)
if err != nil {
t.Errorf("Error getting a value: %s", err)
}
if value != "foo" {
t.Errorf("Expected to get foo back, got %s", value)
}
}
// Test the increment-decrement cases
func incrDecr(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
// Normal increment / decrement operation.
if err = cache.Set("int", 10, DEFAULT); err != nil {
t.Errorf("Error setting int: %s", err)
}
newValue, err := cache.Increment("int", 50)
if err != nil {
t.Errorf("Error incrementing int: %s", err)
}
if newValue != 60 {
t.Errorf("Expected 60, was %d", newValue)
}
if newValue, err = cache.Decrement("int", 50); err != nil {
t.Errorf("Error decrementing: %s", err)
}
if newValue != 10 {
t.Errorf("Expected 10, was %d", newValue)
}
// Increment wraparound
newValue, err = cache.Increment("int", math.MaxUint64-5)
if err != nil {
t.Errorf("Error wrapping around: %s", err)
}
if newValue != 4 {
t.Errorf("Expected wraparound 4, got %d", newValue)
}
// Decrement capped at 0
newValue, err = cache.Decrement("int", 25)
if err != nil {
t.Errorf("Error decrementing below 0: %s", err)
}
if newValue != 0 {
t.Errorf("Expected capped at 0, got %d", newValue)
}
}
func expiration(t *testing.T, newCache cacheFactory) {
// memcached does not support expiration times less than 1 second.
var err error
cache := newCache(t, time.Second)
// Test Set w/ DEFAULT
value := 10
cache.Set("int", value, DEFAULT)
time.Sleep(2 * time.Second)
err = cache.Get("int", &value)
if err != ErrCacheMiss {
t.Errorf("Expected CacheMiss, but got: %s", err)
}
// Test Set w/ short time
cache.Set("int", value, time.Second)
time.Sleep(2 * time.Second)
err = cache.Get("int", &value)
if err != ErrCacheMiss {
t.Errorf("Expected CacheMiss, but got: %s", err)
}
// Test Set w/ longer time.
cache.Set("int", value, time.Hour)
time.Sleep(2 * time.Second)
err = cache.Get("int", &value)
if err != nil {
t.Errorf("Expected to get the value, but got: %s", err)
}
// Test Set w/ forever.
cache.Set("int", value, FOREVER)
time.Sleep(2 * time.Second)
err = cache.Get("int", &value)
if err != nil {
t.Errorf("Expected to get the value, but got: %s", err)
}
}
func emptyCache(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
err = cache.Get("notexist", 0)
if err == nil {
t.Errorf("Error expected for non-existent key")
}
if err != ErrCacheMiss {
t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
}
err = cache.Delete("notexist")
if err != ErrCacheMiss {
t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
}
_, err = cache.Increment("notexist", 1)
if err != ErrCacheMiss {
t.Errorf("Expected cache miss incrementing non-existent key: %s", err)
}
_, err = cache.Decrement("notexist", 1)
if err != ErrCacheMiss {
t.Errorf("Expected cache miss decrementing non-existent key: %s", err)
}
}
func testReplace(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
// Replace in an empty cache.
if err = cache.Replace("notexist", 1, FOREVER); err != ErrNotStored {
t.Errorf("Replace in empty cache: expected ErrNotStored, got: %s", err)
}
// Set a value of 1, and replace it with 2
if err = cache.Set("int", 1, time.Second); err != nil {
t.Errorf("Unexpected error: %s", err)
}
if err = cache.Replace("int", 2, time.Second); err != nil {
t.Errorf("Unexpected error: %s", err)
}
var i int
if err = cache.Get("int", &i); err != nil {
t.Errorf("Unexpected error getting a replaced item: %s", err)
}
if i != 2 {
t.Errorf("Expected 2, got %d", i)
}
// Wait for it to expire and replace with 3 (unsuccessfully).
time.Sleep(2 * time.Second)
if err = cache.Replace("int", 3, time.Second); err != ErrNotStored {
t.Errorf("Expected ErrNotStored, got: %s", err)
}
if err = cache.Get("int", &i); err != ErrCacheMiss {
t.Errorf("Expected cache miss, got: %s", err)
}
}
func testAdd(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
// Add to an empty cache.
if err = cache.Add("int", 1, time.Second); err != nil {
t.Errorf("Unexpected error adding to empty cache: %s", err)
}
// Try to add again. (fail)
if err = cache.Add("int", 2, time.Second); err != ErrNotStored {
t.Errorf("Expected ErrNotStored adding dupe to cache: %s", err)
}
// Wait for it to expire, and add again.
time.Sleep(2 * time.Second)
if err = cache.Add("int", 3, time.Second); err != nil {
t.Errorf("Unexpected error adding to cache: %s", err)
}
// Get and verify the value.
var i int
if err = cache.Get("int", &i); err != nil {
t.Errorf("Unexpected error: %s", err)
}
if i != 3 {
t.Errorf("Expected 3, got: %d", i)
}
}

View File

@@ -0,0 +1,25 @@
package main
import (
"fmt"
"github.com/gin-gonic/contrib/cache"
"github.com/gin-gonic/gin"
"time"
)
func main() {
r := gin.Default()
store := cache.NewInMemoryStore(time.Second)
// Cached Page
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
})
r.GET("/cache_ping", cache.CachePage(store, time.Minute, func(c *gin.Context) {
c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
}))
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}

78
vendor/github.com/johnniedoe/contrib/cache/inmemory.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
package cache
import (
"github.com/robfig/go-cache"
"reflect"
"time"
)
type InMemoryStore struct {
cache.Cache
}
func NewInMemoryStore(defaultExpiration time.Duration) *InMemoryStore {
return &InMemoryStore{*cache.New(defaultExpiration, time.Minute)}
}
func (c *InMemoryStore) Get(key string, value interface{}) error {
val, found := c.Cache.Get(key)
if !found {
return ErrCacheMiss
}
v := reflect.ValueOf(value)
if v.Type().Kind() == reflect.Ptr && v.Elem().CanSet() {
v.Elem().Set(reflect.ValueOf(val))
return nil
}
return ErrNotStored
}
func (c *InMemoryStore) Set(key string, value interface{}, expires time.Duration) error {
// NOTE: go-cache understands the values of DEFAULT and FOREVER
c.Cache.Set(key, value, expires)
return nil
}
func (c *InMemoryStore) Add(key string, value interface{}, expires time.Duration) error {
err := c.Cache.Add(key, value, expires)
if err == cache.ErrKeyExists {
return ErrNotStored
}
return err
}
func (c *InMemoryStore) Replace(key string, value interface{}, expires time.Duration) error {
if err := c.Cache.Replace(key, value, expires); err != nil {
return ErrNotStored
}
return nil
}
func (c *InMemoryStore) Delete(key string) error {
if found := c.Cache.Delete(key); !found {
return ErrCacheMiss
}
return nil
}
func (c *InMemoryStore) Increment(key string, n uint64) (uint64, error) {
newValue, err := c.Cache.Increment(key, n)
if err == cache.ErrCacheMiss {
return 0, ErrCacheMiss
}
return newValue, err
}
func (c *InMemoryStore) Decrement(key string, n uint64) (uint64, error) {
newValue, err := c.Cache.Decrement(key, n)
if err == cache.ErrCacheMiss {
return 0, ErrCacheMiss
}
return newValue, err
}
func (c *InMemoryStore) Flush() error {
c.Cache.Flush()
return nil
}

View File

@@ -0,0 +1,35 @@
package cache
import (
"testing"
"time"
)
var newInMemoryStore = func(_ *testing.T, defaultExpiration time.Duration) CacheStore {
return NewInMemoryStore(defaultExpiration)
}
// Test typical cache interactions
func TestInMemoryCache_TypicalGetSet(t *testing.T) {
typicalGetSet(t, newInMemoryStore)
}
func TestInMemoryCache_IncrDecr(t *testing.T) {
incrDecr(t, newInMemoryStore)
}
func TestInMemoryCache_Expiration(t *testing.T) {
expiration(t, newInMemoryStore)
}
func TestInMemoryCache_EmptyCache(t *testing.T) {
emptyCache(t, newInMemoryStore)
}
func TestInMemoryCache_Replace(t *testing.T) {
testReplace(t, newInMemoryStore)
}
func TestInMemoryCache_Add(t *testing.T) {
testAdd(t, newInMemoryStore)
}

View File

@@ -0,0 +1,87 @@
package cache
import (
"github.com/bradfitz/gomemcache/memcache"
"time"
)
type MemcachedStore struct {
*memcache.Client
defaultExpiration time.Duration
}
func NewMemcachedStore(hostList []string, defaultExpiration time.Duration) *MemcachedStore {
return &MemcachedStore{memcache.New(hostList...), defaultExpiration}
}
func (c *MemcachedStore) Set(key string, value interface{}, expires time.Duration) error {
return c.invoke((*memcache.Client).Set, key, value, expires)
}
func (c *MemcachedStore) Add(key string, value interface{}, expires time.Duration) error {
return c.invoke((*memcache.Client).Add, key, value, expires)
}
func (c *MemcachedStore) Replace(key string, value interface{}, expires time.Duration) error {
return c.invoke((*memcache.Client).Replace, key, value, expires)
}
func (c *MemcachedStore) Get(key string, value interface{}) error {
item, err := c.Client.Get(key)
if err != nil {
return convertMemcacheError(err)
}
return deserialize(item.Value, value)
}
func (c *MemcachedStore) Delete(key string) error {
return convertMemcacheError(c.Client.Delete(key))
}
func (c *MemcachedStore) Increment(key string, delta uint64) (uint64, error) {
newValue, err := c.Client.Increment(key, delta)
return newValue, convertMemcacheError(err)
}
func (c *MemcachedStore) Decrement(key string, delta uint64) (uint64, error) {
newValue, err := c.Client.Decrement(key, delta)
return newValue, convertMemcacheError(err)
}
func (c *MemcachedStore) Flush() error {
return ErrNotSupport
}
func (c *MemcachedStore) invoke(storeFn func(*memcache.Client, *memcache.Item) error,
key string, value interface{}, expire time.Duration) error {
switch expire {
case DEFAULT:
expire = c.defaultExpiration
case FOREVER:
expire = time.Duration(0)
}
b, err := serialize(value)
if err != nil {
return err
}
return convertMemcacheError(storeFn(c.Client, &memcache.Item{
Key: key,
Value: b,
Expiration: int32(expire / time.Second),
}))
}
func convertMemcacheError(err error) error {
switch err {
case nil:
return nil
case memcache.ErrCacheMiss:
return ErrCacheMiss
case memcache.ErrNotStored:
return ErrNotStored
}
return err
}

View File

@@ -0,0 +1,46 @@
package cache
import (
"net"
"testing"
"time"
)
// These tests require memcached running on localhost:11211 (the default)
const testServer = "localhost:11211"
var newMemcachedStore = func(t *testing.T, defaultExpiration time.Duration) CacheStore {
c, err := net.Dial("tcp", testServer)
if err == nil {
c.Write([]byte("flush_all\r\n"))
c.Close()
return NewMemcachedStore([]string{testServer}, defaultExpiration)
}
t.Errorf("couldn't connect to memcached on %s", testServer)
t.FailNow()
panic("")
}
func TestMemcachedCache_TypicalGetSet(t *testing.T) {
typicalGetSet(t, newMemcachedStore)
}
func TestMemcachedCache_IncrDecr(t *testing.T) {
incrDecr(t, newMemcachedStore)
}
func TestMemcachedCache_Expiration(t *testing.T) {
expiration(t, newMemcachedStore)
}
func TestMemcachedCache_EmptyCache(t *testing.T) {
emptyCache(t, newMemcachedStore)
}
func TestMemcachedCache_Replace(t *testing.T) {
testReplace(t, newMemcachedStore)
}
func TestMemcachedCache_Add(t *testing.T) {
testAdd(t, newMemcachedStore)
}

181
vendor/github.com/johnniedoe/contrib/cache/redis.go generated vendored Normal file
View File

@@ -0,0 +1,181 @@
package cache
import (
"github.com/garyburd/redigo/redis"
"time"
)
// Wraps the Redis client to meet the Cache interface.
type RedisStore struct {
pool *redis.Pool
defaultExpiration time.Duration
}
// until redigo supports sharding/clustering, only one host will be in hostList
func NewRedisCache(host string, password string, defaultExpiration time.Duration) *RedisStore {
var pool = &redis.Pool{
MaxIdle: 5,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) {
// the redis protocol should probably be made sett-able
c, err := redis.Dial("tcp", host)
if err != nil {
return nil, err
}
if len(password) > 0 {
if _, err := c.Do("AUTH", password); err != nil {
c.Close()
return nil, err
}
} else {
// check with PING
if _, err := c.Do("PING"); err != nil {
c.Close()
return nil, err
}
}
return c, err
},
// custom connection test method
TestOnBorrow: func(c redis.Conn, t time.Time) error {
if _, err := c.Do("PING"); err != nil {
return err
}
return nil
},
}
return &RedisStore{pool, defaultExpiration}
}
func (c *RedisStore) Set(key string, value interface{}, expires time.Duration) error {
return c.invoke(c.pool.Get().Do, key, value, expires)
}
func (c *RedisStore) Add(key string, value interface{}, expires time.Duration) error {
conn := c.pool.Get()
if exists(conn, key) {
return ErrNotStored
}
return c.invoke(conn.Do, key, value, expires)
}
func (c *RedisStore) Replace(key string, value interface{}, expires time.Duration) error {
conn := c.pool.Get()
if !exists(conn, key) {
return ErrNotStored
}
err := c.invoke(conn.Do, key, value, expires)
if value == nil {
return ErrNotStored
} else {
return err
}
}
func (c *RedisStore) Get(key string, ptrValue interface{}) error {
conn := c.pool.Get()
defer conn.Close()
raw, err := conn.Do("GET", key)
if raw == nil {
return ErrCacheMiss
}
item, err := redis.Bytes(raw, err)
if err != nil {
return err
}
return deserialize(item, ptrValue)
}
func exists(conn redis.Conn, key string) bool {
retval, _ := redis.Bool(conn.Do("EXISTS", key))
return retval
}
func (c *RedisStore) Delete(key string) error {
conn := c.pool.Get()
defer conn.Close()
if !exists(conn, key) {
return ErrCacheMiss
}
_, err := conn.Do("DEL", key)
return err
}
func (c *RedisStore) Increment(key string, delta uint64) (uint64, error) {
conn := c.pool.Get()
defer conn.Close()
// Check for existance *before* increment as per the cache contract.
// redis will auto create the key, and we don't want that. Since we need to do increment
// ourselves instead of natively via INCRBY (redis doesn't support wrapping), we get the value
// and do the exists check this way to minimize calls to Redis
val, err := conn.Do("GET", key)
if val == nil {
return 0, ErrCacheMiss
}
if err == nil {
currentVal, err := redis.Int64(val, nil)
if err != nil {
return 0, err
}
var sum int64 = currentVal + int64(delta)
_, err = conn.Do("SET", key, sum)
if err != nil {
return 0, err
}
return uint64(sum), nil
} else {
return 0, err
}
}
func (c *RedisStore) Decrement(key string, delta uint64) (newValue uint64, err error) {
conn := c.pool.Get()
defer conn.Close()
// Check for existance *before* increment as per the cache contract.
// redis will auto create the key, and we don't want that, hence the exists call
if !exists(conn, key) {
return 0, ErrCacheMiss
}
// Decrement contract says you can only go to 0
// so we go fetch the value and if the delta is greater than the amount,
// 0 out the value
currentVal, err := redis.Int64(conn.Do("GET", key))
if err == nil && delta > uint64(currentVal) {
tempint, err := redis.Int64(conn.Do("DECRBY", key, currentVal))
return uint64(tempint), err
}
tempint, err := redis.Int64(conn.Do("DECRBY", key, delta))
return uint64(tempint), err
}
func (c *RedisStore) Flush() error {
conn := c.pool.Get()
defer conn.Close()
_, err := conn.Do("FLUSHALL")
return err
}
func (c *RedisStore) invoke(f func(string, ...interface{}) (interface{}, error),
key string, value interface{}, expires time.Duration) error {
switch expires {
case DEFAULT:
expires = c.defaultExpiration
case FOREVER:
expires = time.Duration(0)
}
b, err := serialize(value)
if err != nil {
return err
}
conn := c.pool.Get()
defer conn.Close()
if expires > 0 {
_, err := f("SETEX", key, int32(expires/time.Second), b)
return err
} else {
_, err := f("SET", key, b)
return err
}
}

View File

@@ -0,0 +1,48 @@
package cache
import (
"net"
"testing"
"time"
)
// These tests require redis server running on localhost:6379 (the default)
const redisTestServer = "localhost:6379"
var newRedisStore = func(t *testing.T, defaultExpiration time.Duration) CacheStore {
c, err := net.Dial("tcp", redisTestServer)
if err == nil {
c.Write([]byte("flush_all\r\n"))
c.Close()
redisCache := NewRedisCache(redisTestServer, "", defaultExpiration)
redisCache.Flush()
return redisCache
}
t.Errorf("couldn't connect to redis on %s", redisTestServer)
t.FailNow()
panic("")
}
func TestRedisCache_TypicalGetSet(t *testing.T) {
typicalGetSet(t, newRedisStore)
}
func TestRedisCache_IncrDecr(t *testing.T) {
incrDecr(t, newRedisStore)
}
func TestRedisCache_Expiration(t *testing.T) {
expiration(t, newRedisStore)
}
func TestRedisCache_EmptyCache(t *testing.T) {
emptyCache(t, newRedisStore)
}
func TestRedisCache_Replace(t *testing.T) {
testReplace(t, newRedisStore)
}
func TestRedisCache_Add(t *testing.T) {
testAdd(t, newRedisStore)
}

View File

@@ -0,0 +1,66 @@
package cache
import (
"bytes"
"encoding/gob"
"reflect"
"strconv"
)
func serialize(value interface{}) ([]byte, error) {
if bytes, ok := value.([]byte); ok {
return bytes, nil
}
switch v := reflect.ValueOf(value); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return []byte(strconv.FormatInt(v.Int(), 10)), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return []byte(strconv.FormatUint(v.Uint(), 10)), nil
}
var b bytes.Buffer
encoder := gob.NewEncoder(&b)
if err := encoder.Encode(value); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func deserialize(byt []byte, ptr interface{}) (err error) {
if bytes, ok := ptr.(*[]byte); ok {
*bytes = byt
return nil
}
if v := reflect.ValueOf(ptr); v.Kind() == reflect.Ptr {
switch p := v.Elem(); p.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var i int64
i, err = strconv.ParseInt(string(byt), 10, 64)
if err != nil {
return err
} else {
p.SetInt(i)
}
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
var i uint64
i, err = strconv.ParseUint(string(byt), 10, 64)
if err != nil {
return err
} else {
p.SetUint(i)
}
return nil
}
}
b := bytes.NewBuffer(byt)
decoder := gob.NewDecoder(b)
if err = decoder.Decode(ptr); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,55 @@
package commonlog
import (
"bytes"
"io"
"strconv"
"sync"
"time"
"github.com/gin-gonic/gin"
)
// Instances a Logger middleware that will write the logs to gin.DefaultWriter
// By default gin.DefaultWriter = os.Stdout
func New() gin.HandlerFunc {
return NewWithWriter(gin.DefaultWriter)
}
// Instance a Logger middleware with the specified writter buffer.
// Example: os.Stdout, a file opened in write mode, a socket...
func NewWithWriter(out io.Writer) gin.HandlerFunc {
pool := &sync.Pool{
New: func() interface{} {
buf := new(bytes.Buffer)
return buf
},
}
return func(c *gin.Context) {
path := c.Request.URL.Path
// Process request
c.Next()
//127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
w := pool.Get().(*bytes.Buffer)
w.Reset()
w.WriteString(c.ClientIP())
w.WriteString(" ")
w.WriteString(time.Now().Format("[02/Jan/2006:15:04:05 -0700] "))
w.WriteString("\"")
w.WriteString(c.Request.Method)
w.WriteString(" ")
w.WriteString(path)
w.WriteString(" ")
w.WriteString(c.Request.Proto)
w.WriteString("\" ")
w.WriteString(strconv.Itoa(c.Writer.Status()))
w.WriteString(" ")
w.WriteString(strconv.Itoa(c.Writer.Size()))
w.WriteString("\n")
w.WriteTo(out)
pool.Put(w)
}
}

109
vendor/github.com/johnniedoe/contrib/cors/config.go generated vendored Normal file
View File

@@ -0,0 +1,109 @@
package cors
import (
"net/http"
"strconv"
"strings"
"time"
)
type settings struct {
allowAllOrigins bool
allowedOriginFunc func(string) bool
allowedOrigins []string
allowedMethods []string
allowedHeaders []string
exposedHeaders []string
normalHeaders http.Header
preflightHeaders http.Header
}
func newSettings(c Config) *settings {
if err := c.Validate(); err != nil {
panic(err.Error())
}
return &settings{
allowedOriginFunc: c.AllowOriginFunc,
allowAllOrigins: c.AllowAllOrigins,
allowedOrigins: c.AllowedOrigins,
allowedMethods: distinct(c.AllowedMethods),
allowedHeaders: distinct(c.AllowedHeaders),
normalHeaders: generateNormalHeaders(c),
preflightHeaders: generatePreflightHeaders(c),
}
}
func (c *settings) validateOrigin(origin string) (string, bool) {
if c.allowAllOrigins {
return "*", true
}
if c.allowedOriginFunc != nil {
return origin, c.allowedOriginFunc(origin)
}
for _, value := range c.allowedOrigins {
if value == origin {
return origin, true
}
}
return "", false
}
func (c *settings) validateMethod(method string) bool {
// TODO!!!
return true
}
func (c *settings) validateHeader(header string) bool {
// TODO!!!
return true
}
func generateNormalHeaders(c Config) http.Header {
headers := make(http.Header)
if c.AllowCredentials {
headers.Set("Access-Control-Allow-Credentials", "true")
}
if len(c.ExposedHeaders) > 0 {
headers.Set("Access-Control-Expose-Headers", strings.Join(c.ExposedHeaders, ", "))
}
return headers
}
func generatePreflightHeaders(c Config) http.Header {
headers := make(http.Header)
if c.AllowCredentials {
headers.Set("Access-Control-Allow-Credentials", "true")
}
if len(c.AllowedMethods) > 0 {
headers.Set("Access-Control-Allow-Methods", strings.Join(c.AllowedMethods, ", "))
}
if len(c.AllowedHeaders) > 0 {
headers.Set("Access-Control-Allow-Headers", strings.Join(c.AllowedHeaders, ", "))
}
if c.MaxAge > time.Duration(0) {
headers.Set("Access-Control-Max-Age", strconv.FormatInt(int64(c.MaxAge/time.Second), 10))
}
return headers
}
func distinct(s []string) []string {
m := map[string]bool{}
for _, v := range s {
if _, seen := m[v]; !seen {
s[len(m)] = v
m[v] = true
}
}
return s[:len(m)]
}
func parse(content string) []string {
if len(content) == 0 {
return nil
}
parts := strings.Split(content, ",")
for i := 0; i < len(parts); i++ {
parts[i] = strings.TrimSpace(parts[i])
}
return parts
}

145
vendor/github.com/johnniedoe/contrib/cors/cors.go generated vendored Normal file
View File

@@ -0,0 +1,145 @@
package cors
import (
"errors"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
)
type Config struct {
AbortOnError bool
AllowAllOrigins bool
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
// If the special "*" value is present in the list, all origins will be allowed.
// Default value is ["*"]
AllowedOrigins []string
// AllowOriginFunc is a custom function to validate the origin. It take the origin
// as argument and returns true if allowed or false otherwise. If this option is
// set, the content of AllowedOrigins is ignored.
AllowOriginFunc func(origin string) bool
// AllowedMethods is a list of methods the client is allowed to use with
// cross-domain requests. Default value is simple methods (GET and POST)
AllowedMethods []string
// AllowedHeaders is list of non simple headers the client is allowed to use with
// cross-domain requests.
// If the special "*" value is present in the list, all headers will be allowed.
// Default value is [] but "Origin" is always appended to the list.
AllowedHeaders []string
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
// API specification
ExposedHeaders []string
// AllowCredentials indicates whether the request can include user credentials like
// cookies, HTTP authentication or client side SSL certificates.
AllowCredentials bool
// MaxAge indicates how long (in seconds) the results of a preflight request
// can be cached
MaxAge time.Duration
}
func (c *Config) AddAllowedMethods(methods ...string) {
c.AllowedMethods = append(c.AllowedMethods, methods...)
}
func (c *Config) AddAllowedHeaders(headers ...string) {
c.AllowedHeaders = append(c.AllowedHeaders, headers...)
}
func (c *Config) AddExposedHeaders(headers ...string) {
c.ExposedHeaders = append(c.ExposedHeaders, headers...)
}
func (c Config) Validate() error {
if c.AllowAllOrigins && (c.AllowOriginFunc != nil || len(c.AllowedOrigins) > 0) {
return errors.New("conflict settings: all origins are allowed. AllowOriginFunc or AllowedOrigins is not needed")
}
if !c.AllowAllOrigins && c.AllowOriginFunc == nil && len(c.AllowedOrigins) == 0 {
return errors.New("conflict settings: all origins disabled")
}
if c.AllowOriginFunc != nil && len(c.AllowedOrigins) > 0 {
return errors.New("conflict settings: if a allow origin func is provided, AllowedOrigins is not needed")
}
for _, origin := range c.AllowedOrigins {
if !strings.HasPrefix(origin, "http://") && !strings.HasPrefix(origin, "https://") {
return errors.New("bad origin: origins must include http:// or https://")
}
}
return nil
}
var defaultConfig = Config{
AbortOnError: false,
AllowAllOrigins: true,
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "HEAD"},
AllowedHeaders: []string{"Content-Type"},
//ExposedHeaders: "",
AllowCredentials: false,
MaxAge: 12 * time.Hour,
}
func DefaultConfig() Config {
cp := defaultConfig
return cp
}
func Default() gin.HandlerFunc {
return New(defaultConfig)
}
func New(config Config) gin.HandlerFunc {
s := newSettings(config)
// Algorithm based in http://www.html5rocks.com/static/images/cors_server_flowchart.png
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")
if len(origin) == 0 {
return
}
origin, valid := s.validateOrigin(origin)
if valid {
if c.Request.Method == "OPTIONS" {
valid = handlePreflight(c, s)
} else {
valid = handleNormal(c, s)
}
}
if !valid {
if config.AbortOnError {
c.AbortWithStatus(http.StatusForbidden)
}
return
}
c.Header("Access-Control-Allow-Origin", origin)
}
}
func handlePreflight(c *gin.Context, s *settings) bool {
c.AbortWithStatus(200)
if !s.validateMethod(c.Request.Header.Get("Access-Control-Request-Method")) {
return false
}
if !s.validateHeader(c.Request.Header.Get("Access-Control-Request-Header")) {
return false
}
for key, value := range s.preflightHeaders {
c.Writer.Header()[key] = value
}
return true
}
func handleNormal(c *gin.Context, s *settings) bool {
for key, value := range s.normalHeaders {
c.Writer.Header()[key] = value
}
return true
}

104
vendor/github.com/johnniedoe/contrib/cors/cors_test.go generated vendored Normal file
View File

@@ -0,0 +1,104 @@
package cors
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
req, _ := http.NewRequest(method, path, nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
return w
}
func TestBadConfig(t *testing.T) {
assert.Panics(t, func() { New(Options{}) })
assert.Panics(t, func() {
New(Options{
AllowAllOrigins: true,
AllowedOrigins: []string{"http://google.com"},
})
})
assert.Panics(t, func() {
New(Options{
AllowAllOrigins: true,
AllowOriginFunc: func(origin string) bool { return false },
})
})
assert.Panics(t, func() {
New(Options{
AllowedOrigins: []string{"http://google.com"},
AllowOriginFunc: func(origin string) bool { return false },
})
})
assert.Panics(t, func() {
New(Options{
AllowedOrigins: []string{"google.com"},
})
})
}
func TestDeny0(t *testing.T) {
called := false
router := gin.Default()
router.Use(New(Options{
AllowedOrigins: []string{"http://example.com"},
}))
router.GET("/", func(c *gin.Context) {
called = true
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
req.Header.Set("Origin", "https://example.com")
router.ServeHTTP(w, req)
assert.True(t, called)
assert.NotContains(t, w.Header(), "Access-Control")
}
func TestDenyAbortOnError(t *testing.T) {
called := false
router := gin.Default()
router.Use(New(Options{
AbortOnError: true,
AllowedOrigins: []string{"http://example.com"},
}))
router.GET("/", func(c *gin.Context) {
called = true
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
req.Header.Set("Origin", "https://example.com")
router.ServeHTTP(w, req)
assert.False(t, called)
assert.NotContains(t, w.Header(), "Access-Control")
}
func TestDeny2(t *testing.T) {
}
func TestDeny3(t *testing.T) {
}
func TestPasses0(t *testing.T) {
}
func TestPasses1(t *testing.T) {
}
func TestPasses2(t *testing.T) {
}

22
vendor/github.com/johnniedoe/contrib/expvar/README.md generated vendored Normal file
View File

@@ -0,0 +1,22 @@
#expvar for gin
```go
package main
import "github.com/gin-gonic/gin"
import "github.com/gin-gonic/contrib/expvar"
func main() {
router := gin.Default()
router.GET("/debug/vars", expvar.Handler())
router.Run(":8080")
}
```
Request: `http://localhost:8080/debug/vars`
```json
{
"cmdline": ["/var/folders/zg/q__7tncn7kxc34pc3j0_v7rc0000gn/T/go-build115008999/command-line-arguments/_obj/exe/main"],
"memstats": {"Alloc":406968,"TotalAlloc":3025088,"Sys":3999992,"Lookups":15,"Mallocs":10405,"Frees":9228,"HeapAlloc":406968,"HeapSys":1916928,"HeapIdle":1097728,"HeapInuse":819200,"HeapReleased":0,"HeapObjects":1177,"StackInuse":180224,"StackSys":180224,"MSpanInuse":7904,"MSpanSys":16384,"MCacheInuse":1200,"MCacheSys":16384,"BuckHashSys":1441424,"GCSys":137622,"OtherSys":291026,"NextGC":578160,"LastGC":1432428478423798618,"PauseTotalNs":4326675,"PauseNs":[105780,76617,91326,115727,195752,249831,554025,485129,344607,416552,400537,424639,463089,403064,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"PauseEnd":[1432428359211210066,1432428359211415512,1432428359211821428,1432428359212452447,1432428359212906768,1432428359214282772,1432428381213398837,1432428383977712300,1432428452748952359,1432428470574839824,1432428472452814302,1432428474379491025,1432428476329036668,1432428478423798444,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"NumGC":14,"EnableGC":true,"DebugGC":false,"BySize":[{"Size":0,"Mallocs":0,"Frees":0},{"Size":8,"Mallocs":258,"Frees":237},{"Size":16,"Mallocs":4125,"Frees":3670},{"Size":32,"Mallocs":765,"Frees":652},{"Size":48,"Mallocs":495,"Frees":367},{"Size":64,"Mallocs":305,"Frees":274},{"Size":80,"Mallocs":37,"Frees":24},{"Size":96,"Mallocs":32,"Frees":25},{"Size":112,"Mallocs":713,"Frees":569},{"Size":128,"Mallocs":283,"Frees":259},{"Size":144,"Mallocs":91,"Frees":85},{"Size":160,"Mallocs":139,"Frees":112},{"Size":176,"Mallocs":349,"Frees":315},{"Size":192,"Mallocs":0,"Frees":0},{"Size":208,"Mallocs":223,"Frees":196},{"Size":224,"Mallocs":2,"Frees":2},{"Size":240,"Mallocs":86,"Frees":80},{"Size":256,"Mallocs":21,"Frees":18},{"Size":288,"Mallocs":130,"Frees":104},{"Size":320,"Mallocs":19,"Frees":15},{"Size":352,"Mallocs":278,"Frees":259},{"Size":384,"Mallocs":5,"Frees":5},{"Size":416,"Mallocs":17,"Frees":10},{"Size":448,"Mallocs":0,"Frees":0},{"Size":480,"Mallocs":2,"Frees":1},{"Size":512,"Mallocs":16,"Frees":12},{"Size":576,"Mallocs":92,"Frees":83},{"Size":640,"Mallocs":12,"Frees":7},{"Size":704,"Mallocs":2,"Frees":2},{"Size":768,"Mallocs":0,"Frees":0},{"Size":896,"Mallocs":15,"Frees":12},{"Size":1024,"Mallocs":10,"Frees":8},{"Size":1152,"Mallocs":88,"Frees":82},{"Size":1280,"Mallocs":7,"Frees":6},{"Size":1408,"Mallocs":2,"Frees":1},{"Size":1536,"Mallocs":0,"Frees":0},{"Size":1664,"Mallocs":9,"Frees":4},{"Size":2048,"Mallocs":10,"Frees":9},{"Size":2304,"Mallocs":88,"Frees":82},{"Size":2560,"Mallocs":6,"Frees":5},{"Size":2816,"Mallocs":2,"Frees":1},{"Size":3072,"Mallocs":1,"Frees":1},{"Size":3328,"Mallocs":4,"Frees":1},{"Size":4096,"Mallocs":172,"Frees":167},{"Size":4608,"Mallocs":93,"Frees":81},{"Size":5376,"Mallocs":7,"Frees":0},{"Size":6144,"Mallocs":87,"Frees":81},{"Size":6400,"Mallocs":0,"Frees":0},{"Size":6656,"Mallocs":1,"Frees":0},{"Size":6912,"Mallocs":0,"Frees":0},{"Size":8192,"Mallocs":6,"Frees":6},{"Size":8448,"Mallocs":0,"Frees":0},{"Size":8704,"Mallocs":1,"Frees":1},{"Size":9472,"Mallocs":0,"Frees":0},{"Size":10496,"Mallocs":0,"Frees":0},{"Size":12288,"Mallocs":1,"Frees":1},{"Size":13568,"Mallocs":0,"Frees":0},{"Size":14080,"Mallocs":0,"Frees":0},{"Size":16384,"Mallocs":0,"Frees":0},{"Size":16640,"Mallocs":0,"Frees":0},{"Size":17664,"Mallocs":1,"Frees":0}]}
}
```

26
vendor/github.com/johnniedoe/contrib/expvar/expvar.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
package expvar
import (
"expvar"
"fmt"
"github.com/gin-gonic/gin"
)
func Handler() gin.HandlerFunc {
return func(c *gin.Context) {
w := c.Writer
c.Header("Content-Type", "application/json; charset=utf-8")
w.Write([]byte("{\n"))
first := true
expvar.Do(func(kv expvar.KeyValue) {
if !first {
w.Write([]byte(",\n"))
}
first = false
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
})
w.Write([]byte("\n}\n"))
c.AbortWithStatus(200)
}
}

View File

@@ -0,0 +1,10 @@
{
"ImportPath": "github.com/gin-gonic/contrib/ginrus",
"GoVersion": "go1.3",
"Deps": [
{
"ImportPath": "github.com/gin-gonic/gin",
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
}
]
}

View File

@@ -0,0 +1,39 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"time"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/contrib/ginrus"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.New()
// Add a ginrus middleware, which:
// - Logs all requests, like a combined access and error log.
// - Logs to stdout.
// - RFC3339 with UTC time format.
r.Use(ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true))
// Add similar middleware, but:
// - Only logs requests with errors, like an error log.
// - Logs to stderr instead of stdout.
// - Local time zone instead of UTC.
logger := logrus.New()
logger.Level = logrus.ErrorLevel
logger.SetOutput(os.Stderr)
r.Use(ginrus.Ginrus(logger, time.RFC3339, false))
// Example ping request.
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}

51
vendor/github.com/johnniedoe/contrib/ginrus/ginrus.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
// Package ginrus provides log handling using logrus package.
//
// Based on github.com/stephenmuss/ginerus but adds more options.
package ginrus
import (
"time"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
)
// Ginrus returns a gin.HandlerFunc (middleware) that logs requests using logrus.
//
// Requests with errors are logged using logrus.Error().
// Requests without errors are logged using logrus.Info().
//
// It receives:
// 1. A time package format string (e.g. time.RFC3339).
// 2. A boolean stating whether to use UTC time zone or local.
func Ginrus(logger *logrus.Logger, timeFormat string, utc bool) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
// some evil middlewares modify this values
path := c.Request.URL.Path
c.Next()
end := time.Now()
latency := end.Sub(start)
if utc {
end = end.UTC()
}
entry := logger.WithFields(logrus.Fields{
"status": c.Writer.Status(),
"method": c.Request.Method,
"path": path,
"ip": c.ClientIP(),
"latency": latency,
"user-agent": c.Request.UserAgent(),
"time": end.Format(timeFormat),
})
if len(c.Errors) > 0 {
// Append error field if this is an erroneous request.
entry.Error(c.Errors.String())
} else {
entry.Info()
}
}
}

View File

@@ -0,0 +1,10 @@
{
"ImportPath": "github.com/gin-gonic/contrib/gzip",
"GoVersion": "go1.3",
"Deps": [
{
"ImportPath": "github.com/gin-gonic/gin",
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
}
]
}

View File

@@ -0,0 +1,19 @@
package main
import (
"fmt"
"github.com/gin-gonic/contrib/gzip"
"github.com/gin-gonic/gin"
"time"
)
func main() {
r := gin.Default()
r.Use(gzip.Gzip(gzip.DefaultCompression))
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}

68
vendor/github.com/johnniedoe/contrib/gzip/gzip.go generated vendored Normal file
View File

@@ -0,0 +1,68 @@
package gzip
import (
"compress/gzip"
"net/http"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
)
const (
BestCompression = gzip.BestCompression
BestSpeed = gzip.BestSpeed
DefaultCompression = gzip.DefaultCompression
NoCompression = gzip.NoCompression
)
func Gzip(level int) gin.HandlerFunc {
return func(c *gin.Context) {
if !shouldCompress(c.Request) {
return
}
gz, err := gzip.NewWriterLevel(c.Writer, level)
if err != nil {
return
}
c.Header("Content-Encoding", "gzip")
c.Header("Vary", "Accept-Encoding")
c.Writer = &gzipWriter{c.Writer, gz}
defer func() {
c.Header("Content-Length", "")
gz.Close()
}()
c.Next()
}
}
type gzipWriter struct {
gin.ResponseWriter
writer *gzip.Writer
}
func (g *gzipWriter) WriteString(s string) (n int, err error) {
return g.writer.Write([]byte(s))
}
func (g *gzipWriter) Write(data []byte) (int, error) {
return g.writer.Write(data)
}
func shouldCompress(req *http.Request) bool {
if !strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
return false
}
extension := filepath.Ext(req.URL.Path)
if len(extension) < 4 { // fast path
return true
}
switch extension {
case ".png", ".gif", ".jpeg", ".jpg":
return false
default:
return true
}
}

81
vendor/github.com/johnniedoe/contrib/gzip/gzip_test.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
package gzip
import (
"compress/gzip"
"io/ioutil"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
const (
testResponse = "Gzip Test Response "
)
func newServer() *gin.Engine {
router := gin.Default()
router.Use(Gzip(DefaultCompression))
router.GET("/", func(c *gin.Context) {
c.Header("Content-Length", strconv.Itoa(len(testResponse)))
c.String(200, testResponse)
})
return router
}
func TestGzip(t *testing.T) {
req, _ := http.NewRequest("GET", "/", nil)
req.Header.Add("Accept-Encoding", "gzip")
w := httptest.NewRecorder()
r := newServer()
r.ServeHTTP(w, req)
assert.Equal(t, w.Code, 200)
assert.Equal(t, w.Header().Get("Content-Encoding"), "gzip")
assert.Equal(t, w.Header().Get("Vary"), "Accept-Encoding")
assert.Equal(t, w.Header().Get("Content-Length"), "")
assert.NotEqual(t, w.Body.Len(), 19)
gr, err := gzip.NewReader(w.Body)
assert.NoError(t, err)
defer gr.Close()
body, _ := ioutil.ReadAll(gr)
assert.Equal(t, string(body), testResponse)
}
func TestGzipPNG(t *testing.T) {
req, _ := http.NewRequest("GET", "/image.png", nil)
req.Header.Add("Accept-Encoding", "gzip")
router := gin.New()
router.Use(Gzip(DefaultCompression))
router.GET("/image.png", func(c *gin.Context) {
c.String(200, "this is a PNG!")
})
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, w.Code, 200)
assert.Equal(t, w.Header().Get("Content-Encoding"), "")
assert.Equal(t, w.Header().Get("Vary"), "")
assert.Equal(t, w.Body.String(), "this is a PNG!")
}
func TestNoGzip(t *testing.T) {
req, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
r := newServer()
r.ServeHTTP(w, req)
assert.Equal(t, w.Code, 200)
assert.Equal(t, w.Header().Get("Content-Encoding"), "")
assert.Equal(t, w.Header().Get("Content-Length"), "19")
assert.Equal(t, w.Body.String(), testResponse)
}

View File

@@ -0,0 +1,22 @@
{
"ImportPath": "github.com/gin-gonic/contrib/jwt",
"GoVersion": "go1.3",
"Deps": [
{
"ImportPath": "github.com/dgrijalva/jwt-go",
"Rev": "5ca80149b9d3f8b863af0e2bb6742e608603bd99"
},
{
"ImportPath": "github.com/gin-gonic/gin",
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
},
{
"ImportPath": "github.com/jacobsa/oglematchers",
"Rev": "3ecefc49db07722beca986d9bb71ddd026b133f0"
},
{
"ImportPath": "github.com/smartystreets/goconvey/convey",
"Rev": "958443eebb772fc6c1dcc2a44cdc6a59e1b3ff0d"
}
]
}

83
vendor/github.com/johnniedoe/contrib/jwt/README.md generated vendored Normal file
View File

@@ -0,0 +1,83 @@
JWT middleware for go gonic.
JSON Web Token (JWT) more information: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html
EDIT: Below is the test for [christopherL91/Go-API](https://github.com/christopherL91/Go-API)
```go
package jwt_test
import (
"encoding/json"
. "github.com/smartystreets/goconvey/convey"
"io/ioutil"
"net/http"
"strings"
"testing"
)
type User struct {
Username string `json:"username"`
Password string `json:"password"`
}
type Response struct {
Token string `json:"token"`
}
func createNewsUser(username, password string) *User {
return &User{username, password}
}
func TestLogin(t *testing.T) {
Convey("Should be able to login", t, func() {
user := createNewsUser("jonas", "1234")
jsondata, _ := json.Marshal(user)
post_data := strings.NewReader(string(jsondata))
req, _ := http.NewRequest("POST", "http://localhost:3000/api/login", post_data)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
res, _ := client.Do(req)
So(res.StatusCode, ShouldEqual, 200)
Convey("Should be able to parse body", func() {
body, err := ioutil.ReadAll(res.Body)
defer res.Body.Close()
So(err, ShouldBeNil)
Convey("Should be able to get json back", func() {
responseData := new(Response)
err := json.Unmarshal(body, responseData)
So(err, ShouldBeNil)
Convey("Should be able to be authorized", func() {
token := responseData.Token
req, _ := http.NewRequest("GET", "http://localhost:3000/api/auth/testAuth", nil)
req.Header.Set("Authorization", "Bearer "+token)
client = &http.Client{}
res, _ := client.Do(req)
So(res.StatusCode, ShouldEqual, 200)
})
})
})
})
Convey("Should not be able to login with false credentials", t, func() {
user := createNewsUser("jnwfkjnkfneknvjwenv", "wenknfkwnfknfknkfjnwkfenw")
jsondata, _ := json.Marshal(user)
post_data := strings.NewReader(string(jsondata))
req, _ := http.NewRequest("POST", "http://localhost:3000/api/login", post_data)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
res, _ := client.Do(req)
So(res.StatusCode, ShouldEqual, 401)
})
Convey("Should not be able to authorize with false credentials", t, func() {
token := ""
req, _ := http.NewRequest("GET", "http://localhost:3000/api/auth/testAuth", nil)
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
res, _ := client.Do(req)
So(res.StatusCode, ShouldEqual, 401)
})
}
```

View File

@@ -0,0 +1,46 @@
package main
import (
jwt_lib "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/contrib/jwt"
"github.com/gin-gonic/gin"
"time"
)
var (
mysupersecretpassword = "unicornsAreAwesome"
)
func main() {
r := gin.Default()
public := r.Group("/api")
public.GET("/", func(c *gin.Context) {
// Create the token
token := jwt_lib.New(jwt_lib.GetSigningMethod("HS256"))
// Set some claims
token.Claims["ID"] = "Christopher"
token.Claims["exp"] = time.Now().Add(time.Hour * 1).Unix()
// Sign and get the complete encoded token as a string
tokenString, err := token.SignedString([]byte(mysupersecretpassword))
if err != nil {
c.JSON(500, gin.H{"message": "Could not generate token"})
}
c.JSON(200, gin.H{"token": tokenString})
})
private := r.Group("/api/private")
private.Use(jwt.Auth(mysupersecretpassword))
/*
Set this header in your request to get here.
Authorization: Bearer `token`
*/
private.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Hello from private"})
})
r.Run("localhost:8080")
}

19
vendor/github.com/johnniedoe/contrib/jwt/jwt.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
package jwt
import (
jwt_lib "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
)
func Auth(secret string) gin.HandlerFunc {
return func(c *gin.Context) {
_, err := jwt_lib.ParseFromRequest(c.Request, func(token *jwt_lib.Token) (interface{}, error) {
b := ([]byte(secret))
return b, nil
})
if err != nil {
c.AbortWithError(401, err)
}
}
}

View File

@@ -0,0 +1,29 @@
package newrelic
import (
"time"
"github.com/gin-gonic/gin"
metrics "github.com/yvasiyarov/go-metrics"
"github.com/yvasiyarov/gorelic"
)
var agent *gorelic.Agent
func NewRelic(license string, appname string, verbose bool) gin.HandlerFunc {
agent = gorelic.NewAgent()
agent.NewrelicLicense = license
agent.HTTPTimer = metrics.NewTimer()
agent.CollectHTTPStat = true
agent.Verbose = verbose
agent.NewrelicName = appname
agent.Run()
return func(c *gin.Context) {
startTime := time.Now()
c.Next()
agent.HTTPTimer.UpdateSince(startTime)
}
}

View File

@@ -0,0 +1,84 @@
This is a custom HTML render to support multi templates, ie. more than one `*template.Template`.
#Simple example
```go
package main
import (
"html/template"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/contrib/renders/multitemplate"
)
func main() {
router := gin.Default()
router.HTMLRender = createMyRender()
router.GET("/", func(c *gin.Context) {
c.HTML(200, "index", data)
})
router.Run(":8080")
}
func createMyRender() multitemplate.Render {
r := multitemplate.New()
r.AddFromFiles("index", "base.html", "base.html")
r.AddFromFiles("article", "base.html", "article.html")
r.AddFromFiles("login", "base.html", "login.html")
r.AddFromFiles("dashboard", "base.html", "dashboard.html")
return r
}
```
##Advanced example
[https://elithrar.github.io/article/approximating-html-template-inheritance/](https://elithrar.github.io/article/approximating-html-template-inheritance/)
```go
package main
import (
"html/template"
"path/filepath"
"github.com/gin-gonic/contrib/renders/multitemplate"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.HTMLRender = loadTemplates("./templates")
router.GET("/", func(c *gin.Context) {
c.HTML(200, "index.tmpl", gin.H{
"title": "Welcome!",
})
})
router.Run(":8080")
}
func loadTemplates(templatesDir string) multitemplate.Render {
r := multitemplate.New()
layouts, err := filepath.Glob(templatesDir + "layouts/*.tmpl")
if err != nil {
panic(err.Error())
}
includes, err := filepath.Glob(templatesDir + "includes/*.tmpl")
if err != nil {
panic(err.Error())
}
// Generate our templates map from our layouts/ and includes/ directories
for _, layout := range layouts {
files := append(includes, layout)
r.Add(filepath.Base(layout), template.Must(template.ParseFiles(files...)))
}
return r
}
```

View File

@@ -0,0 +1,50 @@
package multitemplate
import (
"html/template"
"github.com/gin-gonic/gin/render"
)
type Render map[string]*template.Template
var _ render.HTMLRender = Render{}
func New() Render {
return make(Render)
}
func (r Render) Add(name string, tmpl *template.Template) {
if tmpl == nil {
panic("template can not be nil")
}
if len(name) == 0 {
panic("template name cannot be empty")
}
r[name] = tmpl
}
func (r Render) AddFromFiles(name string, files ...string) *template.Template {
tmpl := template.Must(template.ParseFiles(files...))
r.Add(name, tmpl)
return tmpl
}
func (r Render) AddFromGlob(name, glob string) *template.Template {
tmpl := template.Must(template.ParseGlob(glob))
r.Add(name, tmpl)
return tmpl
}
func (r *Render) AddFromString(name, templateString string) *template.Template {
tmpl := template.Must(template.New("").Parse(templateString))
r.Add(name, tmpl)
return tmpl
}
func (r Render) Instance(name string, data interface{}) render.Render {
return render.HTML{
Template: r[name],
Data: data,
}
}

46
vendor/github.com/johnniedoe/contrib/rest/rest.go generated vendored Normal file
View File

@@ -0,0 +1,46 @@
package rest
import (
"github.com/gin-gonic/gin"
)
// All of the methods are the same type as HandlerFunc
// if you don't want to support any methods of CRUD, then don't implement it
type CreateSupported interface {
CreateHandler(*gin.Context)
}
type ListSupported interface {
ListHandler(*gin.Context)
}
type TakeSupported interface {
TakeHandler(*gin.Context)
}
type UpdateSupported interface {
UpdateHandler(*gin.Context)
}
type DeleteSupported interface {
DeleteHandler(*gin.Context)
}
// It defines
// POST: /path
// GET: /path
// PUT: /path/:id
// POST: /path/:id
func CRUD(group *gin.RouterGroup, path string, resource interface{}) {
if resource, ok := resource.(CreateSupported); ok {
group.POST(path, resource.CreateHandler)
}
if resource, ok := resource.(ListSupported); ok {
group.GET(path, resource.ListHandler)
}
if resource, ok := resource.(TakeSupported); ok {
group.GET(path+"/:id", resource.TakeHandler)
}
if resource, ok := resource.(UpdateSupported); ok {
group.PUT(path+"/:id", resource.UpdateHandler)
}
if resource, ok := resource.(DeleteSupported); ok {
group.DELETE(path+"/:id", resource.DeleteHandler)
}
}

View File

@@ -0,0 +1,10 @@
{
"ImportPath": "github.com/gin-gonic/contrib/secure",
"GoVersion": "go1.3",
"Deps": [
{
"ImportPath": "github.com/gin-gonic/gin",
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
}
]
}

View File

@@ -0,0 +1,32 @@
package main
import (
"fmt"
"github.com/gin-gonic/contrib/secure"
"github.com/gin-gonic/gin"
"time"
)
func main() {
r := gin.Default()
r.Use(secure.Secure(secure.Options{
AllowedHosts: []string{"example.com", "ssl.example.com"},
SSLRedirect: true,
SSLHost: "ssl.example.com",
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
STSSeconds: 315360000,
STSIncludeSubdomains: true,
FrameDeny: true,
ContentTypeNosniff: true,
BrowserXssFilter: true,
ContentSecurityPolicy: "default-src 'self'",
}))
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}

179
vendor/github.com/johnniedoe/contrib/secure/secure.go generated vendored Normal file
View File

@@ -0,0 +1,179 @@
package secure
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
const (
stsHeader = "Strict-Transport-Security"
stsSubdomainString = "; includeSubdomains"
frameOptionsHeader = "X-Frame-Options"
frameOptionsValue = "DENY"
contentTypeHeader = "X-Content-Type-Options"
contentTypeValue = "nosniff"
xssProtectionHeader = "X-XSS-Protection"
xssProtectionValue = "1; mode=block"
cspHeader = "Content-Security-Policy"
)
func defaultBadHostHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Bad Host", http.StatusInternalServerError)
}
// Options is a struct for specifying configuration options for the secure.Secure middleware.
type Options struct {
// AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
AllowedHosts []string
// If SSLRedirect is set to true, then only allow https requests. Default is false.
SSLRedirect bool
// If SSLTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
SSLTemporaryRedirect bool
// SSLHost is the host name that is used to redirect http requests to https. Default is "", which indicates to use the same host.
SSLHost string
// SSLProxyHeaders is set of header keys with associated values that would indicate a valid https request. Useful when using Nginx: `map[string]string{"X-Forwarded-Proto": "https"}`. Default is blank map.
SSLProxyHeaders map[string]string
// STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
STSSeconds int64
// If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
STSIncludeSubdomains bool
// If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is false.
FrameDeny bool
// CustomFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option.
CustomFrameOptionsValue string
// If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is false.
ContentTypeNosniff bool
// If BrowserXssFilter is true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is false.
BrowserXssFilter bool
// ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
ContentSecurityPolicy string
// When developing, the AllowedHosts, SSL, and STS options can cause some unwanted effects. Usually testing happens on http, not https, and on localhost, not your production domain... so set this to true for dev environment.
// If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as false. Default if false.
IsDevelopment bool
// Handlers for when an error occurs (ie bad host).
BadHostHandler http.Handler
}
// Secure is a middleware that helps setup a few basic security features. A single secure.Options struct can be
// provided to configure which features should be enabled, and the ability to override a few of the default values.
type secure struct {
// Customize Secure with an Options struct.
opt Options
}
// Constructs a new Secure instance with supplied options.
func New(options Options) *secure {
if options.BadHostHandler == nil {
options.BadHostHandler = http.HandlerFunc(defaultBadHostHandler)
}
return &secure{
opt: options,
}
}
func (s *secure) process(w http.ResponseWriter, r *http.Request) error {
// Allowed hosts check.
if len(s.opt.AllowedHosts) > 0 && !s.opt.IsDevelopment {
isGoodHost := false
for _, allowedHost := range s.opt.AllowedHosts {
if strings.EqualFold(allowedHost, r.Host) {
isGoodHost = true
break
}
}
if !isGoodHost {
s.opt.BadHostHandler.ServeHTTP(w, r)
return fmt.Errorf("Bad host name: %s", r.Host)
}
}
// SSL check.
if s.opt.SSLRedirect && s.opt.IsDevelopment == false {
isSSL := false
if strings.EqualFold(r.URL.Scheme, "https") || r.TLS != nil {
isSSL = true
} else {
for k, v := range s.opt.SSLProxyHeaders {
if r.Header.Get(k) == v {
isSSL = true
break
}
}
}
if isSSL == false {
url := r.URL
url.Scheme = "https"
url.Host = r.Host
if len(s.opt.SSLHost) > 0 {
url.Host = s.opt.SSLHost
}
status := http.StatusMovedPermanently
if s.opt.SSLTemporaryRedirect {
status = http.StatusTemporaryRedirect
}
http.Redirect(w, r, url.String(), status)
return fmt.Errorf("Redirecting to HTTPS")
}
}
// Strict Transport Security header.
if s.opt.STSSeconds != 0 && !s.opt.IsDevelopment {
stsSub := ""
if s.opt.STSIncludeSubdomains {
stsSub = stsSubdomainString
}
w.Header().Add(stsHeader, fmt.Sprintf("max-age=%d%s", s.opt.STSSeconds, stsSub))
}
// Frame Options header.
if len(s.opt.CustomFrameOptionsValue) > 0 {
w.Header().Add(frameOptionsHeader, s.opt.CustomFrameOptionsValue)
} else if s.opt.FrameDeny {
w.Header().Add(frameOptionsHeader, frameOptionsValue)
}
// Content Type Options header.
if s.opt.ContentTypeNosniff {
w.Header().Add(contentTypeHeader, contentTypeValue)
}
// XSS Protection header.
if s.opt.BrowserXssFilter {
w.Header().Add(xssProtectionHeader, xssProtectionValue)
}
// Content Security Policy header.
if len(s.opt.ContentSecurityPolicy) > 0 {
w.Header().Add(cspHeader, s.opt.ContentSecurityPolicy)
}
return nil
}
func Secure(options Options) gin.HandlerFunc {
s := New(options)
return func(c *gin.Context) {
err := s.process(c.Writer, c.Request)
if err != nil {
if c.Writer.Written() {
c.AbortWithStatus(c.Writer.Status())
} else {
c.AbortWithError(http.StatusInternalServerError, err)
}
}
}
}

View File

@@ -0,0 +1,470 @@
package secure
import (
"github.com/gin-gonic/gin"
"net/http"
"net/http/httptest"
"reflect"
"testing"
)
const (
testResponse = "bar"
)
func newServer(options Options) *gin.Engine {
r := gin.Default()
r.Use(Secure(options))
r.GET("/foo", func(c *gin.Context) {
c.String(200, testResponse)
})
return r
}
func TestNoConfig(t *testing.T) {
s := newServer(Options{
// Intentionally left blank.
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Body.String(), "bar")
}
func TestNoAllowHosts(t *testing.T) {
s := newServer(Options{
AllowedHosts: []string{},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Body.String(), `bar`)
}
func TestGoodSingleAllowHosts(t *testing.T) {
s := newServer(Options{
AllowedHosts: []string{"www.example.com"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Body.String(), `bar`)
}
func TestBadSingleAllowHosts(t *testing.T) {
s := newServer(Options{
AllowedHosts: []string{"sub.example.com"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusInternalServerError)
}
func TestGoodMultipleAllowHosts(t *testing.T) {
s := newServer(Options{
AllowedHosts: []string{"www.example.com", "sub.example.com"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "sub.example.com"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Body.String(), `bar`)
}
func TestBadMultipleAllowHosts(t *testing.T) {
s := newServer(Options{
AllowedHosts: []string{"www.example.com", "sub.example.com"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www3.example.com"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusInternalServerError)
}
func TestAllowHostsInDevMode(t *testing.T) {
s := newServer(Options{
AllowedHosts: []string{"www.example.com", "sub.example.com"},
IsDevelopment: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www3.example.com"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
}
func TestBadHostHandler(t *testing.T) {
badHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "BadHost", http.StatusInternalServerError)
})
s := newServer(Options{
AllowedHosts: []string{"www.example.com", "sub.example.com"},
BadHostHandler: badHandler,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www3.example.com"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusInternalServerError)
// http.Error outputs a new line character with the response.
expect(t, res.Body.String(), "BadHost\n")
}
func TestSSL(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "https"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
}
func TestSSLInDevMode(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
IsDevelopment: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "http"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
}
func TestBasicSSL(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "http"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusMovedPermanently)
expect(t, res.Header().Get("Location"), "https://www.example.com/foo")
}
func TestBasicSSLWithHost(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
SSLHost: "secure.example.com",
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "http"
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusMovedPermanently)
expect(t, res.Header().Get("Location"), "https://secure.example.com/foo")
}
func TestBadProxySSL(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "http"
req.Header.Add("X-Forwarded-Proto", "https")
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusMovedPermanently)
expect(t, res.Header().Get("Location"), "https://www.example.com/foo")
}
func TestCustomProxySSL(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "http"
req.Header.Add("X-Forwarded-Proto", "https")
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
}
func TestCustomProxySSLInDevMode(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
IsDevelopment: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "http"
req.Header.Add("X-Forwarded-Proto", "http")
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
}
func TestCustomProxyAndHostSSL(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
SSLHost: "secure.example.com",
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "http"
req.Header.Add("X-Forwarded-Proto", "https")
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
}
func TestCustomBadProxyAndHostSSL(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "superman"},
SSLHost: "secure.example.com",
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "http"
req.Header.Add("X-Forwarded-Proto", "https")
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusMovedPermanently)
expect(t, res.Header().Get("Location"), "https://secure.example.com/foo")
}
func TestCustomBadProxyAndHostSSLWithTempRedirect(t *testing.T) {
s := newServer(Options{
SSLRedirect: true,
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "superman"},
SSLHost: "secure.example.com",
SSLTemporaryRedirect: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
req.Host = "www.example.com"
req.URL.Scheme = "http"
req.Header.Add("X-Forwarded-Proto", "https")
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusTemporaryRedirect)
expect(t, res.Header().Get("Location"), "https://secure.example.com/foo")
}
func TestStsHeader(t *testing.T) {
s := newServer(Options{
STSSeconds: 315360000,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("Strict-Transport-Security"), "max-age=315360000")
}
func TestStsHeaderInDevMode(t *testing.T) {
s := newServer(Options{
STSSeconds: 315360000,
IsDevelopment: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("Strict-Transport-Security"), "")
}
func TestStsHeaderWithSubdomain(t *testing.T) {
s := newServer(Options{
STSSeconds: 315360000,
STSIncludeSubdomains: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("Strict-Transport-Security"), "max-age=315360000; includeSubdomains")
}
func TestFrameDeny(t *testing.T) {
s := newServer(Options{
FrameDeny: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("X-Frame-Options"), "DENY")
}
func TestCustomFrameValue(t *testing.T) {
s := newServer(Options{
CustomFrameOptionsValue: "SAMEORIGIN",
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("X-Frame-Options"), "SAMEORIGIN")
}
func TestCustomFrameValueWithDeny(t *testing.T) {
s := newServer(Options{
FrameDeny: true,
CustomFrameOptionsValue: "SAMEORIGIN",
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("X-Frame-Options"), "SAMEORIGIN")
}
func TestContentNosniff(t *testing.T) {
s := newServer(Options{
ContentTypeNosniff: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("X-Content-Type-Options"), "nosniff")
}
func TestXSSProtection(t *testing.T) {
s := newServer(Options{
BrowserXssFilter: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("X-XSS-Protection"), "1; mode=block")
}
func TestCsp(t *testing.T) {
s := newServer(Options{
ContentSecurityPolicy: "default-src 'self'",
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("Content-Security-Policy"), "default-src 'self'")
}
func TestInlineSecure(t *testing.T) {
s := newServer(Options{
FrameDeny: true,
})
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foo", nil)
s.ServeHTTP(res, req)
expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("X-Frame-Options"), "DENY")
}
/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
if a != b {
t.Errorf("Expected [%v] (type %v) - Got [%v] (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
}
}

View File

@@ -0,0 +1,42 @@
package sentry
import (
"errors"
"fmt"
"net/http"
"runtime/debug"
"github.com/getsentry/raven-go"
"github.com/gin-gonic/gin"
)
func Recovery(client *raven.Client, onlyCrashes bool) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
flags := map[string]string{
"endpoint": c.Request.RequestURI,
}
if rval := recover(); rval != nil {
debug.PrintStack()
rvalStr := fmt.Sprint(rval)
packet := raven.NewPacket(rvalStr, raven.NewException(errors.New(rvalStr), raven.NewStacktrace(2, 3, nil)))
client.Capture(packet, map[string]string{
"endpoint": c.Request.RequestURI,
})
c.AbortWithStatus(http.StatusInternalServerError)
}
if !onlyCrashes {
for _, item := range c.Errors {
packet := raven.NewPacket(item.Error(), &raven.Message{
Message: item.Error(),
Params: []interface{}{item.Meta},
})
client.Capture(packet, flags)
}
}
}()
c.Next()
}
}

View File

@@ -0,0 +1,18 @@
{
"ImportPath": "github.com/gin-gonic/contrib/sessions",
"GoVersion": "go1.3",
"Deps": [
{
"ImportPath": "github.com/boj/redistore",
"Rev": "0cd55584b172cefd31c4cb84e2904585d898c7b2"
},
{
"ImportPath": "github.com/gin-gonic/gin",
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
},
{
"ImportPath": "github.com/gorilla/sessions",
"Rev": "f61c3ec2cf65d69e7efedfd4d060fe128882c951"
}
]
}

View File

@@ -0,0 +1,70 @@
# sessions
Gin middleware for session management with multi-backend support (currently cookie, Redis).
## Examples
#### cookie-based
```go
package main
import (
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
store := sessions.NewCookieStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
r.GET("/incr", func(c *gin.Context) {
session := sessions.Default(c)
var count int
v := session.Get("count")
if v == nil {
count = 0
} else {
count = v.(int)
count += 1
}
session.Set("count", count)
session.Save()
c.JSON(200, gin.H{"count": count})
})
r.Run(":8000")
}
```
#### Redis
```go
package main
import (
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
store, _ := sessions.NewRedisStore(10, "tcp", "localhost:6379", "", []byte("secret"))
r.Use(sessions.Sessions("session", store))
r.GET("/incr", func(c *gin.Context) {
session := sessions.Default(c)
var count int
v := session.Get("count")
if v == nil {
count = 0
} else {
count = v.(int)
count += 1
}
session.Set("count", count)
session.Save()
c.JSON(200, gin.H{"count": count})
})
r.Run(":8000")
}
```

View File

@@ -0,0 +1,36 @@
package sessions
import (
"github.com/gorilla/sessions"
)
type CookieStore interface {
Store
}
// Keys are defined in pairs to allow key rotation, but the common case is to set a single
// authentication key and optionally an encryption key.
//
// The first key in a pair is used for authentication and the second for encryption. The
// encryption key can be set to nil or omitted in the last pair, but the authentication key
// is required in all pairs.
//
// It is recommended to use an authentication key with 32 or 64 bytes. The encryption key,
// if set, must be either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256 modes.
func NewCookieStore(keyPairs ...[]byte) CookieStore {
return &cookieStore{sessions.NewCookieStore(keyPairs...)}
}
type cookieStore struct {
*sessions.CookieStore
}
func (c *cookieStore) Options(options Options) {
c.CookieStore.Options = &sessions.Options{
Path: options.Path,
Domain: options.Domain,
MaxAge: options.MaxAge,
Secure: options.Secure,
HttpOnly: options.HttpOnly,
}
}

View File

@@ -0,0 +1,30 @@
package sessions
import (
"testing"
)
var newCookieStore = func(_ *testing.T) Store {
store := NewCookieStore([]byte("secret"))
return store
}
func TestCookie_SessionGetSet(t *testing.T) {
sessionGetSet(t, newCookieStore)
}
func TestCookie_SessionDeleteKey(t *testing.T) {
sessionDeleteKey(t, newCookieStore)
}
func TestCookie_SessionFlashes(t *testing.T) {
sessionFlashes(t, newCookieStore)
}
func TestCookie_SessionClear(t *testing.T) {
sessionClear(t, newCookieStore)
}
func TestCookie_SessionOptions(t *testing.T) {
sessionOptions(t, newCookieStore)
}

View File

@@ -0,0 +1,28 @@
package maincookie
import (
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
store := sessions.NewCookieStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
r.GET("/incr", func(c *gin.Context) {
session := sessions.Default(c)
var count int
v := session.Get("count")
if v == nil {
count = 0
} else {
count = v.(int)
count += 1
}
session.Set("count", count)
session.Save()
c.JSON(200, gin.H{"count": count})
})
r.Run(":8000")
}

View File

@@ -0,0 +1,28 @@
package mainredis
import (
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
store, _ := sessions.NewRedisStore(10, "tcp", "localhost:6379", "", []byte("secret"))
r.Use(sessions.Sessions("mysession", store))
r.GET("/incr", func(c *gin.Context) {
session := sessions.Default(c)
var count int
v := session.Get("count")
if v == nil {
count = 0
} else {
count = v.(int)
count += 1
}
session.Set("count", count)
session.Save()
c.JSON(200, gin.H{"count": count})
})
r.Run(":8000")
}

45
vendor/github.com/johnniedoe/contrib/sessions/redis.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package sessions
import (
"github.com/boj/redistore"
"github.com/gorilla/sessions"
)
type RedisStore interface {
Store
}
// size: maximum number of idle connections.
// network: tcp or udp
// address: host:port
// password: redis-password
// Keys are defined in pairs to allow key rotation, but the common case is to set a single
// authentication key and optionally an encryption key.
//
// The first key in a pair is used for authentication and the second for encryption. The
// encryption key can be set to nil or omitted in the last pair, but the authentication key
// is required in all pairs.
//
// It is recommended to use an authentication key with 32 or 64 bytes. The encryption key,
// if set, must be either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256 modes.
func NewRedisStore(size int, network, address, password string, keyPairs ...[]byte) (RedisStore, error) {
store, err := redistore.NewRediStore(size, network, address, password, keyPairs...)
if err != nil {
return nil, err
}
return &redisStore{store}, nil
}
type redisStore struct {
*redistore.RediStore
}
func (c *redisStore) Options(options Options) {
c.RediStore.Options = &sessions.Options{
Path: options.Path,
Domain: options.Domain,
MaxAge: options.MaxAge,
Secure: options.Secure,
HttpOnly: options.HttpOnly,
}
}

View File

@@ -0,0 +1,35 @@
package sessions
import (
"testing"
)
const redisTestServer = "localhost:6379"
var newRedisStore = func(_ *testing.T) Store {
store, err := NewRedisStore(10, "tcp", redisTestServer, "", []byte("secret"))
if err != nil {
panic(err)
}
return store
}
func TestRedis_SessionGetSet(t *testing.T) {
sessionGetSet(t, newRedisStore)
}
func TestRedis_SessionDeleteKey(t *testing.T) {
sessionDeleteKey(t, newRedisStore)
}
func TestRedis_SessionFlashes(t *testing.T) {
sessionFlashes(t, newRedisStore)
}
func TestRedis_SessionClear(t *testing.T) {
sessionClear(t, newRedisStore)
}
func TestRedis_SessionOptions(t *testing.T) {
sessionOptions(t, newRedisStore)
}

View File

@@ -0,0 +1,147 @@
package sessions
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gorilla/context"
"github.com/gorilla/sessions"
)
const (
DefaultKey = "github.com/gin-gonic/contrib/sessions"
errorFormat = "[sessions] ERROR! %s\n"
)
type Store interface {
sessions.Store
Options(Options)
}
// Options stores configuration for a session or session store.
// Fields are a subset of http.Cookie fields.
type Options struct {
Path string
Domain string
// MaxAge=0 means no 'Max-Age' attribute specified.
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
// MaxAge>0 means Max-Age attribute present and given in seconds.
MaxAge int
Secure bool
HttpOnly bool
}
// Wraps thinly gorilla-session methods.
// Session stores the values and optional configuration for a session.
type Session interface {
// Get returns the session value associated to the given key.
Get(key interface{}) interface{}
// Set sets the session value associated to the given key.
Set(key interface{}, val interface{})
// Delete removes the session value associated to the given key.
Delete(key interface{})
// Clear deletes all values in the session.
Clear()
// AddFlash adds a flash message to the session.
// A single variadic argument is accepted, and it is optional: it defines the flash key.
// If not defined "_flash" is used by default.
AddFlash(value interface{}, vars ...string)
// Flashes returns a slice of flash messages from the session.
// A single variadic argument is accepted, and it is optional: it defines the flash key.
// If not defined "_flash" is used by default.
Flashes(vars ...string) []interface{}
// Options sets confuguration for a session.
Options(Options)
// Save saves all sessions used during the current request.
Save() error
}
func Sessions(name string, store Store) gin.HandlerFunc {
return func(c *gin.Context) {
s := &session{name, c.Request, store, nil, false, c.Writer}
c.Set(DefaultKey, s)
defer context.Clear(c.Request)
c.Next()
}
}
type session struct {
name string
request *http.Request
store Store
session *sessions.Session
written bool
writer http.ResponseWriter
}
func (s *session) Get(key interface{}) interface{} {
return s.Session().Values[key]
}
func (s *session) Set(key interface{}, val interface{}) {
s.Session().Values[key] = val
s.written = true
}
func (s *session) Delete(key interface{}) {
delete(s.Session().Values, key)
s.written = true
}
func (s *session) Clear() {
for key := range s.Session().Values {
s.Delete(key)
}
}
func (s *session) AddFlash(value interface{}, vars ...string) {
s.Session().AddFlash(value, vars...)
s.written = true
}
func (s *session) Flashes(vars ...string) []interface{} {
s.written = true
return s.Session().Flashes(vars...)
}
func (s *session) Options(options Options) {
s.Session().Options = &sessions.Options{
Path: options.Path,
Domain: options.Domain,
MaxAge: options.MaxAge,
Secure: options.Secure,
HttpOnly: options.HttpOnly,
}
}
func (s *session) Save() error {
if s.Written() {
e := s.Session().Save(s.request, s.writer)
if e == nil {
s.written = false
}
return e
}
return nil
}
func (s *session) Session() *sessions.Session {
if s.session == nil {
var err error
s.session, err = s.store.Get(s.request, s.name)
if err != nil {
log.Printf(errorFormat, err)
}
}
return s.session
}
func (s *session) Written() bool {
return s.written
}
// shortcut to get session
func Default(c *gin.Context) Session {
return c.MustGet(DefaultKey).(Session)
}

View File

@@ -0,0 +1,219 @@
package sessions
import (
"github.com/gin-gonic/gin"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
type storeFactory func(*testing.T) Store
const sessionName = "mysession"
const ok = "ok"
func sessionGetSet(t *testing.T, newStore storeFactory) {
r := gin.Default()
r.Use(Sessions(sessionName, newStore(t)))
r.GET("/set", func(c *gin.Context) {
session := Default(c)
session.Set("key", ok)
session.Save()
c.String(200, ok)
})
r.GET("/get", func(c *gin.Context) {
session := Default(c)
if session.Get("key") != ok {
t.Error("Session writing failed")
}
session.Save()
c.String(200, ok)
})
res1 := httptest.NewRecorder()
req1, _ := http.NewRequest("GET", "/set", nil)
r.ServeHTTP(res1, req1)
res2 := httptest.NewRecorder()
req2, _ := http.NewRequest("GET", "/get", nil)
req2.Header.Set("Cookie", res1.Header().Get("Set-Cookie"))
r.ServeHTTP(res2, req2)
}
func sessionDeleteKey(t *testing.T, newStore storeFactory) {
r := gin.Default()
r.Use(Sessions(sessionName, newStore(t)))
r.GET("/set", func(c *gin.Context) {
session := Default(c)
session.Set("key", ok)
session.Save()
c.String(200, ok)
})
r.GET("/delete", func(c *gin.Context) {
session := Default(c)
session.Delete("key")
session.Save()
c.String(200, ok)
})
r.GET("/get", func(c *gin.Context) {
session := Default(c)
if session.Get("key") != nil {
t.Error("Session deleting failed")
}
session.Save()
c.String(200, ok)
})
res1 := httptest.NewRecorder()
req1, _ := http.NewRequest("GET", "/set", nil)
r.ServeHTTP(res1, req1)
res2 := httptest.NewRecorder()
req2, _ := http.NewRequest("GET", "/delete", nil)
req2.Header.Set("Cookie", res1.Header().Get("Set-Cookie"))
r.ServeHTTP(res2, req2)
res3 := httptest.NewRecorder()
req3, _ := http.NewRequest("GET", "/get", nil)
req3.Header.Set("Cookie", res2.Header().Get("Set-Cookie"))
r.ServeHTTP(res3, req3)
}
func sessionFlashes(t *testing.T, newStore storeFactory) {
r := gin.Default()
store := newStore(t)
store.Options(Options{
Domain: "localhost",
})
r.Use(Sessions(sessionName, store))
r.GET("/set", func(c *gin.Context) {
session := Default(c)
session.AddFlash(ok)
session.Save()
c.String(200, ok)
})
r.GET("/flash", func(c *gin.Context) {
session := Default(c)
l := len(session.Flashes())
if l != 1 {
t.Error("Flashes count does not equal 1. Equals ", l)
}
session.Save()
c.String(200, ok)
})
r.GET("/check", func(c *gin.Context) {
session := Default(c)
l := len(session.Flashes())
if l != 0 {
t.Error("flashes count is not 0 after reading. Equals ", l)
}
session.Save()
c.String(200, ok)
})
res1 := httptest.NewRecorder()
req1, _ := http.NewRequest("GET", "/set", nil)
r.ServeHTTP(res1, req1)
res2 := httptest.NewRecorder()
req2, _ := http.NewRequest("GET", "/flash", nil)
req2.Header.Set("Cookie", res1.Header().Get("Set-Cookie"))
r.ServeHTTP(res2, req2)
res3 := httptest.NewRecorder()
req3, _ := http.NewRequest("GET", "/check", nil)
req3.Header.Set("Cookie", res2.Header().Get("Set-Cookie"))
r.ServeHTTP(res3, req3)
}
func sessionClear(t *testing.T, newStore storeFactory) {
data := map[string]string{
"key": "val",
"foo": "bar",
}
r := gin.Default()
store := newStore(t)
r.Use(Sessions(sessionName, store))
r.GET("/set", func(c *gin.Context) {
session := Default(c)
for k, v := range data {
session.Set(k, v)
}
session.Clear()
session.Save()
c.String(200, ok)
})
r.GET("/check", func(c *gin.Context) {
session := Default(c)
for k, v := range data {
if session.Get(k) == v {
t.Fatal("Session clear failed")
}
}
session.Save()
c.String(200, ok)
})
res1 := httptest.NewRecorder()
req1, _ := http.NewRequest("GET", "/set", nil)
r.ServeHTTP(res1, req1)
res2 := httptest.NewRecorder()
req2, _ := http.NewRequest("GET", "/check", nil)
req2.Header.Set("Cookie", res1.Header().Get("Set-Cookie"))
r.ServeHTTP(res2, req2)
}
func sessionOptions(t *testing.T, newStore storeFactory) {
r := gin.Default()
store := newStore(t)
store.Options(Options{
Domain: "localhost",
})
r.Use(Sessions(sessionName, store))
r.GET("/domain", func(c *gin.Context) {
session := Default(c)
session.Set("key", ok)
session.Options(Options{
Path: "/foo/bar/bat",
})
session.Save()
c.String(200, ok)
})
r.GET("/path", func(c *gin.Context) {
session := Default(c)
session.Set("key", ok)
session.Save()
c.String(200, ok)
})
res1 := httptest.NewRecorder()
req1, _ := http.NewRequest("GET", "/domain", nil)
r.ServeHTTP(res1, req1)
res2 := httptest.NewRecorder()
req2, _ := http.NewRequest("GET", "/path", nil)
r.ServeHTTP(res2, req2)
s := strings.Split(res1.Header().Get("Set-Cookie"), ";")
if s[1] != " Path=/foo/bar/bat" {
t.Error("Error writing path with options:", s[1])
}
s = strings.Split(res2.Header().Get("Set-Cookie"), ";")
if s[1] != " Domain=localhost" {
t.Error("Error writing domain with options:", s[1])
}
}

View File

@@ -0,0 +1,115 @@
package main
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"strings"
)
func bindata_read(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
return buf.Bytes(), nil
}
func data_index_html() ([]byte, error) {
return bindata_read([]byte{
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0x44, 0xce,
0xbd, 0xae, 0xc2, 0x30, 0x0c, 0x05, 0xe0, 0xbd, 0x4f, 0xe1, 0x9b, 0xbd,
0xaa, 0xba, 0xdd, 0x21, 0xcd, 0xc2, 0xef, 0x06, 0x43, 0x19, 0x18, 0x5d,
0x62, 0x35, 0x41, 0x4e, 0x22, 0x15, 0x4b, 0x88, 0xb7, 0x27, 0x21, 0x45,
0x4c, 0x39, 0xb1, 0xf5, 0x1d, 0x59, 0xff, 0x6d, 0x4f, 0x9b, 0xf1, 0x7a,
0xde, 0x81, 0x93, 0xc0, 0xa6, 0xd1, 0xe5, 0x01, 0xc6, 0x38, 0x0f, 0xea,
0x8e, 0xca, 0x34, 0x00, 0xda, 0x11, 0xda, 0x12, 0x72, 0x0c, 0x24, 0x08,
0x37, 0x87, 0xcb, 0x83, 0x64, 0x50, 0x97, 0x71, 0xdf, 0xfe, 0x2b, 0xe8,
0xd6, 0xa5, 0x78, 0x61, 0x32, 0x73, 0x6a, 0x27, 0x1f, 0x2d, 0x0a, 0xea,
0xae, 0x4e, 0x4a, 0x47, 0xf7, 0x2d, 0xd1, 0x53, 0xb2, 0xaf, 0x15, 0xb8,
0xde, 0x1c, 0x89, 0x39, 0xc1, 0xc1, 0x47, 0xf8, 0x39, 0x08, 0xde, 0x5a,
0xa6, 0x27, 0x2e, 0x94, 0x5d, 0x5f, 0x7d, 0x65, 0xf9, 0xff, 0x39, 0xf3,
0x1d, 0x00, 0x00, 0xff, 0xff, 0x51, 0x69, 0x85, 0x27, 0xb7, 0x00, 0x00,
0x00,
},
"data/index.html",
)
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
return f()
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() ([]byte, error){
"data/index.html": data_index_html,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
func AssetDir(name string) ([]string, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
node := _bintree
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
}
if node.Func != nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
rv := make([]string, 0, len(node.Children))
for name := range node.Children {
rv = append(rv, name)
}
return rv, nil
}
type _bintree_t struct {
Func func() ([]byte, error)
Children map[string]*_bintree_t
}
var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
"data": &_bintree_t{nil, map[string]*_bintree_t{
"index.html": &_bintree_t{data_index_html, map[string]*_bintree_t{}},
}},
}}

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>go-bindata</title>
</head>
<body>
<h1>Hello Gin go-bindata middleware</h1>
</body>
</html>

View File

@@ -0,0 +1,50 @@
package main
import (
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/gin-gonic/contrib/static"
"github.com/gin-gonic/gin"
"net/http"
"strings"
)
type binaryFileSystem struct {
fs http.FileSystem
}
func (b *binaryFileSystem) Open(name string) (http.File, error) {
return b.fs.Open(name)
}
func (b *binaryFileSystem) Exists(prefix string, filepath string) bool {
if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) {
if _, err := b.fs.Open(p); err != nil {
return false
}
return true
}
return false
}
func BinaryFileSystem(root string) *binaryFileSystem {
fs := &assetfs.AssetFS{Asset, AssetDir, root}
return &binaryFileSystem{
fs,
}
}
// Usage
// $ go-bindata data/
// $ go build && ./bindata
//
func main() {
r := gin.Default()
r.Use(static.Serve("/static", BinaryFileSystem("data")))
r.GET("/ping", func(c *gin.Context) {
c.String(200, "test")
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}

View File

@@ -0,0 +1,22 @@
package main
import (
"github.com/gin-gonic/contrib/static"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// if Allow DirectoryIndex
//r.Use(static.Serve("/", static.LocalFile("/tmp", true)))
// set prefix
//r.Use(static.Serve("/static", static.LocalFile("/tmp", true)))
r.Use(static.Serve("/", static.LocalFile("/tmp", false)))
r.GET("/ping", func(c *gin.Context) {
c.String(200, "test")
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}

62
vendor/github.com/johnniedoe/contrib/static/static.go generated vendored Normal file
View File

@@ -0,0 +1,62 @@
package static
import (
"net/http"
"os"
"path"
"strings"
"github.com/gin-gonic/gin"
)
type ServeFileSystem interface {
http.FileSystem
Exists(prefix string, path string) bool
}
type localFileSystem struct {
http.FileSystem
root string
indexes bool
}
func LocalFile(root string, indexes bool) *localFileSystem {
return &localFileSystem{
FileSystem: gin.Dir(root, indexes),
root: root,
indexes: indexes,
}
}
func (l *localFileSystem) Exists(prefix string, filepath string) bool {
if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) {
name := path.Join(l.root, p)
stats, err := os.Stat(name)
if err != nil {
return false
}
if !l.indexes && stats.IsDir() {
return false
}
return true
}
return false
}
func ServeRoot(urlPrefix, root string) gin.HandlerFunc {
return Serve(urlPrefix, LocalFile(root, false))
}
// Static returns a middleware handler that serves static files in the given directory.
func Serve(urlPrefix string, fs ServeFileSystem) gin.HandlerFunc {
fileserver := http.FileServer(fs)
if urlPrefix != "" {
fileserver = http.StripPrefix(urlPrefix, fileserver)
}
return func(c *gin.Context) {
if fs.Exists(urlPrefix, c.Request.URL.Path) {
fileserver.ServeHTTP(c.Writer, c.Request)
c.Abort()
}
}
}

View File

@@ -0,0 +1,86 @@
package static
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
req, _ := http.NewRequest(method, path, nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
return w
}
func TestEmptyDirectory(t *testing.T) {
// SETUP file
testRoot, _ := os.Getwd()
f, err := ioutil.TempFile(testRoot, "")
if err != nil {
t.Error(err)
}
defer os.Remove(f.Name())
f.WriteString("Gin Web Framework")
f.Close()
dir, filename := filepath.Split(f.Name())
router := gin.New()
router.Use(ServeRoot("/", dir))
router.GET("/", func(c *gin.Context) {
c.String(200, "index")
})
router.GET("/a", func(c *gin.Context) {
c.String(200, "a")
})
router.GET("/"+filename, func(c *gin.Context) {
c.String(200, "this is not printed")
})
w := performRequest(router, "GET", "/")
assert.Equal(t, w.Code, 200)
assert.Equal(t, w.Body.String(), "index")
w = performRequest(router, "GET", "/"+filename)
assert.Equal(t, w.Code, 200)
assert.Equal(t, w.Body.String(), "Gin Web Framework")
w = performRequest(router, "GET", "/"+filename+"a")
assert.Equal(t, w.Code, 404)
w = performRequest(router, "GET", "/a")
assert.Equal(t, w.Code, 200)
assert.Equal(t, w.Body.String(), "a")
router2 := gin.New()
router2.Use(ServeRoot("/static", dir))
router2.GET("/"+filename, func(c *gin.Context) {
c.String(200, "this is printed")
})
w = performRequest(router2, "GET", "/")
assert.Equal(t, w.Code, 404)
w = performRequest(router2, "GET", "/static")
assert.Equal(t, w.Code, 404)
router2.GET("/static", func(c *gin.Context) {
c.String(200, "index")
})
w = performRequest(router2, "GET", "/static")
assert.Equal(t, w.Code, 200)
w = performRequest(router2, "GET", "/"+filename)
assert.Equal(t, w.Code, 200)
assert.Equal(t, w.Body.String(), "this is printed")
w = performRequest(router2, "GET", "/static/"+filename)
assert.Equal(t, w.Code, 200)
assert.Equal(t, w.Body.String(), "Gin Web Framework")
}