replace zxq.co/ripple/hanayo
This commit is contained in:
2
vendor/github.com/johnniedoe/contrib/.gitignore
generated
vendored
Normal file
2
vendor/github.com/johnniedoe/contrib/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*/Godeps/*
|
||||
!*/Godeps/Godeps.json
|
8
vendor/github.com/johnniedoe/contrib/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/johnniedoe/contrib/.travis.yml
generated
vendored
Normal 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
17
vendor/github.com/johnniedoe/contrib/README.md
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# gin-gonic/contrib [](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
|
22
vendor/github.com/johnniedoe/contrib/cache/Godeps/Godeps.json
generated
vendored
Normal file
22
vendor/github.com/johnniedoe/contrib/cache/Godeps/Godeps.json
generated
vendored
Normal 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
153
vendor/github.com/johnniedoe/contrib/cache/cache.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
202
vendor/github.com/johnniedoe/contrib/cache/cache_test.go
generated
vendored
Normal file
202
vendor/github.com/johnniedoe/contrib/cache/cache_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
25
vendor/github.com/johnniedoe/contrib/cache/example/example.go
generated
vendored
Normal file
25
vendor/github.com/johnniedoe/contrib/cache/example/example.go
generated
vendored
Normal 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
78
vendor/github.com/johnniedoe/contrib/cache/inmemory.go
generated
vendored
Normal 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
|
||||
}
|
35
vendor/github.com/johnniedoe/contrib/cache/inmemory_test.go
generated
vendored
Normal file
35
vendor/github.com/johnniedoe/contrib/cache/inmemory_test.go
generated
vendored
Normal 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)
|
||||
}
|
87
vendor/github.com/johnniedoe/contrib/cache/memcached.go
generated
vendored
Normal file
87
vendor/github.com/johnniedoe/contrib/cache/memcached.go
generated
vendored
Normal 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
|
||||
}
|
46
vendor/github.com/johnniedoe/contrib/cache/memcached_test.go
generated
vendored
Normal file
46
vendor/github.com/johnniedoe/contrib/cache/memcached_test.go
generated
vendored
Normal 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
181
vendor/github.com/johnniedoe/contrib/cache/redis.go
generated
vendored
Normal 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
|
||||
}
|
||||
}
|
48
vendor/github.com/johnniedoe/contrib/cache/redis_test.go
generated
vendored
Normal file
48
vendor/github.com/johnniedoe/contrib/cache/redis_test.go
generated
vendored
Normal 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)
|
||||
}
|
66
vendor/github.com/johnniedoe/contrib/cache/serializer.go
generated
vendored
Normal file
66
vendor/github.com/johnniedoe/contrib/cache/serializer.go
generated
vendored
Normal 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
|
||||
}
|
55
vendor/github.com/johnniedoe/contrib/commonlog/commonlog.go
generated
vendored
Normal file
55
vendor/github.com/johnniedoe/contrib/commonlog/commonlog.go
generated
vendored
Normal 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
109
vendor/github.com/johnniedoe/contrib/cors/config.go
generated
vendored
Normal 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
145
vendor/github.com/johnniedoe/contrib/cors/cors.go
generated
vendored
Normal 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
104
vendor/github.com/johnniedoe/contrib/cors/cors_test.go
generated
vendored
Normal 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
22
vendor/github.com/johnniedoe/contrib/expvar/README.md
generated
vendored
Normal 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
26
vendor/github.com/johnniedoe/contrib/expvar/expvar.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
10
vendor/github.com/johnniedoe/contrib/ginrus/Godeps/Godeps.json
generated
vendored
Normal file
10
vendor/github.com/johnniedoe/contrib/ginrus/Godeps/Godeps.json
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/contrib/ginrus",
|
||||
"GoVersion": "go1.3",
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/gin",
|
||||
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
|
||||
}
|
||||
]
|
||||
}
|
39
vendor/github.com/johnniedoe/contrib/ginrus/example/example.go
generated
vendored
Normal file
39
vendor/github.com/johnniedoe/contrib/ginrus/example/example.go
generated
vendored
Normal 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
51
vendor/github.com/johnniedoe/contrib/ginrus/ginrus.go
generated
vendored
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
10
vendor/github.com/johnniedoe/contrib/gzip/Godeps/Godeps.json
generated
vendored
Normal file
10
vendor/github.com/johnniedoe/contrib/gzip/Godeps/Godeps.json
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/contrib/gzip",
|
||||
"GoVersion": "go1.3",
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/gin",
|
||||
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
|
||||
}
|
||||
]
|
||||
}
|
19
vendor/github.com/johnniedoe/contrib/gzip/example/example.go
generated
vendored
Normal file
19
vendor/github.com/johnniedoe/contrib/gzip/example/example.go
generated
vendored
Normal 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
68
vendor/github.com/johnniedoe/contrib/gzip/gzip.go
generated
vendored
Normal 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
81
vendor/github.com/johnniedoe/contrib/gzip/gzip_test.go
generated
vendored
Normal 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)
|
||||
}
|
22
vendor/github.com/johnniedoe/contrib/jwt/Godeps/Godeps.json
generated
vendored
Normal file
22
vendor/github.com/johnniedoe/contrib/jwt/Godeps/Godeps.json
generated
vendored
Normal 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
83
vendor/github.com/johnniedoe/contrib/jwt/README.md
generated
vendored
Normal 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)
|
||||
})
|
||||
}
|
||||
```
|
46
vendor/github.com/johnniedoe/contrib/jwt/example/example.go
generated
vendored
Normal file
46
vendor/github.com/johnniedoe/contrib/jwt/example/example.go
generated
vendored
Normal 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
19
vendor/github.com/johnniedoe/contrib/jwt/jwt.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
29
vendor/github.com/johnniedoe/contrib/newrelic/newrelic.go
generated
vendored
Normal file
29
vendor/github.com/johnniedoe/contrib/newrelic/newrelic.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
84
vendor/github.com/johnniedoe/contrib/renders/multitemplate/README.md
generated
vendored
Normal file
84
vendor/github.com/johnniedoe/contrib/renders/multitemplate/README.md
generated
vendored
Normal 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
|
||||
}
|
||||
```
|
||||
|
50
vendor/github.com/johnniedoe/contrib/renders/multitemplate/multitemplate.go
generated
vendored
Normal file
50
vendor/github.com/johnniedoe/contrib/renders/multitemplate/multitemplate.go
generated
vendored
Normal 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
46
vendor/github.com/johnniedoe/contrib/rest/rest.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
10
vendor/github.com/johnniedoe/contrib/secure/Godeps/Godeps.json
generated
vendored
Normal file
10
vendor/github.com/johnniedoe/contrib/secure/Godeps/Godeps.json
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/contrib/secure",
|
||||
"GoVersion": "go1.3",
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/gin",
|
||||
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
|
||||
}
|
||||
]
|
||||
}
|
32
vendor/github.com/johnniedoe/contrib/secure/example/example.go
generated
vendored
Normal file
32
vendor/github.com/johnniedoe/contrib/secure/example/example.go
generated
vendored
Normal 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
179
vendor/github.com/johnniedoe/contrib/secure/secure.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
470
vendor/github.com/johnniedoe/contrib/secure/secure_test.go
generated
vendored
Normal file
470
vendor/github.com/johnniedoe/contrib/secure/secure_test.go
generated
vendored
Normal 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))
|
||||
}
|
||||
}
|
42
vendor/github.com/johnniedoe/contrib/sentry/recovery.go
generated
vendored
Normal file
42
vendor/github.com/johnniedoe/contrib/sentry/recovery.go
generated
vendored
Normal 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()
|
||||
}
|
||||
}
|
18
vendor/github.com/johnniedoe/contrib/sessions/Godeps/Godeps.json
generated
vendored
Normal file
18
vendor/github.com/johnniedoe/contrib/sessions/Godeps/Godeps.json
generated
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
70
vendor/github.com/johnniedoe/contrib/sessions/README.md
generated
vendored
Normal file
70
vendor/github.com/johnniedoe/contrib/sessions/README.md
generated
vendored
Normal 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")
|
||||
}
|
||||
```
|
36
vendor/github.com/johnniedoe/contrib/sessions/cookie.go
generated
vendored
Normal file
36
vendor/github.com/johnniedoe/contrib/sessions/cookie.go
generated
vendored
Normal 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,
|
||||
}
|
||||
}
|
30
vendor/github.com/johnniedoe/contrib/sessions/cookie_test.go
generated
vendored
Normal file
30
vendor/github.com/johnniedoe/contrib/sessions/cookie_test.go
generated
vendored
Normal 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)
|
||||
}
|
28
vendor/github.com/johnniedoe/contrib/sessions/examples/example_cookie.go
generated
vendored
Normal file
28
vendor/github.com/johnniedoe/contrib/sessions/examples/example_cookie.go
generated
vendored
Normal 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")
|
||||
}
|
28
vendor/github.com/johnniedoe/contrib/sessions/examples/example_redis.go
generated
vendored
Normal file
28
vendor/github.com/johnniedoe/contrib/sessions/examples/example_redis.go
generated
vendored
Normal 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
45
vendor/github.com/johnniedoe/contrib/sessions/redis.go
generated
vendored
Normal 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,
|
||||
}
|
||||
}
|
35
vendor/github.com/johnniedoe/contrib/sessions/redis_test.go
generated
vendored
Normal file
35
vendor/github.com/johnniedoe/contrib/sessions/redis_test.go
generated
vendored
Normal 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)
|
||||
}
|
147
vendor/github.com/johnniedoe/contrib/sessions/sessions.go
generated
vendored
Normal file
147
vendor/github.com/johnniedoe/contrib/sessions/sessions.go
generated
vendored
Normal 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)
|
||||
}
|
219
vendor/github.com/johnniedoe/contrib/sessions/sessions_test.go
generated
vendored
Normal file
219
vendor/github.com/johnniedoe/contrib/sessions/sessions_test.go
generated
vendored
Normal 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])
|
||||
}
|
||||
}
|
115
vendor/github.com/johnniedoe/contrib/static/example/bindata/bindata.go
generated
vendored
Normal file
115
vendor/github.com/johnniedoe/contrib/static/example/bindata/bindata.go
generated
vendored
Normal 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{}},
|
||||
}},
|
||||
}}
|
10
vendor/github.com/johnniedoe/contrib/static/example/bindata/data/index.html
generated
vendored
Normal file
10
vendor/github.com/johnniedoe/contrib/static/example/bindata/data/index.html
generated
vendored
Normal 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>
|
50
vendor/github.com/johnniedoe/contrib/static/example/bindata/example.go
generated
vendored
Normal file
50
vendor/github.com/johnniedoe/contrib/static/example/bindata/example.go
generated
vendored
Normal 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")
|
||||
}
|
22
vendor/github.com/johnniedoe/contrib/static/example/simple/example.go
generated
vendored
Normal file
22
vendor/github.com/johnniedoe/contrib/static/example/simple/example.go
generated
vendored
Normal 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
62
vendor/github.com/johnniedoe/contrib/static/static.go
generated
vendored
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
86
vendor/github.com/johnniedoe/contrib/static/static_test.go
generated
vendored
Normal file
86
vendor/github.com/johnniedoe/contrib/static/static_test.go
generated
vendored
Normal 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")
|
||||
}
|
Reference in New Issue
Block a user