├── Procfile ├── .gitignore ├── vendor ├── github.com │ ├── olebedev │ │ ├── config │ │ │ ├── wercker.yml │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── doc.go │ │ └── graphics-go │ │ │ ├── graphics │ │ │ ├── convolve │ │ │ │ ├── Makefile │ │ │ │ └── convolve.go │ │ │ ├── interp │ │ │ │ ├── Makefile │ │ │ │ ├── doc.go │ │ │ │ ├── interp.go │ │ │ │ └── bilinear.go │ │ │ ├── Makefile │ │ │ ├── scale.go │ │ │ ├── rotate.go │ │ │ ├── thumbnail.go │ │ │ ├── blur.go │ │ │ └── affine.go │ │ │ └── LICENSE │ └── gorilla │ │ ├── context │ │ ├── README.md │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── doc.go │ │ └── context.go │ │ └── mux │ │ ├── .travis.yml │ │ ├── context_native.go │ │ ├── context_gorilla.go │ │ ├── LICENSE │ │ ├── doc.go │ │ ├── regexp.go │ │ └── README.md └── gopkg.in │ ├── mgo.v2 │ ├── raceon.go │ ├── raceoff.go │ ├── Makefile │ ├── README.md │ ├── saslstub.go │ ├── saslimpl.go │ ├── internal │ │ ├── sasl │ │ │ ├── sasl_windows.h │ │ │ ├── sasl.c │ │ │ ├── sspi_windows.h │ │ │ ├── sspi_windows.c │ │ │ ├── sasl.go │ │ │ ├── sasl_windows.go │ │ │ └── sasl_windows.c │ │ ├── json │ │ │ ├── tags.go │ │ │ ├── LICENSE │ │ │ ├── extension.go │ │ │ ├── indent.go │ │ │ └── fold.go │ │ └── scram │ │ │ └── scram.go │ ├── .travis.yml │ ├── doc.go │ ├── bson │ │ ├── LICENSE │ │ └── decimal.go │ ├── LICENSE │ ├── queue.go │ ├── stats.go │ └── log.go │ └── yaml.v2 │ ├── .travis.yml │ ├── LICENSE.libyaml │ ├── writerc.go │ ├── sorter.go │ ├── README.md │ ├── yamlprivateh.go │ ├── resolve.go │ ├── encode.go │ └── LICENSE ├── Godeps ├── Readme └── Godeps.json ├── lib ├── cdn.go ├── utils.go ├── image.go ├── post.go └── get.go ├── Makefile ├── LICENSE ├── cdn.go ├── srlt.yaml └── readme.md /Procfile: -------------------------------------------------------------------------------- 1 | web: cdn 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | cdn -------------------------------------------------------------------------------- /vendor/github.com/olebedev/config/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.1 2 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/raceon.go: -------------------------------------------------------------------------------- 1 | // +build race 2 | 3 | package mgo 4 | 5 | const raceDetector = true 6 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/raceoff.go: -------------------------------------------------------------------------------- 1 | // +build !race 2 | 3 | package mgo 4 | 5 | const raceDetector = false 6 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/Makefile: -------------------------------------------------------------------------------- 1 | startdb: 2 | @harness/setup.sh start 3 | 4 | stopdb: 5 | @harness/setup.sh stop 6 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.4 5 | - 1.5 6 | - 1.6 7 | - tip 8 | 9 | go_import_path: gopkg.in/yaml.v2 10 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/README.md: -------------------------------------------------------------------------------- 1 | The MongoDB driver for Go 2 | ------------------------- 3 | 4 | Please go to [http://labix.org/mgo](http://labix.org/mgo) for all project details. 5 | -------------------------------------------------------------------------------- /lib/cdn.go: -------------------------------------------------------------------------------- 1 | package cdn 2 | 3 | import mgo "gopkg.in/mgo.v2" 4 | 5 | type Config struct { 6 | MaxSize int 7 | TailOnly bool 8 | ShowInfo bool 9 | Db *mgo.Database 10 | } 11 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/saslstub.go: -------------------------------------------------------------------------------- 1 | //+build !sasl 2 | 3 | package mgo 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func saslNew(cred Credential, host string) (saslStepper, error) { 10 | return nil, fmt.Errorf("SASL support not enabled during build (-tags sasl)") 11 | } 12 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/saslimpl.go: -------------------------------------------------------------------------------- 1 | //+build sasl 2 | 3 | package mgo 4 | 5 | import ( 6 | "gopkg.in/mgo.v2/internal/sasl" 7 | ) 8 | 9 | func saslNew(cred Credential, host string) (saslStepper, error) { 10 | return sasl.New(cred.Username, cred.Password, cred.Mechanism, cred.Service, host) 11 | } 12 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/README.md: -------------------------------------------------------------------------------- 1 | context 2 | ======= 3 | [![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) 4 | 5 | gorilla/context is a general purpose registry for global request variables. 6 | 7 | Read the full documentation here: http://www.gorillatoolkit.org/pkg/context 8 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | matrix: 5 | include: 6 | - go: 1.2 7 | - go: 1.3 8 | - go: 1.4 9 | - go: 1.5 10 | - go: 1.6 11 | - go: tip 12 | 13 | install: 14 | - # Skip 15 | 16 | script: 17 | - go get -t -v ./... 18 | - diff -u <(echo -n) <(gofmt -d .) 19 | - go tool vet . 20 | - go test -v -race ./... 21 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/config/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | matrix: 5 | include: 6 | - go: 1.3 7 | - go: 1.4 8 | - go: 1.5 9 | - go: 1.6 10 | - go: tip 11 | 12 | install: 13 | - go get golang.org/x/tools/cmd/vet 14 | 15 | script: 16 | - go get -t -v ./... 17 | - diff -u <(echo -n) <(gofmt -d .) 18 | - go tool vet . 19 | - go test -v -race ./... 20 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/convolve/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2011 The Graphics-Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | include $(GOROOT)/src/Make.inc 6 | 7 | TARG=code.google.com/p/graphics-go/graphics/convolve 8 | GOFILES=\ 9 | convolve.go\ 10 | 11 | include $(GOROOT)/src/Make.pkg 12 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/interp/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2012 The Graphics-Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | include $(GOROOT)/src/Make.inc 6 | 7 | TARG=code.google.com/p/graphics-go/graphics/interp 8 | GOFILES=\ 9 | bilinear.go\ 10 | doc.go\ 11 | interp.go\ 12 | 13 | include $(GOROOT)/src/Make.pkg 14 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2011 The Graphics-Go Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | include $(GOROOT)/src/Make.inc 6 | 7 | TARG=code.google.com/p/graphics-go/graphics 8 | GOFILES=\ 9 | affine.go\ 10 | blur.go\ 11 | rotate.go\ 12 | scale.go\ 13 | thumbnail.go\ 14 | 15 | include $(GOROOT)/src/Make.pkg 16 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/sasl/sasl_windows.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "sspi_windows.h" 4 | 5 | SECURITY_STATUS SEC_ENTRY sspi_acquire_credentials_handle(CredHandle* cred_handle, char* username, char* password, char* domain); 6 | int sspi_step(CredHandle* cred_handle, int has_context, CtxtHandle* context, PVOID buffer, ULONG buffer_length, PVOID* out_buffer, ULONG* out_buffer_length, char* target); 7 | int sspi_send_client_authz_id(CtxtHandle* context, PVOID* buffer, ULONG* buffer_length, char* user_plus_realm); 8 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/context_native.go: -------------------------------------------------------------------------------- 1 | // +build go1.7 2 | 3 | package mux 4 | 5 | import ( 6 | "context" 7 | "net/http" 8 | ) 9 | 10 | func contextGet(r *http.Request, key interface{}) interface{} { 11 | return r.Context().Value(key) 12 | } 13 | 14 | func contextSet(r *http.Request, key, val interface{}) *http.Request { 15 | if val == nil { 16 | return r 17 | } 18 | 19 | return r.WithContext(context.WithValue(r.Context(), key, val)) 20 | } 21 | 22 | func contextClear(r *http.Request) { 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/context_gorilla.go: -------------------------------------------------------------------------------- 1 | // +build !go1.7 2 | 3 | package mux 4 | 5 | import ( 6 | "net/http" 7 | 8 | "github.com/gorilla/context" 9 | ) 10 | 11 | func contextGet(r *http.Request, key interface{}) interface{} { 12 | return context.Get(r, key) 13 | } 14 | 15 | func contextSet(r *http.Request, key, val interface{}) *http.Request { 16 | if val == nil { 17 | return r 18 | } 19 | 20 | context.Set(r, key, val) 21 | return r 22 | } 23 | 24 | func contextClear(r *http.Request) { 25 | context.Clear(r) 26 | } 27 | -------------------------------------------------------------------------------- /lib/utils.go: -------------------------------------------------------------------------------- 1 | package cdn 2 | 3 | import ( 4 | "regexp" 5 | "strconv" 6 | ) 7 | 8 | // it in list 9 | func in(list []string, a string) int { 10 | for i, b := range list { 11 | if b == a { 12 | return i 13 | } 14 | } 15 | return -1 16 | } 17 | 18 | // parse given params, example: '100x100' | '100' 19 | func parseParams(s string) ([]int, error) { 20 | res := make([]int, 2) 21 | var err error 22 | re := regexp.MustCompile("\\d+") 23 | spl := re.FindAllString(s, 2) 24 | 25 | if len(spl) == 0 { 26 | return nil, err 27 | } 28 | 29 | for i, item := range spl { 30 | v, e := strconv.Atoi(item) 31 | if e != nil { 32 | err = e 33 | continue 34 | } 35 | res[i] = v 36 | } 37 | 38 | if len(spl) == 1 { 39 | res[1] = res[0] 40 | } 41 | 42 | return res, err 43 | } 44 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/interp/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Graphics-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package interp implements image interpolation. 7 | 8 | An interpolator provides the Interp interface, which can be used 9 | to interpolate a pixel: 10 | 11 | c := interp.Bilinear.Interp(src, 1.2, 1.8) 12 | 13 | To interpolate a large number of RGBA or Gray pixels, an implementation 14 | may provide a fast-path by implementing the RGBA or Gray interfaces. 15 | 16 | i1, ok := i.(interp.RGBA) 17 | if ok { 18 | c := i1.RGBA(src, 1.2, 1.8) 19 | // use c.R, c.G, etc 20 | return 21 | } 22 | c := i.Interp(src, 1.2, 1.8) 23 | // use generic color.Color 24 | */ 25 | package interp 26 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/scale.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Graphics-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package graphics 6 | 7 | import ( 8 | "errors" 9 | "image" 10 | "image/draw" 11 | 12 | "github.com/olebedev/graphics-go/graphics/interp" 13 | ) 14 | 15 | // Scale produces a scaled version of the image using bilinear interpolation. 16 | func Scale(dst draw.Image, src image.Image) error { 17 | if dst == nil { 18 | return errors.New("graphics: dst is nil") 19 | } 20 | if src == nil { 21 | return errors.New("graphics: src is nil") 22 | } 23 | 24 | b := dst.Bounds() 25 | srcb := src.Bounds() 26 | if b.Empty() || srcb.Empty() { 27 | return nil 28 | } 29 | sx := float64(b.Dx()) / float64(srcb.Dx()) 30 | sy := float64(b.Dy()) / float64(srcb.Dy()) 31 | return I.Scale(sx, sy).Transform(dst, src, interp.Bilinear) 32 | } 33 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/interp/interp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Graphics-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package interp 6 | 7 | import ( 8 | "image" 9 | "image/color" 10 | ) 11 | 12 | // Interp interpolates an image's color at fractional co-ordinates. 13 | type Interp interface { 14 | // Interp interpolates (x, y). 15 | Interp(src image.Image, x, y float64) color.Color 16 | } 17 | 18 | // RGBA is a fast-path interpolation implementation for image.RGBA. 19 | // It is common for an Interp to also implement RGBA. 20 | type RGBA interface { 21 | // RGBA interpolates (x, y). 22 | RGBA(src *image.RGBA, x, y float64) color.RGBA 23 | } 24 | 25 | // Gray is a fast-path interpolation implementation for image.Gray. 26 | type Gray interface { 27 | // Gray interpolates (x, y). 28 | Gray(src *image.Gray, x, y float64) color.Gray 29 | } 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | P = cdn 2 | SOURCE = $(wildcard *.go) 3 | TAG = $(shell git describe --tags) 4 | GOBUILD = go build -ldflags '-w' 5 | 6 | # $(tag) here will contain either `-1.0-` or just `-` 7 | ALL = \ 8 | $(foreach arch,64,\ 9 | $(foreach tag,-$(TAG)-,\ 10 | $(foreach suffix,win.exe linux osx,\ 11 | build/$P$(tag)$(arch)-$(suffix)))) 12 | 13 | build: $(ALL) 14 | 15 | # os is determined as thus: if variable of suffix exists, it's taken, if not, then 16 | # suffix itself is taken 17 | win.exe = windows 18 | osx = darwin 19 | build/$P-$(TAG)-64-%: $(SOURCE) 20 | @mkdir -p $(@D) 21 | CGO_ENABLED=0 GOOS=$(firstword $($*) $*) GOARCH=amd64 $(GOBUILD) -o $@ 22 | @cd $(@D) && tar cvzf $(@F).tar.gz $(@F) 23 | 24 | build/$P-$(TAG)-32-%: $(SOURCE) 25 | @mkdir -p $(@D) 26 | CGO_ENABLED=0 GOOS=$(firstword $($*) $*) GOARCH=386 $(GOBUILD) -o $@ 27 | @cd $(@D) && tar cvzf $(@F).tar.gz $(@F) 28 | 29 | build/$P-%: build/$P-$(TAG)-% 30 | @mkdir -p $(@D) 31 | cd $(@D) && ln -sf $(= 0 { 36 | s, next = s[:i], s[i+1:] 37 | } 38 | if s == optionName { 39 | return true 40 | } 41 | s = next 42 | } 43 | return false 44 | } 45 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/doc.go: -------------------------------------------------------------------------------- 1 | // Package mgo offers a rich MongoDB driver for Go. 2 | // 3 | // Details about the mgo project (pronounced as "mango") are found 4 | // in its web page: 5 | // 6 | // http://labix.org/mgo 7 | // 8 | // Usage of the driver revolves around the concept of sessions. To 9 | // get started, obtain a session using the Dial function: 10 | // 11 | // session, err := mgo.Dial(url) 12 | // 13 | // This will establish one or more connections with the cluster of 14 | // servers defined by the url parameter. From then on, the cluster 15 | // may be queried with multiple consistency rules (see SetMode) and 16 | // documents retrieved with statements such as: 17 | // 18 | // c := session.DB(database).C(collection) 19 | // err := c.Find(query).One(&result) 20 | // 21 | // New sessions are typically created by calling session.Copy on the 22 | // initial session obtained at dial time. These new sessions will share 23 | // the same cluster information and connection pool, and may be easily 24 | // handed into other methods and functions for organizing logic. 25 | // Every session created must have its Close method called at the end 26 | // of its life time, so its resources may be put back in the pool or 27 | // collected, depending on the case. 28 | // 29 | // For more details, see the documentation for the types and methods. 30 | // 31 | package mgo 32 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/config/README.md: -------------------------------------------------------------------------------- 1 | # Config [![wercker status](https://app.wercker.com/status/b4e8561d9a711afcb016bf0018e83897/s/ "wercker status")](https://app.wercker.com/project/bykey/b4e8561d9a711afcb016bf0018e83897) [![GoDoc](https://godoc.org/github.com/olebedev/config?status.png)](https://godoc.org/github.com/olebedev/config) 2 | 3 | Package config provides convenient access methods to configuration 4 | stored as JSON or YAML. 5 | 6 | This is a fork of the [original version](https://github.com/moraes/config). 7 | This version extends the functionality of the original without losing compatibility. 8 | Major features added: 9 | 10 | - [`Set(path string, value interface{}) error`](http://godoc.org/github.com/olebedev/config#Config.Set) method 11 | - [`Env() *config.Config`](http://godoc.org/github.com/olebedev/config#Config.Env) method, for OS environment variables parsing 12 | - [`Flag() *config.Config`](http://godoc.org/github.com/olebedev/config#Config.Flag) method, for command line arguments parsing 13 | - [`U*`](https://godoc.org/github.com/olebedev/config#Config.UBool) methods 14 | - [`Copy(...path) (*config.config, error)`](https://godoc.org/github.com/olebedev/config#Config.Copy) method 15 | - [`Extend(*config.Config) (*config.Config, error)`](https://godoc.org/github.com/olebedev/config#Config.Extend) method 16 | 17 | Example and more information you can find [here](http://godoc.org/github.com/olebedev/config). 18 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/LICENSE.libyaml: -------------------------------------------------------------------------------- 1 | The following files were ported to Go from C files of libyaml, and thus 2 | are still covered by their original copyright and license: 3 | 4 | apic.go 5 | emitterc.go 6 | parserc.go 7 | readerc.go 8 | scannerc.go 9 | writerc.go 10 | yamlh.go 11 | yamlprivateh.go 12 | 13 | Copyright (c) 2006 Kirill Simonov 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy of 16 | this software and associated documentation files (the "Software"), to deal in 17 | the Software without restriction, including without limitation the rights to 18 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 19 | of the Software, and to permit persons to whom the Software is furnished to do 20 | so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/bson/LICENSE: -------------------------------------------------------------------------------- 1 | BSON library for Go 2 | 3 | Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/LICENSE: -------------------------------------------------------------------------------- 1 | mgo - MongoDB driver for Go 2 | 3 | Copyright (c) 2010-2013 - Gustavo Niemeyer 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /cdn.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "os" 8 | 9 | mgo "gopkg.in/mgo.v2" 10 | 11 | "github.com/gorilla/mux" 12 | "github.com/olebedev/cdn/lib" 13 | "github.com/olebedev/config" 14 | ) 15 | 16 | var conf, _ = config.ParseYaml(` 17 | debug: false 18 | port: 5000 19 | maxSize: 1000 20 | showInfo: true 21 | tailOnly: false 22 | mongo: 23 | url: localhost 24 | `) 25 | 26 | func main() { 27 | conf.Env().Flag() 28 | _c, _ := config.RenderYaml(conf.Root) 29 | fmt.Println("start with config:\n", _c, "\n") 30 | 31 | fmt.Println(mgo.ParseURL(conf.UString("mongo.url"))) 32 | session, err := mgo.Dial(conf.UString("mongo.url")) 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | r := mux.NewRouter() 38 | app := cdn.Config{ 39 | MaxSize: conf.UInt("maxSize"), 40 | ShowInfo: conf.UBool("showInfo"), 41 | TailOnly: conf.UBool("tailOnly"), 42 | Db: session.DB(""), 43 | } 44 | 45 | if app.ShowInfo { 46 | r.HandleFunc("/{coll}", app.GetIndex).Methods("GET") 47 | r.HandleFunc("/{coll}/_stats", app.GetStat).Methods("GET") 48 | } 49 | 50 | r.HandleFunc("/{coll}", app.Post).Methods("POST") 51 | r.HandleFunc("/{coll}/{_id}", app.Get).Methods("GET") 52 | r.HandleFunc("/{coll}/{_id}/{file}", app.Get).Methods("GET") 53 | 54 | log.Println("Server started at :" + conf.UString("port", "5000")) 55 | _err := http.ListenAndServe(":"+conf.UString("port", "5000"), r) 56 | if _err != nil { 57 | log.Printf("\x1B[31mServer exit with error: %s\x1B[39m\n", _err) 58 | os.Exit(1) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Rodrigo Moraes. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Rodrigo Moraes. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/json/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 The Graphics-Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /srlt.yaml: -------------------------------------------------------------------------------- 1 | base: ./vendor 2 | dependencies: 3 | github.com/codegangsta/inject: 4 | type: git 5 | name: github.com/codegangsta/inject 6 | remote: https://github.com/codegangsta/inject 7 | commit: 8822b072a3636c1a365c68e1004efd5bf8ab500f 8 | github.com/go-martini/martini: 9 | type: git 10 | name: github.com/go-martini/martini 11 | remote: https://github.com/go-martini/martini 12 | commit: baa42219288cdd775874794381aa987144012263 13 | github.com/gorilla/context: 14 | type: git 15 | name: github.com/gorilla/context 16 | remote: https://github.com/gorilla/context 17 | commit: a08edd30ad9e104612741163dc087a613829a23c 18 | github.com/gorilla/mux: 19 | type: git 20 | name: github.com/gorilla/mux 21 | remote: https://github.com/gorilla/mux 22 | commit: 9ede152210fa25c1377d33e867cb828c19316445 23 | github.com/muesli/smartcrop: 24 | type: git 25 | name: github.com/muesli/smartcrop 26 | remote: https://github.com/muesli/smartcrop 27 | commit: c41337febce9a5b529f993f71b4307e15c3d1f55 28 | github.com/olebedev/graphics-go: 29 | type: git 30 | name: github.com/olebedev/graphics-go 31 | remote: https://github.com/olebedev/graphics-go 32 | commit: b43f31a4a96688fba0b612e25e22648b9267e498 33 | labix.org/v2/mgo: 34 | type: bzr 35 | name: labix.org/v2/mgo 36 | remote: http://bazaar.launchpad.net/~niemeyer/mgo/v2/ 37 | commit: "253" 38 | launchpad.net/~niemeyer/goyaml/beta: 39 | type: bzr 40 | name: launchpad.net/~niemeyer/goyaml/beta 41 | remote: http://bazaar.launchpad.net/~niemeyer/goyaml/beta/ 42 | commit: "64" 43 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/blur.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Graphics-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package graphics 6 | 7 | import ( 8 | "errors" 9 | "image" 10 | "image/draw" 11 | "math" 12 | 13 | "github.com/olebedev/graphics-go/graphics/convolve" 14 | ) 15 | 16 | // DefaultStdDev is the default blurring parameter. 17 | var DefaultStdDev = 0.5 18 | 19 | // BlurOptions are the blurring parameters. 20 | // StdDev is the standard deviation of the normal, higher is blurrier. 21 | // Size is the size of the kernel. If zero, it is set to Ceil(6 * StdDev). 22 | type BlurOptions struct { 23 | StdDev float64 24 | Size int 25 | } 26 | 27 | // Blur produces a blurred version of the image, using a Gaussian blur. 28 | func Blur(dst draw.Image, src image.Image, opt *BlurOptions) error { 29 | if dst == nil { 30 | return errors.New("graphics: dst is nil") 31 | } 32 | if src == nil { 33 | return errors.New("graphics: src is nil") 34 | } 35 | 36 | sd := DefaultStdDev 37 | size := 0 38 | 39 | if opt != nil { 40 | sd = opt.StdDev 41 | size = opt.Size 42 | } 43 | 44 | if size < 1 { 45 | size = int(math.Ceil(sd * 6)) 46 | } 47 | 48 | kernel := make([]float64, 2*size+1) 49 | for i := 0; i <= size; i++ { 50 | x := float64(i) / sd 51 | x = math.Pow(1/math.SqrtE, x*x) 52 | kernel[size-i] = x 53 | kernel[size+i] = x 54 | } 55 | 56 | // Normalize the weights to sum to 1.0. 57 | kSum := 0.0 58 | for _, k := range kernel { 59 | kSum += k 60 | } 61 | for i, k := range kernel { 62 | kernel[i] = k / kSum 63 | } 64 | 65 | return convolve.Convolve(dst, src, &convolve.SeparableKernel{ 66 | X: kernel, 67 | Y: kernel, 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/sasl/sasl.c: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static int mgo_sasl_simple(void *context, int id, const char **result, unsigned int *len) 9 | { 10 | if (!result) { 11 | return SASL_BADPARAM; 12 | } 13 | switch (id) { 14 | case SASL_CB_USER: 15 | *result = (char *)context; 16 | break; 17 | case SASL_CB_AUTHNAME: 18 | *result = (char *)context; 19 | break; 20 | case SASL_CB_LANGUAGE: 21 | *result = NULL; 22 | break; 23 | default: 24 | return SASL_BADPARAM; 25 | } 26 | if (len) { 27 | *len = *result ? strlen(*result) : 0; 28 | } 29 | return SASL_OK; 30 | } 31 | 32 | typedef int (*callback)(void); 33 | 34 | static int mgo_sasl_secret(sasl_conn_t *conn, void *context, int id, sasl_secret_t **result) 35 | { 36 | if (!conn || !result || id != SASL_CB_PASS) { 37 | return SASL_BADPARAM; 38 | } 39 | *result = (sasl_secret_t *)context; 40 | return SASL_OK; 41 | } 42 | 43 | sasl_callback_t *mgo_sasl_callbacks(const char *username, const char *password) 44 | { 45 | sasl_callback_t *cb = malloc(4 * sizeof(sasl_callback_t)); 46 | int n = 0; 47 | 48 | size_t len = strlen(password); 49 | sasl_secret_t *secret = (sasl_secret_t*)malloc(sizeof(sasl_secret_t) + len); 50 | if (!secret) { 51 | free(cb); 52 | return NULL; 53 | } 54 | strcpy((char *)secret->data, password); 55 | secret->len = len; 56 | 57 | cb[n].id = SASL_CB_PASS; 58 | cb[n].proc = (callback)&mgo_sasl_secret; 59 | cb[n].context = secret; 60 | n++; 61 | 62 | cb[n].id = SASL_CB_USER; 63 | cb[n].proc = (callback)&mgo_sasl_simple; 64 | cb[n].context = (char*)username; 65 | n++; 66 | 67 | cb[n].id = SASL_CB_AUTHNAME; 68 | cb[n].proc = (callback)&mgo_sasl_simple; 69 | cb[n].context = (char*)username; 70 | n++; 71 | 72 | cb[n].id = SASL_CB_LIST_END; 73 | cb[n].proc = NULL; 74 | cb[n].context = NULL; 75 | 76 | return cb; 77 | } 78 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/olebedev/cdn", 3 | "GoVersion": "go1.6", 4 | "GodepVersion": "v71", 5 | "Packages": [ 6 | "." 7 | ], 8 | "Deps": [ 9 | { 10 | "ImportPath": "github.com/gorilla/context", 11 | "Comment": "v1.1", 12 | "Rev": "1ea25387ff6f684839d82767c1733ff4d4d15d0a" 13 | }, 14 | { 15 | "ImportPath": "github.com/gorilla/mux", 16 | "Comment": "v1.1-20-g7804150", 17 | "Rev": "780415097119f6f61c55475fe59b66f3c3e9ea53" 18 | }, 19 | { 20 | "ImportPath": "github.com/olebedev/config", 21 | "Rev": "dd1049a3738432dfc49417e2ae5bd97e132ecac6" 22 | }, 23 | { 24 | "ImportPath": "github.com/olebedev/graphics-go/graphics", 25 | "Rev": "43898c463c663129c19e7b31ce2ed7ffa593fc8c" 26 | }, 27 | { 28 | "ImportPath": "github.com/olebedev/graphics-go/graphics/convolve", 29 | "Rev": "43898c463c663129c19e7b31ce2ed7ffa593fc8c" 30 | }, 31 | { 32 | "ImportPath": "github.com/olebedev/graphics-go/graphics/interp", 33 | "Rev": "43898c463c663129c19e7b31ce2ed7ffa593fc8c" 34 | }, 35 | { 36 | "ImportPath": "gopkg.in/mgo.v2", 37 | "Comment": "r2016.08.01-1-g0108465", 38 | "Rev": "01084657862d7b12d3b10ffd0394357abf8f8bc2" 39 | }, 40 | { 41 | "ImportPath": "gopkg.in/mgo.v2/bson", 42 | "Comment": "r2016.08.01-1-g0108465", 43 | "Rev": "01084657862d7b12d3b10ffd0394357abf8f8bc2" 44 | }, 45 | { 46 | "ImportPath": "gopkg.in/mgo.v2/internal/json", 47 | "Comment": "r2016.08.01-1-g0108465", 48 | "Rev": "01084657862d7b12d3b10ffd0394357abf8f8bc2" 49 | }, 50 | { 51 | "ImportPath": "gopkg.in/mgo.v2/internal/sasl", 52 | "Comment": "r2016.08.01-1-g0108465", 53 | "Rev": "01084657862d7b12d3b10ffd0394357abf8f8bc2" 54 | }, 55 | { 56 | "ImportPath": "gopkg.in/mgo.v2/internal/scram", 57 | "Comment": "r2016.08.01-1-g0108465", 58 | "Rev": "01084657862d7b12d3b10ffd0394357abf8f8bc2" 59 | }, 60 | { 61 | "ImportPath": "gopkg.in/yaml.v2", 62 | "Rev": "a83829b6f1293c91addabc89d0571c246397bbf4" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /lib/image.go: -------------------------------------------------------------------------------- 1 | package cdn 2 | 3 | import ( 4 | "fmt" 5 | "image" 6 | "image/jpeg" 7 | "image/png" 8 | "io" 9 | 10 | "github.com/olebedev/graphics-go/graphics" 11 | ) 12 | 13 | func writeByMimetype(w io.Writer, dst image.Image, mimetype string) error { 14 | switch mimetype { 15 | case "jpeg": 16 | return jpeg.Encode(w, dst, &jpeg.Options{jpeg.DefaultQuality}) 17 | case "png": 18 | return png.Encode(w, dst) 19 | default: 20 | return fmt.Errorf("Mimetype '%s' can't be processed.", mimetype) 21 | } 22 | } 23 | 24 | func fitToActualSize(img *image.Image, size []int) []int { 25 | ib := (*img).Bounds() 26 | var x, y int = ib.Dx(), ib.Dy() 27 | if ib.Dx() >= size[0] { 28 | x = size[0] 29 | } 30 | if ib.Dy() >= size[1] { 31 | y = size[1] 32 | } 33 | 34 | return []int{x, y} 35 | } 36 | 37 | func setMaxSize(max int, size []int) []int { 38 | if max <= size[0] { 39 | size[0] = max 40 | } 41 | if max <= size[1] { 42 | size[1] = max 43 | } 44 | return size 45 | } 46 | 47 | // Crop given image file & write it to io.Writer 48 | func crop(w io.Writer, r io.Reader, max int, size []int) error { 49 | img, mimetype, err := image.Decode(r) 50 | if size == nil || err != nil { 51 | io.Copy(w, r) 52 | return nil 53 | } 54 | 55 | size = setMaxSize(max, fitToActualSize(&img, size)) 56 | dst := image.NewRGBA(image.Rect(0, 0, size[0], size[1])) 57 | graphics.Thumbnail(dst, img) 58 | 59 | return writeByMimetype(w, dst, mimetype) 60 | } 61 | 62 | // Resize given image file & write it to io.Writer 63 | func resize(w io.Writer, r io.Reader, size []int) error { 64 | img, mimetype, err := image.Decode(r) 65 | if size == nil || err != nil { 66 | io.Copy(w, r) 67 | return nil 68 | } 69 | 70 | ib := img.Bounds() 71 | 72 | size = fitToActualSize(&img, size) 73 | x := size[0] 74 | y := size[1] 75 | 76 | // set optimal thumbnail size 77 | wrat := float64(x) / float64(ib.Dx()) 78 | hrat := float64(y) / float64(ib.Dy()) 79 | if wrat <= hrat { 80 | y = int(wrat * float64(ib.Dy())) 81 | } else { 82 | x = int(hrat * float64(ib.Dx())) 83 | } 84 | 85 | dst := image.NewRGBA(image.Rect(0, 0, x, y)) 86 | graphics.Thumbnail(dst, img) 87 | 88 | return writeByMimetype(w, dst, mimetype) 89 | } 90 | -------------------------------------------------------------------------------- /lib/post.go: -------------------------------------------------------------------------------- 1 | package cdn 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "net/http" 7 | "strings" 8 | 9 | "gopkg.in/mgo.v2/bson" 10 | 11 | "github.com/gorilla/mux" 12 | ) 13 | 14 | func (c *Config) Post(w http.ResponseWriter, req *http.Request) { 15 | vars := mux.Vars(req) 16 | db := c.Db 17 | formFile, formHead, err := req.FormFile("field") 18 | if err != nil { 19 | w.WriteHeader(http.StatusBadRequest) 20 | w.Write([]byte("400 Bad Request")) 21 | return 22 | } 23 | defer formFile.Close() 24 | 25 | //remove any directory names in the filename 26 | //START: work around IE sending full filepath and manually get filename 27 | itemHead := formHead.Header["Content-Disposition"][0] 28 | lookfor := "filename=\"" 29 | fileIndex := strings.Index(itemHead, lookfor) 30 | 31 | if fileIndex < 0 { 32 | w.WriteHeader(http.StatusBadRequest) 33 | w.Write([]byte("400 Bad Request")) 34 | return 35 | } 36 | 37 | filename := itemHead[fileIndex+len(lookfor):] 38 | filename = filename[:strings.Index(filename, "\"")] 39 | 40 | slashIndex := strings.LastIndex(filename, "\\") 41 | if slashIndex > 0 { 42 | filename = filename[slashIndex+1:] 43 | } 44 | 45 | slashIndex = strings.LastIndex(filename, "/") 46 | if slashIndex > 0 { 47 | filename = filename[slashIndex+1:] 48 | } 49 | //END: work around IE sending full filepath 50 | 51 | // GridFs actions 52 | file, err := db.GridFS(vars["coll"]).Create(filename) 53 | if err != nil { 54 | w.WriteHeader(http.StatusBadRequest) 55 | w.Write([]byte("400 Bad Request")) 56 | return 57 | } 58 | defer file.Close() 59 | 60 | io.Copy(file, formFile) 61 | if err != nil { 62 | w.WriteHeader(http.StatusBadRequest) 63 | w.Write([]byte("400 Bad Request")) 64 | return 65 | } 66 | 67 | b := make([]byte, 512) 68 | formFile.Seek(0, 0) 69 | formFile.Read(b) 70 | 71 | file.SetContentType(http.DetectContentType(b)) 72 | file.SetMeta(req.Form) 73 | err = file.Close() 74 | 75 | // json response 76 | field := "/" + file.Id().(bson.ObjectId).Hex() + "/" + filename 77 | if !c.TailOnly { 78 | field = "/" + vars["coll"] + field 79 | } 80 | 81 | bytes, _ := json.Marshal(map[string]interface{}{ 82 | "error": err, 83 | "field": field, 84 | }) 85 | w.Write(bytes) 86 | } 87 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/writerc.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | // Set the writer error and return false. 4 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 5 | emitter.error = yaml_WRITER_ERROR 6 | emitter.problem = problem 7 | return false 8 | } 9 | 10 | // Flush the output buffer. 11 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 12 | if emitter.write_handler == nil { 13 | panic("write handler not set") 14 | } 15 | 16 | // Check if the buffer is empty. 17 | if emitter.buffer_pos == 0 { 18 | return true 19 | } 20 | 21 | // If the output encoding is UTF-8, we don't need to recode the buffer. 22 | if emitter.encoding == yaml_UTF8_ENCODING { 23 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 24 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 25 | } 26 | emitter.buffer_pos = 0 27 | return true 28 | } 29 | 30 | // Recode the buffer into the raw buffer. 31 | var low, high int 32 | if emitter.encoding == yaml_UTF16LE_ENCODING { 33 | low, high = 0, 1 34 | } else { 35 | high, low = 1, 0 36 | } 37 | 38 | pos := 0 39 | for pos < emitter.buffer_pos { 40 | // See the "reader.c" code for more details on UTF-8 encoding. Note 41 | // that we assume that the buffer contains a valid UTF-8 sequence. 42 | 43 | // Read the next UTF-8 character. 44 | octet := emitter.buffer[pos] 45 | 46 | var w int 47 | var value rune 48 | switch { 49 | case octet&0x80 == 0x00: 50 | w, value = 1, rune(octet&0x7F) 51 | case octet&0xE0 == 0xC0: 52 | w, value = 2, rune(octet&0x1F) 53 | case octet&0xF0 == 0xE0: 54 | w, value = 3, rune(octet&0x0F) 55 | case octet&0xF8 == 0xF0: 56 | w, value = 4, rune(octet&0x07) 57 | } 58 | for k := 1; k < w; k++ { 59 | octet = emitter.buffer[pos+k] 60 | value = (value << 6) + (rune(octet) & 0x3F) 61 | } 62 | pos += w 63 | 64 | // Write the character. 65 | if value < 0x10000 { 66 | var b [2]byte 67 | b[high] = byte(value >> 8) 68 | b[low] = byte(value & 0xFF) 69 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1]) 70 | } else { 71 | // Write the character using a surrogate pair (check "reader.c"). 72 | var b [4]byte 73 | value -= 0x10000 74 | b[high] = byte(0xD8 + (value >> 18)) 75 | b[low] = byte((value >> 10) & 0xFF) 76 | b[high+2] = byte(0xDC + ((value >> 8) & 0xFF)) 77 | b[low+2] = byte(value & 0xFF) 78 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3]) 79 | } 80 | } 81 | 82 | // Write the raw buffer. 83 | if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil { 84 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 85 | } 86 | emitter.buffer_pos = 0 87 | emitter.raw_buffer = emitter.raw_buffer[:0] 88 | return true 89 | } 90 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package context stores values shared during a request lifetime. 7 | 8 | For example, a router can set variables extracted from the URL and later 9 | application handlers can access those values, or it can be used to store 10 | sessions values to be saved at the end of a request. There are several 11 | others common uses. 12 | 13 | The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: 14 | 15 | http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53 16 | 17 | Here's the basic usage: first define the keys that you will need. The key 18 | type is interface{} so a key can be of any type that supports equality. 19 | Here we define a key using a custom int type to avoid name collisions: 20 | 21 | package foo 22 | 23 | import ( 24 | "github.com/gorilla/context" 25 | ) 26 | 27 | type key int 28 | 29 | const MyKey key = 0 30 | 31 | Then set a variable. Variables are bound to an http.Request object, so you 32 | need a request instance to set a value: 33 | 34 | context.Set(r, MyKey, "bar") 35 | 36 | The application can later access the variable using the same key you provided: 37 | 38 | func MyHandler(w http.ResponseWriter, r *http.Request) { 39 | // val is "bar". 40 | val := context.Get(r, foo.MyKey) 41 | 42 | // returns ("bar", true) 43 | val, ok := context.GetOk(r, foo.MyKey) 44 | // ... 45 | } 46 | 47 | And that's all about the basic usage. We discuss some other ideas below. 48 | 49 | Any type can be stored in the context. To enforce a given type, make the key 50 | private and wrap Get() and Set() to accept and return values of a specific 51 | type: 52 | 53 | type key int 54 | 55 | const mykey key = 0 56 | 57 | // GetMyKey returns a value for this package from the request values. 58 | func GetMyKey(r *http.Request) SomeType { 59 | if rv := context.Get(r, mykey); rv != nil { 60 | return rv.(SomeType) 61 | } 62 | return nil 63 | } 64 | 65 | // SetMyKey sets a value for this package in the request values. 66 | func SetMyKey(r *http.Request, val SomeType) { 67 | context.Set(r, mykey, val) 68 | } 69 | 70 | Variables must be cleared at the end of a request, to remove all values 71 | that were stored. This can be done in an http.Handler, after a request was 72 | served. Just call Clear() passing the request: 73 | 74 | context.Clear(r) 75 | 76 | ...or use ClearHandler(), which conveniently wraps an http.Handler to clear 77 | variables at the end of a request lifetime. 78 | 79 | The Routers from the packages gorilla/mux and gorilla/pat call Clear() 80 | so if you are using either of them you don't need to clear the context manually. 81 | */ 82 | package context 83 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/sorter.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "reflect" 5 | "unicode" 6 | ) 7 | 8 | type keyList []reflect.Value 9 | 10 | func (l keyList) Len() int { return len(l) } 11 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 12 | func (l keyList) Less(i, j int) bool { 13 | a := l[i] 14 | b := l[j] 15 | ak := a.Kind() 16 | bk := b.Kind() 17 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 18 | a = a.Elem() 19 | ak = a.Kind() 20 | } 21 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 22 | b = b.Elem() 23 | bk = b.Kind() 24 | } 25 | af, aok := keyFloat(a) 26 | bf, bok := keyFloat(b) 27 | if aok && bok { 28 | if af != bf { 29 | return af < bf 30 | } 31 | if ak != bk { 32 | return ak < bk 33 | } 34 | return numLess(a, b) 35 | } 36 | if ak != reflect.String || bk != reflect.String { 37 | return ak < bk 38 | } 39 | ar, br := []rune(a.String()), []rune(b.String()) 40 | for i := 0; i < len(ar) && i < len(br); i++ { 41 | if ar[i] == br[i] { 42 | continue 43 | } 44 | al := unicode.IsLetter(ar[i]) 45 | bl := unicode.IsLetter(br[i]) 46 | if al && bl { 47 | return ar[i] < br[i] 48 | } 49 | if al || bl { 50 | return bl 51 | } 52 | var ai, bi int 53 | var an, bn int64 54 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 55 | an = an*10 + int64(ar[ai]-'0') 56 | } 57 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 58 | bn = bn*10 + int64(br[bi]-'0') 59 | } 60 | if an != bn { 61 | return an < bn 62 | } 63 | if ai != bi { 64 | return ai < bi 65 | } 66 | return ar[i] < br[i] 67 | } 68 | return len(ar) < len(br) 69 | } 70 | 71 | // keyFloat returns a float value for v if it is a number/bool 72 | // and whether it is a number/bool or not. 73 | func keyFloat(v reflect.Value) (f float64, ok bool) { 74 | switch v.Kind() { 75 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 76 | return float64(v.Int()), true 77 | case reflect.Float32, reflect.Float64: 78 | return v.Float(), true 79 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 80 | return float64(v.Uint()), true 81 | case reflect.Bool: 82 | if v.Bool() { 83 | return 1, true 84 | } 85 | return 0, true 86 | } 87 | return 0, false 88 | } 89 | 90 | // numLess returns whether a < b. 91 | // a and b must necessarily have the same kind. 92 | func numLess(a, b reflect.Value) bool { 93 | switch a.Kind() { 94 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 95 | return a.Int() < b.Int() 96 | case reflect.Float32, reflect.Float64: 97 | return a.Float() < b.Float() 98 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 99 | return a.Uint() < b.Uint() 100 | case reflect.Bool: 101 | return !a.Bool() && b.Bool() 102 | } 103 | panic("not a number") 104 | } 105 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/queue.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package mgo 28 | 29 | type queue struct { 30 | elems []interface{} 31 | nelems, popi, pushi int 32 | } 33 | 34 | func (q *queue) Len() int { 35 | return q.nelems 36 | } 37 | 38 | func (q *queue) Push(elem interface{}) { 39 | //debugf("Pushing(pushi=%d popi=%d cap=%d): %#v\n", 40 | // q.pushi, q.popi, len(q.elems), elem) 41 | if q.nelems == len(q.elems) { 42 | q.expand() 43 | } 44 | q.elems[q.pushi] = elem 45 | q.nelems++ 46 | q.pushi = (q.pushi + 1) % len(q.elems) 47 | //debugf(" Pushed(pushi=%d popi=%d cap=%d): %#v\n", 48 | // q.pushi, q.popi, len(q.elems), elem) 49 | } 50 | 51 | func (q *queue) Pop() (elem interface{}) { 52 | //debugf("Popping(pushi=%d popi=%d cap=%d)\n", 53 | // q.pushi, q.popi, len(q.elems)) 54 | if q.nelems == 0 { 55 | return nil 56 | } 57 | elem = q.elems[q.popi] 58 | q.elems[q.popi] = nil // Help GC. 59 | q.nelems-- 60 | q.popi = (q.popi + 1) % len(q.elems) 61 | //debugf(" Popped(pushi=%d popi=%d cap=%d): %#v\n", 62 | // q.pushi, q.popi, len(q.elems), elem) 63 | return elem 64 | } 65 | 66 | func (q *queue) expand() { 67 | curcap := len(q.elems) 68 | var newcap int 69 | if curcap == 0 { 70 | newcap = 8 71 | } else if curcap < 1024 { 72 | newcap = curcap * 2 73 | } else { 74 | newcap = curcap + (curcap / 4) 75 | } 76 | elems := make([]interface{}, newcap) 77 | 78 | if q.popi == 0 { 79 | copy(elems, q.elems) 80 | q.pushi = curcap 81 | } else { 82 | newpopi := newcap - (curcap - q.popi) 83 | copy(elems, q.elems[:q.popi]) 84 | copy(elems[newpopi:], q.elems[q.popi:]) 85 | q.popi = newpopi 86 | } 87 | for i := range q.elems { 88 | q.elems[i] = nil // Help GC. 89 | } 90 | q.elems = elems 91 | } 92 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/json/extension.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // Extension holds a set of additional rules to be used when unmarshaling 8 | // strict JSON or JSON-like content. 9 | type Extension struct { 10 | funcs map[string]funcExt 11 | consts map[string]interface{} 12 | keyed map[string]func([]byte) (interface{}, error) 13 | encode map[reflect.Type]func(v interface{}) ([]byte, error) 14 | 15 | unquotedKeys bool 16 | trailingCommas bool 17 | } 18 | 19 | type funcExt struct { 20 | key string 21 | args []string 22 | } 23 | 24 | // Extend changes the decoder behavior to consider the provided extension. 25 | func (dec *Decoder) Extend(ext *Extension) { dec.d.ext = *ext } 26 | 27 | // Extend changes the encoder behavior to consider the provided extension. 28 | func (enc *Encoder) Extend(ext *Extension) { enc.ext = *ext } 29 | 30 | // Extend includes in e the extensions defined in ext. 31 | func (e *Extension) Extend(ext *Extension) { 32 | for name, fext := range ext.funcs { 33 | e.DecodeFunc(name, fext.key, fext.args...) 34 | } 35 | for name, value := range ext.consts { 36 | e.DecodeConst(name, value) 37 | } 38 | for key, decode := range ext.keyed { 39 | e.DecodeKeyed(key, decode) 40 | } 41 | for typ, encode := range ext.encode { 42 | if e.encode == nil { 43 | e.encode = make(map[reflect.Type]func(v interface{}) ([]byte, error)) 44 | } 45 | e.encode[typ] = encode 46 | } 47 | } 48 | 49 | // DecodeFunc defines a function call that may be observed inside JSON content. 50 | // A function with the provided name will be unmarshaled as the document 51 | // {key: {args[0]: ..., args[N]: ...}}. 52 | func (e *Extension) DecodeFunc(name string, key string, args ...string) { 53 | if e.funcs == nil { 54 | e.funcs = make(map[string]funcExt) 55 | } 56 | e.funcs[name] = funcExt{key, args} 57 | } 58 | 59 | // DecodeConst defines a constant name that may be observed inside JSON content 60 | // and will be decoded with the provided value. 61 | func (e *Extension) DecodeConst(name string, value interface{}) { 62 | if e.consts == nil { 63 | e.consts = make(map[string]interface{}) 64 | } 65 | e.consts[name] = value 66 | } 67 | 68 | // DecodeKeyed defines a key that when observed as the first element inside a 69 | // JSON document triggers the decoding of that document via the provided 70 | // decode function. 71 | func (e *Extension) DecodeKeyed(key string, decode func(data []byte) (interface{}, error)) { 72 | if e.keyed == nil { 73 | e.keyed = make(map[string]func([]byte) (interface{}, error)) 74 | } 75 | e.keyed[key] = decode 76 | } 77 | 78 | // DecodeUnquotedKeys defines whether to accept map keys that are unquoted strings. 79 | func (e *Extension) DecodeUnquotedKeys(accept bool) { 80 | e.unquotedKeys = accept 81 | } 82 | 83 | // DecodeTrailingCommas defines whether to accept trailing commas in maps and arrays. 84 | func (e *Extension) DecodeTrailingCommas(accept bool) { 85 | e.trailingCommas = accept 86 | } 87 | 88 | // EncodeType registers a function to encode values with the same type of the 89 | // provided sample. 90 | func (e *Extension) EncodeType(sample interface{}, encode func(v interface{}) ([]byte, error)) { 91 | if e.encode == nil { 92 | e.encode = make(map[reflect.Type]func(v interface{}) ([]byte, error)) 93 | } 94 | e.encode[reflect.TypeOf(sample)] = encode 95 | } 96 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/README.md: -------------------------------------------------------------------------------- 1 | # YAML support for the Go language 2 | 3 | Introduction 4 | ------------ 5 | 6 | The yaml package enables Go programs to comfortably encode and decode YAML 7 | values. It was developed within [Canonical](https://www.canonical.com) as 8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 10 | C library to parse and generate YAML data quickly and reliably. 11 | 12 | Compatibility 13 | ------------- 14 | 15 | The yaml package supports most of YAML 1.1 and 1.2, including support for 16 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 17 | implemented, and base-60 floats from YAML 1.1 are purposefully not 18 | supported since they're a poor design and are gone in YAML 1.2. 19 | 20 | Installation and usage 21 | ---------------------- 22 | 23 | The import path for the package is *gopkg.in/yaml.v2*. 24 | 25 | To install it, run: 26 | 27 | go get gopkg.in/yaml.v2 28 | 29 | API documentation 30 | ----------------- 31 | 32 | If opened in a browser, the import path itself leads to the API documentation: 33 | 34 | * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) 35 | 36 | API stability 37 | ------------- 38 | 39 | The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). 40 | 41 | 42 | License 43 | ------- 44 | 45 | The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details. 46 | 47 | 48 | Example 49 | ------- 50 | 51 | ```Go 52 | package main 53 | 54 | import ( 55 | "fmt" 56 | "log" 57 | 58 | "gopkg.in/yaml.v2" 59 | ) 60 | 61 | var data = ` 62 | a: Easy! 63 | b: 64 | c: 2 65 | d: [3, 4] 66 | ` 67 | 68 | type T struct { 69 | A string 70 | B struct { 71 | RenamedC int `yaml:"c"` 72 | D []int `yaml:",flow"` 73 | } 74 | } 75 | 76 | func main() { 77 | t := T{} 78 | 79 | err := yaml.Unmarshal([]byte(data), &t) 80 | if err != nil { 81 | log.Fatalf("error: %v", err) 82 | } 83 | fmt.Printf("--- t:\n%v\n\n", t) 84 | 85 | d, err := yaml.Marshal(&t) 86 | if err != nil { 87 | log.Fatalf("error: %v", err) 88 | } 89 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 90 | 91 | m := make(map[interface{}]interface{}) 92 | 93 | err = yaml.Unmarshal([]byte(data), &m) 94 | if err != nil { 95 | log.Fatalf("error: %v", err) 96 | } 97 | fmt.Printf("--- m:\n%v\n\n", m) 98 | 99 | d, err = yaml.Marshal(&m) 100 | if err != nil { 101 | log.Fatalf("error: %v", err) 102 | } 103 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 104 | } 105 | ``` 106 | 107 | This example will generate the following output: 108 | 109 | ``` 110 | --- t: 111 | {Easy! {2 [3 4]}} 112 | 113 | --- t dump: 114 | a: Easy! 115 | b: 116 | c: 2 117 | d: [3, 4] 118 | 119 | 120 | --- m: 121 | map[a:Easy! b:map[c:2 d:[3 4]]] 122 | 123 | --- m dump: 124 | a: Easy! 125 | b: 126 | c: 2 127 | d: 128 | - 3 129 | - 4 130 | ``` 131 | 132 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/sasl/sspi_windows.h: -------------------------------------------------------------------------------- 1 | // Code adapted from the NodeJS kerberos library: 2 | // 3 | // https://github.com/christkv/kerberos/tree/master/lib/win32/kerberos_sspi.h 4 | // 5 | // Under the terms of the Apache License, Version 2.0: 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | #ifndef SSPI_WINDOWS_H 10 | #define SSPI_WINDOWS_H 11 | 12 | #define SECURITY_WIN32 1 13 | 14 | #include 15 | #include 16 | 17 | int load_secur32_dll(); 18 | 19 | SECURITY_STATUS SEC_ENTRY call_sspi_encrypt_message(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo); 20 | 21 | typedef DWORD (WINAPI *encryptMessage_fn)(PCtxtHandle phContext, ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo); 22 | 23 | SECURITY_STATUS SEC_ENTRY call_sspi_acquire_credentials_handle( 24 | LPSTR pszPrincipal, // Name of principal 25 | LPSTR pszPackage, // Name of package 26 | unsigned long fCredentialUse, // Flags indicating use 27 | void *pvLogonId, // Pointer to logon ID 28 | void *pAuthData, // Package specific data 29 | SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func 30 | void *pvGetKeyArgument, // Value to pass to GetKey() 31 | PCredHandle phCredential, // (out) Cred Handle 32 | PTimeStamp ptsExpiry // (out) Lifetime (optional) 33 | ); 34 | 35 | typedef DWORD (WINAPI *acquireCredentialsHandle_fn)( 36 | LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse, 37 | void *pvLogonId, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument, 38 | PCredHandle phCredential, PTimeStamp ptsExpiry 39 | ); 40 | 41 | SECURITY_STATUS SEC_ENTRY call_sspi_initialize_security_context( 42 | PCredHandle phCredential, // Cred to base context 43 | PCtxtHandle phContext, // Existing context (OPT) 44 | LPSTR pszTargetName, // Name of target 45 | unsigned long fContextReq, // Context Requirements 46 | unsigned long Reserved1, // Reserved, MBZ 47 | unsigned long TargetDataRep, // Data rep of target 48 | PSecBufferDesc pInput, // Input Buffers 49 | unsigned long Reserved2, // Reserved, MBZ 50 | PCtxtHandle phNewContext, // (out) New Context handle 51 | PSecBufferDesc pOutput, // (inout) Output Buffers 52 | unsigned long *pfContextAttr, // (out) Context attrs 53 | PTimeStamp ptsExpiry // (out) Life span (OPT) 54 | ); 55 | 56 | typedef DWORD (WINAPI *initializeSecurityContext_fn)( 57 | PCredHandle phCredential, PCtxtHandle phContext, LPSTR pszTargetName, unsigned long fContextReq, 58 | unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput, unsigned long Reserved2, 59 | PCtxtHandle phNewContext, PSecBufferDesc pOutput, unsigned long *pfContextAttr, PTimeStamp ptsExpiry); 60 | 61 | SECURITY_STATUS SEC_ENTRY call_sspi_query_context_attributes( 62 | PCtxtHandle phContext, // Context to query 63 | unsigned long ulAttribute, // Attribute to query 64 | void *pBuffer // Buffer for attributes 65 | ); 66 | 67 | typedef DWORD (WINAPI *queryContextAttributes_fn)( 68 | PCtxtHandle phContext, unsigned long ulAttribute, void *pBuffer); 69 | 70 | #endif // SSPI_WINDOWS_H 71 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/sasl/sspi_windows.c: -------------------------------------------------------------------------------- 1 | // Code adapted from the NodeJS kerberos library: 2 | // 3 | // https://github.com/christkv/kerberos/tree/master/lib/win32/kerberos_sspi.c 4 | // 5 | // Under the terms of the Apache License, Version 2.0: 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | #include 10 | 11 | #include "sspi_windows.h" 12 | 13 | static HINSTANCE sspi_secur32_dll = NULL; 14 | 15 | int load_secur32_dll() 16 | { 17 | sspi_secur32_dll = LoadLibrary("secur32.dll"); 18 | if (sspi_secur32_dll == NULL) { 19 | return GetLastError(); 20 | } 21 | return 0; 22 | } 23 | 24 | SECURITY_STATUS SEC_ENTRY call_sspi_encrypt_message(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo) 25 | { 26 | if (sspi_secur32_dll == NULL) { 27 | return -1; 28 | } 29 | encryptMessage_fn pfn_encryptMessage = (encryptMessage_fn) GetProcAddress(sspi_secur32_dll, "EncryptMessage"); 30 | if (!pfn_encryptMessage) { 31 | return -2; 32 | } 33 | return (*pfn_encryptMessage)(phContext, fQOP, pMessage, MessageSeqNo); 34 | } 35 | 36 | SECURITY_STATUS SEC_ENTRY call_sspi_acquire_credentials_handle( 37 | LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse, 38 | void *pvLogonId, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument, 39 | PCredHandle phCredential, PTimeStamp ptsExpiry) 40 | { 41 | if (sspi_secur32_dll == NULL) { 42 | return -1; 43 | } 44 | acquireCredentialsHandle_fn pfn_acquireCredentialsHandle; 45 | #ifdef _UNICODE 46 | pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn) GetProcAddress(sspi_secur32_dll, "AcquireCredentialsHandleW"); 47 | #else 48 | pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn) GetProcAddress(sspi_secur32_dll, "AcquireCredentialsHandleA"); 49 | #endif 50 | if (!pfn_acquireCredentialsHandle) { 51 | return -2; 52 | } 53 | return (*pfn_acquireCredentialsHandle)( 54 | pszPrincipal, pszPackage, fCredentialUse, pvLogonId, pAuthData, 55 | pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry); 56 | } 57 | 58 | SECURITY_STATUS SEC_ENTRY call_sspi_initialize_security_context( 59 | PCredHandle phCredential, PCtxtHandle phContext, LPSTR pszTargetName, 60 | unsigned long fContextReq, unsigned long Reserved1, unsigned long TargetDataRep, 61 | PSecBufferDesc pInput, unsigned long Reserved2, PCtxtHandle phNewContext, 62 | PSecBufferDesc pOutput, unsigned long *pfContextAttr, PTimeStamp ptsExpiry) 63 | { 64 | if (sspi_secur32_dll == NULL) { 65 | return -1; 66 | } 67 | initializeSecurityContext_fn pfn_initializeSecurityContext; 68 | #ifdef _UNICODE 69 | pfn_initializeSecurityContext = (initializeSecurityContext_fn) GetProcAddress(sspi_secur32_dll, "InitializeSecurityContextW"); 70 | #else 71 | pfn_initializeSecurityContext = (initializeSecurityContext_fn) GetProcAddress(sspi_secur32_dll, "InitializeSecurityContextA"); 72 | #endif 73 | if (!pfn_initializeSecurityContext) { 74 | return -2; 75 | } 76 | return (*pfn_initializeSecurityContext)( 77 | phCredential, phContext, pszTargetName, fContextReq, Reserved1, TargetDataRep, 78 | pInput, Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry); 79 | } 80 | 81 | SECURITY_STATUS SEC_ENTRY call_sspi_query_context_attributes(PCtxtHandle phContext, unsigned long ulAttribute, void *pBuffer) 82 | { 83 | if (sspi_secur32_dll == NULL) { 84 | return -1; 85 | } 86 | queryContextAttributes_fn pfn_queryContextAttributes; 87 | #ifdef _UNICODE 88 | pfn_queryContextAttributes = (queryContextAttributes_fn) GetProcAddress(sspi_secur32_dll, "QueryContextAttributesW"); 89 | #else 90 | pfn_queryContextAttributes = (queryContextAttributes_fn) GetProcAddress(sspi_secur32_dll, "QueryContextAttributesA"); 91 | #endif 92 | if (!pfn_queryContextAttributes) { 93 | return -2; 94 | } 95 | return (*pfn_queryContextAttributes)(phContext, ulAttribute, pBuffer); 96 | } 97 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/json/indent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import "bytes" 8 | 9 | // Compact appends to dst the JSON-encoded src with 10 | // insignificant space characters elided. 11 | func Compact(dst *bytes.Buffer, src []byte) error { 12 | return compact(dst, src, false) 13 | } 14 | 15 | func compact(dst *bytes.Buffer, src []byte, escape bool) error { 16 | origLen := dst.Len() 17 | var scan scanner 18 | scan.reset() 19 | start := 0 20 | for i, c := range src { 21 | if escape && (c == '<' || c == '>' || c == '&') { 22 | if start < i { 23 | dst.Write(src[start:i]) 24 | } 25 | dst.WriteString(`\u00`) 26 | dst.WriteByte(hex[c>>4]) 27 | dst.WriteByte(hex[c&0xF]) 28 | start = i + 1 29 | } 30 | // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9). 31 | if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 { 32 | if start < i { 33 | dst.Write(src[start:i]) 34 | } 35 | dst.WriteString(`\u202`) 36 | dst.WriteByte(hex[src[i+2]&0xF]) 37 | start = i + 3 38 | } 39 | v := scan.step(&scan, c) 40 | if v >= scanSkipSpace { 41 | if v == scanError { 42 | break 43 | } 44 | if start < i { 45 | dst.Write(src[start:i]) 46 | } 47 | start = i + 1 48 | } 49 | } 50 | if scan.eof() == scanError { 51 | dst.Truncate(origLen) 52 | return scan.err 53 | } 54 | if start < len(src) { 55 | dst.Write(src[start:]) 56 | } 57 | return nil 58 | } 59 | 60 | func newline(dst *bytes.Buffer, prefix, indent string, depth int) { 61 | dst.WriteByte('\n') 62 | dst.WriteString(prefix) 63 | for i := 0; i < depth; i++ { 64 | dst.WriteString(indent) 65 | } 66 | } 67 | 68 | // Indent appends to dst an indented form of the JSON-encoded src. 69 | // Each element in a JSON object or array begins on a new, 70 | // indented line beginning with prefix followed by one or more 71 | // copies of indent according to the indentation nesting. 72 | // The data appended to dst does not begin with the prefix nor 73 | // any indentation, to make it easier to embed inside other formatted JSON data. 74 | // Although leading space characters (space, tab, carriage return, newline) 75 | // at the beginning of src are dropped, trailing space characters 76 | // at the end of src are preserved and copied to dst. 77 | // For example, if src has no trailing spaces, neither will dst; 78 | // if src ends in a trailing newline, so will dst. 79 | func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { 80 | origLen := dst.Len() 81 | var scan scanner 82 | scan.reset() 83 | needIndent := false 84 | depth := 0 85 | for _, c := range src { 86 | scan.bytes++ 87 | v := scan.step(&scan, c) 88 | if v == scanSkipSpace { 89 | continue 90 | } 91 | if v == scanError { 92 | break 93 | } 94 | if needIndent && v != scanEndObject && v != scanEndArray { 95 | needIndent = false 96 | depth++ 97 | newline(dst, prefix, indent, depth) 98 | } 99 | 100 | // Emit semantically uninteresting bytes 101 | // (in particular, punctuation in strings) unmodified. 102 | if v == scanContinue { 103 | dst.WriteByte(c) 104 | continue 105 | } 106 | 107 | // Add spacing around real punctuation. 108 | switch c { 109 | case '{', '[': 110 | // delay indent so that empty object and array are formatted as {} and []. 111 | needIndent = true 112 | dst.WriteByte(c) 113 | 114 | case ',': 115 | dst.WriteByte(c) 116 | newline(dst, prefix, indent, depth) 117 | 118 | case ':': 119 | dst.WriteByte(c) 120 | dst.WriteByte(' ') 121 | 122 | case '}', ']': 123 | if needIndent { 124 | // suppress indent in empty object/array 125 | needIndent = false 126 | } else { 127 | depth-- 128 | newline(dst, prefix, indent, depth) 129 | } 130 | dst.WriteByte(c) 131 | 132 | default: 133 | dst.WriteByte(c) 134 | } 135 | } 136 | if scan.eof() == scanError { 137 | dst.Truncate(origLen) 138 | return scan.err 139 | } 140 | return nil 141 | } 142 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/sasl/sasl.go: -------------------------------------------------------------------------------- 1 | // Package sasl is an implementation detail of the mgo package. 2 | // 3 | // This package is not meant to be used by itself. 4 | // 5 | 6 | // +build !windows 7 | 8 | package sasl 9 | 10 | // #cgo LDFLAGS: -lsasl2 11 | // 12 | // struct sasl_conn {}; 13 | // 14 | // #include 15 | // #include 16 | // 17 | // sasl_callback_t *mgo_sasl_callbacks(const char *username, const char *password); 18 | // 19 | import "C" 20 | 21 | import ( 22 | "fmt" 23 | "strings" 24 | "sync" 25 | "unsafe" 26 | ) 27 | 28 | type saslStepper interface { 29 | Step(serverData []byte) (clientData []byte, done bool, err error) 30 | Close() 31 | } 32 | 33 | type saslSession struct { 34 | conn *C.sasl_conn_t 35 | step int 36 | mech string 37 | 38 | cstrings []*C.char 39 | callbacks *C.sasl_callback_t 40 | } 41 | 42 | var initError error 43 | var initOnce sync.Once 44 | 45 | func initSASL() { 46 | rc := C.sasl_client_init(nil) 47 | if rc != C.SASL_OK { 48 | initError = saslError(rc, nil, "cannot initialize SASL library") 49 | } 50 | } 51 | 52 | func New(username, password, mechanism, service, host string) (saslStepper, error) { 53 | initOnce.Do(initSASL) 54 | if initError != nil { 55 | return nil, initError 56 | } 57 | 58 | ss := &saslSession{mech: mechanism} 59 | if service == "" { 60 | service = "mongodb" 61 | } 62 | if i := strings.Index(host, ":"); i >= 0 { 63 | host = host[:i] 64 | } 65 | ss.callbacks = C.mgo_sasl_callbacks(ss.cstr(username), ss.cstr(password)) 66 | rc := C.sasl_client_new(ss.cstr(service), ss.cstr(host), nil, nil, ss.callbacks, 0, &ss.conn) 67 | if rc != C.SASL_OK { 68 | ss.Close() 69 | return nil, saslError(rc, nil, "cannot create new SASL client") 70 | } 71 | return ss, nil 72 | } 73 | 74 | func (ss *saslSession) cstr(s string) *C.char { 75 | cstr := C.CString(s) 76 | ss.cstrings = append(ss.cstrings, cstr) 77 | return cstr 78 | } 79 | 80 | func (ss *saslSession) Close() { 81 | for _, cstr := range ss.cstrings { 82 | C.free(unsafe.Pointer(cstr)) 83 | } 84 | ss.cstrings = nil 85 | 86 | if ss.callbacks != nil { 87 | C.free(unsafe.Pointer(ss.callbacks)) 88 | } 89 | 90 | // The documentation of SASL dispose makes it clear that this should only 91 | // be done when the connection is done, not when the authentication phase 92 | // is done, because an encryption layer may have been negotiated. 93 | // Even then, we'll do this for now, because it's simpler and prevents 94 | // keeping track of this state for every socket. If it breaks, we'll fix it. 95 | C.sasl_dispose(&ss.conn) 96 | } 97 | 98 | func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) { 99 | ss.step++ 100 | if ss.step > 10 { 101 | return nil, false, fmt.Errorf("too many SASL steps without authentication") 102 | } 103 | var cclientData *C.char 104 | var cclientDataLen C.uint 105 | var rc C.int 106 | if ss.step == 1 { 107 | var mechanism *C.char // ignored - must match cred 108 | rc = C.sasl_client_start(ss.conn, ss.cstr(ss.mech), nil, &cclientData, &cclientDataLen, &mechanism) 109 | } else { 110 | var cserverData *C.char 111 | var cserverDataLen C.uint 112 | if len(serverData) > 0 { 113 | cserverData = (*C.char)(unsafe.Pointer(&serverData[0])) 114 | cserverDataLen = C.uint(len(serverData)) 115 | } 116 | rc = C.sasl_client_step(ss.conn, cserverData, cserverDataLen, nil, &cclientData, &cclientDataLen) 117 | } 118 | if cclientData != nil && cclientDataLen > 0 { 119 | clientData = C.GoBytes(unsafe.Pointer(cclientData), C.int(cclientDataLen)) 120 | } 121 | if rc == C.SASL_OK { 122 | return clientData, true, nil 123 | } 124 | if rc == C.SASL_CONTINUE { 125 | return clientData, false, nil 126 | } 127 | return nil, false, saslError(rc, ss.conn, "cannot establish SASL session") 128 | } 129 | 130 | func saslError(rc C.int, conn *C.sasl_conn_t, msg string) error { 131 | var detail string 132 | if conn == nil { 133 | detail = C.GoString(C.sasl_errstring(rc, nil, nil)) 134 | } else { 135 | detail = C.GoString(C.sasl_errdetail(conn)) 136 | } 137 | return fmt.Errorf(msg + ": " + detail) 138 | } 139 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/stats.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package mgo 28 | 29 | import ( 30 | "sync" 31 | ) 32 | 33 | var stats *Stats 34 | var statsMutex sync.Mutex 35 | 36 | func SetStats(enabled bool) { 37 | statsMutex.Lock() 38 | if enabled { 39 | if stats == nil { 40 | stats = &Stats{} 41 | } 42 | } else { 43 | stats = nil 44 | } 45 | statsMutex.Unlock() 46 | } 47 | 48 | func GetStats() (snapshot Stats) { 49 | statsMutex.Lock() 50 | snapshot = *stats 51 | statsMutex.Unlock() 52 | return 53 | } 54 | 55 | func ResetStats() { 56 | statsMutex.Lock() 57 | debug("Resetting stats") 58 | old := stats 59 | stats = &Stats{} 60 | // These are absolute values: 61 | stats.Clusters = old.Clusters 62 | stats.SocketsInUse = old.SocketsInUse 63 | stats.SocketsAlive = old.SocketsAlive 64 | stats.SocketRefs = old.SocketRefs 65 | statsMutex.Unlock() 66 | return 67 | } 68 | 69 | type Stats struct { 70 | Clusters int 71 | MasterConns int 72 | SlaveConns int 73 | SentOps int 74 | ReceivedOps int 75 | ReceivedDocs int 76 | SocketsAlive int 77 | SocketsInUse int 78 | SocketRefs int 79 | } 80 | 81 | func (stats *Stats) cluster(delta int) { 82 | if stats != nil { 83 | statsMutex.Lock() 84 | stats.Clusters += delta 85 | statsMutex.Unlock() 86 | } 87 | } 88 | 89 | func (stats *Stats) conn(delta int, master bool) { 90 | if stats != nil { 91 | statsMutex.Lock() 92 | if master { 93 | stats.MasterConns += delta 94 | } else { 95 | stats.SlaveConns += delta 96 | } 97 | statsMutex.Unlock() 98 | } 99 | } 100 | 101 | func (stats *Stats) sentOps(delta int) { 102 | if stats != nil { 103 | statsMutex.Lock() 104 | stats.SentOps += delta 105 | statsMutex.Unlock() 106 | } 107 | } 108 | 109 | func (stats *Stats) receivedOps(delta int) { 110 | if stats != nil { 111 | statsMutex.Lock() 112 | stats.ReceivedOps += delta 113 | statsMutex.Unlock() 114 | } 115 | } 116 | 117 | func (stats *Stats) receivedDocs(delta int) { 118 | if stats != nil { 119 | statsMutex.Lock() 120 | stats.ReceivedDocs += delta 121 | statsMutex.Unlock() 122 | } 123 | } 124 | 125 | func (stats *Stats) socketsInUse(delta int) { 126 | if stats != nil { 127 | statsMutex.Lock() 128 | stats.SocketsInUse += delta 129 | statsMutex.Unlock() 130 | } 131 | } 132 | 133 | func (stats *Stats) socketsAlive(delta int) { 134 | if stats != nil { 135 | statsMutex.Lock() 136 | stats.SocketsAlive += delta 137 | statsMutex.Unlock() 138 | } 139 | } 140 | 141 | func (stats *Stats) socketRefs(delta int) { 142 | if stats != nil { 143 | statsMutex.Lock() 144 | stats.SocketRefs += delta 145 | statsMutex.Unlock() 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/json/fold.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "unicode/utf8" 10 | ) 11 | 12 | const ( 13 | caseMask = ^byte(0x20) // Mask to ignore case in ASCII. 14 | kelvin = '\u212a' 15 | smallLongEss = '\u017f' 16 | ) 17 | 18 | // foldFunc returns one of four different case folding equivalence 19 | // functions, from most general (and slow) to fastest: 20 | // 21 | // 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8 22 | // 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S') 23 | // 3) asciiEqualFold, no special, but includes non-letters (including _) 24 | // 4) simpleLetterEqualFold, no specials, no non-letters. 25 | // 26 | // The letters S and K are special because they map to 3 runes, not just 2: 27 | // * S maps to s and to U+017F 'ſ' Latin small letter long s 28 | // * k maps to K and to U+212A 'K' Kelvin sign 29 | // See https://play.golang.org/p/tTxjOc0OGo 30 | // 31 | // The returned function is specialized for matching against s and 32 | // should only be given s. It's not curried for performance reasons. 33 | func foldFunc(s []byte) func(s, t []byte) bool { 34 | nonLetter := false 35 | special := false // special letter 36 | for _, b := range s { 37 | if b >= utf8.RuneSelf { 38 | return bytes.EqualFold 39 | } 40 | upper := b & caseMask 41 | if upper < 'A' || upper > 'Z' { 42 | nonLetter = true 43 | } else if upper == 'K' || upper == 'S' { 44 | // See above for why these letters are special. 45 | special = true 46 | } 47 | } 48 | if special { 49 | return equalFoldRight 50 | } 51 | if nonLetter { 52 | return asciiEqualFold 53 | } 54 | return simpleLetterEqualFold 55 | } 56 | 57 | // equalFoldRight is a specialization of bytes.EqualFold when s is 58 | // known to be all ASCII (including punctuation), but contains an 's', 59 | // 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t. 60 | // See comments on foldFunc. 61 | func equalFoldRight(s, t []byte) bool { 62 | for _, sb := range s { 63 | if len(t) == 0 { 64 | return false 65 | } 66 | tb := t[0] 67 | if tb < utf8.RuneSelf { 68 | if sb != tb { 69 | sbUpper := sb & caseMask 70 | if 'A' <= sbUpper && sbUpper <= 'Z' { 71 | if sbUpper != tb&caseMask { 72 | return false 73 | } 74 | } else { 75 | return false 76 | } 77 | } 78 | t = t[1:] 79 | continue 80 | } 81 | // sb is ASCII and t is not. t must be either kelvin 82 | // sign or long s; sb must be s, S, k, or K. 83 | tr, size := utf8.DecodeRune(t) 84 | switch sb { 85 | case 's', 'S': 86 | if tr != smallLongEss { 87 | return false 88 | } 89 | case 'k', 'K': 90 | if tr != kelvin { 91 | return false 92 | } 93 | default: 94 | return false 95 | } 96 | t = t[size:] 97 | 98 | } 99 | if len(t) > 0 { 100 | return false 101 | } 102 | return true 103 | } 104 | 105 | // asciiEqualFold is a specialization of bytes.EqualFold for use when 106 | // s is all ASCII (but may contain non-letters) and contains no 107 | // special-folding letters. 108 | // See comments on foldFunc. 109 | func asciiEqualFold(s, t []byte) bool { 110 | if len(s) != len(t) { 111 | return false 112 | } 113 | for i, sb := range s { 114 | tb := t[i] 115 | if sb == tb { 116 | continue 117 | } 118 | if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') { 119 | if sb&caseMask != tb&caseMask { 120 | return false 121 | } 122 | } else { 123 | return false 124 | } 125 | } 126 | return true 127 | } 128 | 129 | // simpleLetterEqualFold is a specialization of bytes.EqualFold for 130 | // use when s is all ASCII letters (no underscores, etc) and also 131 | // doesn't contain 'k', 'K', 's', or 'S'. 132 | // See comments on foldFunc. 133 | func simpleLetterEqualFold(s, t []byte) bool { 134 | if len(s) != len(t) { 135 | return false 136 | } 137 | for i, b := range s { 138 | if b&caseMask != t[i]&caseMask { 139 | return false 140 | } 141 | } 142 | return true 143 | } 144 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/context/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package context 6 | 7 | import ( 8 | "net/http" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | var ( 14 | mutex sync.RWMutex 15 | data = make(map[*http.Request]map[interface{}]interface{}) 16 | datat = make(map[*http.Request]int64) 17 | ) 18 | 19 | // Set stores a value for a given key in a given request. 20 | func Set(r *http.Request, key, val interface{}) { 21 | mutex.Lock() 22 | if data[r] == nil { 23 | data[r] = make(map[interface{}]interface{}) 24 | datat[r] = time.Now().Unix() 25 | } 26 | data[r][key] = val 27 | mutex.Unlock() 28 | } 29 | 30 | // Get returns a value stored for a given key in a given request. 31 | func Get(r *http.Request, key interface{}) interface{} { 32 | mutex.RLock() 33 | if ctx := data[r]; ctx != nil { 34 | value := ctx[key] 35 | mutex.RUnlock() 36 | return value 37 | } 38 | mutex.RUnlock() 39 | return nil 40 | } 41 | 42 | // GetOk returns stored value and presence state like multi-value return of map access. 43 | func GetOk(r *http.Request, key interface{}) (interface{}, bool) { 44 | mutex.RLock() 45 | if _, ok := data[r]; ok { 46 | value, ok := data[r][key] 47 | mutex.RUnlock() 48 | return value, ok 49 | } 50 | mutex.RUnlock() 51 | return nil, false 52 | } 53 | 54 | // GetAll returns all stored values for the request as a map. Nil is returned for invalid requests. 55 | func GetAll(r *http.Request) map[interface{}]interface{} { 56 | mutex.RLock() 57 | if context, ok := data[r]; ok { 58 | result := make(map[interface{}]interface{}, len(context)) 59 | for k, v := range context { 60 | result[k] = v 61 | } 62 | mutex.RUnlock() 63 | return result 64 | } 65 | mutex.RUnlock() 66 | return nil 67 | } 68 | 69 | // GetAllOk returns all stored values for the request as a map and a boolean value that indicates if 70 | // the request was registered. 71 | func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { 72 | mutex.RLock() 73 | context, ok := data[r] 74 | result := make(map[interface{}]interface{}, len(context)) 75 | for k, v := range context { 76 | result[k] = v 77 | } 78 | mutex.RUnlock() 79 | return result, ok 80 | } 81 | 82 | // Delete removes a value stored for a given key in a given request. 83 | func Delete(r *http.Request, key interface{}) { 84 | mutex.Lock() 85 | if data[r] != nil { 86 | delete(data[r], key) 87 | } 88 | mutex.Unlock() 89 | } 90 | 91 | // Clear removes all values stored for a given request. 92 | // 93 | // This is usually called by a handler wrapper to clean up request 94 | // variables at the end of a request lifetime. See ClearHandler(). 95 | func Clear(r *http.Request) { 96 | mutex.Lock() 97 | clear(r) 98 | mutex.Unlock() 99 | } 100 | 101 | // clear is Clear without the lock. 102 | func clear(r *http.Request) { 103 | delete(data, r) 104 | delete(datat, r) 105 | } 106 | 107 | // Purge removes request data stored for longer than maxAge, in seconds. 108 | // It returns the amount of requests removed. 109 | // 110 | // If maxAge <= 0, all request data is removed. 111 | // 112 | // This is only used for sanity check: in case context cleaning was not 113 | // properly set some request data can be kept forever, consuming an increasing 114 | // amount of memory. In case this is detected, Purge() must be called 115 | // periodically until the problem is fixed. 116 | func Purge(maxAge int) int { 117 | mutex.Lock() 118 | count := 0 119 | if maxAge <= 0 { 120 | count = len(data) 121 | data = make(map[*http.Request]map[interface{}]interface{}) 122 | datat = make(map[*http.Request]int64) 123 | } else { 124 | min := time.Now().Unix() - int64(maxAge) 125 | for r := range data { 126 | if datat[r] < min { 127 | clear(r) 128 | count++ 129 | } 130 | } 131 | } 132 | mutex.Unlock() 133 | return count 134 | } 135 | 136 | // ClearHandler wraps an http.Handler and clears request values at the end 137 | // of a request lifetime. 138 | func ClearHandler(h http.Handler) http.Handler { 139 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 140 | defer Clear(r) 141 | h.ServeHTTP(w, r) 142 | }) 143 | } 144 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/sasl/sasl_windows.go: -------------------------------------------------------------------------------- 1 | package sasl 2 | 3 | // #include "sasl_windows.h" 4 | import "C" 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | "sync" 10 | "unsafe" 11 | ) 12 | 13 | type saslStepper interface { 14 | Step(serverData []byte) (clientData []byte, done bool, err error) 15 | Close() 16 | } 17 | 18 | type saslSession struct { 19 | // Credentials 20 | mech string 21 | service string 22 | host string 23 | userPlusRealm string 24 | target string 25 | domain string 26 | 27 | // Internal state 28 | authComplete bool 29 | errored bool 30 | step int 31 | 32 | // C internal state 33 | credHandle C.CredHandle 34 | context C.CtxtHandle 35 | hasContext C.int 36 | 37 | // Keep track of pointers we need to explicitly free 38 | stringsToFree []*C.char 39 | } 40 | 41 | var initError error 42 | var initOnce sync.Once 43 | 44 | func initSSPI() { 45 | rc := C.load_secur32_dll() 46 | if rc != 0 { 47 | initError = fmt.Errorf("Error loading libraries: %v", rc) 48 | } 49 | } 50 | 51 | func New(username, password, mechanism, service, host string) (saslStepper, error) { 52 | initOnce.Do(initSSPI) 53 | ss := &saslSession{mech: mechanism, hasContext: 0, userPlusRealm: username} 54 | if service == "" { 55 | service = "mongodb" 56 | } 57 | if i := strings.Index(host, ":"); i >= 0 { 58 | host = host[:i] 59 | } 60 | ss.service = service 61 | ss.host = host 62 | 63 | usernameComponents := strings.Split(username, "@") 64 | if len(usernameComponents) < 2 { 65 | return nil, fmt.Errorf("Username '%v' doesn't contain a realm!", username) 66 | } 67 | user := usernameComponents[0] 68 | ss.domain = usernameComponents[1] 69 | ss.target = fmt.Sprintf("%s/%s", ss.service, ss.host) 70 | 71 | var status C.SECURITY_STATUS 72 | // Step 0: call AcquireCredentialsHandle to get a nice SSPI CredHandle 73 | if len(password) > 0 { 74 | status = C.sspi_acquire_credentials_handle(&ss.credHandle, ss.cstr(user), ss.cstr(password), ss.cstr(ss.domain)) 75 | } else { 76 | status = C.sspi_acquire_credentials_handle(&ss.credHandle, ss.cstr(user), nil, ss.cstr(ss.domain)) 77 | } 78 | if status != C.SEC_E_OK { 79 | ss.errored = true 80 | return nil, fmt.Errorf("Couldn't create new SSPI client, error code %v", status) 81 | } 82 | return ss, nil 83 | } 84 | 85 | func (ss *saslSession) cstr(s string) *C.char { 86 | cstr := C.CString(s) 87 | ss.stringsToFree = append(ss.stringsToFree, cstr) 88 | return cstr 89 | } 90 | 91 | func (ss *saslSession) Close() { 92 | for _, cstr := range ss.stringsToFree { 93 | C.free(unsafe.Pointer(cstr)) 94 | } 95 | } 96 | 97 | func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) { 98 | ss.step++ 99 | if ss.step > 10 { 100 | return nil, false, fmt.Errorf("too many SSPI steps without authentication") 101 | } 102 | var buffer C.PVOID 103 | var bufferLength C.ULONG 104 | var outBuffer C.PVOID 105 | var outBufferLength C.ULONG 106 | if len(serverData) > 0 { 107 | buffer = (C.PVOID)(unsafe.Pointer(&serverData[0])) 108 | bufferLength = C.ULONG(len(serverData)) 109 | } 110 | var status C.int 111 | if ss.authComplete { 112 | // Step 3: last bit of magic to use the correct server credentials 113 | status = C.sspi_send_client_authz_id(&ss.context, &outBuffer, &outBufferLength, ss.cstr(ss.userPlusRealm)) 114 | } else { 115 | // Step 1 + Step 2: set up security context with the server and TGT 116 | status = C.sspi_step(&ss.credHandle, ss.hasContext, &ss.context, buffer, bufferLength, &outBuffer, &outBufferLength, ss.cstr(ss.target)) 117 | } 118 | if outBuffer != C.PVOID(nil) { 119 | defer C.free(unsafe.Pointer(outBuffer)) 120 | } 121 | if status != C.SEC_E_OK && status != C.SEC_I_CONTINUE_NEEDED { 122 | ss.errored = true 123 | return nil, false, ss.handleSSPIErrorCode(status) 124 | } 125 | 126 | clientData = C.GoBytes(unsafe.Pointer(outBuffer), C.int(outBufferLength)) 127 | if status == C.SEC_E_OK { 128 | ss.authComplete = true 129 | return clientData, true, nil 130 | } else { 131 | ss.hasContext = 1 132 | return clientData, false, nil 133 | } 134 | } 135 | 136 | func (ss *saslSession) handleSSPIErrorCode(code C.int) error { 137 | switch { 138 | case code == C.SEC_E_TARGET_UNKNOWN: 139 | return fmt.Errorf("Target %v@%v not found", ss.target, ss.domain) 140 | } 141 | return fmt.Errorf("Unknown error doing step %v, error code %v", ss.step, code) 142 | } 143 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/log.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package mgo 28 | 29 | import ( 30 | "fmt" 31 | "sync" 32 | ) 33 | 34 | // --------------------------------------------------------------------------- 35 | // Logging integration. 36 | 37 | // Avoid importing the log type information unnecessarily. There's a small cost 38 | // associated with using an interface rather than the type. Depending on how 39 | // often the logger is plugged in, it would be worth using the type instead. 40 | type log_Logger interface { 41 | Output(calldepth int, s string) error 42 | } 43 | 44 | var ( 45 | globalLogger log_Logger 46 | globalDebug bool 47 | globalMutex sync.Mutex 48 | ) 49 | 50 | // RACE WARNING: There are known data races when logging, which are manually 51 | // silenced when the race detector is in use. These data races won't be 52 | // observed in typical use, because logging is supposed to be set up once when 53 | // the application starts. Having raceDetector as a constant, the compiler 54 | // should elide the locks altogether in actual use. 55 | 56 | // Specify the *log.Logger object where log messages should be sent to. 57 | func SetLogger(logger log_Logger) { 58 | if raceDetector { 59 | globalMutex.Lock() 60 | defer globalMutex.Unlock() 61 | } 62 | globalLogger = logger 63 | } 64 | 65 | // Enable the delivery of debug messages to the logger. Only meaningful 66 | // if a logger is also set. 67 | func SetDebug(debug bool) { 68 | if raceDetector { 69 | globalMutex.Lock() 70 | defer globalMutex.Unlock() 71 | } 72 | globalDebug = debug 73 | } 74 | 75 | func log(v ...interface{}) { 76 | if raceDetector { 77 | globalMutex.Lock() 78 | defer globalMutex.Unlock() 79 | } 80 | if globalLogger != nil { 81 | globalLogger.Output(2, fmt.Sprint(v...)) 82 | } 83 | } 84 | 85 | func logln(v ...interface{}) { 86 | if raceDetector { 87 | globalMutex.Lock() 88 | defer globalMutex.Unlock() 89 | } 90 | if globalLogger != nil { 91 | globalLogger.Output(2, fmt.Sprintln(v...)) 92 | } 93 | } 94 | 95 | func logf(format string, v ...interface{}) { 96 | if raceDetector { 97 | globalMutex.Lock() 98 | defer globalMutex.Unlock() 99 | } 100 | if globalLogger != nil { 101 | globalLogger.Output(2, fmt.Sprintf(format, v...)) 102 | } 103 | } 104 | 105 | func debug(v ...interface{}) { 106 | if raceDetector { 107 | globalMutex.Lock() 108 | defer globalMutex.Unlock() 109 | } 110 | if globalDebug && globalLogger != nil { 111 | globalLogger.Output(2, fmt.Sprint(v...)) 112 | } 113 | } 114 | 115 | func debugln(v ...interface{}) { 116 | if raceDetector { 117 | globalMutex.Lock() 118 | defer globalMutex.Unlock() 119 | } 120 | if globalDebug && globalLogger != nil { 121 | globalLogger.Output(2, fmt.Sprintln(v...)) 122 | } 123 | } 124 | 125 | func debugf(format string, v ...interface{}) { 126 | if raceDetector { 127 | globalMutex.Lock() 128 | defer globalMutex.Unlock() 129 | } 130 | if globalDebug && globalLogger != nil { 131 | globalLogger.Output(2, fmt.Sprintf(format, v...)) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/config/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package config provides convenient access methods to configuration stored as 7 | JSON or YAML. 8 | 9 | Let's start with a simple YAML example: 10 | 11 | development: 12 | database: 13 | host: localhost 14 | users: 15 | - name: calvin 16 | password: yukon 17 | - name: hobbes 18 | password: tuna 19 | production: 20 | database: 21 | host: 192.168.1.1 22 | 23 | We can parse it using ParseYaml(), which will return a *Config instance on 24 | success: 25 | 26 | cfg, err := config.ParseYaml(yamlString) 27 | 28 | An equivalent JSON configuration could be built using ParseJson(): 29 | 30 | cfg, err := config.ParseJson(jsonString) 31 | 32 | From now, we can retrieve configuration values using a path in dotted notation: 33 | 34 | // "localhost" 35 | host, err := cfg.String("development.database.host") 36 | 37 | // or... 38 | 39 | // "192.168.1.1" 40 | host, err := cfg.String("production.database.host") 41 | 42 | Besides String(), other types can be fetched directly: Bool(), Float64(), 43 | Int(), Map() and List(). All these methods will return an error if the path 44 | doesn't exist, or the value doesn't match or can't be converted to the 45 | requested type. 46 | 47 | A nested configuration can be fetched using Get(). Here we get a new *Config 48 | instance with a subset of the configuration: 49 | 50 | cfg, err := cfg.Get("development") 51 | 52 | Then the inner values are fetched relatively to the subset: 53 | 54 | // "localhost" 55 | host, err := cfg.String("database.host") 56 | 57 | For lists, the dotted path must use an index to refer to a specific value. 58 | To retrieve the information from a user stored in the configuration above: 59 | 60 | // map[string]interface{}{ ... } 61 | user1, err := cfg.Map("development.users.0") 62 | // map[string]interface{}{ ... } 63 | user2, err := cfg.Map("development.users.1") 64 | 65 | // or... 66 | 67 | // "calvin" 68 | name1, err := cfg.String("development.users.0.name") 69 | // "hobbes" 70 | name2, err := cfg.String("development.users.1.name") 71 | 72 | JSON or YAML strings can be created calling the appropriate Render*() 73 | functions. Here's how we render a configuration like the one used in these 74 | examples: 75 | 76 | cfg := map[string]interface{}{ 77 | "development": map[string]interface{}{ 78 | "database": map[string]interface{}{ 79 | "host": "localhost", 80 | }, 81 | "users": []interface{}{ 82 | map[string]interface{}{ 83 | "name": "calvin", 84 | "password": "yukon", 85 | }, 86 | map[string]interface{}{ 87 | "name": "hobbes", 88 | "password": "tuna", 89 | }, 90 | }, 91 | }, 92 | "production": map[string]interface{}{ 93 | "database": map[string]interface{}{ 94 | "host": "192.168.1.1", 95 | }, 96 | }, 97 | } 98 | 99 | json, err := config.RenderJson(cfg) 100 | 101 | // or... 102 | 103 | yaml, err := config.RenderYaml(cfg) 104 | 105 | This results in a configuration string to be stored in a file or database. 106 | 107 | For more more convenience it can parse OS environment variables and command line arguments. 108 | 109 | cfg, err := config.ParseYaml(yamlString) 110 | cfg.Env() 111 | 112 | // or 113 | 114 | cfg.Flag() 115 | 116 | We can also specify the order of parsing: 117 | 118 | cfg.Env().Flag() 119 | 120 | // or 121 | 122 | cfg.Flag().Env() 123 | 124 | In case of OS environment all existing at the moment of parsing keys will be scanned in OS environment, 125 | but in uppercase and the separator will be `_` instead of a `.`. In case of flags separator will be `-`. 126 | In case of command line arguments possible to use regular dot notation syntax for all keys. 127 | For see existing keys we can run application with `-h`. 128 | 129 | We can use unsafe method to get value: 130 | 131 | // "" 132 | cfg.UString("undefined.key") 133 | 134 | // or with default value 135 | unsafeValue := cfg.UString("undefined.key", "default value") 136 | 137 | There is unsafe methods, like regular, but wuth prefix `U`. 138 | */ 139 | package config 140 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/sasl/sasl_windows.c: -------------------------------------------------------------------------------- 1 | #include "sasl_windows.h" 2 | 3 | static const LPSTR SSPI_PACKAGE_NAME = "kerberos"; 4 | 5 | SECURITY_STATUS SEC_ENTRY sspi_acquire_credentials_handle(CredHandle *cred_handle, char *username, char *password, char *domain) 6 | { 7 | SEC_WINNT_AUTH_IDENTITY auth_identity; 8 | SECURITY_INTEGER ignored; 9 | 10 | auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; 11 | auth_identity.User = (LPSTR) username; 12 | auth_identity.UserLength = strlen(username); 13 | auth_identity.Password = NULL; 14 | auth_identity.PasswordLength = 0; 15 | if(password){ 16 | auth_identity.Password = (LPSTR) password; 17 | auth_identity.PasswordLength = strlen(password); 18 | } 19 | auth_identity.Domain = (LPSTR) domain; 20 | auth_identity.DomainLength = strlen(domain); 21 | return call_sspi_acquire_credentials_handle(NULL, SSPI_PACKAGE_NAME, SECPKG_CRED_OUTBOUND, NULL, &auth_identity, NULL, NULL, cred_handle, &ignored); 22 | } 23 | 24 | int sspi_step(CredHandle *cred_handle, int has_context, CtxtHandle *context, PVOID buffer, ULONG buffer_length, PVOID *out_buffer, ULONG *out_buffer_length, char *target) 25 | { 26 | SecBufferDesc inbuf; 27 | SecBuffer in_bufs[1]; 28 | SecBufferDesc outbuf; 29 | SecBuffer out_bufs[1]; 30 | 31 | if (has_context > 0) { 32 | // If we already have a context, we now have data to send. 33 | // Put this data in an inbuf. 34 | inbuf.ulVersion = SECBUFFER_VERSION; 35 | inbuf.cBuffers = 1; 36 | inbuf.pBuffers = in_bufs; 37 | in_bufs[0].pvBuffer = buffer; 38 | in_bufs[0].cbBuffer = buffer_length; 39 | in_bufs[0].BufferType = SECBUFFER_TOKEN; 40 | } 41 | 42 | outbuf.ulVersion = SECBUFFER_VERSION; 43 | outbuf.cBuffers = 1; 44 | outbuf.pBuffers = out_bufs; 45 | out_bufs[0].pvBuffer = NULL; 46 | out_bufs[0].cbBuffer = 0; 47 | out_bufs[0].BufferType = SECBUFFER_TOKEN; 48 | 49 | ULONG context_attr = 0; 50 | 51 | int ret = call_sspi_initialize_security_context(cred_handle, 52 | has_context > 0 ? context : NULL, 53 | (LPSTR) target, 54 | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MUTUAL_AUTH, 55 | 0, 56 | SECURITY_NETWORK_DREP, 57 | has_context > 0 ? &inbuf : NULL, 58 | 0, 59 | context, 60 | &outbuf, 61 | &context_attr, 62 | NULL); 63 | 64 | *out_buffer = malloc(out_bufs[0].cbBuffer); 65 | *out_buffer_length = out_bufs[0].cbBuffer; 66 | memcpy(*out_buffer, out_bufs[0].pvBuffer, *out_buffer_length); 67 | 68 | return ret; 69 | } 70 | 71 | int sspi_send_client_authz_id(CtxtHandle *context, PVOID *buffer, ULONG *buffer_length, char *user_plus_realm) 72 | { 73 | SecPkgContext_Sizes sizes; 74 | SECURITY_STATUS status = call_sspi_query_context_attributes(context, SECPKG_ATTR_SIZES, &sizes); 75 | 76 | if (status != SEC_E_OK) { 77 | return status; 78 | } 79 | 80 | size_t user_plus_realm_length = strlen(user_plus_realm); 81 | int msgSize = 4 + user_plus_realm_length; 82 | char *msg = malloc((sizes.cbSecurityTrailer + msgSize + sizes.cbBlockSize) * sizeof(char)); 83 | msg[sizes.cbSecurityTrailer + 0] = 1; 84 | msg[sizes.cbSecurityTrailer + 1] = 0; 85 | msg[sizes.cbSecurityTrailer + 2] = 0; 86 | msg[sizes.cbSecurityTrailer + 3] = 0; 87 | memcpy(&msg[sizes.cbSecurityTrailer + 4], user_plus_realm, user_plus_realm_length); 88 | 89 | SecBuffer wrapBufs[3]; 90 | SecBufferDesc wrapBufDesc; 91 | wrapBufDesc.cBuffers = 3; 92 | wrapBufDesc.pBuffers = wrapBufs; 93 | wrapBufDesc.ulVersion = SECBUFFER_VERSION; 94 | 95 | wrapBufs[0].cbBuffer = sizes.cbSecurityTrailer; 96 | wrapBufs[0].BufferType = SECBUFFER_TOKEN; 97 | wrapBufs[0].pvBuffer = msg; 98 | 99 | wrapBufs[1].cbBuffer = msgSize; 100 | wrapBufs[1].BufferType = SECBUFFER_DATA; 101 | wrapBufs[1].pvBuffer = msg + sizes.cbSecurityTrailer; 102 | 103 | wrapBufs[2].cbBuffer = sizes.cbBlockSize; 104 | wrapBufs[2].BufferType = SECBUFFER_PADDING; 105 | wrapBufs[2].pvBuffer = msg + sizes.cbSecurityTrailer + msgSize; 106 | 107 | status = call_sspi_encrypt_message(context, SECQOP_WRAP_NO_ENCRYPT, &wrapBufDesc, 0); 108 | if (status != SEC_E_OK) { 109 | free(msg); 110 | return status; 111 | } 112 | 113 | *buffer_length = wrapBufs[0].cbBuffer + wrapBufs[1].cbBuffer + wrapBufs[2].cbBuffer; 114 | *buffer = malloc(*buffer_length); 115 | 116 | memcpy(*buffer, wrapBufs[0].pvBuffer, wrapBufs[0].cbBuffer); 117 | memcpy(*buffer + wrapBufs[0].cbBuffer, wrapBufs[1].pvBuffer, wrapBufs[1].cbBuffer); 118 | memcpy(*buffer + wrapBufs[0].cbBuffer + wrapBufs[1].cbBuffer, wrapBufs[2].pvBuffer, wrapBufs[2].cbBuffer); 119 | 120 | free(msg); 121 | return SEC_E_OK; 122 | } 123 | -------------------------------------------------------------------------------- /lib/get.go: -------------------------------------------------------------------------------- 1 | package cdn 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "net/http" 7 | "strings" 8 | "time" 9 | 10 | "gopkg.in/mgo.v2/bson" 11 | 12 | "github.com/gorilla/mux" 13 | ) 14 | 15 | const FORMAT = "Mon, 2 Jan 2006 15:04:05 GMT" 16 | 17 | func (c *Config) Get(w http.ResponseWriter, req *http.Request) { 18 | vars := mux.Vars(req) 19 | db := c.Db 20 | // validate _id 21 | if !bson.IsObjectIdHex(vars["_id"]) { 22 | w.WriteHeader(http.StatusBadRequest) 23 | return 24 | } 25 | 26 | // define main variables 27 | _id := bson.ObjectIdHex(vars["_id"]) 28 | query := db.C(vars["coll"] + ".files").FindId(_id) 29 | meta := bson.M{} 30 | err := query.One(&meta) 31 | 32 | // found file or not 33 | if err != nil { 34 | if err.Error() == "not found" { 35 | w.WriteHeader(http.StatusNotFound) 36 | } else { 37 | w.WriteHeader(http.StatusBadRequest) 38 | w.Write([]byte(err.Error())) 39 | } 40 | return 41 | } 42 | 43 | uploadDate := meta["uploadDate"].(time.Time) 44 | contentType := meta["contentType"].(string) 45 | fileName := meta["filename"].(string) 46 | 47 | req.ParseForm() 48 | head := w.Header() 49 | head.Add("Accept-Ranges", "bytes") 50 | head.Add("ETag", vars["_id"]+"+"+req.URL.RawQuery) 51 | head.Add("Date", uploadDate.Format(FORMAT)) 52 | head.Add("Last-Modified", uploadDate.Format(FORMAT)) 53 | // Expires after ten years :) 54 | head.Add("Expires", uploadDate.Add(87600*time.Hour).Format(FORMAT)) 55 | head.Add("Cache-Control", "public, max-age=31536000") 56 | head.Add("Content-Type", contentType) 57 | if _, dl := req.Form["dl"]; (contentType == "application/octet-stream") || dl { 58 | head.Add("Content-Disposition", "attachment; filename='"+fileName+"'") 59 | } 60 | 61 | // already served 62 | if h := req.Header.Get("If-None-Match"); h == vars["_id"]+"+"+req.URL.RawQuery { 63 | w.WriteHeader(http.StatusNotModified) 64 | w.Write([]byte("304 Not Modified")) 65 | return 66 | } 67 | 68 | // get file 69 | file, err := db.GridFS(vars["coll"]).OpenId(_id) 70 | defer file.Close() 71 | if err != nil { 72 | w.WriteHeader(http.StatusBadRequest) 73 | w.Write([]byte(err.Error())) 74 | return 75 | } 76 | // check to crop/resize 77 | cr, isCrop := req.Form["crop"] 78 | rsz, isResize := req.Form["resize"] 79 | 80 | isIn := ^in([]string{"image/png", "image/jpeg"}, file.ContentType()) != 0 81 | 82 | if isCrop && isIn && cr != nil { 83 | parsed, _ := parseParams(cr[0]) 84 | if parsed != nil { 85 | crop(w, file, c.MaxSize, parsed) 86 | return 87 | } 88 | } else if isResize && isIn && rsz != nil { 89 | parsed, _ := parseParams(rsz[0]) 90 | if parsed != nil { 91 | resize(w, file, parsed) 92 | return 93 | } 94 | } else { 95 | io.Copy(w, file) 96 | } 97 | 98 | } 99 | 100 | func (c *Config) GetStat(w http.ResponseWriter, req *http.Request) { 101 | vars := mux.Vars(req) 102 | db := c.Db 103 | req.ParseForm() 104 | 105 | // make query & ensure index for meta 106 | keys := []string{} 107 | q := bson.M{} 108 | for k, v := range req.Form { 109 | key := "metadata." + k 110 | value := strings.Join(v, "") 111 | if len(value) > 0 { 112 | keys = append(keys, key) 113 | q[key] = value 114 | } 115 | } 116 | 117 | // async ensure index 118 | go func() { 119 | if len(keys) > 0 { 120 | db.C(vars["coll"] + ".files").EnsureIndexKey(keys...) 121 | } 122 | }() 123 | 124 | pipe := db.C(vars["coll"] + ".files").Pipe([]bson.M{ 125 | {"$match": q}, 126 | {"$group": bson.M{ 127 | "_id": nil, 128 | "fileSize": bson.M{"$sum": "$length"}, 129 | }}, 130 | }) 131 | 132 | result := bson.M{} 133 | pipe.One(&result) 134 | delete(result, "_id") 135 | w.WriteHeader(http.StatusOK) 136 | bytes, _ := json.Marshal(result) 137 | w.Write(bytes) 138 | } 139 | 140 | type Doc struct { 141 | Id bson.ObjectId `bson:"_id"` 142 | Filename string `bson:"filename"` 143 | } 144 | 145 | func (d Doc) Join() string { 146 | return "/" + d.Id.Hex() + "/" + d.Filename 147 | } 148 | 149 | func (c *Config) GetIndex(w http.ResponseWriter, req *http.Request) { 150 | vars := mux.Vars(req) 151 | db := c.Db 152 | req.ParseForm() 153 | // make query & ensure index for meta 154 | keys := []string{} 155 | q := bson.M{} 156 | for k, v := range req.Form { 157 | key := "metadata." + k 158 | value := strings.Join(v, "") 159 | if len(value) > 0 { 160 | keys = append(keys, key) 161 | q[key] = value 162 | } 163 | } 164 | 165 | // async ensure index 166 | go func() { 167 | if len(keys) > 0 { 168 | db.C(vars["coll"] + ".files").EnsureIndexKey(keys...) 169 | } 170 | }() 171 | 172 | result := []Doc{} 173 | db.C(vars["coll"] + ".files").Find(q).All(&result) 174 | names := make([]string, len(result)) 175 | for i, _ := range names { 176 | names[i] = result[i].Join() 177 | } 178 | w.WriteHeader(http.StatusOK) 179 | bytes, _ := json.Marshal(names) 180 | w.Write(bytes) 181 | } 182 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/affine.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Graphics-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package graphics 6 | 7 | import ( 8 | "errors" 9 | "image" 10 | "image/draw" 11 | "math" 12 | 13 | "github.com/olebedev/graphics-go/graphics/interp" 14 | ) 15 | 16 | // I is the identity Affine transform matrix. 17 | var I = Affine{ 18 | 1, 0, 0, 19 | 0, 1, 0, 20 | 0, 0, 1, 21 | } 22 | 23 | // Affine is a 3x3 2D affine transform matrix. 24 | // M(i,j) is Affine[i*3+j]. 25 | type Affine [9]float64 26 | 27 | // Mul returns the multiplication of two affine transform matrices. 28 | func (a Affine) Mul(b Affine) Affine { 29 | return Affine{ 30 | a[0]*b[0] + a[1]*b[3] + a[2]*b[6], 31 | a[0]*b[1] + a[1]*b[4] + a[2]*b[7], 32 | a[0]*b[2] + a[1]*b[5] + a[2]*b[8], 33 | a[3]*b[0] + a[4]*b[3] + a[5]*b[6], 34 | a[3]*b[1] + a[4]*b[4] + a[5]*b[7], 35 | a[3]*b[2] + a[4]*b[5] + a[5]*b[8], 36 | a[6]*b[0] + a[7]*b[3] + a[8]*b[6], 37 | a[6]*b[1] + a[7]*b[4] + a[8]*b[7], 38 | a[6]*b[2] + a[7]*b[5] + a[8]*b[8], 39 | } 40 | } 41 | 42 | func (a Affine) transformRGBA(dst *image.RGBA, src *image.RGBA, i interp.RGBA) error { 43 | srcb := src.Bounds() 44 | b := dst.Bounds() 45 | for y := b.Min.Y; y < b.Max.Y; y++ { 46 | for x := b.Min.X; x < b.Max.X; x++ { 47 | sx, sy := a.pt(x, y) 48 | if inBounds(srcb, sx, sy) { 49 | c := i.RGBA(src, sx, sy) 50 | off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4 51 | dst.Pix[off+0] = c.R 52 | dst.Pix[off+1] = c.G 53 | dst.Pix[off+2] = c.B 54 | dst.Pix[off+3] = c.A 55 | } 56 | } 57 | } 58 | return nil 59 | } 60 | 61 | // Transform applies the affine transform to src and produces dst. 62 | func (a Affine) Transform(dst draw.Image, src image.Image, i interp.Interp) error { 63 | if dst == nil { 64 | return errors.New("graphics: dst is nil") 65 | } 66 | if src == nil { 67 | return errors.New("graphics: src is nil") 68 | } 69 | 70 | // RGBA fast path. 71 | dstRGBA, dstOk := dst.(*image.RGBA) 72 | srcRGBA, srcOk := src.(*image.RGBA) 73 | interpRGBA, interpOk := i.(interp.RGBA) 74 | if dstOk && srcOk && interpOk { 75 | return a.transformRGBA(dstRGBA, srcRGBA, interpRGBA) 76 | } 77 | 78 | srcb := src.Bounds() 79 | b := dst.Bounds() 80 | for y := b.Min.Y; y < b.Max.Y; y++ { 81 | for x := b.Min.X; x < b.Max.X; x++ { 82 | sx, sy := a.pt(x, y) 83 | if inBounds(srcb, sx, sy) { 84 | dst.Set(x, y, i.Interp(src, sx, sy)) 85 | } 86 | } 87 | } 88 | return nil 89 | } 90 | 91 | func inBounds(b image.Rectangle, x, y float64) bool { 92 | if x < float64(b.Min.X) || x >= float64(b.Max.X) { 93 | return false 94 | } 95 | if y < float64(b.Min.Y) || y >= float64(b.Max.Y) { 96 | return false 97 | } 98 | return true 99 | } 100 | 101 | func (a Affine) pt(x0, y0 int) (x1, y1 float64) { 102 | fx := float64(x0) + 0.5 103 | fy := float64(y0) + 0.5 104 | x1 = fx*a[0] + fy*a[1] + a[2] 105 | y1 = fx*a[3] + fy*a[4] + a[5] 106 | return x1, y1 107 | } 108 | 109 | // TransformCenter applies the affine transform to src and produces dst. 110 | // Equivalent to 111 | // a.CenterFit(dst, src).Transform(dst, src, i). 112 | func (a Affine) TransformCenter(dst draw.Image, src image.Image, i interp.Interp) error { 113 | if dst == nil { 114 | return errors.New("graphics: dst is nil") 115 | } 116 | if src == nil { 117 | return errors.New("graphics: src is nil") 118 | } 119 | 120 | return a.CenterFit(dst.Bounds(), src.Bounds()).Transform(dst, src, i) 121 | } 122 | 123 | // Scale produces a scaling transform of factors x and y. 124 | func (a Affine) Scale(x, y float64) Affine { 125 | return a.Mul(Affine{ 126 | 1 / x, 0, 0, 127 | 0, 1 / y, 0, 128 | 0, 0, 1, 129 | }) 130 | } 131 | 132 | // Rotate produces a clockwise rotation transform of angle, in radians. 133 | func (a Affine) Rotate(angle float64) Affine { 134 | s, c := math.Sincos(angle) 135 | return a.Mul(Affine{ 136 | +c, +s, +0, 137 | -s, +c, +0, 138 | +0, +0, +1, 139 | }) 140 | } 141 | 142 | // Shear produces a shear transform by the slopes x and y. 143 | func (a Affine) Shear(x, y float64) Affine { 144 | d := 1 - x*y 145 | return a.Mul(Affine{ 146 | +1 / d, -x / d, 0, 147 | -y / d, +1 / d, 0, 148 | 0, 0, 1, 149 | }) 150 | } 151 | 152 | // Translate produces a translation transform with pixel distances x and y. 153 | func (a Affine) Translate(x, y float64) Affine { 154 | return a.Mul(Affine{ 155 | 1, 0, -x, 156 | 0, 1, -y, 157 | 0, 0, +1, 158 | }) 159 | } 160 | 161 | // Center produces the affine transform, centered around the provided point. 162 | func (a Affine) Center(x, y float64) Affine { 163 | return I.Translate(-x, -y).Mul(a).Translate(x, y) 164 | } 165 | 166 | // CenterFit produces the affine transform, centered around the rectangles. 167 | // It is equivalent to 168 | // I.Translate(-
).Mul(a).Translate(
) 169 | func (a Affine) CenterFit(dst, src image.Rectangle) Affine { 170 | dx := float64(dst.Min.X) + float64(dst.Dx())/2 171 | dy := float64(dst.Min.Y) + float64(dst.Dy())/2 172 | sx := float64(src.Min.X) + float64(src.Dx())/2 173 | sy := float64(src.Min.Y) + float64(src.Dy())/2 174 | return I.Translate(-sx, -sy).Mul(a).Translate(dx, dy) 175 | } 176 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/yamlprivateh.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | const ( 4 | // The size of the input raw buffer. 5 | input_raw_buffer_size = 512 6 | 7 | // The size of the input buffer. 8 | // It should be possible to decode the whole raw buffer. 9 | input_buffer_size = input_raw_buffer_size * 3 10 | 11 | // The size of the output buffer. 12 | output_buffer_size = 128 13 | 14 | // The size of the output raw buffer. 15 | // It should be possible to encode the whole output buffer. 16 | output_raw_buffer_size = (output_buffer_size*2 + 2) 17 | 18 | // The size of other stacks and queues. 19 | initial_stack_size = 16 20 | initial_queue_size = 16 21 | initial_string_size = 16 22 | ) 23 | 24 | // Check if the character at the specified position is an alphabetical 25 | // character, a digit, '_', or '-'. 26 | func is_alpha(b []byte, i int) bool { 27 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' 28 | } 29 | 30 | // Check if the character at the specified position is a digit. 31 | func is_digit(b []byte, i int) bool { 32 | return b[i] >= '0' && b[i] <= '9' 33 | } 34 | 35 | // Get the value of a digit. 36 | func as_digit(b []byte, i int) int { 37 | return int(b[i]) - '0' 38 | } 39 | 40 | // Check if the character at the specified position is a hex-digit. 41 | func is_hex(b []byte, i int) bool { 42 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' 43 | } 44 | 45 | // Get the value of a hex-digit. 46 | func as_hex(b []byte, i int) int { 47 | bi := b[i] 48 | if bi >= 'A' && bi <= 'F' { 49 | return int(bi) - 'A' + 10 50 | } 51 | if bi >= 'a' && bi <= 'f' { 52 | return int(bi) - 'a' + 10 53 | } 54 | return int(bi) - '0' 55 | } 56 | 57 | // Check if the character is ASCII. 58 | func is_ascii(b []byte, i int) bool { 59 | return b[i] <= 0x7F 60 | } 61 | 62 | // Check if the character at the start of the buffer can be printed unescaped. 63 | func is_printable(b []byte, i int) bool { 64 | return ((b[i] == 0x0A) || // . == #x0A 65 | (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E 66 | (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF 67 | (b[i] > 0xC2 && b[i] < 0xED) || 68 | (b[i] == 0xED && b[i+1] < 0xA0) || 69 | (b[i] == 0xEE) || 70 | (b[i] == 0xEF && // #xE000 <= . <= #xFFFD 71 | !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF 72 | !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) 73 | } 74 | 75 | // Check if the character at the specified position is NUL. 76 | func is_z(b []byte, i int) bool { 77 | return b[i] == 0x00 78 | } 79 | 80 | // Check if the beginning of the buffer is a BOM. 81 | func is_bom(b []byte, i int) bool { 82 | return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF 83 | } 84 | 85 | // Check if the character at the specified position is space. 86 | func is_space(b []byte, i int) bool { 87 | return b[i] == ' ' 88 | } 89 | 90 | // Check if the character at the specified position is tab. 91 | func is_tab(b []byte, i int) bool { 92 | return b[i] == '\t' 93 | } 94 | 95 | // Check if the character at the specified position is blank (space or tab). 96 | func is_blank(b []byte, i int) bool { 97 | //return is_space(b, i) || is_tab(b, i) 98 | return b[i] == ' ' || b[i] == '\t' 99 | } 100 | 101 | // Check if the character at the specified position is a line break. 102 | func is_break(b []byte, i int) bool { 103 | return (b[i] == '\r' || // CR (#xD) 104 | b[i] == '\n' || // LF (#xA) 105 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 106 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 107 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) 108 | } 109 | 110 | func is_crlf(b []byte, i int) bool { 111 | return b[i] == '\r' && b[i+1] == '\n' 112 | } 113 | 114 | // Check if the character is a line break or NUL. 115 | func is_breakz(b []byte, i int) bool { 116 | //return is_break(b, i) || is_z(b, i) 117 | return ( // is_break: 118 | b[i] == '\r' || // CR (#xD) 119 | b[i] == '\n' || // LF (#xA) 120 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 121 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 122 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 123 | // is_z: 124 | b[i] == 0) 125 | } 126 | 127 | // Check if the character is a line break, space, or NUL. 128 | func is_spacez(b []byte, i int) bool { 129 | //return is_space(b, i) || is_breakz(b, i) 130 | return ( // is_space: 131 | b[i] == ' ' || 132 | // is_breakz: 133 | b[i] == '\r' || // CR (#xD) 134 | b[i] == '\n' || // LF (#xA) 135 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 136 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 137 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 138 | b[i] == 0) 139 | } 140 | 141 | // Check if the character is a line break, space, tab, or NUL. 142 | func is_blankz(b []byte, i int) bool { 143 | //return is_blank(b, i) || is_breakz(b, i) 144 | return ( // is_blank: 145 | b[i] == ' ' || b[i] == '\t' || 146 | // is_breakz: 147 | b[i] == '\r' || // CR (#xD) 148 | b[i] == '\n' || // LF (#xA) 149 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 150 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 151 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 152 | b[i] == 0) 153 | } 154 | 155 | // Determine the width of the character. 156 | func width(b byte) int { 157 | // Don't replace these by a switch without first 158 | // confirming that it is being inlined. 159 | if b&0x80 == 0x00 { 160 | return 1 161 | } 162 | if b&0xE0 == 0xC0 { 163 | return 2 164 | } 165 | if b&0xF0 == 0xE0 { 166 | return 3 167 | } 168 | if b&0xF8 == 0xF0 { 169 | return 4 170 | } 171 | return 0 172 | 173 | } 174 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/resolve.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "encoding/base64" 5 | "math" 6 | "strconv" 7 | "strings" 8 | "unicode/utf8" 9 | ) 10 | 11 | type resolveMapItem struct { 12 | value interface{} 13 | tag string 14 | } 15 | 16 | var resolveTable = make([]byte, 256) 17 | var resolveMap = make(map[string]resolveMapItem) 18 | 19 | func init() { 20 | t := resolveTable 21 | t[int('+')] = 'S' // Sign 22 | t[int('-')] = 'S' 23 | for _, c := range "0123456789" { 24 | t[int(c)] = 'D' // Digit 25 | } 26 | for _, c := range "yYnNtTfFoO~" { 27 | t[int(c)] = 'M' // In map 28 | } 29 | t[int('.')] = '.' // Float (potentially in map) 30 | 31 | var resolveMapList = []struct { 32 | v interface{} 33 | tag string 34 | l []string 35 | }{ 36 | {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, 37 | {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, 38 | {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, 39 | {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, 40 | {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, 41 | {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, 42 | {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, 43 | {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, 44 | {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, 45 | {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, 46 | {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, 47 | {"<<", yaml_MERGE_TAG, []string{"<<"}}, 48 | } 49 | 50 | m := resolveMap 51 | for _, item := range resolveMapList { 52 | for _, s := range item.l { 53 | m[s] = resolveMapItem{item.v, item.tag} 54 | } 55 | } 56 | } 57 | 58 | const longTagPrefix = "tag:yaml.org,2002:" 59 | 60 | func shortTag(tag string) string { 61 | // TODO This can easily be made faster and produce less garbage. 62 | if strings.HasPrefix(tag, longTagPrefix) { 63 | return "!!" + tag[len(longTagPrefix):] 64 | } 65 | return tag 66 | } 67 | 68 | func longTag(tag string) string { 69 | if strings.HasPrefix(tag, "!!") { 70 | return longTagPrefix + tag[2:] 71 | } 72 | return tag 73 | } 74 | 75 | func resolvableTag(tag string) bool { 76 | switch tag { 77 | case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG: 78 | return true 79 | } 80 | return false 81 | } 82 | 83 | func resolve(tag string, in string) (rtag string, out interface{}) { 84 | if !resolvableTag(tag) { 85 | return tag, in 86 | } 87 | 88 | defer func() { 89 | switch tag { 90 | case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: 91 | return 92 | } 93 | failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) 94 | }() 95 | 96 | // Any data is accepted as a !!str or !!binary. 97 | // Otherwise, the prefix is enough of a hint about what it might be. 98 | hint := byte('N') 99 | if in != "" { 100 | hint = resolveTable[in[0]] 101 | } 102 | if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { 103 | // Handle things we can lookup in a map. 104 | if item, ok := resolveMap[in]; ok { 105 | return item.tag, item.value 106 | } 107 | 108 | // Base 60 floats are a bad idea, were dropped in YAML 1.2, and 109 | // are purposefully unsupported here. They're still quoted on 110 | // the way out for compatibility with other parser, though. 111 | 112 | switch hint { 113 | case 'M': 114 | // We've already checked the map above. 115 | 116 | case '.': 117 | // Not in the map, so maybe a normal float. 118 | floatv, err := strconv.ParseFloat(in, 64) 119 | if err == nil { 120 | return yaml_FLOAT_TAG, floatv 121 | } 122 | 123 | case 'D', 'S': 124 | // Int, float, or timestamp. 125 | plain := strings.Replace(in, "_", "", -1) 126 | intv, err := strconv.ParseInt(plain, 0, 64) 127 | if err == nil { 128 | if intv == int64(int(intv)) { 129 | return yaml_INT_TAG, int(intv) 130 | } else { 131 | return yaml_INT_TAG, intv 132 | } 133 | } 134 | uintv, err := strconv.ParseUint(plain, 0, 64) 135 | if err == nil { 136 | return yaml_INT_TAG, uintv 137 | } 138 | floatv, err := strconv.ParseFloat(plain, 64) 139 | if err == nil { 140 | return yaml_FLOAT_TAG, floatv 141 | } 142 | if strings.HasPrefix(plain, "0b") { 143 | intv, err := strconv.ParseInt(plain[2:], 2, 64) 144 | if err == nil { 145 | if intv == int64(int(intv)) { 146 | return yaml_INT_TAG, int(intv) 147 | } else { 148 | return yaml_INT_TAG, intv 149 | } 150 | } 151 | uintv, err := strconv.ParseUint(plain[2:], 2, 64) 152 | if err == nil { 153 | return yaml_INT_TAG, uintv 154 | } 155 | } else if strings.HasPrefix(plain, "-0b") { 156 | intv, err := strconv.ParseInt(plain[3:], 2, 64) 157 | if err == nil { 158 | if intv == int64(int(intv)) { 159 | return yaml_INT_TAG, -int(intv) 160 | } else { 161 | return yaml_INT_TAG, -intv 162 | } 163 | } 164 | } 165 | // XXX Handle timestamps here. 166 | 167 | default: 168 | panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") 169 | } 170 | } 171 | if tag == yaml_BINARY_TAG { 172 | return yaml_BINARY_TAG, in 173 | } 174 | if utf8.ValidString(in) { 175 | return yaml_STR_TAG, in 176 | } 177 | return yaml_BINARY_TAG, encodeBase64(in) 178 | } 179 | 180 | // encodeBase64 encodes s as base64 that is broken up into multiple lines 181 | // as appropriate for the resulting length. 182 | func encodeBase64(s string) string { 183 | const lineLen = 70 184 | encLen := base64.StdEncoding.EncodedLen(len(s)) 185 | lines := encLen/lineLen + 1 186 | buf := make([]byte, encLen*2+lines) 187 | in := buf[0:encLen] 188 | out := buf[encLen:] 189 | base64.StdEncoding.Encode(in, []byte(s)) 190 | k := 0 191 | for i := 0; i < len(in); i += lineLen { 192 | j := i + lineLen 193 | if j > len(in) { 194 | j = len(in) 195 | } 196 | k += copy(out[k:], in[i:j]) 197 | if lines > 1 { 198 | out[k] = '\n' 199 | k++ 200 | } 201 | } 202 | return string(out[:k]) 203 | } 204 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/interp/bilinear.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Graphics-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package interp 6 | 7 | import ( 8 | "image" 9 | "image/color" 10 | "math" 11 | ) 12 | 13 | // Bilinear implements bilinear interpolation. 14 | var Bilinear Interp = bilinear{} 15 | 16 | type bilinear struct{} 17 | 18 | func (i bilinear) Interp(src image.Image, x, y float64) color.Color { 19 | if src, ok := src.(*image.RGBA); ok { 20 | return i.RGBA(src, x, y) 21 | } 22 | return bilinearGeneral(src, x, y) 23 | } 24 | 25 | func bilinearGeneral(src image.Image, x, y float64) color.Color { 26 | p := findLinearSrc(src.Bounds(), x, y) 27 | var fr, fg, fb, fa float64 28 | var r, g, b, a uint32 29 | 30 | r, g, b, a = src.At(p.low.X, p.low.Y).RGBA() 31 | fr += float64(r) * p.frac00 32 | fg += float64(g) * p.frac00 33 | fb += float64(b) * p.frac00 34 | fa += float64(a) * p.frac00 35 | 36 | r, g, b, a = src.At(p.high.X, p.low.Y).RGBA() 37 | fr += float64(r) * p.frac01 38 | fg += float64(g) * p.frac01 39 | fb += float64(b) * p.frac01 40 | fa += float64(a) * p.frac01 41 | 42 | r, g, b, a = src.At(p.low.X, p.high.Y).RGBA() 43 | fr += float64(r) * p.frac10 44 | fg += float64(g) * p.frac10 45 | fb += float64(b) * p.frac10 46 | fa += float64(a) * p.frac10 47 | 48 | r, g, b, a = src.At(p.high.X, p.high.Y).RGBA() 49 | fr += float64(r) * p.frac11 50 | fg += float64(g) * p.frac11 51 | fb += float64(b) * p.frac11 52 | fa += float64(a) * p.frac11 53 | 54 | var c color.RGBA64 55 | c.R = uint16(fr + 0.5) 56 | c.G = uint16(fg + 0.5) 57 | c.B = uint16(fb + 0.5) 58 | c.A = uint16(fa + 0.5) 59 | return c 60 | } 61 | 62 | func (bilinear) RGBA(src *image.RGBA, x, y float64) color.RGBA { 63 | p := findLinearSrc(src.Bounds(), x, y) 64 | 65 | // Array offsets for the surrounding pixels. 66 | off00 := offRGBA(src, p.low.X, p.low.Y) 67 | off01 := offRGBA(src, p.high.X, p.low.Y) 68 | off10 := offRGBA(src, p.low.X, p.high.Y) 69 | off11 := offRGBA(src, p.high.X, p.high.Y) 70 | 71 | var fr, fg, fb, fa float64 72 | 73 | fr += float64(src.Pix[off00+0]) * p.frac00 74 | fg += float64(src.Pix[off00+1]) * p.frac00 75 | fb += float64(src.Pix[off00+2]) * p.frac00 76 | fa += float64(src.Pix[off00+3]) * p.frac00 77 | 78 | fr += float64(src.Pix[off01+0]) * p.frac01 79 | fg += float64(src.Pix[off01+1]) * p.frac01 80 | fb += float64(src.Pix[off01+2]) * p.frac01 81 | fa += float64(src.Pix[off01+3]) * p.frac01 82 | 83 | fr += float64(src.Pix[off10+0]) * p.frac10 84 | fg += float64(src.Pix[off10+1]) * p.frac10 85 | fb += float64(src.Pix[off10+2]) * p.frac10 86 | fa += float64(src.Pix[off10+3]) * p.frac10 87 | 88 | fr += float64(src.Pix[off11+0]) * p.frac11 89 | fg += float64(src.Pix[off11+1]) * p.frac11 90 | fb += float64(src.Pix[off11+2]) * p.frac11 91 | fa += float64(src.Pix[off11+3]) * p.frac11 92 | 93 | var c color.RGBA 94 | c.R = uint8(fr + 0.5) 95 | c.G = uint8(fg + 0.5) 96 | c.B = uint8(fb + 0.5) 97 | c.A = uint8(fa + 0.5) 98 | return c 99 | } 100 | 101 | func (bilinear) Gray(src *image.Gray, x, y float64) color.Gray { 102 | p := findLinearSrc(src.Bounds(), x, y) 103 | 104 | // Array offsets for the surrounding pixels. 105 | off00 := offGray(src, p.low.X, p.low.Y) 106 | off01 := offGray(src, p.high.X, p.low.Y) 107 | off10 := offGray(src, p.low.X, p.high.Y) 108 | off11 := offGray(src, p.high.X, p.high.Y) 109 | 110 | var fc float64 111 | fc += float64(src.Pix[off00]) * p.frac00 112 | fc += float64(src.Pix[off01]) * p.frac01 113 | fc += float64(src.Pix[off10]) * p.frac10 114 | fc += float64(src.Pix[off11]) * p.frac11 115 | 116 | var c color.Gray 117 | c.Y = uint8(fc + 0.5) 118 | return c 119 | } 120 | 121 | type bilinearSrc struct { 122 | // Top-left and bottom-right interpolation sources 123 | low, high image.Point 124 | // Fraction of each pixel to take. The 0 suffix indicates 125 | // top/left, and the 1 suffix indicates bottom/right. 126 | frac00, frac01, frac10, frac11 float64 127 | } 128 | 129 | func findLinearSrc(b image.Rectangle, sx, sy float64) bilinearSrc { 130 | maxX := float64(b.Max.X) 131 | maxY := float64(b.Max.Y) 132 | minX := float64(b.Min.X) 133 | minY := float64(b.Min.Y) 134 | lowX := math.Floor(sx - 0.5) 135 | lowY := math.Floor(sy - 0.5) 136 | if lowX < minX { 137 | lowX = minX 138 | } 139 | if lowY < minY { 140 | lowY = minY 141 | } 142 | 143 | highX := math.Ceil(sx - 0.5) 144 | highY := math.Ceil(sy - 0.5) 145 | if highX >= maxX { 146 | highX = maxX - 1 147 | } 148 | if highY >= maxY { 149 | highY = maxY - 1 150 | } 151 | 152 | // In the variables below, the 0 suffix indicates top/left, and the 153 | // 1 suffix indicates bottom/right. 154 | 155 | // Center of each surrounding pixel. 156 | x00 := lowX + 0.5 157 | y00 := lowY + 0.5 158 | x01 := highX + 0.5 159 | y01 := lowY + 0.5 160 | x10 := lowX + 0.5 161 | y10 := highY + 0.5 162 | x11 := highX + 0.5 163 | y11 := highY + 0.5 164 | 165 | p := bilinearSrc{ 166 | low: image.Pt(int(lowX), int(lowY)), 167 | high: image.Pt(int(highX), int(highY)), 168 | } 169 | 170 | // Literally, edge cases. If we are close enough to the edge of 171 | // the image, curtail the interpolation sources. 172 | if lowX == highX && lowY == highY { 173 | p.frac00 = 1.0 174 | } else if sy-minY <= 0.5 && sx-minX <= 0.5 { 175 | p.frac00 = 1.0 176 | } else if maxY-sy <= 0.5 && maxX-sx <= 0.5 { 177 | p.frac11 = 1.0 178 | } else if sy-minY <= 0.5 || lowY == highY { 179 | p.frac00 = x01 - sx 180 | p.frac01 = sx - x00 181 | } else if sx-minX <= 0.5 || lowX == highX { 182 | p.frac00 = y10 - sy 183 | p.frac10 = sy - y00 184 | } else if maxY-sy <= 0.5 { 185 | p.frac10 = x11 - sx 186 | p.frac11 = sx - x10 187 | } else if maxX-sx <= 0.5 { 188 | p.frac01 = y11 - sy 189 | p.frac11 = sy - y01 190 | } else { 191 | p.frac00 = (x01 - sx) * (y10 - sy) 192 | p.frac01 = (sx - x00) * (y11 - sy) 193 | p.frac10 = (x11 - sx) * (sy - y00) 194 | p.frac11 = (sx - x10) * (sy - y01) 195 | } 196 | 197 | return p 198 | } 199 | 200 | // TODO(crawshaw): When we have inlining, consider func (p *RGBA) Off(x, y) int 201 | func offRGBA(src *image.RGBA, x, y int) int { 202 | return (y-src.Rect.Min.Y)*src.Stride + (x-src.Rect.Min.X)*4 203 | } 204 | func offGray(src *image.Gray, x, y int) int { 205 | return (y-src.Rect.Min.Y)*src.Stride + (x - src.Rect.Min.X) 206 | } 207 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #CDN over MongoDb GridFs 2 | 3 | [![Join the chat at https://gitter.im/olebedev/cdn](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/olebedev/cdn?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | This utility can be used as stand alone _Content Delivery Network_, using _MongoDB GridFs_ as backend file storage. It can be built from source code or installed as already compiled binaries. 6 | 7 | Also it can be used as a thin file storage library for your projects, based on [martini](https://github.com/go-martini/martini) framework. For example, when you use one of the cloud platforms like _Heroku_, with ephemeral file system for application's instances and you have to save user's data. 8 | 9 | ## Features 10 | 11 | - on the fly crop and resize for `image/png` and `image/jpeg` _mimetypes_ 12 | - cache strategy, based on _HTTP Last-Modified_ header 13 | - additional metadata for each file & aggregated statistic for it 14 | - forced _HTTP Content-Disposition_ header with the file name, for download links(only if flag is specified, see below) 15 | - buckets(_MongoDB_ collections) as separation level 16 | - file listings for buckets, queried by metadata or without 17 | 18 | ## Examples 19 | Let's assume that the process is already running and listening to default `http://localhost:5000`. 20 | 21 | #### Uploading 22 | ~~~ bash 23 | $ curl -F field=@./books.jpg http://localhost:5000/example 24 | { 25 | "error": null, 26 | "field":"/example/5364d634952b829316000001/books.jpg" 27 | } 28 | ~~~ 29 | Uploading with metadata is realy simple - you only should specify it as _GET_ parameters for uploading _URL_: 30 | ~~~ bash 31 | $ curl -F field=@./books.jpg "http://localhost:5000/example?userId=1&some_another_data=useful" 32 | ... 33 | ~~~ 34 | 35 | #### Getting 36 | As expected, the _URL_ for getting is: 37 | ~~~ bash 38 | http://localhost:5000/example/5364d634952b829316000001/books.jpg 39 | ~~~ 40 | or 41 | ~~~ bash 42 | http://localhost:5000/example/5364d634952b829316000001 43 | ~~~ 44 | That means that the filename is not necessary. 45 | For forsed downloading specify `dl` _GET_ parameter: 46 | ~~~ bash 47 | http://localhost:5000/example/5364d634952b829316000001/books.jpg?dl 48 | ~~~ 49 | In this case the file will not be previewed in the browser. 50 | 51 | #### Crop and Resize images 52 | > This works only for files with mimetypes `image/png` & `image/jpeg`! 53 | > In another cases it feature will be ignored. 54 | 55 | Specify _GET_ parameters `crop`, `scrop` or `resize` for _URL_. Crop example: 56 | ~~~ bash 57 | http://localhost:5000/example/5364d634952b829316000001/books.jpg?crop=500 58 | ~~~ 59 | The value should contain one or two(separated by one non-digit character) integer as width and height in pixels. If height is not specified, it will be used width value. For example, value `crop=500` will be interpreted as `crop=500x500`. 60 | 61 | `scrop`, `resize` parameter works the same way. 62 | `scrop` - is _smart_ crop, based on [smartcrop](https://github.com/muesli/smartcrop) library. 63 | > WARNING! Smartcrop algorithm may take a long time. 64 | 65 | #### Aggregation and the listing of files 66 | 67 | To get storage usage information in bytes, based on saved metadata, _GET_ it like this: 68 | ~~~ bash 69 | $ http://localhost:5000/example/_stats?userId=1 70 | { 71 | "fileSize": 204789 72 | } 73 | ~~~ 74 | If metadata is not specified, usage information will be received for the whole bucket. 75 | 76 | To get the listing of files, based on saved metadata, _GET_ it like this: 77 | ~~~ bash 78 | $ http://localhost:5000/example?userId=1 79 | [ 80 | "/5364d634952b829316000001/books.jpg" 81 | ] 82 | ~~~ 83 | If metadata is not specified, usage information will be received for the whole bucket. 84 | 85 | ## Usage 86 | As library for [martini](https://github.com/go-martini/martini) framework. 87 | 88 | ~~~ bash 89 | $ go get github.com/olebedev/cdn 90 | ~~~ 91 | 92 | Simple `server.go` file: 93 | 94 | ~~~ go 95 | package main 96 | 97 | import ( 98 | "log" 99 | "net/http" 100 | "os" 101 | "github.com/go-martini/martini" 102 | "github.com/olebedev/cdn/lib" 103 | "labix.org/v2/mgo" 104 | ) 105 | 106 | // Acceess handler 107 | func Access(res http.ResponseWriter, req *http.Request) { 108 | // check session or something like this 109 | } 110 | 111 | // Set prefix for current collection name, of course, if need it 112 | // It useful when you store another data in one database 113 | func AutoPrefix(params martini.Params) { 114 | // 'coll' - parameter name, which is used 115 | params["coll"] = "cdn." + params["coll"] 116 | // Ok. Now, cdn will work with this prefix for all collections 117 | } 118 | 119 | func main() { 120 | m := martini.Classic() 121 | 122 | session, err := mgo.Dial("localhost") 123 | if err != nil { 124 | panic(err) 125 | } 126 | session.SetMode(mgo.Monotonic, true) 127 | db := session.DB("cdn") 128 | m.Map(db) 129 | 130 | logger := log.New(os.Stdout, "\x1B[36m[cdn] >>\x1B[39m ", 0) 131 | m.Map(logger) 132 | 133 | m.Group("/uploads", 134 | cdn.Cdn(cdn.Config{ 135 | // Maximum width or height with pixels to crop or resize 136 | // Useful to high performance 137 | MaxSize: 1000, 138 | // Show statictics and the listing of files 139 | ShowInfo: true, 140 | // If true it send URL without collection name, like this: 141 | // {"field":"/5364d634952b829316000001/books.jpg", "error": null} 142 | TailOnly: true, 143 | }), 144 | // Access logic here 145 | Access, 146 | // On the fly prefix for collection 147 | AutoPrefix, 148 | ) 149 | 150 | logger.Println("Server started at :3000") 151 | m.Run() 152 | } 153 | ~~~ 154 | Let's start it! 155 | ~~~ bash 156 | $ go run server.go 157 | [cdn] >> Server started at :3000 158 | ~~~ 159 | 160 | That's all. Now you have started CDN at `http://localhost:3000/uploads/`. 161 | 162 | ## Installation as stand alone 163 | 164 | If you want to build it from sources: 165 | ~~~ bash 166 | $ go get github.com/olebedev/cdn 167 | ~~~ 168 | 169 | If you don't know what is _Golang_, check [releases](https://github.com/olebedev/cdn/releases) page and download binaries for your platform. Untar it and type this: 170 | ~~~ bash 171 | $ ./cdn --help 172 | Usage of ./cdn: 173 | -maxSize="1000": 174 | -mongo.name="cdn": 175 | -mongo.uri="localhost": 176 | -port="5000": 177 | -showInfo="true": 178 | -tailOnly="false": 179 | ~~~ 180 | 181 | 182 | ##### TODO: 183 | 184 | - handler for 206 HTTP Status for large file 185 | - cache(save to GridFs croppped & resized image files) 186 | 187 | 188 | -------------------------------------------------------------------------------- /vendor/github.com/olebedev/graphics-go/graphics/convolve/convolve.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Graphics-Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package convolve 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "image" 11 | "image/draw" 12 | "math" 13 | ) 14 | 15 | // clamp clamps x to the range [x0, x1]. 16 | func clamp(x, x0, x1 float64) float64 { 17 | if x < x0 { 18 | return x0 19 | } 20 | if x > x1 { 21 | return x1 22 | } 23 | return x 24 | } 25 | 26 | // Kernel is a square matrix that defines a convolution. 27 | type Kernel interface { 28 | // Weights returns the square matrix of weights in row major order. 29 | Weights() []float64 30 | } 31 | 32 | // SeparableKernel is a linearly separable, square convolution kernel. 33 | // X and Y are the per-axis weights. Each slice must be the same length, and 34 | // have an odd length. The middle element of each slice is the weight for the 35 | // central pixel. For example, the horizontal Sobel kernel is: 36 | // sobelX := &SeparableKernel{ 37 | // X: []float64{-1, 0, +1}, 38 | // Y: []float64{1, 2, 1}, 39 | // } 40 | type SeparableKernel struct { 41 | X, Y []float64 42 | } 43 | 44 | func (k *SeparableKernel) Weights() []float64 { 45 | n := len(k.X) 46 | w := make([]float64, n*n) 47 | for y := 0; y < n; y++ { 48 | for x := 0; x < n; x++ { 49 | w[y*n+x] = k.X[x] * k.Y[y] 50 | } 51 | } 52 | return w 53 | } 54 | 55 | // fullKernel is a square convolution kernel. 56 | type fullKernel []float64 57 | 58 | func (k fullKernel) Weights() []float64 { return k } 59 | 60 | func kernelSize(w []float64) (size int, err error) { 61 | size = int(math.Sqrt(float64(len(w)))) 62 | if size*size != len(w) { 63 | return 0, errors.New("graphics: kernel is not square") 64 | } 65 | if size%2 != 1 { 66 | return 0, errors.New("graphics: kernel size is not odd") 67 | } 68 | return size, nil 69 | } 70 | 71 | // NewKernel returns a square convolution kernel. 72 | func NewKernel(w []float64) (Kernel, error) { 73 | if _, err := kernelSize(w); err != nil { 74 | return nil, err 75 | } 76 | return fullKernel(w), nil 77 | } 78 | 79 | func convolveRGBASep(dst *image.RGBA, src image.Image, k *SeparableKernel) error { 80 | if len(k.X) != len(k.Y) { 81 | return fmt.Errorf("graphics: kernel not square (x %d, y %d)", len(k.X), len(k.Y)) 82 | } 83 | if len(k.X)%2 != 1 { 84 | return fmt.Errorf("graphics: kernel length (%d) not odd", len(k.X)) 85 | } 86 | radius := (len(k.X) - 1) / 2 87 | 88 | // buf holds the result of vertically blurring src. 89 | bounds := dst.Bounds() 90 | width, height := bounds.Dx(), bounds.Dy() 91 | buf := make([]float64, width*height*4) 92 | for y := bounds.Min.Y; y < bounds.Max.Y; y++ { 93 | for x := bounds.Min.X; x < bounds.Max.X; x++ { 94 | var r, g, b, a float64 95 | // k0 is the kernel weight for the center pixel. This may be greater 96 | // than kernel[0], near the boundary of the source image, to avoid 97 | // vignetting. 98 | k0 := k.X[radius] 99 | 100 | // Add the pixels from above. 101 | for i := 1; i <= radius; i++ { 102 | f := k.Y[radius-i] 103 | if y-i < bounds.Min.Y { 104 | k0 += f 105 | } else { 106 | or, og, ob, oa := src.At(x, y-i).RGBA() 107 | r += float64(or>>8) * f 108 | g += float64(og>>8) * f 109 | b += float64(ob>>8) * f 110 | a += float64(oa>>8) * f 111 | } 112 | } 113 | 114 | // Add the pixels from below. 115 | for i := 1; i <= radius; i++ { 116 | f := k.Y[radius+i] 117 | if y+i >= bounds.Max.Y { 118 | k0 += f 119 | } else { 120 | or, og, ob, oa := src.At(x, y+i).RGBA() 121 | r += float64(or>>8) * f 122 | g += float64(og>>8) * f 123 | b += float64(ob>>8) * f 124 | a += float64(oa>>8) * f 125 | } 126 | } 127 | 128 | // Add the central pixel. 129 | or, og, ob, oa := src.At(x, y).RGBA() 130 | r += float64(or>>8) * k0 131 | g += float64(og>>8) * k0 132 | b += float64(ob>>8) * k0 133 | a += float64(oa>>8) * k0 134 | 135 | // Write to buf. 136 | o := (y-bounds.Min.Y)*width*4 + (x-bounds.Min.X)*4 137 | buf[o+0] = r 138 | buf[o+1] = g 139 | buf[o+2] = b 140 | buf[o+3] = a 141 | } 142 | } 143 | 144 | // dst holds the result of horizontally blurring buf. 145 | for y := 0; y < height; y++ { 146 | for x := 0; x < width; x++ { 147 | var r, g, b, a float64 148 | k0, off := k.X[radius], y*width*4+x*4 149 | 150 | // Add the pixels from the left. 151 | for i := 1; i <= radius; i++ { 152 | f := k.X[radius-i] 153 | if x-i < 0 { 154 | k0 += f 155 | } else { 156 | o := off - i*4 157 | r += buf[o+0] * f 158 | g += buf[o+1] * f 159 | b += buf[o+2] * f 160 | a += buf[o+3] * f 161 | } 162 | } 163 | 164 | // Add the pixels from the right. 165 | for i := 1; i <= radius; i++ { 166 | f := k.X[radius+i] 167 | if x+i >= width { 168 | k0 += f 169 | } else { 170 | o := off + i*4 171 | r += buf[o+0] * f 172 | g += buf[o+1] * f 173 | b += buf[o+2] * f 174 | a += buf[o+3] * f 175 | } 176 | } 177 | 178 | // Add the central pixel. 179 | r += buf[off+0] * k0 180 | g += buf[off+1] * k0 181 | b += buf[off+2] * k0 182 | a += buf[off+3] * k0 183 | 184 | // Write to dst, clamping to the range [0, 255]. 185 | dstOff := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4 186 | dst.Pix[dstOff+0] = uint8(clamp(r+0.5, 0, 255)) 187 | dst.Pix[dstOff+1] = uint8(clamp(g+0.5, 0, 255)) 188 | dst.Pix[dstOff+2] = uint8(clamp(b+0.5, 0, 255)) 189 | dst.Pix[dstOff+3] = uint8(clamp(a+0.5, 0, 255)) 190 | } 191 | } 192 | 193 | return nil 194 | } 195 | 196 | func convolveRGBA(dst *image.RGBA, src image.Image, k Kernel) error { 197 | b := dst.Bounds() 198 | bs := src.Bounds() 199 | w := k.Weights() 200 | size, err := kernelSize(w) 201 | if err != nil { 202 | return err 203 | } 204 | radius := (size - 1) / 2 205 | 206 | for y := b.Min.Y; y < b.Max.Y; y++ { 207 | for x := b.Min.X; x < b.Max.X; x++ { 208 | if !image.Pt(x, y).In(bs) { 209 | continue 210 | } 211 | 212 | var r, g, b, a, adj float64 213 | for cy := y - radius; cy <= y+radius; cy++ { 214 | for cx := x - radius; cx <= x+radius; cx++ { 215 | factor := w[(cy-y+radius)*size+cx-x+radius] 216 | if !image.Pt(cx, cy).In(bs) { 217 | adj += factor 218 | } else { 219 | sr, sg, sb, sa := src.At(cx, cy).RGBA() 220 | r += float64(sr>>8) * factor 221 | g += float64(sg>>8) * factor 222 | b += float64(sb>>8) * factor 223 | a += float64(sa>>8) * factor 224 | } 225 | } 226 | } 227 | 228 | if adj != 0 { 229 | sr, sg, sb, sa := src.At(x, y).RGBA() 230 | r += float64(sr>>8) * adj 231 | g += float64(sg>>8) * adj 232 | b += float64(sb>>8) * adj 233 | a += float64(sa>>8) * adj 234 | } 235 | 236 | off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4 237 | dst.Pix[off+0] = uint8(clamp(r+0.5, 0, 0xff)) 238 | dst.Pix[off+1] = uint8(clamp(g+0.5, 0, 0xff)) 239 | dst.Pix[off+2] = uint8(clamp(b+0.5, 0, 0xff)) 240 | dst.Pix[off+3] = uint8(clamp(a+0.5, 0, 0xff)) 241 | } 242 | } 243 | 244 | return nil 245 | } 246 | 247 | // Convolve produces dst by applying the convolution kernel k to src. 248 | func Convolve(dst draw.Image, src image.Image, k Kernel) (err error) { 249 | if dst == nil || src == nil || k == nil { 250 | return nil 251 | } 252 | 253 | b := dst.Bounds() 254 | dstRgba, ok := dst.(*image.RGBA) 255 | if !ok { 256 | dstRgba = image.NewRGBA(b) 257 | } 258 | 259 | switch k := k.(type) { 260 | case *SeparableKernel: 261 | err = convolveRGBASep(dstRgba, src, k) 262 | default: 263 | err = convolveRGBA(dstRgba, src, k) 264 | } 265 | 266 | if err != nil { 267 | return err 268 | } 269 | 270 | if !ok { 271 | draw.Draw(dst, b, dstRgba, b.Min, draw.Src) 272 | } 273 | return nil 274 | } 275 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/bson/decimal.go: -------------------------------------------------------------------------------- 1 | // BSON library for Go 2 | // 3 | // Copyright (c) 2010-2012 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | package bson 28 | 29 | import ( 30 | "fmt" 31 | "strconv" 32 | "strings" 33 | ) 34 | 35 | // Decimal128 holds decimal128 BSON values. 36 | type Decimal128 struct { 37 | h, l uint64 38 | } 39 | 40 | func (d Decimal128) String() string { 41 | var pos int // positive sign 42 | var e int // exponent 43 | var h, l uint64 // significand high/low 44 | 45 | if d.h>>63&1 == 0 { 46 | pos = 1 47 | } 48 | 49 | switch d.h >> 58 & (1<<5 - 1) { 50 | case 0x1F: 51 | return "NaN" 52 | case 0x1E: 53 | return "-Inf"[pos:] 54 | } 55 | 56 | l = d.l 57 | if d.h>>61&3 == 3 { 58 | // Bits: 1*sign 2*ignored 14*exponent 111*significand. 59 | // Implicit 0b100 prefix in significand. 60 | e = int(d.h>>47&(1<<14-1)) - 6176 61 | //h = 4<<47 | d.h&(1<<47-1) 62 | // Spec says all of these values are out of range. 63 | h, l = 0, 0 64 | } else { 65 | // Bits: 1*sign 14*exponent 113*significand 66 | e = int(d.h>>49&(1<<14-1)) - 6176 67 | h = d.h & (1<<49 - 1) 68 | } 69 | 70 | // Would be handled by the logic below, but that's trivial and common. 71 | if h == 0 && l == 0 && e == 0 { 72 | return "-0"[pos:] 73 | } 74 | 75 | var repr [48]byte // Loop 5 times over 9 digits plus dot, negative sign, and leading zero. 76 | var last = len(repr) 77 | var i = len(repr) 78 | var dot = len(repr) + e 79 | var rem uint32 80 | Loop: 81 | for d9 := 0; d9 < 5; d9++ { 82 | h, l, rem = divmod(h, l, 1e9) 83 | for d1 := 0; d1 < 9; d1++ { 84 | // Handle "-0.0", "0.00123400", "-1.00E-6", "1.050E+3", etc. 85 | if i < len(repr) && (dot == i || l == 0 && h == 0 && rem > 0 && rem < 10 && (dot < i-6 || e > 0)) { 86 | e += len(repr) - i 87 | i-- 88 | repr[i] = '.' 89 | last = i - 1 90 | dot = len(repr) // Unmark. 91 | } 92 | c := '0' + byte(rem%10) 93 | rem /= 10 94 | i-- 95 | repr[i] = c 96 | // Handle "0E+3", "1E+3", etc. 97 | if l == 0 && h == 0 && rem == 0 && i == len(repr)-1 && (dot < i-5 || e > 0) { 98 | last = i 99 | break Loop 100 | } 101 | if c != '0' { 102 | last = i 103 | } 104 | // Break early. Works without it, but why. 105 | if dot > i && l == 0 && h == 0 && rem == 0 { 106 | break Loop 107 | } 108 | } 109 | } 110 | repr[last-1] = '-' 111 | last-- 112 | 113 | if e > 0 { 114 | return string(repr[last+pos:]) + "E+" + strconv.Itoa(e) 115 | } 116 | if e < 0 { 117 | return string(repr[last+pos:]) + "E" + strconv.Itoa(e) 118 | } 119 | return string(repr[last+pos:]) 120 | } 121 | 122 | func divmod(h, l uint64, div uint32) (qh, ql uint64, rem uint32) { 123 | div64 := uint64(div) 124 | a := h >> 32 125 | aq := a / div64 126 | ar := a % div64 127 | b := ar<<32 + h&(1<<32-1) 128 | bq := b / div64 129 | br := b % div64 130 | c := br<<32 + l>>32 131 | cq := c / div64 132 | cr := c % div64 133 | d := cr<<32 + l&(1<<32-1) 134 | dq := d / div64 135 | dr := d % div64 136 | return (aq<<32 | bq), (cq<<32 | dq), uint32(dr) 137 | } 138 | 139 | var dNaN = Decimal128{0x1F << 58, 0} 140 | var dPosInf = Decimal128{0x1E << 58, 0} 141 | var dNegInf = Decimal128{0x3E << 58, 0} 142 | 143 | func dErr(s string) (Decimal128, error) { 144 | return dNaN, fmt.Errorf("cannot parse %q as a decimal128", s) 145 | } 146 | 147 | func ParseDecimal128(s string) (Decimal128, error) { 148 | orig := s 149 | if s == "" { 150 | return dErr(orig) 151 | } 152 | neg := s[0] == '-' 153 | if neg || s[0] == '+' { 154 | s = s[1:] 155 | } 156 | 157 | if (len(s) == 3 || len(s) == 8) && (s[0] == 'N' || s[0] == 'n' || s[0] == 'I' || s[0] == 'i') { 158 | if s == "NaN" || s == "nan" || strings.EqualFold(s, "nan") { 159 | return dNaN, nil 160 | } 161 | if s == "Inf" || s == "inf" || strings.EqualFold(s, "inf") || strings.EqualFold(s, "infinity") { 162 | if neg { 163 | return dNegInf, nil 164 | } 165 | return dPosInf, nil 166 | } 167 | return dErr(orig) 168 | } 169 | 170 | var h, l uint64 171 | var e int 172 | 173 | var add, ovr uint32 174 | var mul uint32 = 1 175 | var dot = -1 176 | var digits = 0 177 | var i = 0 178 | for i < len(s) { 179 | c := s[i] 180 | if mul == 1e9 { 181 | h, l, ovr = muladd(h, l, mul, add) 182 | mul, add = 1, 0 183 | if ovr > 0 || h&((1<<15-1)<<49) > 0 { 184 | return dErr(orig) 185 | } 186 | } 187 | if c >= '0' && c <= '9' { 188 | i++ 189 | if c > '0' || digits > 0 { 190 | digits++ 191 | } 192 | if digits > 34 { 193 | if c == '0' { 194 | // Exact rounding. 195 | e++ 196 | continue 197 | } 198 | return dErr(orig) 199 | } 200 | mul *= 10 201 | add *= 10 202 | add += uint32(c - '0') 203 | continue 204 | } 205 | if c == '.' { 206 | i++ 207 | if dot >= 0 || i == 1 && len(s) == 1 { 208 | return dErr(orig) 209 | } 210 | if i == len(s) { 211 | break 212 | } 213 | if s[i] < '0' || s[i] > '9' || e > 0 { 214 | return dErr(orig) 215 | } 216 | dot = i 217 | continue 218 | } 219 | break 220 | } 221 | if i == 0 { 222 | return dErr(orig) 223 | } 224 | if mul > 1 { 225 | h, l, ovr = muladd(h, l, mul, add) 226 | if ovr > 0 || h&((1<<15-1)<<49) > 0 { 227 | return dErr(orig) 228 | } 229 | } 230 | if dot >= 0 { 231 | e += dot - i 232 | } 233 | if i+1 < len(s) && (s[i] == 'E' || s[i] == 'e') { 234 | i++ 235 | eneg := s[i] == '-' 236 | if eneg || s[i] == '+' { 237 | i++ 238 | if i == len(s) { 239 | return dErr(orig) 240 | } 241 | } 242 | n := 0 243 | for i < len(s) && n < 1e4 { 244 | c := s[i] 245 | i++ 246 | if c < '0' || c > '9' { 247 | return dErr(orig) 248 | } 249 | n *= 10 250 | n += int(c - '0') 251 | } 252 | if eneg { 253 | n = -n 254 | } 255 | e += n 256 | for e < -6176 { 257 | // Subnormal. 258 | var div uint32 = 1 259 | for div < 1e9 && e < -6176 { 260 | div *= 10 261 | e++ 262 | } 263 | var rem uint32 264 | h, l, rem = divmod(h, l, div) 265 | if rem > 0 { 266 | return dErr(orig) 267 | } 268 | } 269 | for e > 6111 { 270 | // Clamped. 271 | var mul uint32 = 1 272 | for mul < 1e9 && e > 6111 { 273 | mul *= 10 274 | e-- 275 | } 276 | h, l, ovr = muladd(h, l, mul, 0) 277 | if ovr > 0 || h&((1<<15-1)<<49) > 0 { 278 | return dErr(orig) 279 | } 280 | } 281 | if e < -6176 || e > 6111 { 282 | return dErr(orig) 283 | } 284 | } 285 | 286 | if i < len(s) { 287 | return dErr(orig) 288 | } 289 | 290 | h |= uint64(e+6176) & uint64(1<<14-1) << 49 291 | if neg { 292 | h |= 1 << 63 293 | } 294 | return Decimal128{h, l}, nil 295 | } 296 | 297 | func muladd(h, l uint64, mul uint32, add uint32) (resh, resl uint64, overflow uint32) { 298 | mul64 := uint64(mul) 299 | a := mul64 * (l & (1<<32 - 1)) 300 | b := a>>32 + mul64*(l>>32) 301 | c := b>>32 + mul64*(h&(1<<32-1)) 302 | d := c>>32 + mul64*(h>>32) 303 | 304 | a = a&(1<<32-1) + uint64(add) 305 | b = b&(1<<32-1) + a>>32 306 | c = c&(1<<32-1) + b>>32 307 | d = d&(1<<32-1) + c>>32 308 | 309 | return (d<<32 | c&(1<<32-1)), (b<<32 | a&(1<<32-1)), uint32(d >> 32) 310 | } 311 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/encode.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "encoding" 5 | "fmt" 6 | "reflect" 7 | "regexp" 8 | "sort" 9 | "strconv" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | type encoder struct { 15 | emitter yaml_emitter_t 16 | event yaml_event_t 17 | out []byte 18 | flow bool 19 | } 20 | 21 | func newEncoder() (e *encoder) { 22 | e = &encoder{} 23 | e.must(yaml_emitter_initialize(&e.emitter)) 24 | yaml_emitter_set_output_string(&e.emitter, &e.out) 25 | yaml_emitter_set_unicode(&e.emitter, true) 26 | e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)) 27 | e.emit() 28 | e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true)) 29 | e.emit() 30 | return e 31 | } 32 | 33 | func (e *encoder) finish() { 34 | e.must(yaml_document_end_event_initialize(&e.event, true)) 35 | e.emit() 36 | e.emitter.open_ended = false 37 | e.must(yaml_stream_end_event_initialize(&e.event)) 38 | e.emit() 39 | } 40 | 41 | func (e *encoder) destroy() { 42 | yaml_emitter_delete(&e.emitter) 43 | } 44 | 45 | func (e *encoder) emit() { 46 | // This will internally delete the e.event value. 47 | if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT { 48 | e.must(false) 49 | } 50 | } 51 | 52 | func (e *encoder) must(ok bool) { 53 | if !ok { 54 | msg := e.emitter.problem 55 | if msg == "" { 56 | msg = "unknown problem generating YAML content" 57 | } 58 | failf("%s", msg) 59 | } 60 | } 61 | 62 | func (e *encoder) marshal(tag string, in reflect.Value) { 63 | if !in.IsValid() { 64 | e.nilv() 65 | return 66 | } 67 | iface := in.Interface() 68 | if m, ok := iface.(Marshaler); ok { 69 | v, err := m.MarshalYAML() 70 | if err != nil { 71 | fail(err) 72 | } 73 | if v == nil { 74 | e.nilv() 75 | return 76 | } 77 | in = reflect.ValueOf(v) 78 | } else if m, ok := iface.(encoding.TextMarshaler); ok { 79 | text, err := m.MarshalText() 80 | if err != nil { 81 | fail(err) 82 | } 83 | in = reflect.ValueOf(string(text)) 84 | } 85 | switch in.Kind() { 86 | case reflect.Interface: 87 | if in.IsNil() { 88 | e.nilv() 89 | } else { 90 | e.marshal(tag, in.Elem()) 91 | } 92 | case reflect.Map: 93 | e.mapv(tag, in) 94 | case reflect.Ptr: 95 | if in.IsNil() { 96 | e.nilv() 97 | } else { 98 | e.marshal(tag, in.Elem()) 99 | } 100 | case reflect.Struct: 101 | e.structv(tag, in) 102 | case reflect.Slice: 103 | if in.Type().Elem() == mapItemType { 104 | e.itemsv(tag, in) 105 | } else { 106 | e.slicev(tag, in) 107 | } 108 | case reflect.String: 109 | e.stringv(tag, in) 110 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 111 | if in.Type() == durationType { 112 | e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) 113 | } else { 114 | e.intv(tag, in) 115 | } 116 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 117 | e.uintv(tag, in) 118 | case reflect.Float32, reflect.Float64: 119 | e.floatv(tag, in) 120 | case reflect.Bool: 121 | e.boolv(tag, in) 122 | default: 123 | panic("cannot marshal type: " + in.Type().String()) 124 | } 125 | } 126 | 127 | func (e *encoder) mapv(tag string, in reflect.Value) { 128 | e.mappingv(tag, func() { 129 | keys := keyList(in.MapKeys()) 130 | sort.Sort(keys) 131 | for _, k := range keys { 132 | e.marshal("", k) 133 | e.marshal("", in.MapIndex(k)) 134 | } 135 | }) 136 | } 137 | 138 | func (e *encoder) itemsv(tag string, in reflect.Value) { 139 | e.mappingv(tag, func() { 140 | slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) 141 | for _, item := range slice { 142 | e.marshal("", reflect.ValueOf(item.Key)) 143 | e.marshal("", reflect.ValueOf(item.Value)) 144 | } 145 | }) 146 | } 147 | 148 | func (e *encoder) structv(tag string, in reflect.Value) { 149 | sinfo, err := getStructInfo(in.Type()) 150 | if err != nil { 151 | panic(err) 152 | } 153 | e.mappingv(tag, func() { 154 | for _, info := range sinfo.FieldsList { 155 | var value reflect.Value 156 | if info.Inline == nil { 157 | value = in.Field(info.Num) 158 | } else { 159 | value = in.FieldByIndex(info.Inline) 160 | } 161 | if info.OmitEmpty && isZero(value) { 162 | continue 163 | } 164 | e.marshal("", reflect.ValueOf(info.Key)) 165 | e.flow = info.Flow 166 | e.marshal("", value) 167 | } 168 | if sinfo.InlineMap >= 0 { 169 | m := in.Field(sinfo.InlineMap) 170 | if m.Len() > 0 { 171 | e.flow = false 172 | keys := keyList(m.MapKeys()) 173 | sort.Sort(keys) 174 | for _, k := range keys { 175 | if _, found := sinfo.FieldsMap[k.String()]; found { 176 | panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) 177 | } 178 | e.marshal("", k) 179 | e.flow = false 180 | e.marshal("", m.MapIndex(k)) 181 | } 182 | } 183 | } 184 | }) 185 | } 186 | 187 | func (e *encoder) mappingv(tag string, f func()) { 188 | implicit := tag == "" 189 | style := yaml_BLOCK_MAPPING_STYLE 190 | if e.flow { 191 | e.flow = false 192 | style = yaml_FLOW_MAPPING_STYLE 193 | } 194 | e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 195 | e.emit() 196 | f() 197 | e.must(yaml_mapping_end_event_initialize(&e.event)) 198 | e.emit() 199 | } 200 | 201 | func (e *encoder) slicev(tag string, in reflect.Value) { 202 | implicit := tag == "" 203 | style := yaml_BLOCK_SEQUENCE_STYLE 204 | if e.flow { 205 | e.flow = false 206 | style = yaml_FLOW_SEQUENCE_STYLE 207 | } 208 | e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 209 | e.emit() 210 | n := in.Len() 211 | for i := 0; i < n; i++ { 212 | e.marshal("", in.Index(i)) 213 | } 214 | e.must(yaml_sequence_end_event_initialize(&e.event)) 215 | e.emit() 216 | } 217 | 218 | // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. 219 | // 220 | // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported 221 | // in YAML 1.2 and by this package, but these should be marshalled quoted for 222 | // the time being for compatibility with other parsers. 223 | func isBase60Float(s string) (result bool) { 224 | // Fast path. 225 | if s == "" { 226 | return false 227 | } 228 | c := s[0] 229 | if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { 230 | return false 231 | } 232 | // Do the full match. 233 | return base60float.MatchString(s) 234 | } 235 | 236 | // From http://yaml.org/type/float.html, except the regular expression there 237 | // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. 238 | var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) 239 | 240 | func (e *encoder) stringv(tag string, in reflect.Value) { 241 | var style yaml_scalar_style_t 242 | s := in.String() 243 | rtag, rs := resolve("", s) 244 | if rtag == yaml_BINARY_TAG { 245 | if tag == "" || tag == yaml_STR_TAG { 246 | tag = rtag 247 | s = rs.(string) 248 | } else if tag == yaml_BINARY_TAG { 249 | failf("explicitly tagged !!binary data must be base64-encoded") 250 | } else { 251 | failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) 252 | } 253 | } 254 | if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) { 255 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 256 | } else if strings.Contains(s, "\n") { 257 | style = yaml_LITERAL_SCALAR_STYLE 258 | } else { 259 | style = yaml_PLAIN_SCALAR_STYLE 260 | } 261 | e.emitScalar(s, "", tag, style) 262 | } 263 | 264 | func (e *encoder) boolv(tag string, in reflect.Value) { 265 | var s string 266 | if in.Bool() { 267 | s = "true" 268 | } else { 269 | s = "false" 270 | } 271 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 272 | } 273 | 274 | func (e *encoder) intv(tag string, in reflect.Value) { 275 | s := strconv.FormatInt(in.Int(), 10) 276 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 277 | } 278 | 279 | func (e *encoder) uintv(tag string, in reflect.Value) { 280 | s := strconv.FormatUint(in.Uint(), 10) 281 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 282 | } 283 | 284 | func (e *encoder) floatv(tag string, in reflect.Value) { 285 | // FIXME: Handle 64 bits here. 286 | s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32) 287 | switch s { 288 | case "+Inf": 289 | s = ".inf" 290 | case "-Inf": 291 | s = "-.inf" 292 | case "NaN": 293 | s = ".nan" 294 | } 295 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 296 | } 297 | 298 | func (e *encoder) nilv() { 299 | e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) 300 | } 301 | 302 | func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { 303 | implicit := tag == "" 304 | e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) 305 | e.emit() 306 | } 307 | -------------------------------------------------------------------------------- /vendor/gopkg.in/mgo.v2/internal/scram/scram.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2014 - Gustavo Niemeyer 4 | // 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are met: 9 | // 10 | // 1. Redistributions of source code must retain the above copyright notice, this 11 | // list of conditions and the following disclaimer. 12 | // 2. Redistributions in binary form must reproduce the above copyright notice, 13 | // this list of conditions and the following disclaimer in the documentation 14 | // and/or other materials provided with the distribution. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | // Pacakage scram implements a SCRAM-{SHA-1,etc} client per RFC5802. 28 | // 29 | // http://tools.ietf.org/html/rfc5802 30 | // 31 | package scram 32 | 33 | import ( 34 | "bytes" 35 | "crypto/hmac" 36 | "crypto/rand" 37 | "encoding/base64" 38 | "fmt" 39 | "hash" 40 | "strconv" 41 | "strings" 42 | ) 43 | 44 | // Client implements a SCRAM-* client (SCRAM-SHA-1, SCRAM-SHA-256, etc). 45 | // 46 | // A Client may be used within a SASL conversation with logic resembling: 47 | // 48 | // var in []byte 49 | // var client = scram.NewClient(sha1.New, user, pass) 50 | // for client.Step(in) { 51 | // out := client.Out() 52 | // // send out to server 53 | // in := serverOut 54 | // } 55 | // if client.Err() != nil { 56 | // // auth failed 57 | // } 58 | // 59 | type Client struct { 60 | newHash func() hash.Hash 61 | 62 | user string 63 | pass string 64 | step int 65 | out bytes.Buffer 66 | err error 67 | 68 | clientNonce []byte 69 | serverNonce []byte 70 | saltedPass []byte 71 | authMsg bytes.Buffer 72 | } 73 | 74 | // NewClient returns a new SCRAM-* client with the provided hash algorithm. 75 | // 76 | // For SCRAM-SHA-1, for example, use: 77 | // 78 | // client := scram.NewClient(sha1.New, user, pass) 79 | // 80 | func NewClient(newHash func() hash.Hash, user, pass string) *Client { 81 | c := &Client{ 82 | newHash: newHash, 83 | user: user, 84 | pass: pass, 85 | } 86 | c.out.Grow(256) 87 | c.authMsg.Grow(256) 88 | return c 89 | } 90 | 91 | // Out returns the data to be sent to the server in the current step. 92 | func (c *Client) Out() []byte { 93 | if c.out.Len() == 0 { 94 | return nil 95 | } 96 | return c.out.Bytes() 97 | } 98 | 99 | // Err returns the error that ocurred, or nil if there were no errors. 100 | func (c *Client) Err() error { 101 | return c.err 102 | } 103 | 104 | // SetNonce sets the client nonce to the provided value. 105 | // If not set, the nonce is generated automatically out of crypto/rand on the first step. 106 | func (c *Client) SetNonce(nonce []byte) { 107 | c.clientNonce = nonce 108 | } 109 | 110 | var escaper = strings.NewReplacer("=", "=3D", ",", "=2C") 111 | 112 | // Step processes the incoming data from the server and makes the 113 | // next round of data for the server available via Client.Out. 114 | // Step returns false if there are no errors and more data is 115 | // still expected. 116 | func (c *Client) Step(in []byte) bool { 117 | c.out.Reset() 118 | if c.step > 2 || c.err != nil { 119 | return false 120 | } 121 | c.step++ 122 | switch c.step { 123 | case 1: 124 | c.err = c.step1(in) 125 | case 2: 126 | c.err = c.step2(in) 127 | case 3: 128 | c.err = c.step3(in) 129 | } 130 | return c.step > 2 || c.err != nil 131 | } 132 | 133 | func (c *Client) step1(in []byte) error { 134 | if len(c.clientNonce) == 0 { 135 | const nonceLen = 6 136 | buf := make([]byte, nonceLen + b64.EncodedLen(nonceLen)) 137 | if _, err := rand.Read(buf[:nonceLen]); err != nil { 138 | return fmt.Errorf("cannot read random SCRAM-SHA-1 nonce from operating system: %v", err) 139 | } 140 | c.clientNonce = buf[nonceLen:] 141 | b64.Encode(c.clientNonce, buf[:nonceLen]) 142 | } 143 | c.authMsg.WriteString("n=") 144 | escaper.WriteString(&c.authMsg, c.user) 145 | c.authMsg.WriteString(",r=") 146 | c.authMsg.Write(c.clientNonce) 147 | 148 | c.out.WriteString("n,,") 149 | c.out.Write(c.authMsg.Bytes()) 150 | return nil 151 | } 152 | 153 | var b64 = base64.StdEncoding 154 | 155 | func (c *Client) step2(in []byte) error { 156 | c.authMsg.WriteByte(',') 157 | c.authMsg.Write(in) 158 | 159 | fields := bytes.Split(in, []byte(",")) 160 | if len(fields) != 3 { 161 | return fmt.Errorf("expected 3 fields in first SCRAM-SHA-1 server message, got %d: %q", len(fields), in) 162 | } 163 | if !bytes.HasPrefix(fields[0], []byte("r=")) || len(fields[0]) < 2 { 164 | return fmt.Errorf("server sent an invalid SCRAM-SHA-1 nonce: %q", fields[0]) 165 | } 166 | if !bytes.HasPrefix(fields[1], []byte("s=")) || len(fields[1]) < 6 { 167 | return fmt.Errorf("server sent an invalid SCRAM-SHA-1 salt: %q", fields[1]) 168 | } 169 | if !bytes.HasPrefix(fields[2], []byte("i=")) || len(fields[2]) < 6 { 170 | return fmt.Errorf("server sent an invalid SCRAM-SHA-1 iteration count: %q", fields[2]) 171 | } 172 | 173 | c.serverNonce = fields[0][2:] 174 | if !bytes.HasPrefix(c.serverNonce, c.clientNonce) { 175 | return fmt.Errorf("server SCRAM-SHA-1 nonce is not prefixed by client nonce: got %q, want %q+\"...\"", c.serverNonce, c.clientNonce) 176 | } 177 | 178 | salt := make([]byte, b64.DecodedLen(len(fields[1][2:]))) 179 | n, err := b64.Decode(salt, fields[1][2:]) 180 | if err != nil { 181 | return fmt.Errorf("cannot decode SCRAM-SHA-1 salt sent by server: %q", fields[1]) 182 | } 183 | salt = salt[:n] 184 | iterCount, err := strconv.Atoi(string(fields[2][2:])) 185 | if err != nil { 186 | return fmt.Errorf("server sent an invalid SCRAM-SHA-1 iteration count: %q", fields[2]) 187 | } 188 | c.saltPassword(salt, iterCount) 189 | 190 | c.authMsg.WriteString(",c=biws,r=") 191 | c.authMsg.Write(c.serverNonce) 192 | 193 | c.out.WriteString("c=biws,r=") 194 | c.out.Write(c.serverNonce) 195 | c.out.WriteString(",p=") 196 | c.out.Write(c.clientProof()) 197 | return nil 198 | } 199 | 200 | func (c *Client) step3(in []byte) error { 201 | var isv, ise bool 202 | var fields = bytes.Split(in, []byte(",")) 203 | if len(fields) == 1 { 204 | isv = bytes.HasPrefix(fields[0], []byte("v=")) 205 | ise = bytes.HasPrefix(fields[0], []byte("e=")) 206 | } 207 | if ise { 208 | return fmt.Errorf("SCRAM-SHA-1 authentication error: %s", fields[0][2:]) 209 | } else if !isv { 210 | return fmt.Errorf("unsupported SCRAM-SHA-1 final message from server: %q", in) 211 | } 212 | if !bytes.Equal(c.serverSignature(), fields[0][2:]) { 213 | return fmt.Errorf("cannot authenticate SCRAM-SHA-1 server signature: %q", fields[0][2:]) 214 | } 215 | return nil 216 | } 217 | 218 | func (c *Client) saltPassword(salt []byte, iterCount int) { 219 | mac := hmac.New(c.newHash, []byte(c.pass)) 220 | mac.Write(salt) 221 | mac.Write([]byte{0, 0, 0, 1}) 222 | ui := mac.Sum(nil) 223 | hi := make([]byte, len(ui)) 224 | copy(hi, ui) 225 | for i := 1; i < iterCount; i++ { 226 | mac.Reset() 227 | mac.Write(ui) 228 | mac.Sum(ui[:0]) 229 | for j, b := range ui { 230 | hi[j] ^= b 231 | } 232 | } 233 | c.saltedPass = hi 234 | } 235 | 236 | func (c *Client) clientProof() []byte { 237 | mac := hmac.New(c.newHash, c.saltedPass) 238 | mac.Write([]byte("Client Key")) 239 | clientKey := mac.Sum(nil) 240 | hash := c.newHash() 241 | hash.Write(clientKey) 242 | storedKey := hash.Sum(nil) 243 | mac = hmac.New(c.newHash, storedKey) 244 | mac.Write(c.authMsg.Bytes()) 245 | clientProof := mac.Sum(nil) 246 | for i, b := range clientKey { 247 | clientProof[i] ^= b 248 | } 249 | clientProof64 := make([]byte, b64.EncodedLen(len(clientProof))) 250 | b64.Encode(clientProof64, clientProof) 251 | return clientProof64 252 | } 253 | 254 | func (c *Client) serverSignature() []byte { 255 | mac := hmac.New(c.newHash, c.saltedPass) 256 | mac.Write([]byte("Server Key")) 257 | serverKey := mac.Sum(nil) 258 | 259 | mac = hmac.New(c.newHash, serverKey) 260 | mac.Write(c.authMsg.Bytes()) 261 | serverSignature := mac.Sum(nil) 262 | 263 | encoded := make([]byte, b64.EncodedLen(len(serverSignature))) 264 | b64.Encode(encoded, serverSignature) 265 | return encoded 266 | } 267 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package mux implements a request router and dispatcher. 7 | 8 | The name mux stands for "HTTP request multiplexer". Like the standard 9 | http.ServeMux, mux.Router matches incoming requests against a list of 10 | registered routes and calls a handler for the route that matches the URL 11 | or other conditions. The main features are: 12 | 13 | * Requests can be matched based on URL host, path, path prefix, schemes, 14 | header and query values, HTTP methods or using custom matchers. 15 | * URL hosts and paths can have variables with an optional regular 16 | expression. 17 | * Registered URLs can be built, or "reversed", which helps maintaining 18 | references to resources. 19 | * Routes can be used as subrouters: nested routes are only tested if the 20 | parent route matches. This is useful to define groups of routes that 21 | share common conditions like a host, a path prefix or other repeated 22 | attributes. As a bonus, this optimizes request matching. 23 | * It implements the http.Handler interface so it is compatible with the 24 | standard http.ServeMux. 25 | 26 | Let's start registering a couple of URL paths and handlers: 27 | 28 | func main() { 29 | r := mux.NewRouter() 30 | r.HandleFunc("/", HomeHandler) 31 | r.HandleFunc("/products", ProductsHandler) 32 | r.HandleFunc("/articles", ArticlesHandler) 33 | http.Handle("/", r) 34 | } 35 | 36 | Here we register three routes mapping URL paths to handlers. This is 37 | equivalent to how http.HandleFunc() works: if an incoming request URL matches 38 | one of the paths, the corresponding handler is called passing 39 | (http.ResponseWriter, *http.Request) as parameters. 40 | 41 | Paths can have variables. They are defined using the format {name} or 42 | {name:pattern}. If a regular expression pattern is not defined, the matched 43 | variable will be anything until the next slash. For example: 44 | 45 | r := mux.NewRouter() 46 | r.HandleFunc("/products/{key}", ProductHandler) 47 | r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) 48 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) 49 | 50 | The names are used to create a map of route variables which can be retrieved 51 | calling mux.Vars(): 52 | 53 | vars := mux.Vars(request) 54 | category := vars["category"] 55 | 56 | And this is all you need to know about the basic usage. More advanced options 57 | are explained below. 58 | 59 | Routes can also be restricted to a domain or subdomain. Just define a host 60 | pattern to be matched. They can also have variables: 61 | 62 | r := mux.NewRouter() 63 | // Only matches if domain is "www.example.com". 64 | r.Host("www.example.com") 65 | // Matches a dynamic subdomain. 66 | r.Host("{subdomain:[a-z]+}.domain.com") 67 | 68 | There are several other matchers that can be added. To match path prefixes: 69 | 70 | r.PathPrefix("/products/") 71 | 72 | ...or HTTP methods: 73 | 74 | r.Methods("GET", "POST") 75 | 76 | ...or URL schemes: 77 | 78 | r.Schemes("https") 79 | 80 | ...or header values: 81 | 82 | r.Headers("X-Requested-With", "XMLHttpRequest") 83 | 84 | ...or query values: 85 | 86 | r.Queries("key", "value") 87 | 88 | ...or to use a custom matcher function: 89 | 90 | r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { 91 | return r.ProtoMajor == 0 92 | }) 93 | 94 | ...and finally, it is possible to combine several matchers in a single route: 95 | 96 | r.HandleFunc("/products", ProductsHandler). 97 | Host("www.example.com"). 98 | Methods("GET"). 99 | Schemes("http") 100 | 101 | Setting the same matching conditions again and again can be boring, so we have 102 | a way to group several routes that share the same requirements. 103 | We call it "subrouting". 104 | 105 | For example, let's say we have several URLs that should only match when the 106 | host is "www.example.com". Create a route for that host and get a "subrouter" 107 | from it: 108 | 109 | r := mux.NewRouter() 110 | s := r.Host("www.example.com").Subrouter() 111 | 112 | Then register routes in the subrouter: 113 | 114 | s.HandleFunc("/products/", ProductsHandler) 115 | s.HandleFunc("/products/{key}", ProductHandler) 116 | s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) 117 | 118 | The three URL paths we registered above will only be tested if the domain is 119 | "www.example.com", because the subrouter is tested first. This is not 120 | only convenient, but also optimizes request matching. You can create 121 | subrouters combining any attribute matchers accepted by a route. 122 | 123 | Subrouters can be used to create domain or path "namespaces": you define 124 | subrouters in a central place and then parts of the app can register its 125 | paths relatively to a given subrouter. 126 | 127 | There's one more thing about subroutes. When a subrouter has a path prefix, 128 | the inner routes use it as base for their paths: 129 | 130 | r := mux.NewRouter() 131 | s := r.PathPrefix("/products").Subrouter() 132 | // "/products/" 133 | s.HandleFunc("/", ProductsHandler) 134 | // "/products/{key}/" 135 | s.HandleFunc("/{key}/", ProductHandler) 136 | // "/products/{key}/details" 137 | s.HandleFunc("/{key}/details", ProductDetailsHandler) 138 | 139 | Note that the path provided to PathPrefix() represents a "wildcard": calling 140 | PathPrefix("/static/").Handler(...) means that the handler will be passed any 141 | request that matches "/static/*". This makes it easy to serve static files with mux: 142 | 143 | func main() { 144 | var dir string 145 | 146 | flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir") 147 | flag.Parse() 148 | r := mux.NewRouter() 149 | 150 | // This will serve files under http://localhost:8000/static/ 151 | r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) 152 | 153 | srv := &http.Server{ 154 | Handler: r, 155 | Addr: "127.0.0.1:8000", 156 | // Good practice: enforce timeouts for servers you create! 157 | WriteTimeout: 15 * time.Second, 158 | ReadTimeout: 15 * time.Second, 159 | } 160 | 161 | log.Fatal(srv.ListenAndServe()) 162 | } 163 | 164 | Now let's see how to build registered URLs. 165 | 166 | Routes can be named. All routes that define a name can have their URLs built, 167 | or "reversed". We define a name calling Name() on a route. For example: 168 | 169 | r := mux.NewRouter() 170 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). 171 | Name("article") 172 | 173 | To build a URL, get the route and call the URL() method, passing a sequence of 174 | key/value pairs for the route variables. For the previous route, we would do: 175 | 176 | url, err := r.Get("article").URL("category", "technology", "id", "42") 177 | 178 | ...and the result will be a url.URL with the following path: 179 | 180 | "/articles/technology/42" 181 | 182 | This also works for host variables: 183 | 184 | r := mux.NewRouter() 185 | r.Host("{subdomain}.domain.com"). 186 | Path("/articles/{category}/{id:[0-9]+}"). 187 | HandlerFunc(ArticleHandler). 188 | Name("article") 189 | 190 | // url.String() will be "http://news.domain.com/articles/technology/42" 191 | url, err := r.Get("article").URL("subdomain", "news", 192 | "category", "technology", 193 | "id", "42") 194 | 195 | All variables defined in the route are required, and their values must 196 | conform to the corresponding patterns. These requirements guarantee that a 197 | generated URL will always match a registered route -- the only exception is 198 | for explicitly defined "build-only" routes which never match. 199 | 200 | Regex support also exists for matching Headers within a route. For example, we could do: 201 | 202 | r.HeadersRegexp("Content-Type", "application/(text|json)") 203 | 204 | ...and the route will match both requests with a Content-Type of `application/json` as well as 205 | `application/text` 206 | 207 | There's also a way to build only the URL host or path for a route: 208 | use the methods URLHost() or URLPath() instead. For the previous route, 209 | we would do: 210 | 211 | // "http://news.domain.com/" 212 | host, err := r.Get("article").URLHost("subdomain", "news") 213 | 214 | // "/articles/technology/42" 215 | path, err := r.Get("article").URLPath("category", "technology", "id", "42") 216 | 217 | And if you use subrouters, host and path defined separately can be built 218 | as well: 219 | 220 | r := mux.NewRouter() 221 | s := r.Host("{subdomain}.domain.com").Subrouter() 222 | s.Path("/articles/{category}/{id:[0-9]+}"). 223 | HandlerFunc(ArticleHandler). 224 | Name("article") 225 | 226 | // "http://news.domain.com/articles/technology/42" 227 | url, err := r.Get("article").URL("subdomain", "news", 228 | "category", "technology", 229 | "id", "42") 230 | */ 231 | package mux 232 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2011-2014 - Canonical Inc. 3 | 4 | This software is licensed under the LGPLv3, included below. 5 | 6 | As a special exception to the GNU Lesser General Public License version 3 7 | ("LGPL3"), the copyright holders of this Library give you permission to 8 | convey to a third party a Combined Work that links statically or dynamically 9 | to this Library without providing any Minimal Corresponding Source or 10 | Minimal Application Code as set out in 4d or providing the installation 11 | information set out in section 4e, provided that you comply with the other 12 | provisions of LGPL3 and provided that you meet, for the Application the 13 | terms and conditions of the license(s) which apply to the Application. 14 | 15 | Except as stated in this special exception, the provisions of LGPL3 will 16 | continue to comply in full to this Library. If you modify this Library, you 17 | may apply this exception to your version of this Library, but you are not 18 | obliged to do so. If you do not wish to do so, delete this exception 19 | statement from your version. This exception does not (and cannot) modify any 20 | license terms which apply to the Application, with which you must still 21 | comply. 22 | 23 | 24 | GNU LESSER GENERAL PUBLIC LICENSE 25 | Version 3, 29 June 2007 26 | 27 | Copyright (C) 2007 Free Software Foundation, Inc. 28 | Everyone is permitted to copy and distribute verbatim copies 29 | of this license document, but changing it is not allowed. 30 | 31 | 32 | This version of the GNU Lesser General Public License incorporates 33 | the terms and conditions of version 3 of the GNU General Public 34 | License, supplemented by the additional permissions listed below. 35 | 36 | 0. Additional Definitions. 37 | 38 | As used herein, "this License" refers to version 3 of the GNU Lesser 39 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 40 | General Public License. 41 | 42 | "The Library" refers to a covered work governed by this License, 43 | other than an Application or a Combined Work as defined below. 44 | 45 | An "Application" is any work that makes use of an interface provided 46 | by the Library, but which is not otherwise based on the Library. 47 | Defining a subclass of a class defined by the Library is deemed a mode 48 | of using an interface provided by the Library. 49 | 50 | A "Combined Work" is a work produced by combining or linking an 51 | Application with the Library. The particular version of the Library 52 | with which the Combined Work was made is also called the "Linked 53 | Version". 54 | 55 | The "Minimal Corresponding Source" for a Combined Work means the 56 | Corresponding Source for the Combined Work, excluding any source code 57 | for portions of the Combined Work that, considered in isolation, are 58 | based on the Application, and not on the Linked Version. 59 | 60 | The "Corresponding Application Code" for a Combined Work means the 61 | object code and/or source code for the Application, including any data 62 | and utility programs needed for reproducing the Combined Work from the 63 | Application, but excluding the System Libraries of the Combined Work. 64 | 65 | 1. Exception to Section 3 of the GNU GPL. 66 | 67 | You may convey a covered work under sections 3 and 4 of this License 68 | without being bound by section 3 of the GNU GPL. 69 | 70 | 2. Conveying Modified Versions. 71 | 72 | If you modify a copy of the Library, and, in your modifications, a 73 | facility refers to a function or data to be supplied by an Application 74 | that uses the facility (other than as an argument passed when the 75 | facility is invoked), then you may convey a copy of the modified 76 | version: 77 | 78 | a) under this License, provided that you make a good faith effort to 79 | ensure that, in the event an Application does not supply the 80 | function or data, the facility still operates, and performs 81 | whatever part of its purpose remains meaningful, or 82 | 83 | b) under the GNU GPL, with none of the additional permissions of 84 | this License applicable to that copy. 85 | 86 | 3. Object Code Incorporating Material from Library Header Files. 87 | 88 | The object code form of an Application may incorporate material from 89 | a header file that is part of the Library. You may convey such object 90 | code under terms of your choice, provided that, if the incorporated 91 | material is not limited to numerical parameters, data structure 92 | layouts and accessors, or small macros, inline functions and templates 93 | (ten or fewer lines in length), you do both of the following: 94 | 95 | a) Give prominent notice with each copy of the object code that the 96 | Library is used in it and that the Library and its use are 97 | covered by this License. 98 | 99 | b) Accompany the object code with a copy of the GNU GPL and this license 100 | document. 101 | 102 | 4. Combined Works. 103 | 104 | You may convey a Combined Work under terms of your choice that, 105 | taken together, effectively do not restrict modification of the 106 | portions of the Library contained in the Combined Work and reverse 107 | engineering for debugging such modifications, if you also do each of 108 | the following: 109 | 110 | a) Give prominent notice with each copy of the Combined Work that 111 | the Library is used in it and that the Library and its use are 112 | covered by this License. 113 | 114 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 115 | document. 116 | 117 | c) For a Combined Work that displays copyright notices during 118 | execution, include the copyright notice for the Library among 119 | these notices, as well as a reference directing the user to the 120 | copies of the GNU GPL and this license document. 121 | 122 | d) Do one of the following: 123 | 124 | 0) Convey the Minimal Corresponding Source under the terms of this 125 | License, and the Corresponding Application Code in a form 126 | suitable for, and under terms that permit, the user to 127 | recombine or relink the Application with a modified version of 128 | the Linked Version to produce a modified Combined Work, in the 129 | manner specified by section 6 of the GNU GPL for conveying 130 | Corresponding Source. 131 | 132 | 1) Use a suitable shared library mechanism for linking with the 133 | Library. A suitable mechanism is one that (a) uses at run time 134 | a copy of the Library already present on the user's computer 135 | system, and (b) will operate properly with a modified version 136 | of the Library that is interface-compatible with the Linked 137 | Version. 138 | 139 | e) Provide Installation Information, but only if you would otherwise 140 | be required to provide such information under section 6 of the 141 | GNU GPL, and only to the extent that such information is 142 | necessary to install and execute a modified version of the 143 | Combined Work produced by recombining or relinking the 144 | Application with a modified version of the Linked Version. (If 145 | you use option 4d0, the Installation Information must accompany 146 | the Minimal Corresponding Source and Corresponding Application 147 | Code. If you use option 4d1, you must provide the Installation 148 | Information in the manner specified by section 6 of the GNU GPL 149 | for conveying Corresponding Source.) 150 | 151 | 5. Combined Libraries. 152 | 153 | You may place library facilities that are a work based on the 154 | Library side by side in a single library together with other library 155 | facilities that are not Applications and are not covered by this 156 | License, and convey such a combined library under terms of your 157 | choice, if you do both of the following: 158 | 159 | a) Accompany the combined library with a copy of the same work based 160 | on the Library, uncombined with any other library facilities, 161 | conveyed under the terms of this License. 162 | 163 | b) Give prominent notice with the combined library that part of it 164 | is a work based on the Library, and explaining where to find the 165 | accompanying uncombined form of the same work. 166 | 167 | 6. Revised Versions of the GNU Lesser General Public License. 168 | 169 | The Free Software Foundation may publish revised and/or new versions 170 | of the GNU Lesser General Public License from time to time. Such new 171 | versions will be similar in spirit to the present version, but may 172 | differ in detail to address new problems or concerns. 173 | 174 | Each version is given a distinguishing version number. If the 175 | Library as you received it specifies that a certain numbered version 176 | of the GNU Lesser General Public License "or any later version" 177 | applies to it, you have the option of following the terms and 178 | conditions either of that published version or of any later version 179 | published by the Free Software Foundation. If the Library as you 180 | received it does not specify a version number of the GNU Lesser 181 | General Public License, you may choose any version of the GNU Lesser 182 | General Public License ever published by the Free Software Foundation. 183 | 184 | If the Library as you received it specifies that a proxy can decide 185 | whether future versions of the GNU Lesser General Public License shall 186 | apply, that proxy's public statement of acceptance of any version is 187 | permanent authorization for you to choose that version for the 188 | Library. 189 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/regexp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mux 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "net/http" 11 | "net/url" 12 | "regexp" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | // newRouteRegexp parses a route template and returns a routeRegexp, 18 | // used to match a host, a path or a query string. 19 | // 20 | // It will extract named variables, assemble a regexp to be matched, create 21 | // a "reverse" template to build URLs and compile regexps to validate variable 22 | // values used in URL building. 23 | // 24 | // Previously we accepted only Python-like identifiers for variable 25 | // names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that 26 | // name and pattern can't be empty, and names can't contain a colon. 27 | func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) { 28 | // Check if it is well-formed. 29 | idxs, errBraces := braceIndices(tpl) 30 | if errBraces != nil { 31 | return nil, errBraces 32 | } 33 | // Backup the original. 34 | template := tpl 35 | // Now let's parse it. 36 | defaultPattern := "[^/]+" 37 | if matchQuery { 38 | defaultPattern = "[^?&]*" 39 | } else if matchHost { 40 | defaultPattern = "[^.]+" 41 | matchPrefix = false 42 | } 43 | // Only match strict slash if not matching 44 | if matchPrefix || matchHost || matchQuery { 45 | strictSlash = false 46 | } 47 | // Set a flag for strictSlash. 48 | endSlash := false 49 | if strictSlash && strings.HasSuffix(tpl, "/") { 50 | tpl = tpl[:len(tpl)-1] 51 | endSlash = true 52 | } 53 | varsN := make([]string, len(idxs)/2) 54 | varsR := make([]*regexp.Regexp, len(idxs)/2) 55 | pattern := bytes.NewBufferString("") 56 | pattern.WriteByte('^') 57 | reverse := bytes.NewBufferString("") 58 | var end int 59 | var err error 60 | for i := 0; i < len(idxs); i += 2 { 61 | // Set all values we are interested in. 62 | raw := tpl[end:idxs[i]] 63 | end = idxs[i+1] 64 | parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2) 65 | name := parts[0] 66 | patt := defaultPattern 67 | if len(parts) == 2 { 68 | patt = parts[1] 69 | } 70 | // Name or pattern can't be empty. 71 | if name == "" || patt == "" { 72 | return nil, fmt.Errorf("mux: missing name or pattern in %q", 73 | tpl[idxs[i]:end]) 74 | } 75 | // Build the regexp pattern. 76 | fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt) 77 | 78 | // Build the reverse template. 79 | fmt.Fprintf(reverse, "%s%%s", raw) 80 | 81 | // Append variable name and compiled pattern. 82 | varsN[i/2] = name 83 | varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt)) 84 | if err != nil { 85 | return nil, err 86 | } 87 | } 88 | // Add the remaining. 89 | raw := tpl[end:] 90 | pattern.WriteString(regexp.QuoteMeta(raw)) 91 | if strictSlash { 92 | pattern.WriteString("[/]?") 93 | } 94 | if matchQuery { 95 | // Add the default pattern if the query value is empty 96 | if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" { 97 | pattern.WriteString(defaultPattern) 98 | } 99 | } 100 | if !matchPrefix { 101 | pattern.WriteByte('$') 102 | } 103 | reverse.WriteString(raw) 104 | if endSlash { 105 | reverse.WriteByte('/') 106 | } 107 | // Compile full regexp. 108 | reg, errCompile := regexp.Compile(pattern.String()) 109 | if errCompile != nil { 110 | return nil, errCompile 111 | } 112 | // Done! 113 | return &routeRegexp{ 114 | template: template, 115 | matchHost: matchHost, 116 | matchQuery: matchQuery, 117 | strictSlash: strictSlash, 118 | regexp: reg, 119 | reverse: reverse.String(), 120 | varsN: varsN, 121 | varsR: varsR, 122 | }, nil 123 | } 124 | 125 | // routeRegexp stores a regexp to match a host or path and information to 126 | // collect and validate route variables. 127 | type routeRegexp struct { 128 | // The unmodified template. 129 | template string 130 | // True for host match, false for path or query string match. 131 | matchHost bool 132 | // True for query string match, false for path and host match. 133 | matchQuery bool 134 | // The strictSlash value defined on the route, but disabled if PathPrefix was used. 135 | strictSlash bool 136 | // Expanded regexp. 137 | regexp *regexp.Regexp 138 | // Reverse template. 139 | reverse string 140 | // Variable names. 141 | varsN []string 142 | // Variable regexps (validators). 143 | varsR []*regexp.Regexp 144 | } 145 | 146 | // Match matches the regexp against the URL host or path. 147 | func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { 148 | if !r.matchHost { 149 | if r.matchQuery { 150 | return r.matchQueryString(req) 151 | } 152 | 153 | return r.regexp.MatchString(req.URL.Path) 154 | } 155 | 156 | return r.regexp.MatchString(getHost(req)) 157 | } 158 | 159 | // url builds a URL part using the given values. 160 | func (r *routeRegexp) url(values map[string]string) (string, error) { 161 | urlValues := make([]interface{}, len(r.varsN)) 162 | for k, v := range r.varsN { 163 | value, ok := values[v] 164 | if !ok { 165 | return "", fmt.Errorf("mux: missing route variable %q", v) 166 | } 167 | urlValues[k] = value 168 | } 169 | rv := fmt.Sprintf(r.reverse, urlValues...) 170 | if !r.regexp.MatchString(rv) { 171 | // The URL is checked against the full regexp, instead of checking 172 | // individual variables. This is faster but to provide a good error 173 | // message, we check individual regexps if the URL doesn't match. 174 | for k, v := range r.varsN { 175 | if !r.varsR[k].MatchString(values[v]) { 176 | return "", fmt.Errorf( 177 | "mux: variable %q doesn't match, expected %q", values[v], 178 | r.varsR[k].String()) 179 | } 180 | } 181 | } 182 | return rv, nil 183 | } 184 | 185 | // getURLQuery returns a single query parameter from a request URL. 186 | // For a URL with foo=bar&baz=ding, we return only the relevant key 187 | // value pair for the routeRegexp. 188 | func (r *routeRegexp) getURLQuery(req *http.Request) string { 189 | if !r.matchQuery { 190 | return "" 191 | } 192 | templateKey := strings.SplitN(r.template, "=", 2)[0] 193 | for key, vals := range req.URL.Query() { 194 | if key == templateKey && len(vals) > 0 { 195 | return key + "=" + vals[0] 196 | } 197 | } 198 | return "" 199 | } 200 | 201 | func (r *routeRegexp) matchQueryString(req *http.Request) bool { 202 | return r.regexp.MatchString(r.getURLQuery(req)) 203 | } 204 | 205 | // braceIndices returns the first level curly brace indices from a string. 206 | // It returns an error in case of unbalanced braces. 207 | func braceIndices(s string) ([]int, error) { 208 | var level, idx int 209 | var idxs []int 210 | for i := 0; i < len(s); i++ { 211 | switch s[i] { 212 | case '{': 213 | if level++; level == 1 { 214 | idx = i 215 | } 216 | case '}': 217 | if level--; level == 0 { 218 | idxs = append(idxs, idx, i+1) 219 | } else if level < 0 { 220 | return nil, fmt.Errorf("mux: unbalanced braces in %q", s) 221 | } 222 | } 223 | } 224 | if level != 0 { 225 | return nil, fmt.Errorf("mux: unbalanced braces in %q", s) 226 | } 227 | return idxs, nil 228 | } 229 | 230 | // varGroupName builds a capturing group name for the indexed variable. 231 | func varGroupName(idx int) string { 232 | return "v" + strconv.Itoa(idx) 233 | } 234 | 235 | // ---------------------------------------------------------------------------- 236 | // routeRegexpGroup 237 | // ---------------------------------------------------------------------------- 238 | 239 | // routeRegexpGroup groups the route matchers that carry variables. 240 | type routeRegexpGroup struct { 241 | host *routeRegexp 242 | path *routeRegexp 243 | queries []*routeRegexp 244 | } 245 | 246 | // setMatch extracts the variables from the URL once a route matches. 247 | func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { 248 | // Store host variables. 249 | if v.host != nil { 250 | host := getHost(req) 251 | matches := v.host.regexp.FindStringSubmatchIndex(host) 252 | if len(matches) > 0 { 253 | extractVars(host, matches, v.host.varsN, m.Vars) 254 | } 255 | } 256 | // Store path variables. 257 | if v.path != nil { 258 | matches := v.path.regexp.FindStringSubmatchIndex(req.URL.Path) 259 | if len(matches) > 0 { 260 | extractVars(req.URL.Path, matches, v.path.varsN, m.Vars) 261 | // Check if we should redirect. 262 | if v.path.strictSlash { 263 | p1 := strings.HasSuffix(req.URL.Path, "/") 264 | p2 := strings.HasSuffix(v.path.template, "/") 265 | if p1 != p2 { 266 | u, _ := url.Parse(req.URL.String()) 267 | if p1 { 268 | u.Path = u.Path[:len(u.Path)-1] 269 | } else { 270 | u.Path += "/" 271 | } 272 | m.Handler = http.RedirectHandler(u.String(), 301) 273 | } 274 | } 275 | } 276 | } 277 | // Store query string variables. 278 | for _, q := range v.queries { 279 | queryURL := q.getURLQuery(req) 280 | matches := q.regexp.FindStringSubmatchIndex(queryURL) 281 | if len(matches) > 0 { 282 | extractVars(queryURL, matches, q.varsN, m.Vars) 283 | } 284 | } 285 | } 286 | 287 | // getHost tries its best to return the request host. 288 | func getHost(r *http.Request) string { 289 | if r.URL.IsAbs() { 290 | return r.URL.Host 291 | } 292 | host := r.Host 293 | // Slice off any port information. 294 | if i := strings.Index(host, ":"); i != -1 { 295 | host = host[:i] 296 | } 297 | return host 298 | 299 | } 300 | 301 | func extractVars(input string, matches []int, names []string, output map[string]string) { 302 | matchesCount := 0 303 | prevEnd := -1 304 | for i := 2; i < len(matches) && matchesCount < len(names); i += 2 { 305 | if prevEnd < matches[i+1] { 306 | value := input[matches[i]:matches[i+1]] 307 | output[names[matchesCount]] = value 308 | prevEnd = matches[i+1] 309 | matchesCount++ 310 | } 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/README.md: -------------------------------------------------------------------------------- 1 | gorilla/mux 2 | === 3 | [![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) 4 | [![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux) 5 | 6 | ![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png) 7 | 8 | http://www.gorillatoolkit.org/pkg/mux 9 | 10 | Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to 11 | their respective handler. 12 | 13 | The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are: 14 | 15 | * It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`. 16 | * Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers. 17 | * URL hosts and paths can have variables with an optional regular expression. 18 | * Registered URLs can be built, or "reversed", which helps maintaining references to resources. 19 | * Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching. 20 | 21 | --- 22 | 23 | * [Install](#install) 24 | * [Examples](#examples) 25 | * [Matching Routes](#matching-routes) 26 | * [Static Files](#static-files) 27 | * [Registered URLs](#registered-urls) 28 | * [Full Example](#full-example) 29 | 30 | --- 31 | 32 | ## Install 33 | 34 | With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain: 35 | 36 | ```sh 37 | go get -u github.com/gorilla/mux 38 | ``` 39 | 40 | ## Examples 41 | 42 | Let's start registering a couple of URL paths and handlers: 43 | 44 | ```go 45 | func main() { 46 | r := mux.NewRouter() 47 | r.HandleFunc("/", HomeHandler) 48 | r.HandleFunc("/products", ProductsHandler) 49 | r.HandleFunc("/articles", ArticlesHandler) 50 | http.Handle("/", r) 51 | } 52 | ``` 53 | 54 | Here we register three routes mapping URL paths to handlers. This is equivalent to how `http.HandleFunc()` works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (`http.ResponseWriter`, `*http.Request`) as parameters. 55 | 56 | Paths can have variables. They are defined using the format `{name}` or `{name:pattern}`. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example: 57 | 58 | ```go 59 | r := mux.NewRouter() 60 | r.HandleFunc("/products/{key}", ProductHandler) 61 | r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) 62 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) 63 | ``` 64 | 65 | The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`: 66 | 67 | ```go 68 | vars := mux.Vars(request) 69 | category := vars["category"] 70 | ``` 71 | 72 | And this is all you need to know about the basic usage. More advanced options are explained below. 73 | 74 | ### Matching Routes 75 | 76 | Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables: 77 | 78 | ```go 79 | r := mux.NewRouter() 80 | // Only matches if domain is "www.example.com". 81 | r.Host("www.example.com") 82 | // Matches a dynamic subdomain. 83 | r.Host("{subdomain:[a-z]+}.domain.com") 84 | ``` 85 | 86 | There are several other matchers that can be added. To match path prefixes: 87 | 88 | ```go 89 | r.PathPrefix("/products/") 90 | ``` 91 | 92 | ...or HTTP methods: 93 | 94 | ```go 95 | r.Methods("GET", "POST") 96 | ``` 97 | 98 | ...or URL schemes: 99 | 100 | ```go 101 | r.Schemes("https") 102 | ``` 103 | 104 | ...or header values: 105 | 106 | ```go 107 | r.Headers("X-Requested-With", "XMLHttpRequest") 108 | ``` 109 | 110 | ...or query values: 111 | 112 | ```go 113 | r.Queries("key", "value") 114 | ``` 115 | 116 | ...or to use a custom matcher function: 117 | 118 | ```go 119 | r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { 120 | return r.ProtoMajor == 0 121 | }) 122 | ``` 123 | 124 | ...and finally, it is possible to combine several matchers in a single route: 125 | 126 | ```go 127 | r.HandleFunc("/products", ProductsHandler). 128 | Host("www.example.com"). 129 | Methods("GET"). 130 | Schemes("http") 131 | ``` 132 | 133 | Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting". 134 | 135 | For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it: 136 | 137 | ```go 138 | r := mux.NewRouter() 139 | s := r.Host("www.example.com").Subrouter() 140 | ``` 141 | 142 | Then register routes in the subrouter: 143 | 144 | ```go 145 | s.HandleFunc("/products/", ProductsHandler) 146 | s.HandleFunc("/products/{key}", ProductHandler) 147 | s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) 148 | ``` 149 | 150 | The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route. 151 | 152 | Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter. 153 | 154 | There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths: 155 | 156 | ```go 157 | r := mux.NewRouter() 158 | s := r.PathPrefix("/products").Subrouter() 159 | // "/products/" 160 | s.HandleFunc("/", ProductsHandler) 161 | // "/products/{key}/" 162 | s.HandleFunc("/{key}/", ProductHandler) 163 | // "/products/{key}/details" 164 | s.HandleFunc("/{key}/details", ProductDetailsHandler) 165 | ``` 166 | 167 | ### Static Files 168 | 169 | Note that the path provided to `PathPrefix()` represents a "wildcard": calling 170 | `PathPrefix("/static/").Handler(...)` means that the handler will be passed any 171 | request that matches "/static/*". This makes it easy to serve static files with mux: 172 | 173 | ```go 174 | func main() { 175 | var dir string 176 | 177 | flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir") 178 | flag.Parse() 179 | r := mux.NewRouter() 180 | 181 | // This will serve files under http://localhost:8000/static/ 182 | r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) 183 | 184 | srv := &http.Server{ 185 | Handler: r, 186 | Addr: "127.0.0.1:8000", 187 | // Good practice: enforce timeouts for servers you create! 188 | WriteTimeout: 15 * time.Second, 189 | ReadTimeout: 15 * time.Second, 190 | } 191 | 192 | log.Fatal(srv.ListenAndServe()) 193 | } 194 | ``` 195 | 196 | ### Registered URLs 197 | 198 | Now let's see how to build registered URLs. 199 | 200 | Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example: 201 | 202 | ```go 203 | r := mux.NewRouter() 204 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). 205 | Name("article") 206 | ``` 207 | 208 | To build a URL, get the route and call the `URL()` method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do: 209 | 210 | ```go 211 | url, err := r.Get("article").URL("category", "technology", "id", "42") 212 | ``` 213 | 214 | ...and the result will be a `url.URL` with the following path: 215 | 216 | ``` 217 | "/articles/technology/42" 218 | ``` 219 | 220 | This also works for host variables: 221 | 222 | ```go 223 | r := mux.NewRouter() 224 | r.Host("{subdomain}.domain.com"). 225 | Path("/articles/{category}/{id:[0-9]+}"). 226 | HandlerFunc(ArticleHandler). 227 | Name("article") 228 | 229 | // url.String() will be "http://news.domain.com/articles/technology/42" 230 | url, err := r.Get("article").URL("subdomain", "news", 231 | "category", "technology", 232 | "id", "42") 233 | ``` 234 | 235 | All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match. 236 | 237 | Regex support also exists for matching Headers within a route. For example, we could do: 238 | 239 | ```go 240 | r.HeadersRegexp("Content-Type", "application/(text|json)") 241 | ``` 242 | 243 | ...and the route will match both requests with a Content-Type of `application/json` as well as `application/text` 244 | 245 | There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do: 246 | 247 | ```go 248 | // "http://news.domain.com/" 249 | host, err := r.Get("article").URLHost("subdomain", "news") 250 | 251 | // "/articles/technology/42" 252 | path, err := r.Get("article").URLPath("category", "technology", "id", "42") 253 | ``` 254 | 255 | And if you use subrouters, host and path defined separately can be built as well: 256 | 257 | ```go 258 | r := mux.NewRouter() 259 | s := r.Host("{subdomain}.domain.com").Subrouter() 260 | s.Path("/articles/{category}/{id:[0-9]+}"). 261 | HandlerFunc(ArticleHandler). 262 | Name("article") 263 | 264 | // "http://news.domain.com/articles/technology/42" 265 | url, err := r.Get("article").URL("subdomain", "news", 266 | "category", "technology", 267 | "id", "42") 268 | ``` 269 | 270 | ## Full Example 271 | 272 | Here's a complete, runnable example of a small `mux` based server: 273 | 274 | ```go 275 | package main 276 | 277 | import ( 278 | "net/http" 279 | "log" 280 | "github.com/gorilla/mux" 281 | ) 282 | 283 | func YourHandler(w http.ResponseWriter, r *http.Request) { 284 | w.Write([]byte("Gorilla!\n")) 285 | } 286 | 287 | func main() { 288 | r := mux.NewRouter() 289 | // Routes consist of a path and a handler function. 290 | r.HandleFunc("/", YourHandler) 291 | 292 | // Bind to a port and pass our router in 293 | log.Fatal(http.ListenAndServe(":8000", r)) 294 | } 295 | ``` 296 | 297 | ## License 298 | 299 | BSD licensed. See the LICENSE file for details. 300 | --------------------------------------------------------------------------------