├── .travis.yml ├── Dockerfile ├── Godeps ├── Godeps.json ├── Readme └── _workspace │ ├── .gitignore │ └── src │ ├── github.com │ ├── ant0ine │ │ └── go-json-rest │ │ │ └── rest │ │ │ ├── access_log_apache.go │ │ │ ├── access_log_apache_test.go │ │ │ ├── access_log_json.go │ │ │ ├── access_log_json_test.go │ │ │ ├── api.go │ │ │ ├── api_test.go │ │ │ ├── auth_basic.go │ │ │ ├── auth_basic_test.go │ │ │ ├── content_type_checker.go │ │ │ ├── content_type_checker_test.go │ │ │ ├── cors.go │ │ │ ├── doc.go │ │ │ ├── gzip.go │ │ │ ├── gzip_test.go │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── if.go │ │ │ ├── if_test.go │ │ │ ├── json_indent.go │ │ │ ├── json_indent_test.go │ │ │ ├── jsonp.go │ │ │ ├── jsonp_test.go │ │ │ ├── middleware.go │ │ │ ├── middleware_test.go │ │ │ ├── powered_by.go │ │ │ ├── powered_by_test.go │ │ │ ├── recorder.go │ │ │ ├── recorder_test.go │ │ │ ├── recover.go │ │ │ ├── recover_test.go │ │ │ ├── request.go │ │ │ ├── request_test.go │ │ │ ├── response.go │ │ │ ├── response_test.go │ │ │ ├── route.go │ │ │ ├── route_test.go │ │ │ ├── router.go │ │ │ ├── router_benchmark_test.go │ │ │ ├── router_test.go │ │ │ ├── status.go │ │ │ ├── status_test.go │ │ │ ├── test │ │ │ ├── doc.go │ │ │ └── util.go │ │ │ ├── timer.go │ │ │ ├── timer_test.go │ │ │ └── trie │ │ │ ├── impl.go │ │ │ └── impl_test.go │ ├── codegangsta │ │ └── cli │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── app.go │ │ │ ├── app_test.go │ │ │ ├── autocomplete │ │ │ ├── bash_autocomplete │ │ │ └── zsh_autocomplete │ │ │ ├── cli.go │ │ │ ├── cli_test.go │ │ │ ├── command.go │ │ │ ├── command_test.go │ │ │ ├── context.go │ │ │ ├── context_test.go │ │ │ ├── flag.go │ │ │ ├── flag_test.go │ │ │ ├── help.go │ │ │ └── helpers_test.go │ ├── robfig │ │ └── cron │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── constantdelay.go │ │ │ ├── constantdelay_test.go │ │ │ ├── cron.go │ │ │ ├── cron_test.go │ │ │ ├── doc.go │ │ │ ├── parser.go │ │ │ ├── parser_test.go │ │ │ ├── spec.go │ │ │ └── spec_test.go │ ├── stretchr │ │ └── graceful │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── graceful.go │ │ │ ├── graceful_test.go │ │ │ └── tests │ │ │ └── main.go │ └── tj │ │ └── go-debug │ │ ├── History.md │ │ ├── Makefile │ │ ├── Readme.md │ │ ├── debug.go │ │ ├── debug_test.go │ │ └── example │ │ ├── multiple.go │ │ └── single.go │ ├── golang.org │ └── x │ │ └── net │ │ └── netutil │ │ ├── listen.go │ │ └── listen_test.go │ └── gopkg.in │ ├── mgo.v2 │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── auth.go │ ├── auth_test.go │ ├── bson │ │ ├── LICENSE │ │ ├── bson.go │ │ ├── bson_test.go │ │ ├── decode.go │ │ └── encode.go │ ├── bulk.go │ ├── bulk_test.go │ ├── cluster.go │ ├── cluster_test.go │ ├── doc.go │ ├── export_test.go │ ├── gridfs.go │ ├── gridfs_test.go │ ├── internal │ │ └── scram │ │ │ ├── scram.go │ │ │ └── scram_test.go │ ├── log.go │ ├── queue.go │ ├── queue_test.go │ ├── raceoff.go │ ├── raceon.go │ ├── sasl │ │ ├── sasl.c │ │ ├── sasl.go │ │ ├── sasl_windows.c │ │ ├── sasl_windows.go │ │ ├── sasl_windows.h │ │ ├── sspi_windows.c │ │ └── sspi_windows.h │ ├── saslimpl.go │ ├── saslstub.go │ ├── server.go │ ├── session.go │ ├── session_test.go │ ├── socket.go │ ├── stats.go │ ├── suite_test.go │ ├── syscall_test.go │ ├── syscall_windows_test.go │ ├── testdb │ │ ├── client.pem │ │ ├── dropall.js │ │ ├── init.js │ │ ├── server.pem │ │ ├── setup.sh │ │ ├── supervisord.conf │ │ └── wait.js │ └── txn │ │ ├── chaos.go │ │ ├── debug.go │ │ ├── dockey_test.go │ │ ├── flusher.go │ │ ├── mgo_test.go │ │ ├── sim_test.go │ │ ├── tarjan.go │ │ ├── tarjan_test.go │ │ ├── txn.go │ │ └── txn_test.go │ └── yaml.v2 │ ├── LICENSE │ ├── LICENSE.libyaml │ ├── README.md │ ├── apic.go │ ├── decode.go │ ├── decode_test.go │ ├── emitterc.go │ ├── encode.go │ ├── encode_test.go │ ├── parserc.go │ ├── readerc.go │ ├── resolve.go │ ├── scannerc.go │ ├── sorter.go │ ├── suite_test.go │ ├── writerc.go │ ├── yaml.go │ ├── yamlh.go │ └── yamlprivateh.go ├── LICENSE ├── README.md ├── client └── crontab.go ├── cmd ├── hooky │ └── main.go └── hookyd │ └── main.go ├── dist ├── Dockerfile.build ├── Dockerfile.dist ├── Dockerfile.travis └── ca-certificates.crt ├── docker-build.sh ├── docker-compose.yml ├── example-cron.yaml ├── models ├── account.go ├── application.go ├── attempt.go ├── base.go ├── httpauth.go ├── listresult.go ├── queue.go ├── retry.go └── task.go ├── restapi ├── account.go ├── application.go ├── attempt.go ├── auth.go ├── queue.go ├── restapi.go ├── status.go ├── task.go └── utils.go ├── scheduler └── scheduler.go ├── store └── store.go └── swagger.yml /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | language: go 7 | 8 | go: 1.4 9 | 10 | env: 11 | global: 12 | - COMMIT=${TRAVIS_COMMIT::8} 13 | - secure: "YfR7vFVwdf1h1pOXkmSbpIwKw1RJa9v6LVgETS9BRN+WBLjOHW+qde5bidXT8HmYz7uJu3m1UVg1qnynjSLikoDZ0E4HQjt8eHB88c6LefTAHdmvl8PG1QmLnDJvBhpudR+xw3ZT+Dct2HyOia09hAnvoSSZmOhDXymkF42aulIekaEz9kkLSzaEAawR05ip11YBwBNav16Ok5dujwPowwvvTIe0lJMUPa4wXm5MuTON52bCc/kxfY8Jgqht2LH+AUij6FbxP/kiF/nyTLrqNf2VzgwpeXzU+mQkO06H9pQjU8w4LDRl4mRA6qK/eKEqwX8nWXqYTuzX9uCrycCJHiGkkuAGAoipo3NCJLD/aKh9peU8U9Tot1RNgzO26BN1Qckxefxgwy7K4FurqIfC0OMdNWTHIH6XLX8U3KtCmUvVpk43bkaqTm19du49BS70ejRMVkF0OEozSqkZcy2CKIh5JqBMSpi3yuu26k8mq0tyGf4GpHXM6CbVqQo6q4u797lIaYiI0Wjodc8bmG/DOP6PrYRLlquC13lR/zNgAnk0w+hVVzsZr/BDgnMF/7cn+v9pGjnjxAbGdXCQe0K2hwUICIJ5gnnQYWi6Yv8nx0uXskO5ivOJbMys1hxgUmt7iBh+P4Ja/JHyhPZGgZ9wc8E5JHRUI90vzfoCyCMQc2k=" 14 | - secure: "0SIwJang/0IEPoCJqXaSDQD2fJEkij1dXNMD9jaGsRlQKQcX7xHnpsPgme3tNEPvPsQVVbvn2Emfdq0YNuNTBStV/hsWvlRMu7rRltKQzajR8tqcbdI7h6SBcHz4MYVOEfdbjsixfka1DibbVsXBrwy63/WgV0QXuEDV5fdGeojEru0lfRNqnrktABW1dg2B6HWIOr92lTKxUXd2BNqe/R0f5+69I2jqPRXpg7bypQgAVsGq0TjGUIvQquiQMTQSKf0q3f+FEdh0VC8unvFZbEuQGR5ZfpQDaj0RJVqrMNpKMOMuoaFWaRJLB0fjbv2uKMMdXnSCND+dHdzgr49fKmYKpShaGHBGPJ2tQPpQGAvRv+S9FtY3SjrqVeT4oQOglLk+p4V52ot2T8BKUJIeCSwY/NMv5ErXM0O21AEqXzhcJ+lum0CSyjLUtmB/v1RDEJW8oyjTzssyJJwFxMPMOb5cIB4M2JwbbiXwIqeY7LKJx3loodyWV71qGOdwiMXkSz3q/TR3YRH2QVoiv3WFyTVkSVT61iPjWdiDagZgU1VrwryuBgJIQhMiXx9KANC5K8wAjf5ebSWS2wqbFg0Jn3P66UVw1QKy9GFOQ0u3fhpQ1UvNBXwsZtEZKEMeqqvktt/OsWDE9SvKZ8DeOjgJKy7IOyhOzj2ld64Ccd+lplE=" 15 | - secure: "EPnLrotXZqKX6lhNH3EtaCyZz5LNoYhPUHkfNiPND9QC7SPZJvbCZW6KdD1ypWUAwH058omM9dPi/+Ms5UmtNLHHBjV8qiGVM2zF3LVgZZb5V1aSbK39CTUsko63DyF12auotLOBAGRmBBNUeSgJW/dLmWrKpSXfGjJ4qLT9jNSs+FUdhkAyQRzgEcKPiwpaVvElPLLKcE+Rh3AEDgnxmispRnejjbQkTYI4bFD5jFE8KxrKfxgZhCy5RlvV+108wg1C/rW/dxUKuUNb9BfohMQ4g08P4PysYRBJg4Q3RDZzTIg5OJPpZUsF4Nbwj/nAXlEJlZYagsQoTXeKWfggls4KzXhxCcLImNGSf1Ifyk/kZe+6Kiazmj+2WLGpzp3LhaVMaEsvjCC/5EeF9OCIWf5ksTISoJ47r1jlXB8fAcOiv6pi7zyFSdQmnzwfItoAGzpQPCY73w+DxOT739G8YcVMVZV7knQR4y3y0TOQhEyv7OKbnjCmqYZrpHX+1RmQumS8RJf+dXOGs+TUJYIIVMwjroxmrEFZYZmq6OhPrStlJoSpJjvFR9wns5MkpFR0pKlzF1uD+qYZv6SfbOhO27Ywg5fBvP0Vj5OOKV2cu2K1MLdGuNi+VP4NS6z6JHs68T6KFZ+T5Kk52YF+xaFvS2eXYyFc1G3/p0BUHOzLuWE=" 16 | 17 | branches: 18 | only: 19 | - master 20 | 21 | script: 22 | - /bin/true 23 | 24 | after_success: 25 | - CGO_ENABLED=0 go build -a -installsuffix cgo -o hookyd cmd/hookyd/main.go 26 | - CGO_ENABLED=0 go build -a -installsuffix cgo -o hooky cmd/hooky/main.go 27 | - export REPO=sebest/hooky 28 | - export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo "latest"; else echo "$TRAVIS_BRANCH" ; fi` 29 | - docker build -f dist/Dockerfile.travis -t $REPO:$COMMIT . 30 | - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS 31 | - docker tag $REPO:$COMMIT $REPO:$TAG 32 | - docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER 33 | - docker push $REPO 34 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.4.2 2 | 3 | ENV GOAPP github.com/sebest/hooky 4 | 5 | RUN go get github.com/tools/godep 6 | 7 | ADD . /go/src/${GOAPP} 8 | WORKDIR /go/src/${GOAPP} 9 | 10 | RUN godep go install ${GOAPP}/cmd/hookyd 11 | 12 | EXPOSE 8000 13 | 14 | ENTRYPOINT /go/bin/hookyd 15 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/sebest/hooky", 3 | "GoVersion": "go1.4.2", 4 | "Packages": [ 5 | "./..." 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "github.com/ant0ine/go-json-rest/rest", 10 | "Comment": "v3.2.0-8-g57d3250", 11 | "Rev": "57d3250d22392611efe69a00345f3ca2905bb18a" 12 | }, 13 | { 14 | "ImportPath": "github.com/codegangsta/cli", 15 | "Comment": "1.2.0-66-g6086d79", 16 | "Rev": "6086d7927ec35315964d9fea46df6c04e6d697c1" 17 | }, 18 | { 19 | "ImportPath": "github.com/robfig/cron", 20 | "Comment": "v1-2-g67823cd", 21 | "Rev": "67823cd24dece1b04cced3a0a0b3ca2bc84d875e" 22 | }, 23 | { 24 | "ImportPath": "github.com/stretchr/graceful", 25 | "Comment": "v1-3-g147ae82", 26 | "Rev": "147ae82f68aa1aad4db0a228cfb3995ad42ad879" 27 | }, 28 | { 29 | "ImportPath": "github.com/tj/go-debug", 30 | "Comment": "v2.0.0", 31 | "Rev": "ff4a55a20a86994118644bbddc6a216da193cc13" 32 | }, 33 | { 34 | "ImportPath": "golang.org/x/net/netutil", 35 | "Rev": "614fbbebc9fd409185656c66a2d860ad01907316" 36 | }, 37 | { 38 | "ImportPath": "gopkg.in/mgo.v2", 39 | "Comment": "r2015.01.24", 40 | "Rev": "c6a7dce14133ccac2dcac3793f1d6e2ef048503a" 41 | }, 42 | { 43 | "ImportPath": "gopkg.in/yaml.v2", 44 | "Rev": "49c95bdc21843256fb6c4e0d370a05f24a0bf213" 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/access_log_apache_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "bytes" 5 | "github.com/ant0ine/go-json-rest/rest/test" 6 | "log" 7 | "regexp" 8 | "testing" 9 | ) 10 | 11 | func TestAccessLogApacheMiddleware(t *testing.T) { 12 | 13 | api := NewApi() 14 | 15 | // the middlewares stack 16 | buffer := bytes.NewBufferString("") 17 | api.Use(&AccessLogApacheMiddleware{ 18 | Logger: log.New(buffer, "", 0), 19 | Format: CommonLogFormat, 20 | textTemplate: nil, 21 | }) 22 | api.Use(&TimerMiddleware{}) 23 | api.Use(&RecorderMiddleware{}) 24 | 25 | // a simple app 26 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 27 | w.WriteJson(map[string]string{"Id": "123"}) 28 | })) 29 | 30 | // wrap all 31 | handler := api.MakeHandler() 32 | 33 | req := test.MakeSimpleRequest("GET", "http://localhost/", nil) 34 | req.RemoteAddr = "127.0.0.1:1234" 35 | recorded := test.RunRequest(t, handler, req) 36 | recorded.CodeIs(200) 37 | recorded.ContentTypeIsJson() 38 | 39 | // log tests, eg: '127.0.0.1 - - 29/Nov/2014:22:28:34 +0000 "GET / HTTP/1.1" 200 12' 40 | apacheCommon := regexp.MustCompile(`127.0.0.1 - - \d{2}/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} [+\-]\d{4}\ "GET / HTTP/1.1" 200 12`) 41 | 42 | if !apacheCommon.Match(buffer.Bytes()) { 43 | t.Errorf("Got: %s", buffer.String()) 44 | } 45 | } 46 | 47 | func TestAccessLogApacheMiddlewareMissingData(t *testing.T) { 48 | 49 | api := NewApi() 50 | 51 | // the uncomplete middlewares stack 52 | buffer := bytes.NewBufferString("") 53 | api.Use(&AccessLogApacheMiddleware{ 54 | Logger: log.New(buffer, "", 0), 55 | Format: CommonLogFormat, 56 | textTemplate: nil, 57 | }) 58 | 59 | // a simple app 60 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 61 | w.WriteJson(map[string]string{"Id": "123"}) 62 | })) 63 | 64 | // wrap all 65 | handler := api.MakeHandler() 66 | 67 | req := test.MakeSimpleRequest("GET", "http://localhost/", nil) 68 | recorded := test.RunRequest(t, handler, req) 69 | recorded.CodeIs(200) 70 | recorded.ContentTypeIsJson() 71 | 72 | // not much to log when the Env data is missing, but this should still work 73 | apacheCommon := regexp.MustCompile(` - - "GET / HTTP/1.1" 0 -`) 74 | 75 | if !apacheCommon.Match(buffer.Bytes()) { 76 | t.Errorf("Got: %s", buffer.String()) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/access_log_json.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "os" 7 | "time" 8 | ) 9 | 10 | // AccessLogJsonMiddleware produces the access log with records written as JSON. This middleware 11 | // depends on TimerMiddleware and RecorderMiddleware that must be in the wrapped middlewares. It 12 | // also uses request.Env["REMOTE_USER"].(string) set by the auth middlewares. 13 | type AccessLogJsonMiddleware struct { 14 | 15 | // Logger points to the logger object used by this middleware, it defaults to 16 | // log.New(os.Stderr, "", 0). 17 | Logger *log.Logger 18 | } 19 | 20 | // MiddlewareFunc makes AccessLogJsonMiddleware implement the Middleware interface. 21 | func (mw *AccessLogJsonMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc { 22 | 23 | // set the default Logger 24 | if mw.Logger == nil { 25 | mw.Logger = log.New(os.Stderr, "", 0) 26 | } 27 | 28 | return func(w ResponseWriter, r *Request) { 29 | 30 | // call the handler 31 | h(w, r) 32 | 33 | mw.Logger.Printf("%s", makeAccessLogJsonRecord(r).asJson()) 34 | } 35 | } 36 | 37 | // AccessLogJsonRecord is the data structure used by AccessLogJsonMiddleware to create the JSON 38 | // records. (Public for documentation only, no public method uses it) 39 | type AccessLogJsonRecord struct { 40 | Timestamp *time.Time 41 | StatusCode int 42 | ResponseTime *time.Duration 43 | HttpMethod string 44 | RequestURI string 45 | RemoteUser string 46 | UserAgent string 47 | } 48 | 49 | func makeAccessLogJsonRecord(r *Request) *AccessLogJsonRecord { 50 | 51 | var timestamp *time.Time 52 | if r.Env["START_TIME"] != nil { 53 | timestamp = r.Env["START_TIME"].(*time.Time) 54 | } 55 | 56 | var statusCode int 57 | if r.Env["STATUS_CODE"] != nil { 58 | statusCode = r.Env["STATUS_CODE"].(int) 59 | } 60 | 61 | var responseTime *time.Duration 62 | if r.Env["ELAPSED_TIME"] != nil { 63 | responseTime = r.Env["ELAPSED_TIME"].(*time.Duration) 64 | } 65 | 66 | var remoteUser string 67 | if r.Env["REMOTE_USER"] != nil { 68 | remoteUser = r.Env["REMOTE_USER"].(string) 69 | } 70 | 71 | return &AccessLogJsonRecord{ 72 | Timestamp: timestamp, 73 | StatusCode: statusCode, 74 | ResponseTime: responseTime, 75 | HttpMethod: r.Method, 76 | RequestURI: r.URL.RequestURI(), 77 | RemoteUser: remoteUser, 78 | UserAgent: r.UserAgent(), 79 | } 80 | } 81 | 82 | func (r *AccessLogJsonRecord) asJson() []byte { 83 | b, err := json.Marshal(r) 84 | if err != nil { 85 | panic(err) 86 | } 87 | return b 88 | } 89 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/access_log_json_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/ant0ine/go-json-rest/rest/test" 7 | "log" 8 | "testing" 9 | ) 10 | 11 | func TestAccessLogJsonMiddleware(t *testing.T) { 12 | 13 | api := NewApi() 14 | 15 | // the middlewares stack 16 | buffer := bytes.NewBufferString("") 17 | api.Use(&AccessLogJsonMiddleware{ 18 | Logger: log.New(buffer, "", 0), 19 | }) 20 | api.Use(&TimerMiddleware{}) 21 | api.Use(&RecorderMiddleware{}) 22 | 23 | // a simple app 24 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 25 | w.WriteJson(map[string]string{"Id": "123"}) 26 | })) 27 | 28 | // wrap all 29 | handler := api.MakeHandler() 30 | 31 | req := test.MakeSimpleRequest("GET", "http://localhost/", nil) 32 | req.RemoteAddr = "127.0.0.1:1234" 33 | recorded := test.RunRequest(t, handler, req) 34 | recorded.CodeIs(200) 35 | recorded.ContentTypeIsJson() 36 | 37 | // log tests 38 | decoded := &AccessLogJsonRecord{} 39 | err := json.Unmarshal(buffer.Bytes(), decoded) 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | 44 | if decoded.StatusCode != 200 { 45 | t.Errorf("StatusCode 200 expected, got %d", decoded.StatusCode) 46 | } 47 | if decoded.RequestURI != "/" { 48 | t.Errorf("RequestURI / expected, got %s", decoded.RequestURI) 49 | } 50 | if decoded.HttpMethod != "GET" { 51 | t.Errorf("HttpMethod GET expected, got %s", decoded.HttpMethod) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/api.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // Api defines a stack of Middlewares and an App. 8 | type Api struct { 9 | stack []Middleware 10 | app App 11 | } 12 | 13 | // NewApi makes a new Api object. The Middleware stack is empty, and the App is nil. 14 | func NewApi() *Api { 15 | return &Api{ 16 | stack: []Middleware{}, 17 | app: nil, 18 | } 19 | } 20 | 21 | // Use pushes one or multiple middlewares to the stack for middlewares 22 | // maintained in the Api object. 23 | func (api *Api) Use(middlewares ...Middleware) { 24 | api.stack = append(api.stack, middlewares...) 25 | } 26 | 27 | // SetApp sets the App in the Api object. 28 | func (api *Api) SetApp(app App) { 29 | api.app = app 30 | } 31 | 32 | // MakeHandler wraps all the Middlewares of the stack and the App together, and returns an 33 | // http.Handler ready to be used. If the Middleware stack is empty the App is used directly. If the 34 | // App is nil, a HandlerFunc that does nothing is used instead. 35 | func (api *Api) MakeHandler() http.Handler { 36 | var appFunc HandlerFunc 37 | if api.app != nil { 38 | appFunc = api.app.AppFunc() 39 | } else { 40 | appFunc = func(w ResponseWriter, r *Request) {} 41 | } 42 | return http.HandlerFunc( 43 | adapterFunc( 44 | WrapMiddlewares(api.stack, appFunc), 45 | ), 46 | ) 47 | } 48 | 49 | // Defines a stack of middlewares convenient for development. Among other things: 50 | // console friendly logging, JSON indentation, error stack strace in the response. 51 | var DefaultDevStack = []Middleware{ 52 | &AccessLogApacheMiddleware{}, 53 | &TimerMiddleware{}, 54 | &RecorderMiddleware{}, 55 | &PoweredByMiddleware{}, 56 | &RecoverMiddleware{ 57 | EnableResponseStackTrace: true, 58 | }, 59 | &JsonIndentMiddleware{}, 60 | &ContentTypeCheckerMiddleware{}, 61 | } 62 | 63 | // Defines a stack of middlewares convenient for production. Among other things: 64 | // Apache CombinedLogFormat logging, gzip compression. 65 | var DefaultProdStack = []Middleware{ 66 | &AccessLogApacheMiddleware{ 67 | Format: CombinedLogFormat, 68 | }, 69 | &TimerMiddleware{}, 70 | &RecorderMiddleware{}, 71 | &PoweredByMiddleware{}, 72 | &RecoverMiddleware{}, 73 | &GzipMiddleware{}, 74 | &ContentTypeCheckerMiddleware{}, 75 | } 76 | 77 | // Defines a stack of middlewares that should be common to most of the middleware stacks. 78 | var DefaultCommonStack = []Middleware{ 79 | &TimerMiddleware{}, 80 | &RecorderMiddleware{}, 81 | &PoweredByMiddleware{}, 82 | &RecoverMiddleware{}, 83 | } 84 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/api_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "testing" 6 | ) 7 | 8 | func TestApiNoAppNoMiddleware(t *testing.T) { 9 | 10 | api := NewApi() 11 | if api == nil { 12 | t.Fatal("Api object must be instantiated") 13 | } 14 | 15 | handler := api.MakeHandler() 16 | if handler == nil { 17 | t.Fatal("the http.Handler must be have been create") 18 | } 19 | 20 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) 21 | recorded.CodeIs(200) 22 | } 23 | 24 | func TestApiSimpleAppNoMiddleware(t *testing.T) { 25 | 26 | api := NewApi() 27 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 28 | w.WriteJson(map[string]string{"Id": "123"}) 29 | })) 30 | 31 | handler := api.MakeHandler() 32 | if handler == nil { 33 | t.Fatal("the http.Handler must be have been create") 34 | } 35 | 36 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) 37 | recorded.CodeIs(200) 38 | recorded.ContentTypeIsJson() 39 | recorded.BodyIs(`{"Id":"123"}`) 40 | } 41 | 42 | func TestDevStack(t *testing.T) { 43 | 44 | api := NewApi() 45 | api.Use(DefaultDevStack...) 46 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 47 | w.WriteJson(map[string]string{"Id": "123"}) 48 | })) 49 | 50 | handler := api.MakeHandler() 51 | if handler == nil { 52 | t.Fatal("the http.Handler must be have been create") 53 | } 54 | 55 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) 56 | recorded.CodeIs(200) 57 | recorded.ContentTypeIsJson() 58 | recorded.BodyIs("{\n \"Id\": \"123\"\n}") 59 | } 60 | 61 | func TestProdStack(t *testing.T) { 62 | 63 | api := NewApi() 64 | api.Use(DefaultProdStack...) 65 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 66 | w.WriteJson(map[string]string{"Id": "123"}) 67 | })) 68 | 69 | handler := api.MakeHandler() 70 | if handler == nil { 71 | t.Fatal("the http.Handler must be have been create") 72 | } 73 | 74 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) 75 | recorded.CodeIs(200) 76 | recorded.ContentTypeIsJson() 77 | recorded.ContentEncodingIsGzip() 78 | } 79 | 80 | func TestCommonStack(t *testing.T) { 81 | 82 | api := NewApi() 83 | api.Use(DefaultCommonStack...) 84 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 85 | w.WriteJson(map[string]string{"Id": "123"}) 86 | })) 87 | 88 | handler := api.MakeHandler() 89 | if handler == nil { 90 | t.Fatal("the http.Handler must be have been create") 91 | } 92 | 93 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) 94 | recorded.CodeIs(200) 95 | recorded.ContentTypeIsJson() 96 | recorded.BodyIs(`{"Id":"123"}`) 97 | } 98 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/auth_basic.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | "log" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | // AuthBasicMiddleware provides a simple AuthBasic implementation. On failure, a 401 HTTP response 12 | //is returned. On success, the wrapped middleware is called, and the userId is made available as 13 | // request.Env["REMOTE_USER"].(string) 14 | type AuthBasicMiddleware struct { 15 | 16 | // Realm name to display to the user. Required. 17 | Realm string 18 | 19 | // Callback function that should perform the authentication of the user based on userId and 20 | // password. Must return true on success, false on failure. Required. 21 | Authenticator func(userId string, password string) bool 22 | 23 | // Callback function that should perform the authorization of the authenticated user. Called 24 | // only after an authentication success. Must return true on success, false on failure. 25 | // Optional, default to success. 26 | Authorizator func(userId string, request *Request) bool 27 | } 28 | 29 | // MiddlewareFunc makes AuthBasicMiddleware implement the Middleware interface. 30 | func (mw *AuthBasicMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc { 31 | 32 | if mw.Realm == "" { 33 | log.Fatal("Realm is required") 34 | } 35 | 36 | if mw.Authenticator == nil { 37 | log.Fatal("Authenticator is required") 38 | } 39 | 40 | if mw.Authorizator == nil { 41 | mw.Authorizator = func(userId string, request *Request) bool { 42 | return true 43 | } 44 | } 45 | 46 | return func(writer ResponseWriter, request *Request) { 47 | 48 | authHeader := request.Header.Get("Authorization") 49 | if authHeader == "" { 50 | mw.unauthorized(writer) 51 | return 52 | } 53 | 54 | providedUserId, providedPassword, err := mw.decodeBasicAuthHeader(authHeader) 55 | 56 | if err != nil { 57 | Error(writer, "Invalid authentication", http.StatusBadRequest) 58 | return 59 | } 60 | 61 | if !mw.Authenticator(providedUserId, providedPassword) { 62 | mw.unauthorized(writer) 63 | return 64 | } 65 | 66 | if !mw.Authorizator(providedUserId, request) { 67 | mw.unauthorized(writer) 68 | return 69 | } 70 | 71 | request.Env["REMOTE_USER"] = providedUserId 72 | 73 | handler(writer, request) 74 | } 75 | } 76 | 77 | func (mw *AuthBasicMiddleware) unauthorized(writer ResponseWriter) { 78 | writer.Header().Set("WWW-Authenticate", "Basic realm="+mw.Realm) 79 | Error(writer, "Not Authorized", http.StatusUnauthorized) 80 | } 81 | 82 | func (mw *AuthBasicMiddleware) decodeBasicAuthHeader(header string) (user string, password string, err error) { 83 | 84 | parts := strings.SplitN(header, " ", 2) 85 | if !(len(parts) == 2 && parts[0] == "Basic") { 86 | return "", "", errors.New("Invalid authentication") 87 | } 88 | 89 | decoded, err := base64.StdEncoding.DecodeString(parts[1]) 90 | if err != nil { 91 | return "", "", errors.New("Invalid base64") 92 | } 93 | 94 | creds := strings.SplitN(string(decoded), ":", 2) 95 | if len(creds) != 2 { 96 | return "", "", errors.New("Invalid authentication") 97 | } 98 | 99 | return creds[0], creds[1], nil 100 | } 101 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/auth_basic_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "encoding/base64" 5 | "github.com/ant0ine/go-json-rest/rest/test" 6 | "testing" 7 | ) 8 | 9 | func TestAuthBasic(t *testing.T) { 10 | 11 | // the middleware to test 12 | authMiddleware := &AuthBasicMiddleware{ 13 | Realm: "test zone", 14 | Authenticator: func(userId string, password string) bool { 15 | if userId == "admin" && password == "admin" { 16 | return true 17 | } 18 | return false 19 | }, 20 | Authorizator: func(userId string, request *Request) bool { 21 | if request.Method == "GET" { 22 | return true 23 | } 24 | return false 25 | }, 26 | } 27 | 28 | // api for testing failure 29 | apiFailure := NewApi() 30 | apiFailure.Use(authMiddleware) 31 | apiFailure.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 32 | t.Error("Should never be executed") 33 | })) 34 | handler := apiFailure.MakeHandler() 35 | 36 | // simple request fails 37 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) 38 | recorded.CodeIs(401) 39 | recorded.ContentTypeIsJson() 40 | 41 | // auth with wrong cred and right method fails 42 | wrongCredReq := test.MakeSimpleRequest("GET", "http://localhost/", nil) 43 | encoded := base64.StdEncoding.EncodeToString([]byte("admin:AdmIn")) 44 | wrongCredReq.Header.Set("Authorization", "Basic "+encoded) 45 | recorded = test.RunRequest(t, handler, wrongCredReq) 46 | recorded.CodeIs(401) 47 | recorded.ContentTypeIsJson() 48 | 49 | // auth with right cred and wrong method fails 50 | rightCredReq := test.MakeSimpleRequest("POST", "http://localhost/", nil) 51 | encoded = base64.StdEncoding.EncodeToString([]byte("admin:admin")) 52 | rightCredReq.Header.Set("Authorization", "Basic "+encoded) 53 | recorded = test.RunRequest(t, handler, rightCredReq) 54 | recorded.CodeIs(401) 55 | recorded.ContentTypeIsJson() 56 | 57 | // api for testing success 58 | apiSuccess := NewApi() 59 | apiSuccess.Use(authMiddleware) 60 | apiSuccess.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 61 | if r.Env["REMOTE_USER"] == nil { 62 | t.Error("REMOTE_USER is nil") 63 | } 64 | user := r.Env["REMOTE_USER"].(string) 65 | if user != "admin" { 66 | t.Error("REMOTE_USER is expected to be 'admin'") 67 | } 68 | w.WriteJson(map[string]string{"Id": "123"}) 69 | })) 70 | 71 | // auth with right cred and right method succeeds 72 | rightCredReq = test.MakeSimpleRequest("GET", "http://localhost/", nil) 73 | encoded = base64.StdEncoding.EncodeToString([]byte("admin:admin")) 74 | rightCredReq.Header.Set("Authorization", "Basic "+encoded) 75 | recorded = test.RunRequest(t, apiSuccess.MakeHandler(), rightCredReq) 76 | recorded.CodeIs(200) 77 | recorded.ContentTypeIsJson() 78 | } 79 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/content_type_checker.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "mime" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | // ContentTypeCheckerMiddleware verifies the request Content-Type header and returns a 10 | // StatusUnsupportedMediaType (415) HTTP error response if it's incorrect. The expected 11 | // Content-Type is 'application/json' if the content is non-null. Note: If a charset parameter 12 | // exists, it MUST be UTF-8. 13 | type ContentTypeCheckerMiddleware struct{} 14 | 15 | // MiddlewareFunc makes ContentTypeCheckerMiddleware implement the Middleware interface. 16 | func (mw *ContentTypeCheckerMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc { 17 | 18 | return func(w ResponseWriter, r *Request) { 19 | 20 | mediatype, params, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) 21 | charset, ok := params["charset"] 22 | if !ok { 23 | charset = "UTF-8" 24 | } 25 | 26 | // per net/http doc, means that the length is known and non-null 27 | if r.ContentLength > 0 && 28 | !(mediatype == "application/json" && strings.ToUpper(charset) == "UTF-8") { 29 | 30 | Error(w, 31 | "Bad Content-Type or charset, expected 'application/json'", 32 | http.StatusUnsupportedMediaType, 33 | ) 34 | return 35 | } 36 | 37 | // call the wrapped handler 38 | handler(w, r) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/content_type_checker_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "testing" 6 | ) 7 | 8 | func TestContentTypeCheckerMiddleware(t *testing.T) { 9 | 10 | api := NewApi() 11 | 12 | // the middleware to test 13 | api.Use(&ContentTypeCheckerMiddleware{}) 14 | 15 | // a simple app 16 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 17 | w.WriteJson(map[string]string{"Id": "123"}) 18 | })) 19 | 20 | // wrap all 21 | handler := api.MakeHandler() 22 | 23 | // no payload, no content length, no check 24 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) 25 | recorded.CodeIs(200) 26 | 27 | // JSON payload with correct content type 28 | recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("POST", "http://localhost/", map[string]string{"Id": "123"})) 29 | recorded.CodeIs(200) 30 | 31 | // JSON payload with correct content type specifying the utf-8 charset 32 | req := test.MakeSimpleRequest("POST", "http://localhost/", map[string]string{"Id": "123"}) 33 | req.Header.Set("Content-Type", "application/json; charset=utf-8") 34 | recorded = test.RunRequest(t, handler, req) 35 | recorded.CodeIs(200) 36 | 37 | // JSON payload with incorrect content type 38 | req = test.MakeSimpleRequest("POST", "http://localhost/", map[string]string{"Id": "123"}) 39 | req.Header.Set("Content-Type", "text/x-json") 40 | recorded = test.RunRequest(t, handler, req) 41 | recorded.CodeIs(415) 42 | } 43 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/doc.go: -------------------------------------------------------------------------------- 1 | // A quick and easy way to setup a RESTful JSON API 2 | // 3 | // http://ant0ine.github.io/go-json-rest/ 4 | // 5 | // Go-Json-Rest is a thin layer on top of net/http that helps building RESTful JSON APIs easily. 6 | // It provides fast and scalable request routing using a Trie based implementation, helpers to deal 7 | // with JSON requests and responses, and middlewares for functionalities like CORS, Auth, Gzip, 8 | // Status, ... 9 | // 10 | // Example: 11 | // 12 | // package main 13 | // 14 | // import ( 15 | // "github.com/ant0ine/go-json-rest/rest" 16 | // "log" 17 | // "net/http" 18 | // ) 19 | // 20 | // type User struct { 21 | // Id string 22 | // Name string 23 | // } 24 | // 25 | // func GetUser(w rest.ResponseWriter, req *rest.Request) { 26 | // user := User{ 27 | // Id: req.PathParam("id"), 28 | // Name: "Antoine", 29 | // } 30 | // w.WriteJson(&user) 31 | // } 32 | // 33 | // func main() { 34 | // api := rest.NewApi() 35 | // api.Use(rest.DefaultDevStack...) 36 | // router, err := rest.MakeRouter( 37 | // rest.Get("/users/:id", GetUser), 38 | // ) 39 | // if err != nil { 40 | // log.Fatal(err) 41 | // } 42 | // api.SetApp(router) 43 | // log.Fatal(http.ListenAndServe(":8080", api.MakeHandler())) 44 | // } 45 | // 46 | // 47 | package rest 48 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/gzip.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "bufio" 5 | "compress/gzip" 6 | "net" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | // GzipMiddleware is responsible for compressing the payload with gzip and setting the proper 12 | // headers when supported by the client. It must be wrapped by TimerMiddleware for the 13 | // compression time to be captured. And It must be wrapped by RecorderMiddleware for the 14 | // compressed BYTES_WRITTEN to be captured. 15 | type GzipMiddleware struct{} 16 | 17 | // MiddlewareFunc makes GzipMiddleware implement the Middleware interface. 18 | func (mw *GzipMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc { 19 | return func(w ResponseWriter, r *Request) { 20 | // gzip support enabled 21 | canGzip := strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") 22 | // client accepts gzip ? 23 | writer := &gzipResponseWriter{w, false, canGzip, nil} 24 | // call the handler with the wrapped writer 25 | h(writer, r) 26 | } 27 | } 28 | 29 | // Private responseWriter intantiated by the gzip middleware. 30 | // It encodes the payload with gzip and set the proper headers. 31 | // It implements the following interfaces: 32 | // ResponseWriter 33 | // http.ResponseWriter 34 | // http.Flusher 35 | // http.CloseNotifier 36 | // http.Hijacker 37 | type gzipResponseWriter struct { 38 | ResponseWriter 39 | wroteHeader bool 40 | canGzip bool 41 | gzipWriter *gzip.Writer 42 | } 43 | 44 | // Set the right headers for gzip encoded responses. 45 | func (w *gzipResponseWriter) WriteHeader(code int) { 46 | 47 | // Always set the Vary header, even if this particular request 48 | // is not gzipped. 49 | w.Header().Add("Vary", "Accept-Encoding") 50 | 51 | if w.canGzip { 52 | w.Header().Set("Content-Encoding", "gzip") 53 | } 54 | 55 | w.ResponseWriter.WriteHeader(code) 56 | w.wroteHeader = true 57 | } 58 | 59 | // Make sure the local Write is called. 60 | func (w *gzipResponseWriter) WriteJson(v interface{}) error { 61 | b, err := w.EncodeJson(v) 62 | if err != nil { 63 | return err 64 | } 65 | _, err = w.Write(b) 66 | if err != nil { 67 | return err 68 | } 69 | return nil 70 | } 71 | 72 | // Make sure the local WriteHeader is called, and call the parent Flush. 73 | // Provided in order to implement the http.Flusher interface. 74 | func (w *gzipResponseWriter) Flush() { 75 | if !w.wroteHeader { 76 | w.WriteHeader(http.StatusOK) 77 | } 78 | flusher := w.ResponseWriter.(http.Flusher) 79 | flusher.Flush() 80 | } 81 | 82 | // Call the parent CloseNotify. 83 | // Provided in order to implement the http.CloseNotifier interface. 84 | func (w *gzipResponseWriter) CloseNotify() <-chan bool { 85 | notifier := w.ResponseWriter.(http.CloseNotifier) 86 | return notifier.CloseNotify() 87 | } 88 | 89 | // Provided in order to implement the http.Hijacker interface. 90 | func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 91 | hijacker := w.ResponseWriter.(http.Hijacker) 92 | return hijacker.Hijack() 93 | } 94 | 95 | // Make sure the local WriteHeader is called, and encode the payload if necessary. 96 | // Provided in order to implement the http.ResponseWriter interface. 97 | func (w *gzipResponseWriter) Write(b []byte) (int, error) { 98 | 99 | if !w.wroteHeader { 100 | w.WriteHeader(http.StatusOK) 101 | } 102 | 103 | writer := w.ResponseWriter.(http.ResponseWriter) 104 | 105 | if w.canGzip { 106 | // Write can be called multiple times for a given response. 107 | // (see the streaming example: 108 | // https://github.com/ant0ine/go-json-rest-examples/tree/master/streaming) 109 | // The gzipWriter is instantiated only once, and flushed after 110 | // each write. 111 | if w.gzipWriter == nil { 112 | w.gzipWriter = gzip.NewWriter(writer) 113 | } 114 | count, errW := w.gzipWriter.Write(b) 115 | errF := w.gzipWriter.Flush() 116 | if errW != nil { 117 | return count, errW 118 | } 119 | if errF != nil { 120 | return count, errF 121 | } 122 | return count, nil 123 | } 124 | 125 | return writer.Write(b) 126 | } 127 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/gzip_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "testing" 6 | ) 7 | 8 | func TestGzipEnabled(t *testing.T) { 9 | 10 | api := NewApi() 11 | 12 | // the middleware to test 13 | api.Use(&GzipMiddleware{}) 14 | 15 | // router app with success and error paths 16 | router, err := MakeRouter( 17 | Get("/ok", func(w ResponseWriter, r *Request) { 18 | w.WriteJson(map[string]string{"Id": "123"}) 19 | }), 20 | Get("/error", func(w ResponseWriter, r *Request) { 21 | Error(w, "gzipped error", 500) 22 | }), 23 | ) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | 28 | api.SetApp(router) 29 | 30 | // wrap all 31 | handler := api.MakeHandler() 32 | 33 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/ok", nil)) 34 | recorded.CodeIs(200) 35 | recorded.ContentTypeIsJson() 36 | recorded.ContentEncodingIsGzip() 37 | recorded.HeaderIs("Vary", "Accept-Encoding") 38 | 39 | recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/error", nil)) 40 | recorded.CodeIs(500) 41 | recorded.ContentTypeIsJson() 42 | recorded.ContentEncodingIsGzip() 43 | recorded.HeaderIs("Vary", "Accept-Encoding") 44 | } 45 | 46 | func TestGzipDisabled(t *testing.T) { 47 | 48 | api := NewApi() 49 | 50 | // router app with success and error paths 51 | router, err := MakeRouter( 52 | Get("/ok", func(w ResponseWriter, r *Request) { 53 | w.WriteJson(map[string]string{"Id": "123"}) 54 | }), 55 | ) 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | api.SetApp(router) 61 | handler := api.MakeHandler() 62 | 63 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/ok", nil)) 64 | recorded.CodeIs(200) 65 | recorded.ContentTypeIsJson() 66 | recorded.HeaderIs("Content-Encoding", "") 67 | recorded.HeaderIs("Vary", "") 68 | } 69 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/if.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | // IfMiddleware evaluates at runtime a condition based on the current request, and decides to 8 | // execute one of the other Middleware based on this boolean. 9 | type IfMiddleware struct { 10 | 11 | // Runtime condition that decides of the execution of IfTrue of IfFalse. 12 | Condition func(r *Request) bool 13 | 14 | // Middleware to run when the condition is true. Note that the middleware is initialized 15 | // weather if will be used or not. (Optional, pass-through if not set) 16 | IfTrue Middleware 17 | 18 | // Middleware to run when the condition is false. Note that the middleware is initialized 19 | // weather if will be used or not. (Optional, pass-through if not set) 20 | IfFalse Middleware 21 | } 22 | 23 | // MiddlewareFunc makes TimerMiddleware implement the Middleware interface. 24 | func (mw *IfMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc { 25 | 26 | if mw.Condition == nil { 27 | log.Fatal("IfMiddleware Condition is required") 28 | } 29 | 30 | var ifTrueHandler HandlerFunc 31 | if mw.IfTrue != nil { 32 | ifTrueHandler = mw.IfTrue.MiddlewareFunc(h) 33 | } else { 34 | ifTrueHandler = h 35 | } 36 | 37 | var ifFalseHandler HandlerFunc 38 | if mw.IfFalse != nil { 39 | ifFalseHandler = mw.IfFalse.MiddlewareFunc(h) 40 | } else { 41 | ifFalseHandler = h 42 | } 43 | 44 | return func(w ResponseWriter, r *Request) { 45 | 46 | if mw.Condition(r) { 47 | ifTrueHandler(w, r) 48 | } else { 49 | ifFalseHandler(w, r) 50 | } 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/if_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "testing" 6 | ) 7 | 8 | func TestIfMiddleware(t *testing.T) { 9 | 10 | api := NewApi() 11 | 12 | // the middleware to test 13 | api.Use(&IfMiddleware{ 14 | Condition: func(r *Request) bool { 15 | if r.URL.Path == "/true" { 16 | return true 17 | } 18 | return false 19 | }, 20 | IfTrue: MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { 21 | return func(w ResponseWriter, r *Request) { 22 | r.Env["TRUE_MIDDLEWARE"] = true 23 | handler(w, r) 24 | } 25 | }), 26 | IfFalse: MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { 27 | return func(w ResponseWriter, r *Request) { 28 | r.Env["FALSE_MIDDLEWARE"] = true 29 | handler(w, r) 30 | } 31 | }), 32 | }) 33 | 34 | // a simple app 35 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 36 | w.WriteJson(r.Env) 37 | })) 38 | 39 | // wrap all 40 | handler := api.MakeHandler() 41 | 42 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) 43 | recorded.CodeIs(200) 44 | recorded.ContentTypeIsJson() 45 | recorded.BodyIs("{\"FALSE_MIDDLEWARE\":true}") 46 | 47 | recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/true", nil)) 48 | recorded.CodeIs(200) 49 | recorded.ContentTypeIsJson() 50 | recorded.BodyIs("{\"TRUE_MIDDLEWARE\":true}") 51 | } 52 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/json_indent.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "net" 7 | "net/http" 8 | ) 9 | 10 | // JsonIndentMiddleware provides JSON encoding with indentation. 11 | // It could be convenient to use it during development. 12 | // It works by "subclassing" the responseWriter provided by the wrapping middleware, 13 | // replacing the writer.EncodeJson and writer.WriteJson implementations, 14 | // and making the parent implementations ignored. 15 | type JsonIndentMiddleware struct { 16 | 17 | // prefix string, as in json.MarshalIndent 18 | Prefix string 19 | 20 | // indentation string, as in json.MarshalIndent 21 | Indent string 22 | } 23 | 24 | // MiddlewareFunc makes JsonIndentMiddleware implement the Middleware interface. 25 | func (mw *JsonIndentMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc { 26 | 27 | if mw.Indent == "" { 28 | mw.Indent = " " 29 | } 30 | 31 | return func(w ResponseWriter, r *Request) { 32 | 33 | writer := &jsonIndentResponseWriter{w, false, mw.Prefix, mw.Indent} 34 | // call the wrapped handler 35 | handler(writer, r) 36 | } 37 | } 38 | 39 | // Private responseWriter intantiated by the middleware. 40 | // It implements the following interfaces: 41 | // ResponseWriter 42 | // http.ResponseWriter 43 | // http.Flusher 44 | // http.CloseNotifier 45 | // http.Hijacker 46 | type jsonIndentResponseWriter struct { 47 | ResponseWriter 48 | wroteHeader bool 49 | prefix string 50 | indent string 51 | } 52 | 53 | // Replace the parent EncodeJson to provide indentation. 54 | func (w *jsonIndentResponseWriter) EncodeJson(v interface{}) ([]byte, error) { 55 | b, err := json.MarshalIndent(v, w.prefix, w.indent) 56 | if err != nil { 57 | return nil, err 58 | } 59 | return b, nil 60 | } 61 | 62 | // Make sure the local EncodeJson and local Write are called. 63 | // Does not call the parent WriteJson. 64 | func (w *jsonIndentResponseWriter) WriteJson(v interface{}) error { 65 | b, err := w.EncodeJson(v) 66 | if err != nil { 67 | return err 68 | } 69 | _, err = w.Write(b) 70 | if err != nil { 71 | return err 72 | } 73 | return nil 74 | } 75 | 76 | // Call the parent WriteHeader. 77 | func (w *jsonIndentResponseWriter) WriteHeader(code int) { 78 | w.ResponseWriter.WriteHeader(code) 79 | w.wroteHeader = true 80 | } 81 | 82 | // Make sure the local WriteHeader is called, and call the parent Flush. 83 | // Provided in order to implement the http.Flusher interface. 84 | func (w *jsonIndentResponseWriter) Flush() { 85 | if !w.wroteHeader { 86 | w.WriteHeader(http.StatusOK) 87 | } 88 | flusher := w.ResponseWriter.(http.Flusher) 89 | flusher.Flush() 90 | } 91 | 92 | // Call the parent CloseNotify. 93 | // Provided in order to implement the http.CloseNotifier interface. 94 | func (w *jsonIndentResponseWriter) CloseNotify() <-chan bool { 95 | notifier := w.ResponseWriter.(http.CloseNotifier) 96 | return notifier.CloseNotify() 97 | } 98 | 99 | // Provided in order to implement the http.Hijacker interface. 100 | func (w *jsonIndentResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 101 | hijacker := w.ResponseWriter.(http.Hijacker) 102 | return hijacker.Hijack() 103 | } 104 | 105 | // Make sure the local WriteHeader is called, and call the parent Write. 106 | // Provided in order to implement the http.ResponseWriter interface. 107 | func (w *jsonIndentResponseWriter) Write(b []byte) (int, error) { 108 | if !w.wroteHeader { 109 | w.WriteHeader(http.StatusOK) 110 | } 111 | writer := w.ResponseWriter.(http.ResponseWriter) 112 | return writer.Write(b) 113 | } 114 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/json_indent_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "testing" 6 | ) 7 | 8 | func TestJsonIndentMiddleware(t *testing.T) { 9 | 10 | api := NewApi() 11 | 12 | // the middleware to test 13 | api.Use(&JsonIndentMiddleware{}) 14 | 15 | // a simple app 16 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 17 | w.WriteJson(map[string]string{"Id": "123"}) 18 | })) 19 | 20 | // wrap all 21 | handler := api.MakeHandler() 22 | 23 | req := test.MakeSimpleRequest("GET", "http://localhost/", nil) 24 | recorded := test.RunRequest(t, handler, req) 25 | recorded.CodeIs(200) 26 | recorded.ContentTypeIsJson() 27 | recorded.BodyIs("{\n \"Id\": \"123\"\n}") 28 | } 29 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/jsonp.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "bufio" 5 | "net" 6 | "net/http" 7 | ) 8 | 9 | // JsonpMiddleware provides JSONP responses on demand, based on the presence 10 | // of a query string argument specifying the callback name. 11 | type JsonpMiddleware struct { 12 | 13 | // Name of the query string parameter used to specify the 14 | // the name of the JS callback used for the padding. 15 | // Defaults to "callback". 16 | CallbackNameKey string 17 | } 18 | 19 | // MiddlewareFunc returns a HandlerFunc that implements the middleware. 20 | func (mw *JsonpMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc { 21 | 22 | if mw.CallbackNameKey == "" { 23 | mw.CallbackNameKey = "callback" 24 | } 25 | 26 | return func(w ResponseWriter, r *Request) { 27 | 28 | callbackName := r.URL.Query().Get(mw.CallbackNameKey) 29 | // TODO validate the callbackName ? 30 | 31 | if callbackName != "" { 32 | // the client request JSONP, instantiate JsonpMiddleware. 33 | writer := &jsonpResponseWriter{w, false, callbackName} 34 | // call the handler with the wrapped writer 35 | h(writer, r) 36 | } else { 37 | // do nothing special 38 | h(w, r) 39 | } 40 | 41 | } 42 | } 43 | 44 | // Private responseWriter intantiated by the JSONP middleware. 45 | // It adds the padding to the payload and set the proper headers. 46 | // It implements the following interfaces: 47 | // ResponseWriter 48 | // http.ResponseWriter 49 | // http.Flusher 50 | // http.CloseNotifier 51 | // http.Hijacker 52 | type jsonpResponseWriter struct { 53 | ResponseWriter 54 | wroteHeader bool 55 | callbackName string 56 | } 57 | 58 | // Overwrite the Content-Type to be text/javascript 59 | func (w *jsonpResponseWriter) WriteHeader(code int) { 60 | 61 | w.Header().Set("Content-Type", "text/javascript") 62 | 63 | w.ResponseWriter.WriteHeader(code) 64 | w.wroteHeader = true 65 | } 66 | 67 | // Make sure the local Write is called. 68 | func (w *jsonpResponseWriter) WriteJson(v interface{}) error { 69 | b, err := w.EncodeJson(v) 70 | if err != nil { 71 | return err 72 | } 73 | // JSONP security fix (http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/) 74 | w.Header().Set("Content-Disposition", "filename=f.txt") 75 | w.Header().Set("X-Content-Type-Options", "nosniff") 76 | w.Write([]byte("/**/" + w.callbackName + "(")) 77 | w.Write(b) 78 | w.Write([]byte(")")) 79 | return nil 80 | } 81 | 82 | // Make sure the local WriteHeader is called, and call the parent Flush. 83 | // Provided in order to implement the http.Flusher interface. 84 | func (w *jsonpResponseWriter) Flush() { 85 | if !w.wroteHeader { 86 | w.WriteHeader(http.StatusOK) 87 | } 88 | flusher := w.ResponseWriter.(http.Flusher) 89 | flusher.Flush() 90 | } 91 | 92 | // Call the parent CloseNotify. 93 | // Provided in order to implement the http.CloseNotifier interface. 94 | func (w *jsonpResponseWriter) CloseNotify() <-chan bool { 95 | notifier := w.ResponseWriter.(http.CloseNotifier) 96 | return notifier.CloseNotify() 97 | } 98 | 99 | // Provided in order to implement the http.Hijacker interface. 100 | func (w *jsonpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 101 | hijacker := w.ResponseWriter.(http.Hijacker) 102 | return hijacker.Hijack() 103 | } 104 | 105 | // Make sure the local WriteHeader is called. 106 | // Provided in order to implement the http.ResponseWriter interface. 107 | func (w *jsonpResponseWriter) Write(b []byte) (int, error) { 108 | 109 | if !w.wroteHeader { 110 | w.WriteHeader(http.StatusOK) 111 | } 112 | 113 | writer := w.ResponseWriter.(http.ResponseWriter) 114 | 115 | return writer.Write(b) 116 | } 117 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/jsonp_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ant0ine/go-json-rest/rest/test" 7 | ) 8 | 9 | func TestJsonpMiddleware(t *testing.T) { 10 | 11 | api := NewApi() 12 | 13 | // the middleware to test 14 | api.Use(&JsonpMiddleware{}) 15 | 16 | // router app with success and error paths 17 | router, err := MakeRouter( 18 | Get("/ok", func(w ResponseWriter, r *Request) { 19 | w.WriteJson(map[string]string{"Id": "123"}) 20 | }), 21 | Get("/error", func(w ResponseWriter, r *Request) { 22 | Error(w, "jsonp error", 500) 23 | }), 24 | ) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | api.SetApp(router) 30 | 31 | // wrap all 32 | handler := api.MakeHandler() 33 | 34 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/ok?callback=parseResponse", nil)) 35 | recorded.CodeIs(200) 36 | recorded.HeaderIs("Content-Type", "text/javascript") 37 | recorded.HeaderIs("Content-Disposition", "filename=f.txt") 38 | recorded.HeaderIs("X-Content-Type-Options", "nosniff") 39 | recorded.BodyIs("/**/parseResponse({\"Id\":\"123\"})") 40 | 41 | recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/error?callback=parseResponse", nil)) 42 | recorded.CodeIs(500) 43 | recorded.HeaderIs("Content-Type", "text/javascript") 44 | recorded.HeaderIs("Content-Disposition", "filename=f.txt") 45 | recorded.HeaderIs("X-Content-Type-Options", "nosniff") 46 | recorded.BodyIs("/**/parseResponse({\"Error\":\"jsonp error\"})") 47 | } 48 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/middleware.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // HandlerFunc defines the handler function. It is the go-json-rest equivalent of http.HandlerFunc. 8 | type HandlerFunc func(ResponseWriter, *Request) 9 | 10 | // App defines the interface that an object should implement to be used as an app in this framework 11 | // stack. The App is the top element of the stack, the other elements being middlewares. 12 | type App interface { 13 | AppFunc() HandlerFunc 14 | } 15 | 16 | // AppSimple is an adapter type that makes it easy to write an App with a simple function. 17 | // eg: rest.NewApi(rest.AppSimple(func(w rest.ResponseWriter, r *rest.Request) { ... })) 18 | type AppSimple HandlerFunc 19 | 20 | // AppFunc makes AppSimple implement the App interface. 21 | func (as AppSimple) AppFunc() HandlerFunc { 22 | return HandlerFunc(as) 23 | } 24 | 25 | // Middleware defines the interface that objects must implement in order to wrap a HandlerFunc and 26 | // be used in the middleware stack. 27 | type Middleware interface { 28 | MiddlewareFunc(handler HandlerFunc) HandlerFunc 29 | } 30 | 31 | // MiddlewareSimple is an adapter type that makes it easy to write a Middleware with a simple 32 | // function. eg: api.Use(rest.MiddlewareSimple(func(h HandlerFunc) Handlerfunc { ... })) 33 | type MiddlewareSimple func(handler HandlerFunc) HandlerFunc 34 | 35 | // MiddlewareFunc makes MiddlewareSimple implement the Middleware interface. 36 | func (ms MiddlewareSimple) MiddlewareFunc(handler HandlerFunc) HandlerFunc { 37 | return ms(handler) 38 | } 39 | 40 | // WrapMiddlewares calls the MiddlewareFunc methods in the reverse order and returns an HandlerFunc 41 | // ready to be executed. This can be used to wrap a set of middlewares, post routing, on a per Route 42 | // basis. 43 | func WrapMiddlewares(middlewares []Middleware, handler HandlerFunc) HandlerFunc { 44 | wrapped := handler 45 | for i := len(middlewares) - 1; i >= 0; i-- { 46 | wrapped = middlewares[i].MiddlewareFunc(wrapped) 47 | } 48 | return wrapped 49 | } 50 | 51 | // Handle the transition between net/http and go-json-rest objects. 52 | // It intanciates the rest.Request and rest.ResponseWriter, ... 53 | func adapterFunc(handler HandlerFunc) http.HandlerFunc { 54 | 55 | return func(origWriter http.ResponseWriter, origRequest *http.Request) { 56 | 57 | // instantiate the rest objects 58 | request := &Request{ 59 | origRequest, 60 | nil, 61 | map[string]interface{}{}, 62 | } 63 | 64 | writer := &responseWriter{ 65 | origWriter, 66 | false, 67 | } 68 | 69 | // call the wrapped handler 70 | handler(writer, request) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/middleware_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type testMiddleware struct { 8 | name string 9 | } 10 | 11 | func (mw *testMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc { 12 | return func(w ResponseWriter, r *Request) { 13 | if r.Env["BEFORE"] == nil { 14 | r.Env["BEFORE"] = mw.name 15 | } else { 16 | r.Env["BEFORE"] = r.Env["BEFORE"].(string) + mw.name 17 | } 18 | handler(w, r) 19 | if r.Env["AFTER"] == nil { 20 | r.Env["AFTER"] = mw.name 21 | } else { 22 | r.Env["AFTER"] = r.Env["AFTER"].(string) + mw.name 23 | } 24 | } 25 | } 26 | 27 | func TestWrapMiddlewares(t *testing.T) { 28 | 29 | a := &testMiddleware{"A"} 30 | b := &testMiddleware{"B"} 31 | c := &testMiddleware{"C"} 32 | 33 | app := func(w ResponseWriter, r *Request) { 34 | // do nothing 35 | } 36 | 37 | handlerFunc := WrapMiddlewares([]Middleware{a, b, c}, app) 38 | 39 | // fake request 40 | r := &Request{ 41 | nil, 42 | nil, 43 | map[string]interface{}{}, 44 | } 45 | 46 | handlerFunc(nil, r) 47 | 48 | before := r.Env["BEFORE"].(string) 49 | if before != "ABC" { 50 | t.Error("middleware executed in the wrong order, expected ABC") 51 | } 52 | 53 | after := r.Env["AFTER"].(string) 54 | if after != "CBA" { 55 | t.Error("middleware executed in the wrong order, expected CBA") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/powered_by.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | const xPoweredByDefault = "go-json-rest" 4 | 5 | // PoweredByMiddleware adds the "X-Powered-By" header to the HTTP response. 6 | type PoweredByMiddleware struct { 7 | 8 | // If specified, used as the value for the "X-Powered-By" response header. 9 | // Defaults to "go-json-rest". 10 | XPoweredBy string 11 | } 12 | 13 | // MiddlewareFunc makes PoweredByMiddleware implement the Middleware interface. 14 | func (mw *PoweredByMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc { 15 | 16 | poweredBy := xPoweredByDefault 17 | if mw.XPoweredBy != "" { 18 | poweredBy = mw.XPoweredBy 19 | } 20 | 21 | return func(w ResponseWriter, r *Request) { 22 | 23 | w.Header().Add("X-Powered-By", poweredBy) 24 | 25 | // call the handler 26 | h(w, r) 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/powered_by_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "testing" 6 | ) 7 | 8 | func TestPoweredByMiddleware(t *testing.T) { 9 | 10 | api := NewApi() 11 | 12 | // the middleware to test 13 | api.Use(&PoweredByMiddleware{ 14 | XPoweredBy: "test", 15 | }) 16 | 17 | // a simple app 18 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 19 | w.WriteJson(map[string]string{"Id": "123"}) 20 | })) 21 | 22 | // wrap all 23 | handler := api.MakeHandler() 24 | 25 | req := test.MakeSimpleRequest("GET", "http://localhost/", nil) 26 | recorded := test.RunRequest(t, handler, req) 27 | recorded.CodeIs(200) 28 | recorded.ContentTypeIsJson() 29 | recorded.HeaderIs("X-Powered-By", "test") 30 | } 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/recorder.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "bufio" 5 | "net" 6 | "net/http" 7 | ) 8 | 9 | // RecorderMiddleware keeps a record of the HTTP status code of the response, 10 | // and the number of bytes written. 11 | // The result is available to the wrapping handlers as request.Env["STATUS_CODE"].(int), 12 | // and as request.Env["BYTES_WRITTEN"].(int64) 13 | type RecorderMiddleware struct{} 14 | 15 | // MiddlewareFunc makes RecorderMiddleware implement the Middleware interface. 16 | func (mw *RecorderMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc { 17 | return func(w ResponseWriter, r *Request) { 18 | 19 | writer := &recorderResponseWriter{w, 0, false, 0} 20 | 21 | // call the handler 22 | h(writer, r) 23 | 24 | r.Env["STATUS_CODE"] = writer.statusCode 25 | r.Env["BYTES_WRITTEN"] = writer.bytesWritten 26 | } 27 | } 28 | 29 | // Private responseWriter intantiated by the recorder middleware. 30 | // It keeps a record of the HTTP status code of the response. 31 | // It implements the following interfaces: 32 | // ResponseWriter 33 | // http.ResponseWriter 34 | // http.Flusher 35 | // http.CloseNotifier 36 | // http.Hijacker 37 | type recorderResponseWriter struct { 38 | ResponseWriter 39 | statusCode int 40 | wroteHeader bool 41 | bytesWritten int64 42 | } 43 | 44 | // Record the status code. 45 | func (w *recorderResponseWriter) WriteHeader(code int) { 46 | w.ResponseWriter.WriteHeader(code) 47 | w.statusCode = code 48 | w.wroteHeader = true 49 | } 50 | 51 | // Make sure the local Write is called. 52 | func (w *recorderResponseWriter) WriteJson(v interface{}) error { 53 | b, err := w.EncodeJson(v) 54 | if err != nil { 55 | return err 56 | } 57 | _, err = w.Write(b) 58 | if err != nil { 59 | return err 60 | } 61 | return nil 62 | } 63 | 64 | // Make sure the local WriteHeader is called, and call the parent Flush. 65 | // Provided in order to implement the http.Flusher interface. 66 | func (w *recorderResponseWriter) Flush() { 67 | if !w.wroteHeader { 68 | w.WriteHeader(http.StatusOK) 69 | } 70 | flusher := w.ResponseWriter.(http.Flusher) 71 | flusher.Flush() 72 | } 73 | 74 | // Call the parent CloseNotify. 75 | // Provided in order to implement the http.CloseNotifier interface. 76 | func (w *recorderResponseWriter) CloseNotify() <-chan bool { 77 | notifier := w.ResponseWriter.(http.CloseNotifier) 78 | return notifier.CloseNotify() 79 | } 80 | 81 | // Provided in order to implement the http.Hijacker interface. 82 | func (w *recorderResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 83 | hijacker := w.ResponseWriter.(http.Hijacker) 84 | return hijacker.Hijack() 85 | } 86 | 87 | // Make sure the local WriteHeader is called, and call the parent Write. 88 | // Provided in order to implement the http.ResponseWriter interface. 89 | func (w *recorderResponseWriter) Write(b []byte) (int, error) { 90 | if !w.wroteHeader { 91 | w.WriteHeader(http.StatusOK) 92 | } 93 | writer := w.ResponseWriter.(http.ResponseWriter) 94 | written, err := writer.Write(b) 95 | w.bytesWritten += int64(written) 96 | return written, err 97 | } 98 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/recorder_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "testing" 6 | ) 7 | 8 | func TestRecorderMiddleware(t *testing.T) { 9 | 10 | api := NewApi() 11 | 12 | // a middleware carrying the Env tests 13 | api.Use(MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { 14 | return func(w ResponseWriter, r *Request) { 15 | 16 | handler(w, r) 17 | 18 | if r.Env["STATUS_CODE"] == nil { 19 | t.Error("STATUS_CODE is nil") 20 | } 21 | statusCode := r.Env["STATUS_CODE"].(int) 22 | if statusCode != 200 { 23 | t.Errorf("STATUS_CODE = 200 expected, got %d", statusCode) 24 | } 25 | 26 | if r.Env["BYTES_WRITTEN"] == nil { 27 | t.Error("BYTES_WRITTEN is nil") 28 | } 29 | bytesWritten := r.Env["BYTES_WRITTEN"].(int64) 30 | // '{"Id":"123"}' => 12 chars 31 | if bytesWritten != 12 { 32 | t.Errorf("BYTES_WRITTEN 12 expected, got %d", bytesWritten) 33 | } 34 | } 35 | })) 36 | 37 | // the middleware to test 38 | api.Use(&RecorderMiddleware{}) 39 | 40 | // a simple app 41 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 42 | w.WriteJson(map[string]string{"Id": "123"}) 43 | })) 44 | 45 | // wrap all 46 | handler := api.MakeHandler() 47 | 48 | req := test.MakeSimpleRequest("GET", "http://localhost/", nil) 49 | recorded := test.RunRequest(t, handler, req) 50 | recorded.CodeIs(200) 51 | recorded.ContentTypeIsJson() 52 | } 53 | 54 | // See how many bytes are written when gzipping 55 | func TestRecorderAndGzipMiddleware(t *testing.T) { 56 | 57 | api := NewApi() 58 | 59 | // a middleware carrying the Env tests 60 | api.Use(MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { 61 | return func(w ResponseWriter, r *Request) { 62 | 63 | handler(w, r) 64 | 65 | if r.Env["BYTES_WRITTEN"] == nil { 66 | t.Error("BYTES_WRITTEN is nil") 67 | } 68 | bytesWritten := r.Env["BYTES_WRITTEN"].(int64) 69 | // Yes, the gzipped version actually takes more space. 70 | if bytesWritten != 28 { 71 | t.Errorf("BYTES_WRITTEN 28 expected, got %d", bytesWritten) 72 | } 73 | } 74 | })) 75 | 76 | // the middlewares to test 77 | api.Use(&RecorderMiddleware{}) 78 | api.Use(&GzipMiddleware{}) 79 | 80 | // a simple app 81 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 82 | w.WriteJson(map[string]string{"Id": "123"}) 83 | })) 84 | 85 | // wrap all 86 | handler := api.MakeHandler() 87 | 88 | req := test.MakeSimpleRequest("GET", "http://localhost/", nil) 89 | // "Accept-Encoding", "gzip" is set by test.MakeSimpleRequest 90 | recorded := test.RunRequest(t, handler, req) 91 | recorded.CodeIs(200) 92 | recorded.ContentTypeIsJson() 93 | } 94 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/recover.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "runtime/debug" 10 | ) 11 | 12 | // RecoverMiddleware catches the panic errors that occur in the wrapped HandleFunc, 13 | // and convert them to 500 responses. 14 | type RecoverMiddleware struct { 15 | 16 | // Custom logger used for logging the panic errors, 17 | // optional, defaults to log.New(os.Stderr, "", 0) 18 | Logger *log.Logger 19 | 20 | // If true, the log records will be printed as JSON. Convenient for log parsing. 21 | EnableLogAsJson bool 22 | 23 | // If true, when a "panic" happens, the error string and the stack trace will be 24 | // printed in the 500 response body. 25 | EnableResponseStackTrace bool 26 | } 27 | 28 | // MiddlewareFunc makes RecoverMiddleware implement the Middleware interface. 29 | func (mw *RecoverMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc { 30 | 31 | // set the default Logger 32 | if mw.Logger == nil { 33 | mw.Logger = log.New(os.Stderr, "", 0) 34 | } 35 | 36 | return func(w ResponseWriter, r *Request) { 37 | 38 | // catch user code's panic, and convert to http response 39 | defer func() { 40 | if reco := recover(); reco != nil { 41 | trace := debug.Stack() 42 | 43 | // log the trace 44 | message := fmt.Sprintf("%s\n%s", reco, trace) 45 | mw.logError(message) 46 | 47 | // write error response 48 | if mw.EnableResponseStackTrace { 49 | Error(w, message, http.StatusInternalServerError) 50 | } else { 51 | Error(w, "Internal Server Error", http.StatusInternalServerError) 52 | } 53 | } 54 | }() 55 | 56 | // call the handler 57 | h(w, r) 58 | } 59 | } 60 | 61 | func (mw *RecoverMiddleware) logError(message string) { 62 | if mw.EnableLogAsJson { 63 | record := map[string]string{ 64 | "error": message, 65 | } 66 | b, err := json.Marshal(&record) 67 | if err != nil { 68 | panic(err) 69 | } 70 | mw.Logger.Printf("%s", b) 71 | } else { 72 | mw.Logger.Print(message) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/recover_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "io/ioutil" 6 | "log" 7 | "testing" 8 | ) 9 | 10 | func TestRecoverMiddleware(t *testing.T) { 11 | 12 | api := NewApi() 13 | 14 | // the middleware to test 15 | api.Use(&RecoverMiddleware{ 16 | Logger: log.New(ioutil.Discard, "", 0), 17 | EnableLogAsJson: false, 18 | EnableResponseStackTrace: true, 19 | }) 20 | 21 | // a simple app that fails 22 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 23 | panic("test") 24 | })) 25 | 26 | // wrap all 27 | handler := api.MakeHandler() 28 | 29 | req := test.MakeSimpleRequest("GET", "http://localhost/", nil) 30 | recorded := test.RunRequest(t, handler, req) 31 | recorded.CodeIs(500) 32 | recorded.ContentTypeIsJson() 33 | 34 | // payload 35 | payload := map[string]string{} 36 | err := recorded.DecodeJsonPayload(&payload) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | if payload["Error"] == "" { 41 | t.Errorf("Expected an error message, got: %v", payload) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/request.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io/ioutil" 7 | "net/http" 8 | "net/url" 9 | "strings" 10 | ) 11 | 12 | var ( 13 | // ErrJsonPayloadEmpty is returned when the JSON payload is empty. 14 | ErrJsonPayloadEmpty = errors.New("JSON payload is empty") 15 | ) 16 | 17 | // Request inherits from http.Request, and provides additional methods. 18 | type Request struct { 19 | *http.Request 20 | 21 | // Map of parameters that have been matched in the URL Path. 22 | PathParams map[string]string 23 | 24 | // Environment used by middlewares to communicate. 25 | Env map[string]interface{} 26 | } 27 | 28 | // PathParam provides a convenient access to the PathParams map. 29 | func (r *Request) PathParam(name string) string { 30 | return r.PathParams[name] 31 | } 32 | 33 | // DecodeJsonPayload reads the request body and decodes the JSON using json.Unmarshal. 34 | func (r *Request) DecodeJsonPayload(v interface{}) error { 35 | content, err := ioutil.ReadAll(r.Body) 36 | r.Body.Close() 37 | if err != nil { 38 | return err 39 | } 40 | if len(content) == 0 { 41 | return ErrJsonPayloadEmpty 42 | } 43 | err = json.Unmarshal(content, v) 44 | if err != nil { 45 | return err 46 | } 47 | return nil 48 | } 49 | 50 | // BaseUrl returns a new URL object with the Host and Scheme taken from the request. 51 | // (without the trailing slash in the host) 52 | func (r *Request) BaseUrl() *url.URL { 53 | scheme := r.URL.Scheme 54 | if scheme == "" { 55 | scheme = "http" 56 | } 57 | 58 | host := r.Host 59 | if len(host) > 0 && host[len(host)-1] == '/' { 60 | host = host[:len(host)-1] 61 | } 62 | 63 | return &url.URL{ 64 | Scheme: scheme, 65 | Host: host, 66 | } 67 | } 68 | 69 | // UrlFor returns the URL object from UriBase with the Path set to path, and the query 70 | // string built with queryParams. 71 | func (r *Request) UrlFor(path string, queryParams map[string][]string) *url.URL { 72 | baseUrl := r.BaseUrl() 73 | baseUrl.Path = path 74 | if queryParams != nil { 75 | query := url.Values{} 76 | for k, v := range queryParams { 77 | for _, vv := range v { 78 | query.Add(k, vv) 79 | } 80 | } 81 | baseUrl.RawQuery = query.Encode() 82 | } 83 | return baseUrl 84 | } 85 | 86 | // CorsInfo contains the CORS request info derived from a rest.Request. 87 | type CorsInfo struct { 88 | IsCors bool 89 | IsPreflight bool 90 | Origin string 91 | OriginUrl *url.URL 92 | 93 | // The header value is converted to uppercase to avoid common mistakes. 94 | AccessControlRequestMethod string 95 | 96 | // The header values are normalized with http.CanonicalHeaderKey. 97 | AccessControlRequestHeaders []string 98 | } 99 | 100 | // GetCorsInfo derives CorsInfo from Request. 101 | func (r *Request) GetCorsInfo() *CorsInfo { 102 | 103 | origin := r.Header.Get("Origin") 104 | 105 | var originUrl *url.URL 106 | var isCors bool 107 | 108 | if origin == "" { 109 | isCors = false 110 | } else if origin == "null" { 111 | isCors = true 112 | } else { 113 | var err error 114 | originUrl, err = url.ParseRequestURI(origin) 115 | isCors = err == nil && r.Host != originUrl.Host 116 | } 117 | 118 | reqMethod := r.Header.Get("Access-Control-Request-Method") 119 | 120 | reqHeaders := []string{} 121 | rawReqHeaders := r.Header[http.CanonicalHeaderKey("Access-Control-Request-Headers")] 122 | for _, rawReqHeader := range rawReqHeaders { 123 | // net/http does not handle comma delimited headers for us 124 | for _, reqHeader := range strings.Split(rawReqHeader, ",") { 125 | reqHeaders = append(reqHeaders, http.CanonicalHeaderKey(strings.TrimSpace(reqHeader))) 126 | } 127 | } 128 | 129 | isPreflight := isCors && r.Method == "OPTIONS" && reqMethod != "" 130 | 131 | return &CorsInfo{ 132 | IsCors: isCors, 133 | IsPreflight: isPreflight, 134 | Origin: origin, 135 | OriginUrl: originUrl, 136 | AccessControlRequestMethod: strings.ToUpper(reqMethod), 137 | AccessControlRequestHeaders: reqHeaders, 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/response.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "net" 7 | "net/http" 8 | ) 9 | 10 | // A ResponseWriter interface dedicated to JSON HTTP response. 11 | // Note, the responseWriter object instantiated by the framework also implements many other interfaces 12 | // accessible by type assertion: http.ResponseWriter, http.Flusher, http.CloseNotifier, http.Hijacker. 13 | type ResponseWriter interface { 14 | 15 | // Identical to the http.ResponseWriter interface 16 | Header() http.Header 17 | 18 | // Use EncodeJson to generate the payload, write the headers with http.StatusOK if 19 | // they are not already written, then write the payload. 20 | // The Content-Type header is set to "application/json", unless already specified. 21 | WriteJson(v interface{}) error 22 | 23 | // Encode the data structure to JSON, mainly used to wrap ResponseWriter in 24 | // middlewares. 25 | EncodeJson(v interface{}) ([]byte, error) 26 | 27 | // Similar to the http.ResponseWriter interface, with additional JSON related 28 | // headers set. 29 | WriteHeader(int) 30 | } 31 | 32 | // Error produces an error response in JSON with the following structure, '{"Error":"My error message"}' 33 | // The standard plain text net/http Error helper can still be called like this: 34 | // http.Error(w, "error message", code) 35 | func Error(w ResponseWriter, error string, code int) { 36 | w.WriteHeader(code) 37 | err := w.WriteJson(map[string]string{"Error": error}) 38 | if err != nil { 39 | panic(err) 40 | } 41 | } 42 | 43 | // NotFound produces a 404 response with the following JSON, '{"Error":"Resource not found"}' 44 | // The standard plain text net/http NotFound helper can still be called like this: 45 | // http.NotFound(w, r.Request) 46 | func NotFound(w ResponseWriter, r *Request) { 47 | Error(w, "Resource not found", http.StatusNotFound) 48 | } 49 | 50 | // Private responseWriter intantiated by the resource handler. 51 | // It implements the following interfaces: 52 | // ResponseWriter 53 | // http.ResponseWriter 54 | // http.Flusher 55 | // http.CloseNotifier 56 | // http.Hijacker 57 | type responseWriter struct { 58 | http.ResponseWriter 59 | wroteHeader bool 60 | } 61 | 62 | func (w *responseWriter) WriteHeader(code int) { 63 | if w.Header().Get("Content-Type") == "" { 64 | w.Header().Set("Content-Type", "application/json") 65 | } 66 | w.ResponseWriter.WriteHeader(code) 67 | w.wroteHeader = true 68 | } 69 | 70 | func (w *responseWriter) EncodeJson(v interface{}) ([]byte, error) { 71 | b, err := json.Marshal(v) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return b, nil 76 | } 77 | 78 | // Encode the object in JSON and call Write. 79 | func (w *responseWriter) WriteJson(v interface{}) error { 80 | b, err := w.EncodeJson(v) 81 | if err != nil { 82 | return err 83 | } 84 | _, err = w.Write(b) 85 | if err != nil { 86 | return err 87 | } 88 | return nil 89 | } 90 | 91 | // Provided in order to implement the http.ResponseWriter interface. 92 | func (w *responseWriter) Write(b []byte) (int, error) { 93 | if !w.wroteHeader { 94 | w.WriteHeader(http.StatusOK) 95 | } 96 | return w.ResponseWriter.Write(b) 97 | } 98 | 99 | // Provided in order to implement the http.Flusher interface. 100 | func (w *responseWriter) Flush() { 101 | if !w.wroteHeader { 102 | w.WriteHeader(http.StatusOK) 103 | } 104 | flusher := w.ResponseWriter.(http.Flusher) 105 | flusher.Flush() 106 | } 107 | 108 | // Provided in order to implement the http.CloseNotifier interface. 109 | func (w *responseWriter) CloseNotify() <-chan bool { 110 | notifier := w.ResponseWriter.(http.CloseNotifier) 111 | return notifier.CloseNotify() 112 | } 113 | 114 | // Provided in order to implement the http.Hijacker interface. 115 | func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 116 | hijacker := w.ResponseWriter.(http.Hijacker) 117 | return hijacker.Hijack() 118 | } 119 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/response_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestResponseNotIndent(t *testing.T) { 8 | 9 | writer := responseWriter{ 10 | nil, 11 | false, 12 | } 13 | 14 | got, err := writer.EncodeJson(map[string]bool{"test": true}) 15 | if err != nil { 16 | t.Error(err.Error()) 17 | } 18 | gotStr := string(got) 19 | expected := "{\"test\":true}" 20 | if gotStr != expected { 21 | t.Error(expected + " was the expected, but instead got " + gotStr) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/route.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Route defines a route as consumed by the router. It can be instantiated directly, or using one 8 | // of the shortcut methods: rest.Get, rest.Post, rest.Put, rest.Patch and rest.Delete. 9 | type Route struct { 10 | 11 | // Any HTTP method. It will be used as uppercase to avoid common mistakes. 12 | HttpMethod string 13 | 14 | // A string like "/resource/:id.json". 15 | // Placeholders supported are: 16 | // :paramName that matches any char to the first '/' or '.' 17 | // #paramName that matches any char to the first '/' 18 | // *paramName that matches everything to the end of the string 19 | // (placeholder names must be unique per PathExp) 20 | PathExp string 21 | 22 | // Code that will be executed when this route is taken. 23 | Func HandlerFunc 24 | } 25 | 26 | // MakePath generates the path corresponding to this Route and the provided path parameters. 27 | // This is used for reverse route resolution. 28 | func (route *Route) MakePath(pathParams map[string]string) string { 29 | path := route.PathExp 30 | for paramName, paramValue := range pathParams { 31 | paramPlaceholder := ":" + paramName 32 | relaxedPlaceholder := "#" + paramName 33 | splatPlaceholder := "*" + paramName 34 | r := strings.NewReplacer(paramPlaceholder, paramValue, splatPlaceholder, paramValue, relaxedPlaceholder, paramValue) 35 | path = r.Replace(path) 36 | } 37 | return path 38 | } 39 | 40 | // Head is a shortcut method that instantiates a HEAD route. See the Route object the parameters definitions. 41 | // Equivalent to &Route{"HEAD", pathExp, handlerFunc} 42 | func Head(pathExp string, handlerFunc HandlerFunc) *Route { 43 | return &Route{ 44 | HttpMethod: "HEAD", 45 | PathExp: pathExp, 46 | Func: handlerFunc, 47 | } 48 | } 49 | 50 | // Get is a shortcut method that instantiates a GET route. See the Route object the parameters definitions. 51 | // Equivalent to &Route{"GET", pathExp, handlerFunc} 52 | func Get(pathExp string, handlerFunc HandlerFunc) *Route { 53 | return &Route{ 54 | HttpMethod: "GET", 55 | PathExp: pathExp, 56 | Func: handlerFunc, 57 | } 58 | } 59 | 60 | // Post is a shortcut method that instantiates a POST route. See the Route object the parameters definitions. 61 | // Equivalent to &Route{"POST", pathExp, handlerFunc} 62 | func Post(pathExp string, handlerFunc HandlerFunc) *Route { 63 | return &Route{ 64 | HttpMethod: "POST", 65 | PathExp: pathExp, 66 | Func: handlerFunc, 67 | } 68 | } 69 | 70 | // Put is a shortcut method that instantiates a PUT route. See the Route object the parameters definitions. 71 | // Equivalent to &Route{"PUT", pathExp, handlerFunc} 72 | func Put(pathExp string, handlerFunc HandlerFunc) *Route { 73 | return &Route{ 74 | HttpMethod: "PUT", 75 | PathExp: pathExp, 76 | Func: handlerFunc, 77 | } 78 | } 79 | 80 | // Patch is a shortcut method that instantiates a PATCH route. See the Route object the parameters definitions. 81 | // Equivalent to &Route{"PATCH", pathExp, handlerFunc} 82 | func Patch(pathExp string, handlerFunc HandlerFunc) *Route { 83 | return &Route{ 84 | HttpMethod: "PATCH", 85 | PathExp: pathExp, 86 | Func: handlerFunc, 87 | } 88 | } 89 | 90 | // Delete is a shortcut method that instantiates a DELETE route. Equivalent to &Route{"DELETE", pathExp, handlerFunc} 91 | func Delete(pathExp string, handlerFunc HandlerFunc) *Route { 92 | return &Route{ 93 | HttpMethod: "DELETE", 94 | PathExp: pathExp, 95 | Func: handlerFunc, 96 | } 97 | } 98 | 99 | // Options is a shortcut method that instantiates an OPTIONS route. See the Route object the parameters definitions. 100 | // Equivalent to &Route{"OPTIONS", pathExp, handlerFunc} 101 | func Options(pathExp string, handlerFunc HandlerFunc) *Route { 102 | return &Route{ 103 | HttpMethod: "OPTIONS", 104 | PathExp: pathExp, 105 | Func: handlerFunc, 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/route_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestReverseRouteResolution(t *testing.T) { 8 | 9 | noParam := &Route{"GET", "/", nil} 10 | got := noParam.MakePath(nil) 11 | expected := "/" 12 | if got != expected { 13 | t.Errorf("expected %s, got %s", expected, got) 14 | } 15 | 16 | twoParams := &Route{"GET", "/:id.:format", nil} 17 | got = twoParams.MakePath( 18 | map[string]string{ 19 | "id": "123", 20 | "format": "json", 21 | }, 22 | ) 23 | expected = "/123.json" 24 | if got != expected { 25 | t.Errorf("expected %s, got %s", expected, got) 26 | } 27 | 28 | splatParam := &Route{"GET", "/:id.*format", nil} 29 | got = splatParam.MakePath( 30 | map[string]string{ 31 | "id": "123", 32 | "format": "tar.gz", 33 | }, 34 | ) 35 | expected = "/123.tar.gz" 36 | if got != expected { 37 | t.Errorf("expected %s, got %s", expected, got) 38 | } 39 | 40 | relaxedParam := &Route{"GET", "/#file", nil} 41 | got = relaxedParam.MakePath( 42 | map[string]string{ 43 | "file": "a.txt", 44 | }, 45 | ) 46 | expected = "/a.txt" 47 | if got != expected { 48 | t.Errorf("expected %s, got %s", expected, got) 49 | } 50 | } 51 | 52 | func TestShortcutMethods(t *testing.T) { 53 | 54 | r := Head("/", nil) 55 | if r.HttpMethod != "HEAD" { 56 | t.Errorf("expected HEAD, got %s", r.HttpMethod) 57 | } 58 | 59 | r = Get("/", nil) 60 | if r.HttpMethod != "GET" { 61 | t.Errorf("expected GET, got %s", r.HttpMethod) 62 | } 63 | 64 | r = Post("/", nil) 65 | if r.HttpMethod != "POST" { 66 | t.Errorf("expected POST, got %s", r.HttpMethod) 67 | } 68 | 69 | r = Put("/", nil) 70 | if r.HttpMethod != "PUT" { 71 | t.Errorf("expected PUT, got %s", r.HttpMethod) 72 | } 73 | 74 | r = Patch("/", nil) 75 | if r.HttpMethod != "PATCH" { 76 | t.Errorf("expected PATCH, got %s", r.HttpMethod) 77 | } 78 | 79 | r = Delete("/", nil) 80 | if r.HttpMethod != "DELETE" { 81 | t.Errorf("expected DELETE, got %s", r.HttpMethod) 82 | } 83 | 84 | r = Options("/", nil) 85 | if r.HttpMethod != "OPTIONS" { 86 | t.Errorf("expected OPTIONS, got %s", r.HttpMethod) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/router_benchmark_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "regexp" 7 | "testing" 8 | ) 9 | 10 | func routes() []*Route { 11 | // simulate the routes of a real but reasonable app. 12 | // 6 + 10 * (5 + 2) + 1 = 77 routes 13 | routePaths := []string{ 14 | "/", 15 | "/signin", 16 | "/signout", 17 | "/profile", 18 | "/settings", 19 | "/upload/*file", 20 | } 21 | for i := 0; i < 10; i++ { 22 | for j := 0; j < 5; j++ { 23 | routePaths = append(routePaths, fmt.Sprintf("/resource%d/:id/property%d", i, j)) 24 | } 25 | routePaths = append(routePaths, fmt.Sprintf("/resource%d/:id", i)) 26 | routePaths = append(routePaths, fmt.Sprintf("/resource%d", i)) 27 | } 28 | routePaths = append(routePaths, "/*") 29 | 30 | routes := []*Route{} 31 | for _, path := range routePaths { 32 | routes = append(routes, &Route{ 33 | HttpMethod: "GET", 34 | PathExp: path, 35 | }) 36 | } 37 | return routes 38 | } 39 | 40 | func requestUrls() []*url.URL { 41 | // simulate a few requests 42 | urlStrs := []string{ 43 | "http://example.org/", 44 | "http://example.org/resource9/123", 45 | "http://example.org/resource9/123/property1", 46 | "http://example.org/doesnotexist", 47 | } 48 | urlObjs := []*url.URL{} 49 | for _, urlStr := range urlStrs { 50 | urlObj, _ := url.Parse(urlStr) 51 | urlObjs = append(urlObjs, urlObj) 52 | } 53 | return urlObjs 54 | } 55 | 56 | func BenchmarkNoCompression(b *testing.B) { 57 | 58 | b.StopTimer() 59 | 60 | r := router{ 61 | Routes: routes(), 62 | disableTrieCompression: true, 63 | } 64 | r.start() 65 | urlObjs := requestUrls() 66 | 67 | b.StartTimer() 68 | 69 | for i := 0; i < b.N; i++ { 70 | for _, urlObj := range urlObjs { 71 | r.findRouteFromURL("GET", urlObj) 72 | } 73 | } 74 | } 75 | 76 | func BenchmarkCompression(b *testing.B) { 77 | 78 | b.StopTimer() 79 | 80 | r := router{ 81 | Routes: routes(), 82 | } 83 | r.start() 84 | urlObjs := requestUrls() 85 | 86 | b.StartTimer() 87 | 88 | for i := 0; i < b.N; i++ { 89 | for _, urlObj := range urlObjs { 90 | r.findRouteFromURL("GET", urlObj) 91 | } 92 | } 93 | } 94 | 95 | func BenchmarkRegExpLoop(b *testing.B) { 96 | // reference benchmark using the usual RegExps + Loop strategy 97 | 98 | b.StopTimer() 99 | 100 | routes := routes() 101 | urlObjs := requestUrls() 102 | 103 | // build the route regexps 104 | r1, err := regexp.Compile(":[^/\\.]*") 105 | if err != nil { 106 | panic(err) 107 | } 108 | r2, err := regexp.Compile("\\*.*") 109 | if err != nil { 110 | panic(err) 111 | } 112 | routeRegexps := []*regexp.Regexp{} 113 | for _, route := range routes { 114 | 115 | // generate the regexp string 116 | regStr := r2.ReplaceAllString(route.PathExp, "([^/\\.]+)") 117 | regStr = r1.ReplaceAllString(regStr, "(.+)") 118 | regStr = "^" + regStr + "$" 119 | 120 | // compile it 121 | reg, err := regexp.Compile(regStr) 122 | if err != nil { 123 | panic(err) 124 | } 125 | 126 | routeRegexps = append(routeRegexps, reg) 127 | } 128 | 129 | b.StartTimer() 130 | 131 | for i := 0; i < b.N; i++ { 132 | // do it for a few urls 133 | for _, urlObj := range urlObjs { 134 | // stop at the first route that matches 135 | for index, reg := range routeRegexps { 136 | if reg.FindAllString(urlObj.Path, 1) != nil { 137 | _ = routes[index] 138 | break 139 | } 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/status_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "testing" 6 | ) 7 | 8 | func TestStatusMiddleware(t *testing.T) { 9 | 10 | api := NewApi() 11 | 12 | // the middlewares 13 | status := &StatusMiddleware{} 14 | api.Use(status) 15 | api.Use(&TimerMiddleware{}) 16 | api.Use(&RecorderMiddleware{}) 17 | 18 | // an app that return the Status 19 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 20 | w.WriteJson(status.GetStatus()) 21 | })) 22 | 23 | // wrap all 24 | handler := api.MakeHandler() 25 | 26 | // one request 27 | recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/1", nil)) 28 | recorded.CodeIs(200) 29 | recorded.ContentTypeIsJson() 30 | 31 | // another request 32 | recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/2", nil)) 33 | recorded.CodeIs(200) 34 | recorded.ContentTypeIsJson() 35 | 36 | // payload 37 | payload := map[string]interface{}{} 38 | err := recorded.DecodeJsonPayload(&payload) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | 43 | if payload["Pid"] == nil { 44 | t.Error("Expected a non nil Pid") 45 | } 46 | 47 | if payload["TotalCount"].(float64) != 1 { 48 | t.Errorf("TotalCount 1 Expected, got: %f", payload["TotalCount"].(float64)) 49 | } 50 | 51 | if payload["StatusCodeCount"].(map[string]interface{})["200"].(float64) != 1 { 52 | t.Errorf("StatusCodeCount 200 1 Expected, got: %f", payload["StatusCodeCount"].(map[string]interface{})["200"].(float64)) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/test/doc.go: -------------------------------------------------------------------------------- 1 | // Utility functions to help writing tests for a Go-Json-Rest app 2 | // 3 | // Go comes with net/http/httptest to help writing test for an http 4 | // server. When this http server implements a JSON REST API, some basic 5 | // checks end up to be always the same. This test package tries to save 6 | // some typing by providing helpers for this particular use case. 7 | // 8 | // package main 9 | // 10 | // import ( 11 | // "github.com/ant0ine/go-json-rest/rest" 12 | // "github.com/ant0ine/go-json-rest/rest/test" 13 | // "testing" 14 | // ) 15 | // 16 | // func TestSimpleRequest(t *testing.T) { 17 | // api := rest.NewApi() 18 | // api.Use(rest.DefaultDevStack...) 19 | // router, err := rest.MakeRouter( 20 | // rest.Get("/r", func(w rest.ResponseWriter, r *rest.Request) { 21 | // w.WriteJson(map[string]string{"Id": "123"}) 22 | // }), 23 | // ) 24 | // if err != nil { 25 | // log.Fatal(err) 26 | // } 27 | // api.SetApp(router) 28 | // recorded := test.RunRequest(t, api.MakeHandler(), 29 | // test.MakeSimpleRequest("GET", "http://1.2.3.4/r", nil)) 30 | // recorded.CodeIs(200) 31 | // recorded.ContentTypeIsJson() 32 | // } 33 | package test 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/test/util.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | // MakeSimpleRequest returns a http.Request. The returned request object can be 14 | // further prepared by adding headers and query string parmaters, for instance. 15 | func MakeSimpleRequest(method string, urlStr string, payload interface{}) *http.Request { 16 | var s string 17 | 18 | if payload != nil { 19 | b, err := json.Marshal(payload) 20 | if err != nil { 21 | panic(err) 22 | } 23 | s = fmt.Sprintf("%s", b) 24 | } 25 | 26 | r, err := http.NewRequest(method, urlStr, strings.NewReader(s)) 27 | if err != nil { 28 | panic(err) 29 | } 30 | r.Header.Set("Accept-Encoding", "gzip") 31 | if payload != nil { 32 | r.Header.Set("Content-Type", "application/json") 33 | } 34 | 35 | return r 36 | } 37 | 38 | // CodeIs compares the rescorded status code 39 | func CodeIs(t *testing.T, r *httptest.ResponseRecorder, expectedCode int) { 40 | if r.Code != expectedCode { 41 | t.Errorf("Code %d expected, got: %d", expectedCode, r.Code) 42 | } 43 | } 44 | 45 | // HeaderIs tests the first value for the given headerKey 46 | func HeaderIs(t *testing.T, r *httptest.ResponseRecorder, headerKey, expectedValue string) { 47 | value := r.HeaderMap.Get(headerKey) 48 | if value != expectedValue { 49 | t.Errorf( 50 | "%s: %s expected, got: %s", 51 | headerKey, 52 | expectedValue, 53 | value, 54 | ) 55 | } 56 | } 57 | 58 | func ContentTypeIsJson(t *testing.T, r *httptest.ResponseRecorder) { 59 | HeaderIs(t, r, "Content-Type", "application/json") 60 | } 61 | 62 | func ContentEncodingIsGzip(t *testing.T, r *httptest.ResponseRecorder) { 63 | HeaderIs(t, r, "Content-Encoding", "gzip") 64 | } 65 | 66 | func BodyIs(t *testing.T, r *httptest.ResponseRecorder, expectedBody string) { 67 | body := r.Body.String() 68 | if body != expectedBody { 69 | t.Errorf("Body '%s' expected, got: '%s'", expectedBody, body) 70 | } 71 | } 72 | 73 | func DecodeJsonPayload(r *httptest.ResponseRecorder, v interface{}) error { 74 | content, err := ioutil.ReadAll(r.Body) 75 | if err != nil { 76 | return err 77 | } 78 | err = json.Unmarshal(content, v) 79 | if err != nil { 80 | return err 81 | } 82 | return nil 83 | } 84 | 85 | type Recorded struct { 86 | T *testing.T 87 | Recorder *httptest.ResponseRecorder 88 | } 89 | 90 | // RunRequest runs a HTTP request through the given handler 91 | func RunRequest(t *testing.T, handler http.Handler, request *http.Request) *Recorded { 92 | recorder := httptest.NewRecorder() 93 | handler.ServeHTTP(recorder, request) 94 | return &Recorded{t, recorder} 95 | } 96 | 97 | func (rd *Recorded) CodeIs(expectedCode int) { 98 | CodeIs(rd.T, rd.Recorder, expectedCode) 99 | } 100 | 101 | func (rd *Recorded) HeaderIs(headerKey, expectedValue string) { 102 | HeaderIs(rd.T, rd.Recorder, headerKey, expectedValue) 103 | } 104 | 105 | func (rd *Recorded) ContentTypeIsJson() { 106 | rd.HeaderIs("Content-Type", "application/json") 107 | } 108 | 109 | func (rd *Recorded) ContentEncodingIsGzip() { 110 | rd.HeaderIs("Content-Encoding", "gzip") 111 | } 112 | 113 | func (rd *Recorded) BodyIs(expectedBody string) { 114 | BodyIs(rd.T, rd.Recorder, expectedBody) 115 | } 116 | 117 | func (rd *Recorded) DecodeJsonPayload(v interface{}) error { 118 | return DecodeJsonPayload(rd.Recorder, v) 119 | } 120 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/timer.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // TimerMiddleware computes the elapsed time spent during the execution of the wrapped handler. 8 | // The result is available to the wrapping handlers as request.Env["ELAPSED_TIME"].(*time.Duration), 9 | // and as request.Env["START_TIME"].(*time.Time) 10 | type TimerMiddleware struct{} 11 | 12 | // MiddlewareFunc makes TimerMiddleware implement the Middleware interface. 13 | func (mw *TimerMiddleware) MiddlewareFunc(h HandlerFunc) HandlerFunc { 14 | return func(w ResponseWriter, r *Request) { 15 | 16 | start := time.Now() 17 | r.Env["START_TIME"] = &start 18 | 19 | // call the handler 20 | h(w, r) 21 | 22 | end := time.Now() 23 | elapsed := end.Sub(start) 24 | r.Env["ELAPSED_TIME"] = &elapsed 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ant0ine/go-json-rest/rest/timer_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/ant0ine/go-json-rest/rest/test" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestTimerMiddleware(t *testing.T) { 10 | 11 | api := NewApi() 12 | 13 | // a middleware carrying the Env tests 14 | api.Use(MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { 15 | return func(w ResponseWriter, r *Request) { 16 | 17 | handler(w, r) 18 | 19 | if r.Env["ELAPSED_TIME"] == nil { 20 | t.Error("ELAPSED_TIME is nil") 21 | } 22 | elapsedTime := r.Env["ELAPSED_TIME"].(*time.Duration) 23 | if elapsedTime.Nanoseconds() <= 0 { 24 | t.Errorf( 25 | "ELAPSED_TIME is expected to be at least 1 nanosecond %d", 26 | elapsedTime.Nanoseconds(), 27 | ) 28 | } 29 | 30 | if r.Env["START_TIME"] == nil { 31 | t.Error("START_TIME is nil") 32 | } 33 | start := r.Env["START_TIME"].(*time.Time) 34 | if start.After(time.Now()) { 35 | t.Errorf( 36 | "START_TIME is expected to be in the past %s", 37 | start.String(), 38 | ) 39 | } 40 | } 41 | })) 42 | 43 | // the middleware to test 44 | api.Use(&TimerMiddleware{}) 45 | 46 | // a simple app 47 | api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { 48 | w.WriteJson(map[string]string{"Id": "123"}) 49 | })) 50 | 51 | // wrap all 52 | handler := api.MakeHandler() 53 | 54 | req := test.MakeSimpleRequest("GET", "http://localhost/", nil) 55 | recorded := test.RunRequest(t, handler, req) 56 | recorded.CodeIs(200) 57 | recorded.ContentTypeIsJson() 58 | } 59 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 1.1 3 | 4 | script: 5 | - go vet ./... 6 | - go test -v ./... 7 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Jeremy Saenz 2 | All Rights Reserved. 3 | 4 | MIT LICENSE 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | _cli_bash_autocomplete() { 4 | local cur prev opts base 5 | COMPREPLY=() 6 | cur="${COMP_WORDS[COMP_CWORD]}" 7 | prev="${COMP_WORDS[COMP_CWORD-1]}" 8 | opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) 9 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 10 | return 0 11 | } 12 | 13 | complete -F _cli_bash_autocomplete $PROG -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete: -------------------------------------------------------------------------------- 1 | autoload -U compinit && compinit 2 | autoload -U bashcompinit && bashcompinit 3 | 4 | script_dir=$(dirname $0) 5 | source ${script_dir}/bash_autocomplete 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) { 14 | // println("Greetings") 15 | // } 16 | // 17 | // app.Run(os.Args) 18 | // } 19 | package cli 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/codegangsta/cli" 7 | ) 8 | 9 | func Example() { 10 | app := cli.NewApp() 11 | app.Name = "todo" 12 | app.Usage = "task list on the command line" 13 | app.Commands = []cli.Command{ 14 | { 15 | Name: "add", 16 | ShortName: "a", 17 | Usage: "add a task to the list", 18 | Action: func(c *cli.Context) { 19 | println("added task: ", c.Args().First()) 20 | }, 21 | }, 22 | { 23 | Name: "complete", 24 | ShortName: "c", 25 | Usage: "complete a task on the list", 26 | Action: func(c *cli.Context) { 27 | println("completed task: ", c.Args().First()) 28 | }, 29 | }, 30 | } 31 | 32 | app.Run(os.Args) 33 | } 34 | 35 | func ExampleSubcommand() { 36 | app := cli.NewApp() 37 | app.Name = "say" 38 | app.Commands = []cli.Command{ 39 | { 40 | Name: "hello", 41 | ShortName: "hi", 42 | Usage: "use it to see a description", 43 | Description: "This is how we describe hello the function", 44 | Subcommands: []cli.Command{ 45 | { 46 | Name: "english", 47 | ShortName: "en", 48 | Usage: "sends a greeting in english", 49 | Description: "greets someone in english", 50 | Flags: []cli.Flag{ 51 | cli.StringFlag{ 52 | Name: "name", 53 | Value: "Bob", 54 | Usage: "Name of the person to greet", 55 | }, 56 | }, 57 | Action: func(c *cli.Context) { 58 | println("Hello, ", c.String("name")) 59 | }, 60 | }, { 61 | Name: "spanish", 62 | ShortName: "sp", 63 | Usage: "sends a greeting in spanish", 64 | Flags: []cli.Flag{ 65 | cli.StringFlag{ 66 | Name: "surname", 67 | Value: "Jones", 68 | Usage: "Surname of the person to greet", 69 | }, 70 | }, 71 | Action: func(c *cli.Context) { 72 | println("Hola, ", c.String("surname")) 73 | }, 74 | }, { 75 | Name: "french", 76 | ShortName: "fr", 77 | Usage: "sends a greeting in french", 78 | Flags: []cli.Flag{ 79 | cli.StringFlag{ 80 | Name: "nickname", 81 | Value: "Stevie", 82 | Usage: "Nickname of the person to greet", 83 | }, 84 | }, 85 | Action: func(c *cli.Context) { 86 | println("Bonjour, ", c.String("nickname")) 87 | }, 88 | }, 89 | }, 90 | }, { 91 | Name: "bye", 92 | Usage: "says goodbye", 93 | Action: func(c *cli.Context) { 94 | println("bye") 95 | }, 96 | }, 97 | } 98 | 99 | app.Run(os.Args) 100 | } 101 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | 7 | "github.com/codegangsta/cli" 8 | ) 9 | 10 | func TestCommandDoNotIgnoreFlags(t *testing.T) { 11 | app := cli.NewApp() 12 | set := flag.NewFlagSet("test", 0) 13 | test := []string{"blah", "blah", "-break"} 14 | set.Parse(test) 15 | 16 | c := cli.NewContext(app, set, set) 17 | 18 | command := cli.Command{ 19 | Name: "test-cmd", 20 | ShortName: "tc", 21 | Usage: "this is for testing", 22 | Description: "testing", 23 | Action: func(_ *cli.Context) {}, 24 | } 25 | err := command.Run(c) 26 | 27 | expect(t, err.Error(), "flag provided but not defined: -break") 28 | } 29 | 30 | func TestCommandIgnoreFlags(t *testing.T) { 31 | app := cli.NewApp() 32 | set := flag.NewFlagSet("test", 0) 33 | test := []string{"blah", "blah"} 34 | set.Parse(test) 35 | 36 | c := cli.NewContext(app, set, set) 37 | 38 | command := cli.Command{ 39 | Name: "test-cmd", 40 | ShortName: "tc", 41 | Usage: "this is for testing", 42 | Description: "testing", 43 | Action: func(_ *cli.Context) {}, 44 | SkipFlagParsing: true, 45 | } 46 | err := command.Run(c) 47 | 48 | expect(t, err, nil) 49 | } 50 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | "time" 7 | 8 | "github.com/codegangsta/cli" 9 | ) 10 | 11 | func TestNewContext(t *testing.T) { 12 | set := flag.NewFlagSet("test", 0) 13 | set.Int("myflag", 12, "doc") 14 | globalSet := flag.NewFlagSet("test", 0) 15 | globalSet.Int("myflag", 42, "doc") 16 | command := cli.Command{Name: "mycommand"} 17 | c := cli.NewContext(nil, set, globalSet) 18 | c.Command = command 19 | expect(t, c.Int("myflag"), 12) 20 | expect(t, c.GlobalInt("myflag"), 42) 21 | expect(t, c.Command.Name, "mycommand") 22 | } 23 | 24 | func TestContext_Int(t *testing.T) { 25 | set := flag.NewFlagSet("test", 0) 26 | set.Int("myflag", 12, "doc") 27 | c := cli.NewContext(nil, set, set) 28 | expect(t, c.Int("myflag"), 12) 29 | } 30 | 31 | func TestContext_Duration(t *testing.T) { 32 | set := flag.NewFlagSet("test", 0) 33 | set.Duration("myflag", time.Duration(12*time.Second), "doc") 34 | c := cli.NewContext(nil, set, set) 35 | expect(t, c.Duration("myflag"), time.Duration(12*time.Second)) 36 | } 37 | 38 | func TestContext_String(t *testing.T) { 39 | set := flag.NewFlagSet("test", 0) 40 | set.String("myflag", "hello world", "doc") 41 | c := cli.NewContext(nil, set, set) 42 | expect(t, c.String("myflag"), "hello world") 43 | } 44 | 45 | func TestContext_Bool(t *testing.T) { 46 | set := flag.NewFlagSet("test", 0) 47 | set.Bool("myflag", false, "doc") 48 | c := cli.NewContext(nil, set, set) 49 | expect(t, c.Bool("myflag"), false) 50 | } 51 | 52 | func TestContext_BoolT(t *testing.T) { 53 | set := flag.NewFlagSet("test", 0) 54 | set.Bool("myflag", true, "doc") 55 | c := cli.NewContext(nil, set, set) 56 | expect(t, c.BoolT("myflag"), true) 57 | } 58 | 59 | func TestContext_Args(t *testing.T) { 60 | set := flag.NewFlagSet("test", 0) 61 | set.Bool("myflag", false, "doc") 62 | c := cli.NewContext(nil, set, set) 63 | set.Parse([]string{"--myflag", "bat", "baz"}) 64 | expect(t, len(c.Args()), 2) 65 | expect(t, c.Bool("myflag"), true) 66 | } 67 | 68 | func TestContext_IsSet(t *testing.T) { 69 | set := flag.NewFlagSet("test", 0) 70 | set.Bool("myflag", false, "doc") 71 | set.String("otherflag", "hello world", "doc") 72 | globalSet := flag.NewFlagSet("test", 0) 73 | globalSet.Bool("myflagGlobal", true, "doc") 74 | c := cli.NewContext(nil, set, globalSet) 75 | set.Parse([]string{"--myflag", "bat", "baz"}) 76 | globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) 77 | expect(t, c.IsSet("myflag"), true) 78 | expect(t, c.IsSet("otherflag"), false) 79 | expect(t, c.IsSet("bogusflag"), false) 80 | expect(t, c.IsSet("myflagGlobal"), false) 81 | } 82 | 83 | func TestContext_GlobalIsSet(t *testing.T) { 84 | set := flag.NewFlagSet("test", 0) 85 | set.Bool("myflag", false, "doc") 86 | set.String("otherflag", "hello world", "doc") 87 | globalSet := flag.NewFlagSet("test", 0) 88 | globalSet.Bool("myflagGlobal", true, "doc") 89 | globalSet.Bool("myflagGlobalUnset", true, "doc") 90 | c := cli.NewContext(nil, set, globalSet) 91 | set.Parse([]string{"--myflag", "bat", "baz"}) 92 | globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) 93 | expect(t, c.GlobalIsSet("myflag"), false) 94 | expect(t, c.GlobalIsSet("otherflag"), false) 95 | expect(t, c.GlobalIsSet("bogusflag"), false) 96 | expect(t, c.GlobalIsSet("myflagGlobal"), true) 97 | expect(t, c.GlobalIsSet("myflagGlobalUnset"), false) 98 | expect(t, c.GlobalIsSet("bogusGlobal"), false) 99 | } 100 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | /* Test Helpers */ 9 | func expect(t *testing.T, a interface{}, b interface{}) { 10 | if a != b { 11 | t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 12 | } 13 | } 14 | 15 | func refute(t *testing.T, a interface{}, b interface{}) { 16 | if a == b { 17 | t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/robfig/cron/.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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/robfig/cron/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/robfig/cron/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Rob Figueiredo 2 | All Rights Reserved. 3 | 4 | MIT LICENSE 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/robfig/cron/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron) 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/robfig/cron/constantdelay.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import "time" 4 | 5 | // ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes". 6 | // It does not support jobs more frequent than once a second. 7 | type ConstantDelaySchedule struct { 8 | Delay time.Duration 9 | } 10 | 11 | // Every returns a crontab Schedule that activates once every duration. 12 | // Delays of less than a second are not supported (will round up to 1 second). 13 | // Any fields less than a Second are truncated. 14 | func Every(duration time.Duration) ConstantDelaySchedule { 15 | if duration < time.Second { 16 | duration = time.Second 17 | } 18 | return ConstantDelaySchedule{ 19 | Delay: duration - time.Duration(duration.Nanoseconds())%time.Second, 20 | } 21 | } 22 | 23 | // Next returns the next time this should be run. 24 | // This rounds so that the next activation time will be on the second. 25 | func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time { 26 | return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond) 27 | } 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/robfig/cron/constantdelay_test.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestConstantDelayNext(t *testing.T) { 9 | tests := []struct { 10 | time string 11 | delay time.Duration 12 | expected string 13 | }{ 14 | // Simple cases 15 | {"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"}, 16 | {"Mon Jul 9 14:59 2012", 15 * time.Minute, "Mon Jul 9 15:14 2012"}, 17 | {"Mon Jul 9 14:59:59 2012", 15 * time.Minute, "Mon Jul 9 15:14:59 2012"}, 18 | 19 | // Wrap around hours 20 | {"Mon Jul 9 15:45 2012", 35 * time.Minute, "Mon Jul 9 16:20 2012"}, 21 | 22 | // Wrap around days 23 | {"Mon Jul 9 23:46 2012", 14 * time.Minute, "Tue Jul 10 00:00 2012"}, 24 | {"Mon Jul 9 23:45 2012", 35 * time.Minute, "Tue Jul 10 00:20 2012"}, 25 | {"Mon Jul 9 23:35:51 2012", 44*time.Minute + 24*time.Second, "Tue Jul 10 00:20:15 2012"}, 26 | {"Mon Jul 9 23:35:51 2012", 25*time.Hour + 44*time.Minute + 24*time.Second, "Thu Jul 11 01:20:15 2012"}, 27 | 28 | // Wrap around months 29 | {"Mon Jul 9 23:35 2012", 91*24*time.Hour + 25*time.Minute, "Thu Oct 9 00:00 2012"}, 30 | 31 | // Wrap around minute, hour, day, month, and year 32 | {"Mon Dec 31 23:59:45 2012", 15 * time.Second, "Tue Jan 1 00:00:00 2013"}, 33 | 34 | // Round to nearest second on the delay 35 | {"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"}, 36 | 37 | // Round up to 1 second if the duration is less. 38 | {"Mon Jul 9 14:45:00 2012", 15 * time.Millisecond, "Mon Jul 9 14:45:01 2012"}, 39 | 40 | // Round to nearest second when calculating the next time. 41 | {"Mon Jul 9 14:45:00.005 2012", 15 * time.Minute, "Mon Jul 9 15:00 2012"}, 42 | 43 | // Round to nearest second for both. 44 | {"Mon Jul 9 14:45:00.005 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"}, 45 | } 46 | 47 | for _, c := range tests { 48 | actual := Every(c.delay).Next(getTime(c.time)) 49 | expected := getTime(c.expected) 50 | if actual != expected { 51 | t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.delay, expected, actual) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/robfig/cron/parser_test.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestRange(t *testing.T) { 10 | ranges := []struct { 11 | expr string 12 | min, max uint 13 | expected uint64 14 | }{ 15 | {"5", 0, 7, 1 << 5}, 16 | {"0", 0, 7, 1 << 0}, 17 | {"7", 0, 7, 1 << 7}, 18 | 19 | {"5-5", 0, 7, 1 << 5}, 20 | {"5-6", 0, 7, 1<<5 | 1<<6}, 21 | {"5-7", 0, 7, 1<<5 | 1<<6 | 1<<7}, 22 | 23 | {"5-6/2", 0, 7, 1 << 5}, 24 | {"5-7/2", 0, 7, 1<<5 | 1<<7}, 25 | {"5-7/1", 0, 7, 1<<5 | 1<<6 | 1<<7}, 26 | 27 | {"*", 1, 3, 1<<1 | 1<<2 | 1<<3 | starBit}, 28 | {"*/2", 1, 3, 1<<1 | 1<<3 | starBit}, 29 | } 30 | 31 | for _, c := range ranges { 32 | actual := getRange(c.expr, bounds{c.min, c.max, nil}) 33 | if actual != c.expected { 34 | t.Errorf("%s => (expected) %d != %d (actual)", c.expr, c.expected, actual) 35 | } 36 | } 37 | } 38 | 39 | func TestField(t *testing.T) { 40 | fields := []struct { 41 | expr string 42 | min, max uint 43 | expected uint64 44 | }{ 45 | {"5", 1, 7, 1 << 5}, 46 | {"5,6", 1, 7, 1<<5 | 1<<6}, 47 | {"5,6,7", 1, 7, 1<<5 | 1<<6 | 1<<7}, 48 | {"1,5-7/2,3", 1, 7, 1<<1 | 1<<5 | 1<<7 | 1<<3}, 49 | } 50 | 51 | for _, c := range fields { 52 | actual := getField(c.expr, bounds{c.min, c.max, nil}) 53 | if actual != c.expected { 54 | t.Errorf("%s => (expected) %d != %d (actual)", c.expr, c.expected, actual) 55 | } 56 | } 57 | } 58 | 59 | func TestBits(t *testing.T) { 60 | allBits := []struct { 61 | r bounds 62 | expected uint64 63 | }{ 64 | {minutes, 0xfffffffffffffff}, // 0-59: 60 ones 65 | {hours, 0xffffff}, // 0-23: 24 ones 66 | {dom, 0xfffffffe}, // 1-31: 31 ones, 1 zero 67 | {months, 0x1ffe}, // 1-12: 12 ones, 1 zero 68 | {dow, 0x7f}, // 0-6: 7 ones 69 | } 70 | 71 | for _, c := range allBits { 72 | actual := all(c.r) // all() adds the starBit, so compensate for that.. 73 | if c.expected|starBit != actual { 74 | t.Errorf("%d-%d/%d => (expected) %b != %b (actual)", 75 | c.r.min, c.r.max, 1, c.expected|starBit, actual) 76 | } 77 | } 78 | 79 | bits := []struct { 80 | min, max, step uint 81 | expected uint64 82 | }{ 83 | 84 | {0, 0, 1, 0x1}, 85 | {1, 1, 1, 0x2}, 86 | {1, 5, 2, 0x2a}, // 101010 87 | {1, 4, 2, 0xa}, // 1010 88 | } 89 | 90 | for _, c := range bits { 91 | actual := getBits(c.min, c.max, c.step) 92 | if c.expected != actual { 93 | t.Errorf("%d-%d/%d => (expected) %b != %b (actual)", 94 | c.min, c.max, c.step, c.expected, actual) 95 | } 96 | } 97 | } 98 | 99 | func TestSpecSchedule(t *testing.T) { 100 | entries := []struct { 101 | expr string 102 | expected Schedule 103 | }{ 104 | {"* 5 * * * *", &SpecSchedule{all(seconds), 1 << 5, all(hours), all(dom), all(months), all(dow)}}, 105 | {"@every 5m", ConstantDelaySchedule{time.Duration(5) * time.Minute}}, 106 | } 107 | 108 | for _, c := range entries { 109 | actual, err := Parse(c.expr) 110 | if err != nil { 111 | t.Error(err) 112 | } 113 | if !reflect.DeepEqual(actual, c.expected) { 114 | t.Errorf("%s => (expected) %b != %b (actual)", c.expr, c.expected, actual) 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/graceful/.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 | *.test 24 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/graceful/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Tyler Bunnell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/stretchr/graceful/tests/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "github.com/codegangsta/negroni" 8 | "github.com/tylerb/graceful" 9 | ) 10 | 11 | func main() { 12 | 13 | var wg sync.WaitGroup 14 | 15 | wg.Add(3) 16 | go func() { 17 | n := negroni.New() 18 | fmt.Println("Launching server on :3000") 19 | graceful.Run(":3000", 0, n) 20 | fmt.Println("Terminated server on :3000") 21 | wg.Done() 22 | }() 23 | go func() { 24 | n := negroni.New() 25 | fmt.Println("Launching server on :3001") 26 | graceful.Run(":3001", 0, n) 27 | fmt.Println("Terminated server on :3001") 28 | wg.Done() 29 | }() 30 | go func() { 31 | n := negroni.New() 32 | fmt.Println("Launching server on :3002") 33 | graceful.Run(":3002", 0, n) 34 | fmt.Println("Terminated server on :3002") 35 | wg.Done() 36 | }() 37 | fmt.Println("Press ctrl+c. All servers should terminate.") 38 | wg.Wait() 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/tj/go-debug/History.md: -------------------------------------------------------------------------------- 1 | 2 | v2.0.0 / 2014-10-22 3 | ================== 4 | 5 | * remove live toggling feature. Closes #10 6 | 7 | 1.1.1 / 2014-07-07 8 | ================== 9 | 10 | * fix: dispose socket. Closes #1 11 | 12 | 1.1.0 / 2014-06-29 13 | ================== 14 | 15 | * add unix domain socket live debugging support 16 | * add support for enabling/disabling at runtime 17 | 18 | 0.1.0 / 2014-05-24 19 | ================== 20 | 21 | * add global and debug relative deltas 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/tj/go-debug/Makefile: -------------------------------------------------------------------------------- 1 | 2 | test: 3 | @go test 4 | 5 | bench: 6 | @go test -bench=. 7 | 8 | .PHONY: bench test -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/tj/go-debug/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # go-debug 3 | 4 | Conditional debug logging for Go libraries. 5 | 6 | View the [docs](http://godoc.org/github.com/tj/go-debug). 7 | 8 | ## Installation 9 | 10 | ``` 11 | $ go get github.com/tj/go-debug 12 | ``` 13 | 14 | ## Example 15 | 16 | ```go 17 | package main 18 | 19 | import . "github.com/tj/go-debug" 20 | import "time" 21 | 22 | var debug = Debug("single") 23 | 24 | func main() { 25 | for { 26 | debug("sending mail") 27 | debug("send email to %s", "tobi@segment.io") 28 | debug("send email to %s", "loki@segment.io") 29 | debug("send email to %s", "jane@segment.io") 30 | time.Sleep(500 * time.Millisecond) 31 | } 32 | } 33 | ``` 34 | 35 | If you run the program with the `DEBUG=*` environment variable you will see: 36 | 37 | ``` 38 | 15:58:15.115 34us 33us single - sending mail 39 | 15:58:15.116 3us 3us single - send email to tobi@segment.io 40 | 15:58:15.116 1us 1us single - send email to loki@segment.io 41 | 15:58:15.116 1us 1us single - send email to jane@segment.io 42 | 15:58:15.620 504ms 504ms single - sending mail 43 | 15:58:15.620 6us 6us single - send email to tobi@segment.io 44 | 15:58:15.620 4us 4us single - send email to loki@segment.io 45 | 15:58:15.620 4us 4us single - send email to jane@segment.io 46 | 15:58:16.123 503ms 503ms single - sending mail 47 | 15:58:16.123 7us 7us single - send email to tobi@segment.io 48 | 15:58:16.123 4us 4us single - send email to loki@segment.io 49 | 15:58:16.123 4us 4us single - send email to jane@segment.io 50 | 15:58:16.625 501ms 501ms single - sending mail 51 | 15:58:16.625 4us 4us single - send email to tobi@segment.io 52 | 15:58:16.625 4us 4us single - send email to loki@segment.io 53 | 15:58:16.625 5us 5us single - send email to jane@segment.io 54 | ``` 55 | 56 | A timestamp and two deltas are displayed. The timestamp consists of hour, minute, second and microseconds. The left-most delta is relative to the previous debug call of any name, followed by a delta specific to that debug function. These may be useful to identify timing issues and potential bottlenecks. 57 | 58 | ## The DEBUG environment variable 59 | 60 | Executables often support `--verbose` flags for conditional logging, however 61 | libraries typically either require altering your code to enable logging, 62 | or simply omit logging all together. go-debug allows conditional logging 63 | to be enabled via the __DEBUG__ environment variable, where one or more 64 | patterns may be specified. 65 | 66 | For example suppose your application has several models and you want 67 | to output logs for users only, you might use `DEBUG=models:user`. In contrast 68 | if you wanted to see what all database activity was you might use `DEBUG=models:*`, 69 | or if you're love being swamped with logs: `DEBUG=*`. You may also specify a list of names delimited by a comma, for example `DEBUG=mongo,redis:*`. 70 | 71 | The name given _should_ be the package name, however you can use whatever you like. 72 | 73 | # License 74 | 75 | MIT -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/tj/go-debug/debug.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "math/rand" 7 | "os" 8 | "regexp" 9 | "strconv" 10 | "strings" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | var ( 16 | writer io.Writer = os.Stderr 17 | reg *regexp.Regexp 18 | m sync.Mutex 19 | enabled = false 20 | ) 21 | 22 | // Debugger function. 23 | type DebugFunction func(string, ...interface{}) 24 | 25 | // Terminal colors used at random. 26 | var colors []string = []string{ 27 | "31", 28 | "32", 29 | "33", 30 | "34", 31 | "35", 32 | "36", 33 | } 34 | 35 | // Initialize with DEBUG environment variable. 36 | func init() { 37 | env := os.Getenv("DEBUG") 38 | 39 | if "" != env { 40 | Enable(env) 41 | } 42 | } 43 | 44 | // SetWriter replaces the default of os.Stderr with `w`. 45 | func SetWriter(w io.Writer) { 46 | m.Lock() 47 | defer m.Unlock() 48 | writer = w 49 | } 50 | 51 | // Disable all pattern matching. This function is thread-safe. 52 | func Disable() { 53 | m.Lock() 54 | defer m.Unlock() 55 | enabled = false 56 | } 57 | 58 | // Enable the given debug `pattern`. Patterns take a glob-like form, 59 | // for example if you wanted to enable everything, just use "*", or 60 | // if you had a library named mongodb you could use "mongodb:connection", 61 | // or "mongodb:*". Multiple matches can be made with a comma, for 62 | // example "mongo*,redis*". 63 | // 64 | // This function is thread-safe. 65 | func Enable(pattern string) { 66 | m.Lock() 67 | defer m.Unlock() 68 | pattern = regexp.QuoteMeta(pattern) 69 | pattern = strings.Replace(pattern, "\\*", ".*?", -1) 70 | pattern = strings.Replace(pattern, ",", "|", -1) 71 | pattern = "^(" + pattern + ")$" 72 | reg = regexp.MustCompile(pattern) 73 | enabled = true 74 | } 75 | 76 | // Debug creates a debug function for `name` which you call 77 | // with printf-style arguments in your application or library. 78 | func Debug(name string) DebugFunction { 79 | prevGlobal := time.Now() 80 | color := colors[rand.Intn(len(colors))] 81 | prev := time.Now() 82 | 83 | return func(format string, args ...interface{}) { 84 | if !enabled { 85 | return 86 | } 87 | 88 | if !reg.MatchString(name) { 89 | return 90 | } 91 | 92 | d := deltas(prevGlobal, prev, color) 93 | fmt.Fprintf(writer, d+" \033["+color+"m"+name+"\033[0m - "+format+"\n", args...) 94 | prevGlobal = time.Now() 95 | prev = time.Now() 96 | } 97 | } 98 | 99 | // Return formatting for deltas. 100 | func deltas(prevGlobal, prev time.Time, color string) string { 101 | now := time.Now() 102 | global := now.Sub(prevGlobal).Nanoseconds() 103 | delta := now.Sub(prev).Nanoseconds() 104 | ts := now.UTC().Format("15:04:05.000") 105 | deltas := fmt.Sprintf("%s %-6s \033["+color+"m%-6s", ts, humanizeNano(global), humanizeNano(delta)) 106 | return deltas 107 | } 108 | 109 | // Humanize nanoseconds to a string. 110 | func humanizeNano(n int64) string { 111 | var suffix string 112 | 113 | switch { 114 | case n > 1e9: 115 | n /= 1e9 116 | suffix = "s" 117 | case n > 1e6: 118 | n /= 1e6 119 | suffix = "ms" 120 | case n > 1e3: 121 | n /= 1e3 122 | suffix = "us" 123 | default: 124 | suffix = "ns" 125 | } 126 | 127 | return strconv.Itoa(int(n)) + suffix 128 | } 129 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/tj/go-debug/debug_test.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import "testing" 4 | import "strings" 5 | import "bytes" 6 | import "time" 7 | 8 | func assertContains(t *testing.T, str, substr string) { 9 | if !strings.Contains(str, substr) { 10 | t.Fatalf("expected %q to contain %q", str, substr) 11 | } 12 | } 13 | 14 | func assertNotContains(t *testing.T, str, substr string) { 15 | if strings.Contains(str, substr) { 16 | t.Fatalf("expected %q to not contain %q", str, substr) 17 | } 18 | } 19 | 20 | func TestDefault(t *testing.T) { 21 | var b []byte 22 | buf := bytes.NewBuffer(b) 23 | SetWriter(buf) 24 | 25 | debug := Debug("foo") 26 | debug("something") 27 | debug("here") 28 | debug("whoop") 29 | 30 | if buf.Len() != 0 { 31 | t.Fatalf("buffer should be empty") 32 | } 33 | } 34 | 35 | func TestEnable(t *testing.T) { 36 | var b []byte 37 | buf := bytes.NewBuffer(b) 38 | SetWriter(buf) 39 | 40 | Enable("foo") 41 | 42 | debug := Debug("foo") 43 | debug("something") 44 | debug("here") 45 | debug("whoop") 46 | 47 | if buf.Len() == 0 { 48 | t.Fatalf("buffer should have output") 49 | } 50 | 51 | str := string(buf.Bytes()) 52 | assertContains(t, str, "something") 53 | assertContains(t, str, "here") 54 | assertContains(t, str, "whoop") 55 | } 56 | 57 | func TestMultipleOneEnabled(t *testing.T) { 58 | var b []byte 59 | buf := bytes.NewBuffer(b) 60 | SetWriter(buf) 61 | 62 | Enable("foo") 63 | 64 | foo := Debug("foo") 65 | foo("foo") 66 | 67 | bar := Debug("bar") 68 | bar("bar") 69 | 70 | if buf.Len() == 0 { 71 | t.Fatalf("buffer should have output") 72 | } 73 | 74 | str := string(buf.Bytes()) 75 | assertContains(t, str, "foo") 76 | assertNotContains(t, str, "bar") 77 | } 78 | 79 | func TestMultipleEnabled(t *testing.T) { 80 | var b []byte 81 | buf := bytes.NewBuffer(b) 82 | SetWriter(buf) 83 | 84 | Enable("foo,bar") 85 | 86 | foo := Debug("foo") 87 | foo("foo") 88 | 89 | bar := Debug("bar") 90 | bar("bar") 91 | 92 | if buf.Len() == 0 { 93 | t.Fatalf("buffer should have output") 94 | } 95 | 96 | str := string(buf.Bytes()) 97 | assertContains(t, str, "foo") 98 | assertContains(t, str, "bar") 99 | } 100 | 101 | func TestEnableDisable(t *testing.T) { 102 | var b []byte 103 | buf := bytes.NewBuffer(b) 104 | SetWriter(buf) 105 | 106 | Enable("foo,bar") 107 | Disable() 108 | 109 | foo := Debug("foo") 110 | foo("foo") 111 | 112 | bar := Debug("bar") 113 | bar("bar") 114 | 115 | if buf.Len() != 0 { 116 | t.Fatalf("buffer should not have output") 117 | } 118 | } 119 | 120 | func ExampleEnable() { 121 | Enable("mongo:connection") 122 | Enable("mongo:*") 123 | Enable("foo,bar,baz") 124 | Enable("*") 125 | } 126 | 127 | func ExampleDebug() { 128 | var debug = Debug("single") 129 | 130 | for { 131 | debug("sending mail") 132 | debug("send email to %s", "tobi@segment.io") 133 | debug("send email to %s", "loki@segment.io") 134 | debug("send email to %s", "jane@segment.io") 135 | time.Sleep(500 * time.Millisecond) 136 | } 137 | } 138 | 139 | func BenchmarkDisabled(b *testing.B) { 140 | debug := Debug("something") 141 | for i := 0; i < b.N; i++ { 142 | debug("stuff") 143 | } 144 | } 145 | 146 | func BenchmarkNonMatch(b *testing.B) { 147 | debug := Debug("something") 148 | Enable("nonmatch") 149 | for i := 0; i < b.N; i++ { 150 | debug("stuff") 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/tj/go-debug/example/multiple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import . "github.com/visionmedia/go-debug" 4 | import "time" 5 | 6 | var a = Debug("multiple:a") 7 | var b = Debug("multiple:b") 8 | var c = Debug("multiple:c") 9 | 10 | func work(debug DebugFunction, delay time.Duration) { 11 | for { 12 | debug("doing stuff") 13 | time.Sleep(delay) 14 | } 15 | } 16 | 17 | func main() { 18 | q := make(chan bool) 19 | 20 | go work(a, 1000*time.Millisecond) 21 | go work(b, 250*time.Millisecond) 22 | go work(c, 100*time.Millisecond) 23 | 24 | <-q 25 | } 26 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/tj/go-debug/example/single.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import . "github.com/visionmedia/go-debug" 4 | import "time" 5 | 6 | var debug = Debug("single") 7 | 8 | func main() { 9 | for { 10 | debug("sending mail") 11 | debug("send email to %s", "tobi@segment.io") 12 | debug("send email to %s", "loki@segment.io") 13 | debug("send email to %s", "jane@segment.io") 14 | time.Sleep(500 * time.Millisecond) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/net/netutil/listen.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 netutil provides network utility functions, complementing the more 6 | // common ones in the net package. 7 | package netutil 8 | 9 | import ( 10 | "net" 11 | "sync" 12 | ) 13 | 14 | // LimitListener returns a Listener that accepts at most n simultaneous 15 | // connections from the provided Listener. 16 | func LimitListener(l net.Listener, n int) net.Listener { 17 | return &limitListener{l, make(chan struct{}, n)} 18 | } 19 | 20 | type limitListener struct { 21 | net.Listener 22 | sem chan struct{} 23 | } 24 | 25 | func (l *limitListener) acquire() { l.sem <- struct{}{} } 26 | func (l *limitListener) release() { <-l.sem } 27 | 28 | func (l *limitListener) Accept() (net.Conn, error) { 29 | l.acquire() 30 | c, err := l.Listener.Accept() 31 | if err != nil { 32 | l.release() 33 | return nil, err 34 | } 35 | return &limitListenerConn{Conn: c, release: l.release}, nil 36 | } 37 | 38 | type limitListenerConn struct { 39 | net.Conn 40 | releaseOnce sync.Once 41 | release func() 42 | } 43 | 44 | func (l *limitListenerConn) Close() error { 45 | err := l.Conn.Close() 46 | l.releaseOnce.Do(l.release) 47 | return err 48 | } 49 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/net/netutil/listen_test.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 | // +build go1.3 6 | 7 | // (We only run this test on Go 1.3 because the HTTP client timeout behavior 8 | // was bad in previous releases, causing occasional deadlocks.) 9 | 10 | package netutil 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | "io" 16 | "io/ioutil" 17 | "net" 18 | "net/http" 19 | "sync" 20 | "sync/atomic" 21 | "testing" 22 | "time" 23 | ) 24 | 25 | func TestLimitListener(t *testing.T) { 26 | const ( 27 | max = 5 28 | num = 200 29 | ) 30 | 31 | l, err := net.Listen("tcp", "127.0.0.1:0") 32 | if err != nil { 33 | t.Fatalf("Listen: %v", err) 34 | } 35 | defer l.Close() 36 | l = LimitListener(l, max) 37 | 38 | var open int32 39 | go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 40 | if n := atomic.AddInt32(&open, 1); n > max { 41 | t.Errorf("%d open connections, want <= %d", n, max) 42 | } 43 | defer atomic.AddInt32(&open, -1) 44 | time.Sleep(10 * time.Millisecond) 45 | fmt.Fprint(w, "some body") 46 | })) 47 | 48 | var wg sync.WaitGroup 49 | var failed int32 50 | for i := 0; i < num; i++ { 51 | wg.Add(1) 52 | go func() { 53 | defer wg.Done() 54 | c := http.Client{Timeout: 3 * time.Second} 55 | r, err := c.Get("http://" + l.Addr().String()) 56 | if err != nil { 57 | t.Logf("Get: %v", err) 58 | atomic.AddInt32(&failed, 1) 59 | return 60 | } 61 | defer r.Body.Close() 62 | io.Copy(ioutil.Discard, r.Body) 63 | }() 64 | } 65 | wg.Wait() 66 | 67 | // We expect some Gets to fail as the kernel's accept queue is filled, 68 | // but most should succeed. 69 | if failed >= num/2 { 70 | t.Errorf("too many Gets failed: %v", failed) 71 | } 72 | } 73 | 74 | type errorListener struct { 75 | net.Listener 76 | } 77 | 78 | func (errorListener) Accept() (net.Conn, error) { 79 | return nil, errFake 80 | } 81 | 82 | var errFake = errors.New("fake error from errorListener") 83 | 84 | // This used to hang. 85 | func TestLimitListenerError(t *testing.T) { 86 | donec := make(chan bool, 1) 87 | go func() { 88 | const n = 2 89 | ll := LimitListener(errorListener{}, n) 90 | for i := 0; i < n+1; i++ { 91 | _, err := ll.Accept() 92 | if err != errFake { 93 | t.Fatalf("Accept error = %v; want errFake", err) 94 | } 95 | } 96 | donec <- true 97 | }() 98 | select { 99 | case <-donec: 100 | case <-time.After(5 * time.Second): 101 | t.Fatal("timeout. deadlock?") 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/Makefile: -------------------------------------------------------------------------------- 1 | startdb: 2 | @testdb/setup.sh start 3 | 4 | stopdb: 5 | @testdb/setup.sh stop 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/bulk.go: -------------------------------------------------------------------------------- 1 | package mgo 2 | 3 | // Bulk represents an operation that can be prepared with several 4 | // orthogonal changes before being delivered to the server. 5 | // 6 | // WARNING: This API is still experimental. 7 | // 8 | // Relevant documentation: 9 | // 10 | // http://blog.mongodb.org/post/84922794768/mongodbs-new-bulk-api 11 | // 12 | type Bulk struct { 13 | c *Collection 14 | ordered bool 15 | inserts []interface{} 16 | } 17 | 18 | // BulkError holds an error returned from running a Bulk operation. 19 | // 20 | // TODO: This is private for the moment, until we understand exactly how 21 | // to report these multi-errors in a useful and convenient way. 22 | type bulkError struct { 23 | err error 24 | } 25 | 26 | // BulkResult holds the results for a bulk operation. 27 | type BulkResult struct { 28 | // Be conservative while we understand exactly how to report these 29 | // results in a useful and convenient way, and also how to emulate 30 | // them with prior servers. 31 | private bool 32 | } 33 | 34 | func (e *bulkError) Error() string { 35 | return e.err.Error() 36 | } 37 | 38 | // Bulk returns a value to prepare the execution of a bulk operation. 39 | // 40 | // WARNING: This API is still experimental. 41 | // 42 | func (c *Collection) Bulk() *Bulk { 43 | return &Bulk{c: c, ordered: true} 44 | } 45 | 46 | // Unordered puts the bulk operation in unordered mode. 47 | // 48 | // In unordered mode the indvidual operations may be sent 49 | // out of order, which means latter operations may proceed 50 | // even if prior ones have failed. 51 | func (b *Bulk) Unordered() { 52 | b.ordered = false 53 | } 54 | 55 | // Insert queues up the provided documents for insertion. 56 | func (b *Bulk) Insert(docs ...interface{}) { 57 | b.inserts = append(b.inserts, docs...) 58 | } 59 | 60 | // Run runs all the operations queued up. 61 | func (b *Bulk) Run() (*BulkResult, error) { 62 | op := &insertOp{b.c.FullName, b.inserts, 0} 63 | if !b.ordered { 64 | op.flags = 1 // ContinueOnError 65 | } 66 | _, err := b.c.writeQuery(op) 67 | if err != nil { 68 | return nil, &bulkError{err} 69 | } 70 | return &BulkResult{}, nil 71 | } 72 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/bulk_test.go: -------------------------------------------------------------------------------- 1 | // mgo - MongoDB driver for Go 2 | // 3 | // Copyright (c) 2010-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 | package mgo_test 28 | 29 | import ( 30 | . "gopkg.in/check.v1" 31 | "gopkg.in/mgo.v2" 32 | ) 33 | 34 | func (s *S) TestBulkInsert(c *C) { 35 | session, err := mgo.Dial("localhost:40001") 36 | c.Assert(err, IsNil) 37 | defer session.Close() 38 | 39 | coll := session.DB("mydb").C("mycoll") 40 | bulk := coll.Bulk() 41 | bulk.Insert(M{"n": 1}) 42 | bulk.Insert(M{"n": 2}, M{"n": 3}) 43 | r, err := bulk.Run() 44 | c.Assert(err, IsNil) 45 | c.Assert(r, FitsTypeOf, &mgo.BulkResult{}) 46 | 47 | type doc struct{ N int } 48 | var res []doc 49 | err = coll.Find(nil).Sort("n").All(&res) 50 | c.Assert(err, IsNil) 51 | c.Assert(res, DeepEquals, []doc{{1}, {2}, {3}}) 52 | } 53 | 54 | func (s *S) TestBulkInsertError(c *C) { 55 | session, err := mgo.Dial("localhost:40001") 56 | c.Assert(err, IsNil) 57 | defer session.Close() 58 | 59 | coll := session.DB("mydb").C("mycoll") 60 | bulk := coll.Bulk() 61 | bulk.Insert(M{"_id": 1}, M{"_id": 2}, M{"_id": 2}, M{"n": 3}) 62 | _, err = bulk.Run() 63 | c.Assert(err, ErrorMatches, ".*duplicate key.*") 64 | 65 | type doc struct { 66 | N int `_id` 67 | } 68 | var res []doc 69 | err = coll.Find(nil).Sort("_id").All(&res) 70 | c.Assert(err, IsNil) 71 | c.Assert(res, DeepEquals, []doc{{1}, {2}}) 72 | } 73 | 74 | func (s *S) TestBulkInsertErrorUnordered(c *C) { 75 | session, err := mgo.Dial("localhost:40001") 76 | c.Assert(err, IsNil) 77 | defer session.Close() 78 | 79 | coll := session.DB("mydb").C("mycoll") 80 | bulk := coll.Bulk() 81 | bulk.Unordered() 82 | bulk.Insert(M{"_id": 1}, M{"_id": 2}, M{"_id": 2}, M{"_id": 3}) 83 | _, err = bulk.Run() 84 | c.Assert(err, ErrorMatches, ".*duplicate key.*") 85 | 86 | type doc struct { 87 | N int `_id` 88 | } 89 | var res []doc 90 | err = coll.Find(nil).Sort("_id").All(&res) 91 | c.Assert(err, IsNil) 92 | c.Assert(res, DeepEquals, []doc{{1}, {2}, {3}}) 93 | } 94 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 cache, 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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/export_test.go: -------------------------------------------------------------------------------- 1 | package mgo 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func HackPingDelay(newDelay time.Duration) (restore func()) { 8 | globalMutex.Lock() 9 | defer globalMutex.Unlock() 10 | 11 | oldDelay := pingDelay 12 | restore = func() { 13 | globalMutex.Lock() 14 | pingDelay = oldDelay 15 | globalMutex.Unlock() 16 | } 17 | pingDelay = newDelay 18 | return 19 | } 20 | 21 | func HackSyncSocketTimeout(newTimeout time.Duration) (restore func()) { 22 | globalMutex.Lock() 23 | defer globalMutex.Unlock() 24 | 25 | oldTimeout := syncSocketTimeout 26 | restore = func() { 27 | globalMutex.Lock() 28 | syncSocketTimeout = oldTimeout 29 | globalMutex.Unlock() 30 | } 31 | syncSocketTimeout = newTimeout 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/internal/scram/scram_test.go: -------------------------------------------------------------------------------- 1 | package scram_test 2 | 3 | import ( 4 | "crypto/sha1" 5 | "testing" 6 | 7 | . "gopkg.in/check.v1" 8 | "gopkg.in/mgo.v2/internal/scram" 9 | "strings" 10 | ) 11 | 12 | var _ = Suite(&S{}) 13 | 14 | func Test(t *testing.T) { TestingT(t) } 15 | 16 | type S struct{} 17 | 18 | var tests = [][]string{{ 19 | "U: user pencil", 20 | "N: fyko+d2lbbFgONRv9qkxdawL", 21 | "C: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL", 22 | "S: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096", 23 | "C: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=", 24 | "S: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=", 25 | }, { 26 | "U: root fe8c89e308ec08763df36333cbf5d3a2", 27 | "N: OTcxNDk5NjM2MzE5", 28 | "C: n,,n=root,r=OTcxNDk5NjM2MzE5", 29 | "S: r=OTcxNDk5NjM2MzE581Ra3provgG0iDsMkDiIAlrh4532dDLp,s=XRDkVrFC9JuL7/F4tG0acQ==,i=10000", 30 | "C: c=biws,r=OTcxNDk5NjM2MzE581Ra3provgG0iDsMkDiIAlrh4532dDLp,p=6y1jp9R7ETyouTXS9fW9k5UHdBc=", 31 | "S: v=LBnd9dUJRxdqZiEq91NKP3z/bHA=", 32 | }} 33 | 34 | func (s *S) TestExamples(c *C) { 35 | for _, steps := range tests { 36 | if len(steps) < 2 || len(steps[0]) < 3 || !strings.HasPrefix(steps[0], "U: ") { 37 | c.Fatalf("Invalid test: %#v", steps) 38 | } 39 | auth := strings.Fields(steps[0][3:]) 40 | client := scram.NewClient(sha1.New, auth[0], auth[1]) 41 | first, done := true, false 42 | c.Logf("-----") 43 | c.Logf("%s", steps[0]) 44 | for _, step := range steps[1:] { 45 | c.Logf("%s", step) 46 | switch step[:3] { 47 | case "N: ": 48 | client.SetNonce([]byte(step[3:])) 49 | case "C: ": 50 | if first { 51 | first = false 52 | done = client.Step(nil) 53 | } 54 | c.Assert(done, Equals, false) 55 | c.Assert(client.Err(), IsNil) 56 | c.Assert(string(client.Out()), Equals, step[3:]) 57 | case "S: ": 58 | first = false 59 | done = client.Step([]byte(step[3:])) 60 | default: 61 | panic("invalid test line: " + step) 62 | } 63 | } 64 | c.Assert(done, Equals, true) 65 | c.Assert(client.Err(), IsNil) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/queue_test.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 | . "gopkg.in/check.v1" 31 | ) 32 | 33 | type QS struct{} 34 | 35 | var _ = Suite(&QS{}) 36 | 37 | func (s *QS) TestSequentialGrowth(c *C) { 38 | q := queue{} 39 | n := 2048 40 | for i := 0; i != n; i++ { 41 | q.Push(i) 42 | } 43 | for i := 0; i != n; i++ { 44 | c.Assert(q.Pop(), Equals, i) 45 | } 46 | } 47 | 48 | var queueTestLists = [][]int{ 49 | // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 50 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 51 | 52 | // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7} 53 | {0, 1, 2, 3, 4, 5, 6, 7, -1, -1, 8, 9, 10, 11}, 54 | 55 | // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7} 56 | {0, 1, 2, 3, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11}, 57 | 58 | // {0, 1, 2, 3, 4, 5, 6, 7, 8} 59 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 60 | -1, -1, -1, -1, -1, -1, -1, -1, -1, 61 | 0, 1, 2, 3, 4, 5, 6, 7, 8}, 62 | } 63 | 64 | func (s *QS) TestQueueTestLists(c *C) { 65 | test := []int{} 66 | testi := 0 67 | reset := func() { 68 | test = test[0:0] 69 | testi = 0 70 | } 71 | push := func(i int) { 72 | test = append(test, i) 73 | } 74 | pop := func() (i int) { 75 | if testi == len(test) { 76 | return -1 77 | } 78 | i = test[testi] 79 | testi++ 80 | return 81 | } 82 | 83 | for _, list := range queueTestLists { 84 | reset() 85 | q := queue{} 86 | for _, n := range list { 87 | if n == -1 { 88 | c.Assert(q.Pop(), Equals, pop(), Commentf("With list %#v", list)) 89 | } else { 90 | q.Push(n) 91 | push(n) 92 | } 93 | } 94 | 95 | for n := pop(); n != -1; n = pop() { 96 | c.Assert(q.Pop(), Equals, n, Commentf("With list %#v", list)) 97 | } 98 | 99 | c.Assert(q.Pop(), Equals, nil, Commentf("With list %#v", list)) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/raceoff.go: -------------------------------------------------------------------------------- 1 | // +build !race 2 | 3 | package mgo 4 | 5 | const raceDetector = false 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/raceon.go: -------------------------------------------------------------------------------- 1 | // +build race 2 | 3 | package mgo 4 | 5 | const raceDetector = true 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/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/_workspace/src/gopkg.in/mgo.v2/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/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, char* target); 7 | int sspi_send_client_authz_id(CtxtHandle* context, PVOID* buffer, ULONG* buffer_length, char* user_plus_realm); 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/saslimpl.go: -------------------------------------------------------------------------------- 1 | //+build sasl 2 | 3 | package mgo 4 | 5 | import ( 6 | "gopkg.in/mgo.v2/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/syscall_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package mgo_test 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | func stop(pid int) (err error) { 10 | return syscall.Kill(pid, syscall.SIGSTOP) 11 | } 12 | 13 | func cont(pid int) (err error) { 14 | return syscall.Kill(pid, syscall.SIGCONT) 15 | } 16 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/syscall_windows_test.go: -------------------------------------------------------------------------------- 1 | package mgo_test 2 | 3 | func stop(pid int) (err error) { 4 | panicOnWindows() // Always does. 5 | return nil 6 | } 7 | 8 | func cont(pid int) (err error) { 9 | panicOnWindows() // Always does. 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/testdb/client.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAwE2sl8YeTTSetwo9kykJ5mCZ/FtfPtn/0X4nOlTM2Qc/uWzA 3 | sjSYoSV4UkuOiWjKQQH2EDeXaltshOo7F0oCY5ozVeQe+phe987iKTvLtf7NoXJD 4 | KqNqR4Kb4ylbCrEky7+Xvw6yrrqw8qgWy+9VsrilR3q8LsETE9SBMtfp3BUaaNQp 5 | peNm+iAhx3uZSv3mdzSLFSA/o61kAyG0scLExYDjo/7xyMNQoloLvNmx4Io160+y 6 | lOz077/qqU620tmuDLRz1QdxK/bptmXTnsBCRxl+U8nzbwVZgWFENhXplbcN+SjN 7 | LhdnvTiU2qFhgZmc7ZtCKdPIpx3W6pH9bx7kTwIDAQABAoIBAQCOQygyo8NY9FuS 8 | J8ZDrvF+9+oS8fm1QorpDT2x/ngI+j7fSyAG9bgQRusLXpAVAWvWyb+iYa3nZbkT 9 | X0DVys+XpcTifr+YPc7L3sYbIPxkKBsxm5kq2vfN7Uart7V9ZG1HOfblxdbUQpKT 10 | AVzUA7vPWqATEC5VHEqjuerWlTqRr9YLZE/nkE7ICLISqdl4WDYfUYJwoXWfYkXQ 11 | Lfl5Qh2leyri9S3urvDrhnURTQ1lM182IbTRA+9rUiFzsRW+9U4HPY7Ao2Itp8dr 12 | GRP4rcq4TP+NcF0Ky64cNfKXCWmwqTBRFYAlTD6gwjN/s2BzvWD/2nlnc0DYAXrB 13 | TgFCPk7xAoGBAOwuHICwwTxtzrdWjuRGU3RxL4eLEXedtL8yon/yPci3e+8eploX 14 | 1Fp0rEK2gIGDp/X8DiOtrKXih8XPusCwE/I3EvjHdI0RylLZXTPOp1Ei21dXRsiV 15 | YxcF+d5s11q5tJtF+5ISUeIz2iSc9Z2LBnb8JDK1jcCRa5Q212q3ZWW5AoGBANBw 16 | 9CoMbxINLG1q0NvOOSwMKDk2OB+9JbQ5lwF4ijZl2I6qRoOCzQ3lBs0Qv/AeBjNR 17 | SerDs2+eWnIBUbgSdiqcOKnXAI/Qbl1IkVFYV/2g9m6dgu1fNWNBv8NIYDHCLfDx 18 | W3fpO5JMf+iE5XC4XqCfSBIME2yxPSGQjal6tB5HAoGAddYDzolhv/6hVoPPQ0F7 19 | PeuC5UOTcXSzy3k97kw0W0KAiStnoCengYIYuChKMVQ4ptgdTdvG+fTt/NnJuX2g 20 | Vgb4ZjtNgVzQ70kX4VNH04lqmkcnP8iY6dHHexwezls9KwNdouGVDSEFw6K0QOgu 21 | T4s5nDtNADkNzaMXE11xL7ECgYBoML3rstFmTY1ymB0Uck3jtaP5jR+axdpt7weL 22 | Zax4qooILhcXL6++DUhMAt5ecTOaPTzci7xKw/Xj3MLzZs8IV5R/WQhf2sj/+gEh 23 | jy5UijwEaNmEO74dAkWPoMLsvGpocMzO8JeldnXNTXi+0noCgfvtgXnIMAQlnfMh 24 | z0LviwKBgQCg5KR9JC4iuKses7Kfv2YelcO8vOZkRzBu3NdRWMsiJQC+qfetgd57 25 | RjRjlRWd1WCHJ5Kmx3hkUaZZOrX5knqfsRW3Nl0I74xgWl7Bli2eSJ9VWl59bcd6 26 | DqphhY7/gcW+QZlhXpnqbf0W8jB2gPhTYERyCBoS9LfhZWZu/11wuQ== 27 | -----END RSA PRIVATE KEY----- 28 | -----BEGIN CERTIFICATE----- 29 | MIICyTCCAjKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBcMQswCQYDVQQGEwJHTzEM 30 | MAoGA1UECBMDTUdPMQwwCgYDVQQHEwNNR08xDDAKBgNVBAoTA01HTzEPMA0GA1UE 31 | CxMGU2VydmVyMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTQwOTI0MTQwMzUzWhcN 32 | MTUwOTI0MTQwMzUzWjBcMQswCQYDVQQGEwJHTzEMMAoGA1UECBMDTUdPMQwwCgYD 33 | VQQHEwNNR08xDDAKBgNVBAoTA01HTzEPMA0GA1UECxMGQ2xpZW50MRIwEAYDVQQD 34 | Ewlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDATayX 35 | xh5NNJ63Cj2TKQnmYJn8W18+2f/Rfic6VMzZBz+5bMCyNJihJXhSS46JaMpBAfYQ 36 | N5dqW2yE6jsXSgJjmjNV5B76mF73zuIpO8u1/s2hckMqo2pHgpvjKVsKsSTLv5e/ 37 | DrKuurDyqBbL71WyuKVHerwuwRMT1IEy1+ncFRpo1Cml42b6ICHHe5lK/eZ3NIsV 38 | ID+jrWQDIbSxwsTFgOOj/vHIw1CiWgu82bHgijXrT7KU7PTvv+qpTrbS2a4MtHPV 39 | B3Er9um2ZdOewEJHGX5TyfNvBVmBYUQ2FemVtw35KM0uF2e9OJTaoWGBmZztm0Ip 40 | 08inHdbqkf1vHuRPAgMBAAGjFzAVMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqG 41 | SIb3DQEBBQUAA4GBAJZD7idSIRzhGlJYARPKWnX2CxD4VVB0F5cH5Mlc2YnoUSU/ 42 | rKuPZFuOYND3awKqez6K3rNb3+tQmNitmoOT8ImmX1uJKBo5w9tuo4B2MmLQcPMk 43 | 3fhPePuQCjtlArSmKVrNTrYPkyB9NwKS6q0+FzseFTw9ZJUIKiO9sSjMe+HP 44 | -----END CERTIFICATE----- 45 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/testdb/dropall.js: -------------------------------------------------------------------------------- 1 | 2 | var ports = [40001, 40002, 40011, 40012, 40013, 40021, 40022, 40023, 40041, 40101, 40102, 40103, 40201, 40202, 40203] 3 | var auth = [40002, 40103, 40203, 40031] 4 | var db1 = new Mongo("localhost:40001") 5 | 6 | if (db1.getDB("admin").serverBuildInfo().OpenSSLVersion != "") { 7 | ports.push(40003) 8 | auth.push(40003) 9 | } 10 | 11 | for (var i in ports) { 12 | var port = ports[i] 13 | var server = "localhost:" + port 14 | var mongo = new Mongo("localhost:" + port) 15 | var admin = mongo.getDB("admin") 16 | 17 | for (var j in auth) { 18 | if (auth[j] == port) { 19 | admin.auth("root", "rapadura") 20 | admin.system.users.find().forEach(function(u) { 21 | if (u.user == "root" || u.user == "reader") { 22 | return; 23 | } 24 | if (typeof admin.dropUser == "function") { 25 | mongo.getDB(u.db).dropUser(u.user); 26 | } else { 27 | admin.removeUser(u.user); 28 | } 29 | }) 30 | break 31 | } 32 | } 33 | var result = admin.runCommand({"listDatabases": 1}) 34 | for (var j = 0; j != 100; j++) { 35 | if (typeof result.databases != "undefined" || result.errmsg == "not master") { 36 | break 37 | } 38 | result = admin.runCommand({"listDatabases": 1}) 39 | } 40 | if (result.errmsg == "not master") { 41 | continue 42 | } 43 | if (typeof result.databases == "undefined") { 44 | print("Could not list databases. Command result:") 45 | print(JSON.stringify(result)) 46 | quit(12) 47 | } 48 | var dbs = result.databases 49 | for (var j = 0; j != dbs.length; j++) { 50 | var db = dbs[j] 51 | switch (db.name) { 52 | case "admin": 53 | case "local": 54 | case "config": 55 | break 56 | default: 57 | mongo.getDB(db.name).dropDatabase() 58 | } 59 | } 60 | } 61 | 62 | // vim:ts=4:sw=4:et 63 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/testdb/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC+DCCAmGgAwIBAgIJAJ5pBAq2HXAsMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNV 3 | BAYTAkdPMQwwCgYDVQQIEwNNR08xDDAKBgNVBAcTA01HTzEMMAoGA1UEChMDTUdP 4 | MQ8wDQYDVQQLEwZTZXJ2ZXIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNDA5MjQx 5 | MzUxMTBaFw0xNTA5MjQxMzUxMTBaMFwxCzAJBgNVBAYTAkdPMQwwCgYDVQQIEwNN 6 | R08xDDAKBgNVBAcTA01HTzEMMAoGA1UEChMDTUdPMQ8wDQYDVQQLEwZTZXJ2ZXIx 7 | EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA 8 | pQ5wO2L23xMI4PzpVt/Ftvez82IvA9amwr3fUd7RjlYwiFsFeMnG24a4CUoOeKF0 9 | fpQWc9rmCs0EeP5ofZ2otOsfxoVWXZAZWdgauuwlYB6EeFaAMH3fxVH3IiH+21RR 10 | q2w9sH/s4fqh5stavUfyPdVmCcb8NW0jD8jlqniJL0kCAwEAAaOBwTCBvjAdBgNV 11 | HQ4EFgQUjyVWGMHBrmPDGwCY5VusHsKIpzIwgY4GA1UdIwSBhjCBg4AUjyVWGMHB 12 | rmPDGwCY5VusHsKIpzKhYKReMFwxCzAJBgNVBAYTAkdPMQwwCgYDVQQIEwNNR08x 13 | DDAKBgNVBAcTA01HTzEMMAoGA1UEChMDTUdPMQ8wDQYDVQQLEwZTZXJ2ZXIxEjAQ 14 | BgNVBAMTCWxvY2FsaG9zdIIJAJ5pBAq2HXAsMAwGA1UdEwQFMAMBAf8wDQYJKoZI 15 | hvcNAQEFBQADgYEAa65TgDKp3SRUDNAILSuQOCEbenWh/DMPL4vTVgo/Dxd4emoO 16 | 7i8/4HMTa0XeYIVbAsxO+dqtxqt32IcV7DurmQozdUZ7q0ueJRXon6APnCN0IqPC 17 | sF71w63xXfpmnvTAfQXi7x6TUAyAQ2nScHExAjzc000DF1dO/6+nIINqNQE= 18 | -----END CERTIFICATE----- 19 | -----BEGIN RSA PRIVATE KEY----- 20 | MIICWwIBAAKBgQClDnA7YvbfEwjg/OlW38W297PzYi8D1qbCvd9R3tGOVjCIWwV4 21 | ycbbhrgJSg54oXR+lBZz2uYKzQR4/mh9nai06x/GhVZdkBlZ2Bq67CVgHoR4VoAw 22 | fd/FUfciIf7bVFGrbD2wf+zh+qHmy1q9R/I91WYJxvw1bSMPyOWqeIkvSQIDAQAB 23 | AoGABA9S22MXx2zkbwRJiQWAC3wURQxJM8L33xpkf9MHPIUKNJBolgwAhC3QIQpd 24 | SMJP5z0lQDxGJEXesksvrsdN+vsgbleRfQsAIcY/rEhr9h8m6auM08f+69oIX32o 25 | aTOWJJRofjbgzE5c/RijqhIaYGdq54a0EE9mAaODwZoa2/ECQQDRGrIRI5L3pdRA 26 | yifDKNjvAFOk6TbdGe+J9zHFw4F7bA2In/b+rno9vrj+EanOevD8LRLzeFshzXrG 27 | WQFzZ69/AkEAyhLSY7WNiQTeJWCwXawVnoSl5AMSRYFA/A2sEUokfORR5BS7gqvL 28 | mmEKmvslnZp5qlMtM4AyrW2OaoGvE6sFNwJACB3xK5kl61cUli9Cu+CqCx0IIi6r 29 | YonPMpvV4sdkD1ZycAtFmz1KoXr102b8IHfFQwS855aUcwt26Jwr4j70IQJAXv9+ 30 | PTXq9hF9xiCwiTkPaNh/jLQM8PQU8uoSjIZIpRZJkWpVxNay/z7D15xeULuAmxxD 31 | UcThDjtFCrkw75Qk/QJAFfcM+5r31R1RrBGM1QPKwDqkFTGsFKnMWuS/pXyLTTOv 32 | I+In9ZJyA/R5zKeJZjM7xtZs0ANU9HpOpgespq6CvA== 33 | -----END RSA PRIVATE KEY----- 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/testdb/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | start() { 4 | mkdir _testdb 5 | cd _testdb 6 | mkdir db1 db2 db3 rs1a rs1b rs1c rs2a rs2b rs2c rs3a rs3b rs3c rs4a cfg1 cfg2 cfg3 7 | cp ../testdb/supervisord.conf supervisord.conf 8 | cp ../testdb/server.pem server.pem 9 | echo keyfile > keyfile 10 | chmod 600 keyfile 11 | COUNT=$(grep '^\[program' supervisord.conf | wc -l | tr -d ' ') 12 | if ! mongod --help | grep -q -- --ssl; then 13 | COUNT=$(($COUNT - 1)) 14 | fi 15 | echo "Running supervisord..." 16 | supervisord || ( echo "Supervisord failed executing ($?)" && exit 1 ) 17 | echo "Supervisord is up, starting $COUNT processes..." 18 | for i in $(seq 10); do 19 | RUNNING=$(supervisorctl status | grep RUNNING | wc -l | tr -d ' ') 20 | echo "$RUNNING processes running..." 21 | if [ x$COUNT = x$RUNNING ]; then 22 | echo "Running setup.js with mongo..." 23 | mongo --nodb ../testdb/init.js 24 | exit 0 25 | fi 26 | sleep 1 27 | done 28 | echo "Failed to start all processes. Check out what's up at $PWD now!" 29 | exit 1 30 | } 31 | 32 | stop() { 33 | if [ -d _testdb ]; then 34 | echo "Shutting down test cluster..." 35 | (cd _testdb && supervisorctl shutdown) 36 | rm -rf _testdb 37 | fi 38 | } 39 | 40 | 41 | if [ ! -f suite_test.go ]; then 42 | echo "This script must be run from within the source directory." 43 | exit 1 44 | fi 45 | 46 | case "$1" in 47 | 48 | start) 49 | start $2 50 | ;; 51 | 52 | stop) 53 | stop $2 54 | ;; 55 | 56 | esac 57 | 58 | # vim:ts=4:sw=4:et 59 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/testdb/wait.js: -------------------------------------------------------------------------------- 1 | // We know the master of the first set (pri=1), but not of the second. 2 | var settings = {} 3 | var rs1cfg = {_id: "rs1", 4 | members: [{_id: 1, host: "127.0.0.1:40011", priority: 1}, 5 | {_id: 2, host: "127.0.0.1:40012", priority: 0}, 6 | {_id: 3, host: "127.0.0.1:40013", priority: 0}]} 7 | var rs2cfg = {_id: "rs2", 8 | members: [{_id: 1, host: "127.0.0.1:40021", priority: 1}, 9 | {_id: 2, host: "127.0.0.1:40022", priority: 1}, 10 | {_id: 3, host: "127.0.0.1:40023", priority: 0}]} 11 | var rs3cfg = {_id: "rs3", 12 | members: [{_id: 1, host: "127.0.0.1:40031", priority: 1}, 13 | {_id: 2, host: "127.0.0.1:40032", priority: 1}, 14 | {_id: 3, host: "127.0.0.1:40033", priority: 1}], 15 | settings: settings} 16 | 17 | for (var i = 0; i != 60; i++) { 18 | try { 19 | rs1a = new Mongo("127.0.0.1:40011").getDB("admin") 20 | rs2a = new Mongo("127.0.0.1:40021").getDB("admin") 21 | rs3a = new Mongo("127.0.0.1:40031").getDB("admin") 22 | rs3a.auth("root", "rapadura") 23 | db1 = new Mongo("127.0.0.1:40001").getDB("admin") 24 | db2 = new Mongo("127.0.0.1:40002").getDB("admin") 25 | break 26 | } catch(err) { 27 | print("Can't connect yet...") 28 | } 29 | sleep(1000) 30 | } 31 | 32 | function countHealthy(rs) { 33 | var status = rs.runCommand({replSetGetStatus: 1}) 34 | var count = 0 35 | if (typeof status.members != "undefined") { 36 | for (var i = 0; i != status.members.length; i++) { 37 | var m = status.members[i] 38 | if (m.health == 1 && (m.state == 1 || m.state == 2)) { 39 | count += 1 40 | } 41 | } 42 | } 43 | return count 44 | } 45 | 46 | var totalRSMembers = rs1cfg.members.length + rs2cfg.members.length + rs3cfg.members.length 47 | 48 | for (var i = 0; i != 60; i++) { 49 | var count = countHealthy(rs1a) + countHealthy(rs2a) + countHealthy(rs3a) 50 | print("Replica sets have", count, "healthy nodes.") 51 | if (count == totalRSMembers) { 52 | quit(0) 53 | } 54 | sleep(1000) 55 | } 56 | 57 | print("Replica sets didn't sync up properly.") 58 | quit(12) 59 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/txn/chaos.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | mrand "math/rand" 5 | "time" 6 | ) 7 | 8 | var chaosEnabled = false 9 | var chaosSetting Chaos 10 | 11 | // Chaos holds parameters for the failure injection mechanism. 12 | type Chaos struct { 13 | // KillChance is the 0.0 to 1.0 chance that a given checkpoint 14 | // within the algorithm will raise an interruption that will 15 | // stop the procedure. 16 | KillChance float64 17 | 18 | // SlowdownChance is the 0.0 to 1.0 chance that a given checkpoint 19 | // within the algorithm will be delayed by Slowdown before 20 | // continuing. 21 | SlowdownChance float64 22 | Slowdown time.Duration 23 | 24 | // If Breakpoint is set, the above settings will only affect the 25 | // named breakpoint. 26 | Breakpoint string 27 | } 28 | 29 | // SetChaos sets the failure injection parameters to c. 30 | func SetChaos(c Chaos) { 31 | chaosSetting = c 32 | chaosEnabled = c.KillChance > 0 || c.SlowdownChance > 0 33 | } 34 | 35 | func chaos(bpname string) { 36 | if !chaosEnabled { 37 | return 38 | } 39 | switch chaosSetting.Breakpoint { 40 | case "", bpname: 41 | kc := chaosSetting.KillChance 42 | if kc > 0 && mrand.Intn(1000) < int(kc*1000) { 43 | panic(chaosError{}) 44 | } 45 | if bpname == "insert" { 46 | return 47 | } 48 | sc := chaosSetting.SlowdownChance 49 | if sc > 0 && mrand.Intn(1000) < int(sc*1000) { 50 | time.Sleep(chaosSetting.Slowdown) 51 | } 52 | } 53 | } 54 | 55 | type chaosError struct{} 56 | 57 | func (f *flusher) handleChaos(err *error) { 58 | v := recover() 59 | if v == nil { 60 | return 61 | } 62 | if _, ok := v.(chaosError); ok { 63 | f.debugf("Killed by chaos!") 64 | *err = ErrChaos 65 | return 66 | } 67 | panic(v) 68 | } 69 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/txn/debug.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "sort" 7 | "sync/atomic" 8 | 9 | "gopkg.in/mgo.v2/bson" 10 | ) 11 | 12 | var ( 13 | debugEnabled bool 14 | logger log_Logger 15 | ) 16 | 17 | type log_Logger interface { 18 | Output(calldepth int, s string) error 19 | } 20 | 21 | // Specify the *log.Logger where logged messages should be sent to. 22 | func SetLogger(l log_Logger) { 23 | logger = l 24 | } 25 | 26 | // SetDebug enables or disables debugging. 27 | func SetDebug(debug bool) { 28 | debugEnabled = debug 29 | } 30 | 31 | var ErrChaos = fmt.Errorf("interrupted by chaos") 32 | 33 | var debugId uint32 34 | 35 | func debugPrefix() string { 36 | d := atomic.AddUint32(&debugId, 1) - 1 37 | s := make([]byte, 0, 10) 38 | for i := uint(0); i < 8; i++ { 39 | s = append(s, "abcdefghijklmnop"[(d>>(4*i))&0xf]) 40 | if d>>(4*(i+1)) == 0 { 41 | break 42 | } 43 | } 44 | s = append(s, ')', ' ') 45 | return string(s) 46 | } 47 | 48 | func logf(format string, args ...interface{}) { 49 | if logger != nil { 50 | logger.Output(2, fmt.Sprintf(format, argsForLog(args)...)) 51 | } 52 | } 53 | 54 | func debugf(format string, args ...interface{}) { 55 | if debugEnabled && logger != nil { 56 | logger.Output(2, fmt.Sprintf(format, argsForLog(args)...)) 57 | } 58 | } 59 | 60 | func argsForLog(args []interface{}) []interface{} { 61 | for i, arg := range args { 62 | switch v := arg.(type) { 63 | case bson.ObjectId: 64 | args[i] = v.Hex() 65 | case []bson.ObjectId: 66 | lst := make([]string, len(v)) 67 | for j, id := range v { 68 | lst[j] = id.Hex() 69 | } 70 | args[i] = lst 71 | case map[docKey][]bson.ObjectId: 72 | buf := &bytes.Buffer{} 73 | var dkeys docKeys 74 | for dkey := range v { 75 | dkeys = append(dkeys, dkey) 76 | } 77 | sort.Sort(dkeys) 78 | for i, dkey := range dkeys { 79 | if i > 0 { 80 | buf.WriteByte(' ') 81 | } 82 | buf.WriteString(fmt.Sprintf("%v: {", dkey)) 83 | for j, id := range v[dkey] { 84 | if j > 0 { 85 | buf.WriteByte(' ') 86 | } 87 | buf.WriteString(id.Hex()) 88 | } 89 | buf.WriteByte('}') 90 | } 91 | args[i] = buf.String() 92 | case map[docKey][]int64: 93 | buf := &bytes.Buffer{} 94 | var dkeys docKeys 95 | for dkey := range v { 96 | dkeys = append(dkeys, dkey) 97 | } 98 | sort.Sort(dkeys) 99 | for i, dkey := range dkeys { 100 | if i > 0 { 101 | buf.WriteByte(' ') 102 | } 103 | buf.WriteString(fmt.Sprintf("%v: %v", dkey, v[dkey])) 104 | } 105 | args[i] = buf.String() 106 | } 107 | } 108 | return args 109 | } 110 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/txn/dockey_test.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "sort" 5 | 6 | . "gopkg.in/check.v1" 7 | ) 8 | 9 | type DocKeySuite struct{} 10 | 11 | var _ = Suite(&DocKeySuite{}) 12 | 13 | type T struct { 14 | A int 15 | B string 16 | } 17 | 18 | type T2 struct { 19 | A int 20 | B string 21 | } 22 | 23 | type T3 struct { 24 | A int 25 | B string 26 | } 27 | 28 | type T4 struct { 29 | A int 30 | B string 31 | } 32 | 33 | type T5 struct { 34 | F int 35 | Q string 36 | } 37 | 38 | type T6 struct { 39 | A int 40 | B string 41 | } 42 | 43 | type T7 struct { 44 | A bool 45 | B float64 46 | } 47 | 48 | type T8 struct { 49 | A int 50 | B string 51 | } 52 | 53 | type T9 struct { 54 | A int 55 | B string 56 | C bool 57 | } 58 | 59 | type T10 struct { 60 | C int `bson:"a"` 61 | D string `bson:"b,omitempty"` 62 | } 63 | 64 | type T11 struct { 65 | C int 66 | D string 67 | } 68 | 69 | type T12 struct { 70 | S string 71 | } 72 | 73 | type T13 struct { 74 | p, q, r bool 75 | S string 76 | } 77 | 78 | var docKeysTests = [][]docKeys{ 79 | {{ 80 | {"c", 1}, 81 | {"c", 5}, 82 | {"c", 2}, 83 | }, { 84 | {"c", 1}, 85 | {"c", 2}, 86 | {"c", 5}, 87 | }}, {{ 88 | {"c", "foo"}, 89 | {"c", "bar"}, 90 | {"c", "bob"}, 91 | }, { 92 | {"c", "bar"}, 93 | {"c", "bob"}, 94 | {"c", "foo"}, 95 | }}, {{ 96 | {"c", 0.2}, 97 | {"c", 0.07}, 98 | {"c", 0.9}, 99 | }, { 100 | {"c", 0.07}, 101 | {"c", 0.2}, 102 | {"c", 0.9}, 103 | }}, {{ 104 | {"c", true}, 105 | {"c", false}, 106 | {"c", true}, 107 | }, { 108 | {"c", false}, 109 | {"c", true}, 110 | {"c", true}, 111 | }}, {{ 112 | {"c", T{1, "b"}}, 113 | {"c", T{1, "a"}}, 114 | {"c", T{0, "b"}}, 115 | {"c", T{0, "a"}}, 116 | }, { 117 | {"c", T{0, "a"}}, 118 | {"c", T{0, "b"}}, 119 | {"c", T{1, "a"}}, 120 | {"c", T{1, "b"}}, 121 | }}, {{ 122 | {"c", T{1, "a"}}, 123 | {"c", T{0, "a"}}, 124 | }, { 125 | {"c", T{0, "a"}}, 126 | {"c", T{1, "a"}}, 127 | }}, {{ 128 | {"c", T3{0, "b"}}, 129 | {"c", T2{1, "b"}}, 130 | {"c", T3{1, "a"}}, 131 | {"c", T2{0, "a"}}, 132 | }, { 133 | {"c", T2{0, "a"}}, 134 | {"c", T3{0, "b"}}, 135 | {"c", T3{1, "a"}}, 136 | {"c", T2{1, "b"}}, 137 | }}, {{ 138 | {"c", T5{1, "b"}}, 139 | {"c", T4{1, "b"}}, 140 | {"c", T5{0, "a"}}, 141 | {"c", T4{0, "a"}}, 142 | }, { 143 | {"c", T4{0, "a"}}, 144 | {"c", T5{0, "a"}}, 145 | {"c", T4{1, "b"}}, 146 | {"c", T5{1, "b"}}, 147 | }}, {{ 148 | {"c", T6{1, "b"}}, 149 | {"c", T7{true, 0.2}}, 150 | {"c", T6{0, "a"}}, 151 | {"c", T7{false, 0.04}}, 152 | }, { 153 | {"c", T6{0, "a"}}, 154 | {"c", T6{1, "b"}}, 155 | {"c", T7{false, 0.04}}, 156 | {"c", T7{true, 0.2}}, 157 | }}, {{ 158 | {"c", T9{1, "b", true}}, 159 | {"c", T8{1, "b"}}, 160 | {"c", T9{0, "a", false}}, 161 | {"c", T8{0, "a"}}, 162 | }, { 163 | {"c", T9{0, "a", false}}, 164 | {"c", T8{0, "a"}}, 165 | {"c", T9{1, "b", true}}, 166 | {"c", T8{1, "b"}}, 167 | }}, {{ 168 | {"b", 2}, 169 | {"a", 5}, 170 | {"c", 2}, 171 | {"b", 1}, 172 | }, { 173 | {"a", 5}, 174 | {"b", 1}, 175 | {"b", 2}, 176 | {"c", 2}, 177 | }}, {{ 178 | {"c", T11{1, "a"}}, 179 | {"c", T11{1, "a"}}, 180 | {"c", T10{1, "a"}}, 181 | }, { 182 | {"c", T10{1, "a"}}, 183 | {"c", T11{1, "a"}}, 184 | {"c", T11{1, "a"}}, 185 | }}, {{ 186 | {"c", T12{"a"}}, 187 | {"c", T13{false, true, false, "a"}}, 188 | {"c", T12{"b"}}, 189 | {"c", T13{false, true, false, "b"}}, 190 | }, { 191 | {"c", T12{"a"}}, 192 | {"c", T13{false, true, false, "a"}}, 193 | {"c", T12{"b"}}, 194 | {"c", T13{false, true, false, "b"}}, 195 | }}, 196 | } 197 | 198 | func (s *DocKeySuite) TestSort(c *C) { 199 | for _, test := range docKeysTests { 200 | keys := test[0] 201 | expected := test[1] 202 | sort.Sort(keys) 203 | c.Check(keys, DeepEquals, expected) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/txn/mgo_test.go: -------------------------------------------------------------------------------- 1 | package txn_test 2 | 3 | import ( 4 | "bytes" 5 | "gopkg.in/mgo.v2" 6 | . "gopkg.in/check.v1" 7 | "os/exec" 8 | "time" 9 | ) 10 | 11 | // ---------------------------------------------------------------------------- 12 | // The mgo test suite 13 | 14 | type MgoSuite struct { 15 | output bytes.Buffer 16 | server *exec.Cmd 17 | session *mgo.Session 18 | } 19 | 20 | var mgoaddr = "127.0.0.1:50017" 21 | 22 | func (s *MgoSuite) SetUpSuite(c *C) { 23 | //mgo.SetDebug(true) 24 | mgo.SetStats(true) 25 | dbdir := c.MkDir() 26 | args := []string{ 27 | "--dbpath", dbdir, 28 | "--bind_ip", "127.0.0.1", 29 | "--port", "50017", 30 | "--nssize", "1", 31 | "--noprealloc", 32 | "--smallfiles", 33 | "--nojournal", 34 | "-vvvvv", 35 | } 36 | s.server = exec.Command("mongod", args...) 37 | s.server.Stdout = &s.output 38 | s.server.Stderr = &s.output 39 | err := s.server.Start() 40 | if err != nil { 41 | panic(err) 42 | } 43 | } 44 | 45 | func (s *MgoSuite) TearDownSuite(c *C) { 46 | s.server.Process.Kill() 47 | s.server.Process.Wait() 48 | } 49 | 50 | func (s *MgoSuite) SetUpTest(c *C) { 51 | err := DropAll(mgoaddr) 52 | if err != nil { 53 | panic(err) 54 | } 55 | mgo.SetLogger(c) 56 | mgo.ResetStats() 57 | 58 | s.session, err = mgo.Dial(mgoaddr) 59 | c.Assert(err, IsNil) 60 | } 61 | 62 | func (s *MgoSuite) TearDownTest(c *C) { 63 | if s.session != nil { 64 | s.session.Close() 65 | } 66 | for i := 0; ; i++ { 67 | stats := mgo.GetStats() 68 | if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 { 69 | break 70 | } 71 | if i == 20 { 72 | c.Fatal("Test left sockets in a dirty state") 73 | } 74 | c.Logf("Waiting for sockets to die: %d in use, %d alive", stats.SocketsInUse, stats.SocketsAlive) 75 | time.Sleep(500 * time.Millisecond) 76 | } 77 | } 78 | 79 | func DropAll(mongourl string) (err error) { 80 | session, err := mgo.Dial(mongourl) 81 | if err != nil { 82 | return err 83 | } 84 | defer session.Close() 85 | 86 | names, err := session.DatabaseNames() 87 | if err != nil { 88 | return err 89 | } 90 | for _, name := range names { 91 | switch name { 92 | case "admin", "local", "config": 93 | default: 94 | err = session.DB(name).DropDatabase() 95 | if err != nil { 96 | return err 97 | } 98 | } 99 | } 100 | return nil 101 | } 102 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/txn/tarjan.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "gopkg.in/mgo.v2/bson" 5 | "sort" 6 | ) 7 | 8 | func tarjanSort(successors map[bson.ObjectId][]bson.ObjectId) [][]bson.ObjectId { 9 | // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm 10 | data := &tarjanData{ 11 | successors: successors, 12 | nodes: make([]tarjanNode, 0, len(successors)), 13 | index: make(map[bson.ObjectId]int, len(successors)), 14 | } 15 | 16 | for id := range successors { 17 | id := bson.ObjectId(string(id)) 18 | if _, seen := data.index[id]; !seen { 19 | data.strongConnect(id) 20 | } 21 | } 22 | 23 | // Sort connected components to stabilize the algorithm. 24 | for _, ids := range data.output { 25 | if len(ids) > 1 { 26 | sort.Sort(idList(ids)) 27 | } 28 | } 29 | return data.output 30 | } 31 | 32 | type tarjanData struct { 33 | successors map[bson.ObjectId][]bson.ObjectId 34 | output [][]bson.ObjectId 35 | 36 | nodes []tarjanNode 37 | stack []bson.ObjectId 38 | index map[bson.ObjectId]int 39 | } 40 | 41 | type tarjanNode struct { 42 | lowlink int 43 | stacked bool 44 | } 45 | 46 | type idList []bson.ObjectId 47 | 48 | func (l idList) Len() int { return len(l) } 49 | func (l idList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 50 | func (l idList) Less(i, j int) bool { return l[i] < l[j] } 51 | 52 | func (data *tarjanData) strongConnect(id bson.ObjectId) *tarjanNode { 53 | index := len(data.nodes) 54 | data.index[id] = index 55 | data.stack = append(data.stack, id) 56 | data.nodes = append(data.nodes, tarjanNode{index, true}) 57 | node := &data.nodes[index] 58 | 59 | for _, succid := range data.successors[id] { 60 | succindex, seen := data.index[succid] 61 | if !seen { 62 | succnode := data.strongConnect(succid) 63 | if succnode.lowlink < node.lowlink { 64 | node.lowlink = succnode.lowlink 65 | } 66 | } else if data.nodes[succindex].stacked { 67 | // Part of the current strongly-connected component. 68 | if succindex < node.lowlink { 69 | node.lowlink = succindex 70 | } 71 | } 72 | } 73 | 74 | if node.lowlink == index { 75 | // Root node; pop stack and output new 76 | // strongly-connected component. 77 | var scc []bson.ObjectId 78 | i := len(data.stack) - 1 79 | for { 80 | stackid := data.stack[i] 81 | stackindex := data.index[stackid] 82 | data.nodes[stackindex].stacked = false 83 | scc = append(scc, stackid) 84 | if stackindex == index { 85 | break 86 | } 87 | i-- 88 | } 89 | data.stack = data.stack[:i] 90 | data.output = append(data.output, scc) 91 | } 92 | 93 | return node 94 | } 95 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/mgo.v2/txn/tarjan_test.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "fmt" 5 | "gopkg.in/mgo.v2/bson" 6 | . "gopkg.in/check.v1" 7 | ) 8 | 9 | type TarjanSuite struct{} 10 | 11 | var _ = Suite(TarjanSuite{}) 12 | 13 | func bid(n int) bson.ObjectId { 14 | return bson.ObjectId(fmt.Sprintf("%024d", n)) 15 | } 16 | 17 | func bids(ns ...int) (ids []bson.ObjectId) { 18 | for _, n := range ns { 19 | ids = append(ids, bid(n)) 20 | } 21 | return 22 | } 23 | 24 | func (TarjanSuite) TestExample(c *C) { 25 | successors := map[bson.ObjectId][]bson.ObjectId{ 26 | bid(1): bids(2, 3), 27 | bid(2): bids(1, 5), 28 | bid(3): bids(4), 29 | bid(4): bids(3, 5), 30 | bid(5): bids(6), 31 | bid(6): bids(7), 32 | bid(7): bids(8), 33 | bid(8): bids(6, 9), 34 | bid(9): bids(), 35 | } 36 | 37 | c.Assert(tarjanSort(successors), DeepEquals, [][]bson.ObjectId{ 38 | bids(9), 39 | bids(6, 7, 8), 40 | bids(5), 41 | bids(3, 4), 42 | bids(1, 2), 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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{C int; D []int ",flow"} 71 | } 72 | 73 | func main() { 74 | t := T{} 75 | 76 | err := yaml.Unmarshal([]byte(data), &t) 77 | if err != nil { 78 | log.Fatalf("error: %v", err) 79 | } 80 | fmt.Printf("--- t:\n%v\n\n", t) 81 | 82 | d, err := yaml.Marshal(&t) 83 | if err != nil { 84 | log.Fatalf("error: %v", err) 85 | } 86 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 87 | 88 | m := make(map[interface{}]interface{}) 89 | 90 | err = yaml.Unmarshal([]byte(data), &m) 91 | if err != nil { 92 | log.Fatalf("error: %v", err) 93 | } 94 | fmt.Printf("--- m:\n%v\n\n", m) 95 | 96 | d, err = yaml.Marshal(&m) 97 | if err != nil { 98 | log.Fatalf("error: %v", err) 99 | } 100 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 101 | } 102 | ``` 103 | 104 | This example will generate the following output: 105 | 106 | ``` 107 | --- t: 108 | {Easy! {2 [3 4]}} 109 | 110 | --- t dump: 111 | a: Easy! 112 | b: 113 | c: 2 114 | d: [3, 4] 115 | 116 | 117 | --- m: 118 | map[a:Easy! b:map[c:2 d:[3 4]]] 119 | 120 | --- m dump: 121 | a: Easy! 122 | b: 123 | c: 2 124 | d: 125 | - 3 126 | - 4 127 | ``` 128 | 129 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/yaml.v2/suite_test.go: -------------------------------------------------------------------------------- 1 | package yaml_test 2 | 3 | import ( 4 | . "gopkg.in/check.v1" 5 | "testing" 6 | ) 7 | 8 | func Test(t *testing.T) { TestingT(t) } 9 | 10 | type S struct{} 11 | 12 | var _ = Suite(&S{}) 13 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /cmd/hooky/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/sebest/hooky/client" 9 | "gopkg.in/yaml.v2" 10 | ) 11 | 12 | var ( 13 | baseURL = flag.String("base-url", "http://127.0.0.1:8000", "HookyD base URL") 14 | crontabFile = flag.String("crontab-file", "", "load a crontab file") 15 | ) 16 | 17 | func main() { 18 | flag.Parse() 19 | 20 | if *crontabFile != "" { 21 | crontab, err := hooky.NewCrontabFromFile(*crontabFile) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | hooky.SyncCrontab(*baseURL, crontab) 26 | payload, err := yaml.Marshal(crontab) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | fmt.Println(string(payload)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cmd/hookyd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "os" 7 | "time" 8 | 9 | "github.com/codegangsta/cli" 10 | "github.com/sebest/hooky/models" 11 | "github.com/sebest/hooky/restapi" 12 | "github.com/sebest/hooky/scheduler" 13 | "github.com/sebest/hooky/store" 14 | "github.com/stretchr/graceful" 15 | ) 16 | 17 | func main() { 18 | app := cli.NewApp() 19 | app.Name = "hooky" 20 | app.Usage = "the webhooks scheduler" 21 | app.Version = "0.1" 22 | app.Author = "Sébastien Estienne" 23 | app.Email = "sebastien.estienne@gmail.com" 24 | app.Flags = []cli.Flag{ 25 | cli.StringFlag{ 26 | Name: "bind-address", 27 | Value: "", 28 | Usage: "host address to bind on", 29 | EnvVar: "HOOKY_BIND_ADDRESS", 30 | }, 31 | cli.StringFlag{ 32 | Name: "bind-port", 33 | Value: "8000", 34 | Usage: "port number to bind on", 35 | EnvVar: "HOOKY_BIND_PORT,PORT", 36 | }, 37 | cli.StringFlag{ 38 | Name: "mongo-uri", 39 | Value: "mongodb://127.0.0.1/hooky", 40 | Usage: "MongoDB URI to connect to", 41 | EnvVar: "HOOKY_MONGO_URI", 42 | }, 43 | cli.StringFlag{ 44 | Name: "admin-password", 45 | Value: "admin", 46 | Usage: "admin password", 47 | EnvVar: "HOOKY_ADMIN_PASSWORD", 48 | }, 49 | cli.StringFlag{ 50 | Name: "accesslog-format", 51 | Value: "none", 52 | Usage: "format of the access log: json, apache-fancy, apache-combined, apache-common or none", 53 | EnvVar: "HOOKY_ACCESSLOG_FORMAT", 54 | }, 55 | cli.IntFlag{ 56 | Name: "max-mongo-query", 57 | Value: 1, 58 | Usage: "maximum number of parallel queries on MongoDB", 59 | EnvVar: "HOOKY_MAX_MONGO_QUERY", 60 | }, 61 | cli.IntFlag{ 62 | Name: "max-http-request", 63 | Value: 20, 64 | Usage: "maximum number of parallel HTTP requests", 65 | EnvVar: "HOOKY_MAX_HTTP_REQUEST", 66 | }, 67 | cli.IntFlag{ 68 | Name: "touch-interval", 69 | Value: 5, 70 | Usage: "frequency to update the tasks reservation duration in seconds", 71 | EnvVar: "HOOKY_TOUCH_INTERVAL", 72 | }, 73 | cli.IntFlag{ 74 | Name: "clean-finished-attempts", 75 | Value: 7 * 24, 76 | Usage: "delete finished attempts that are older than this age in hours", 77 | EnvVar: "HOOKY_CLEAN_FINISHED_ATTEMPTS", 78 | }, 79 | } 80 | app.Action = func(c *cli.Context) { 81 | s, err := store.New(c.String("mongo-uri")) 82 | if err != nil { 83 | log.Fatal(err) 84 | } 85 | 86 | db := s.DB() 87 | if err := models.NewBase(db).Bootstrap(); err != nil { 88 | log.Fatal(err) 89 | } 90 | db.Session.Close() 91 | 92 | sched := scheduler.New(s, c.Int("max-mongo-query"), c.Int("max-http-request"), c.Int("touch-interval"), c.Int("clean-finished-attempts")*3600) 93 | sched.Start() 94 | ra, err := restapi.New(s, c.String("admin-password"), c.String("accesslog-format")) 95 | if err != nil { 96 | log.Fatal(err) 97 | } 98 | server := &graceful.Server{ 99 | Timeout: 10 * time.Second, 100 | Server: &http.Server{ 101 | Addr: c.String("bind-host") + ":" + c.String("bind-port"), 102 | Handler: ra.MakeHandler(), 103 | }, 104 | } 105 | err = server.ListenAndServe() 106 | if err != nil { 107 | log.Println(err) 108 | } 109 | log.Println("exiting...") 110 | sched.Stop() 111 | log.Println("exited") 112 | } 113 | app.Run(os.Args) 114 | } 115 | -------------------------------------------------------------------------------- /dist/Dockerfile.build: -------------------------------------------------------------------------------- 1 | FROM golang:1.4 2 | 3 | ENV CGO_ENABLED 0 4 | ENV GOOS linux 5 | ENV HOOKY_DIR /go/src/github.com/sebest/hooky 6 | 7 | RUN mkdir -p $HOOKY_DIR 8 | WORKDIR $HOOKY_DIR 9 | 10 | ADD . $HOOKY_DIR 11 | 12 | RUN go get github.com/tools/godep 13 | RUN godep go build -a -installsuffix cgo -o hookyd cmd/hookyd/main.go 14 | RUN godep go build -a -installsuffix cgo -o hooky cmd/hooky/main.go 15 | 16 | CMD tar -czf - hooky hookyd 17 | -------------------------------------------------------------------------------- /dist/Dockerfile.dist: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | ADD dist/ca-certificates.crt /etc/ssl/certs/ 4 | ADD hooky-build.tar.gz / 5 | 6 | EXPOSE 8000 7 | 8 | CMD ["/hookyd"] -------------------------------------------------------------------------------- /dist/Dockerfile.travis: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | ADD dist/ca-certificates.crt /etc/ssl/certs/ 4 | ADD hooky / 5 | ADD hookyd / 6 | 7 | EXPOSE 8000 8 | 9 | CMD ["/hookyd"] -------------------------------------------------------------------------------- /docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | NAME=hooky 4 | TAG=`git rev-parse --short=8 HEAD` 5 | 6 | TMPD=`mktemp -d /tmp/$NAME-build.XXXXXX` || exit 1 7 | git archive --format=tar $TAG | (cd $TMPD ; tar -xpf -) 8 | 9 | pushd $TMPD 10 | 11 | BV=$RANDOM 12 | cp dist/Dockerfile.build Dockerfile 13 | docker build --rm -t $NAME-build:$BV . 14 | docker run --rm $NAME-build:$BV > $NAME-build.tar.gz 15 | docker rmi $NAME-build:$BV 16 | 17 | cp dist/Dockerfile.dist Dockerfile 18 | docker build --rm -t sebest/$NAME:$TAG . 19 | 20 | rm -rf $TMPD 21 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | hooky: 2 | build: . 3 | working_dir: /go/src/github.com/sebest/hooky 4 | command: godep go run cmd/hookyd/main.go 5 | volumes: 6 | - .:/go/src/github.com/sebest/hooky 7 | environment: 8 | - HOOKY_MONGO_URI=mongo/hooky 9 | ports: 10 | - "8000:8000" 11 | links: 12 | - mongo 13 | 14 | mongo: 15 | image: mongo 16 | command: mongod --smallfiles --quiet --logpath=/dev/null 17 | ports: 18 | - "27017" 19 | -------------------------------------------------------------------------------- /example-cron.yaml: -------------------------------------------------------------------------------- 1 | account: 2 | id: YYYYYYYYYYYYYYYYYYYYY 3 | key: XXXXXXXXXXXXXXXXXXXXXXXXX 4 | 5 | tasks: 6 | - name: run_campaigns_scheduler 7 | retry: 8 | max: 100 9 | max_attempts: 10 10 | schedule: "0 */5 * * * *" 11 | url: "https://api.domain.com/admin/run_campaigns_scheduler" 12 | - name: placements_statistics 13 | method: DELETE 14 | schedule: "0 5 0 1 * *" 15 | url: "https://api.domain.com/placements/statistics" 16 | auth: 17 | username: admin 18 | password: ZZZZZZZZ 19 | -------------------------------------------------------------------------------- /models/account.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "math/rand" 5 | 6 | "gopkg.in/mgo.v2" 7 | "gopkg.in/mgo.v2/bson" 8 | ) 9 | 10 | // Account is an account to access the service. 11 | type Account struct { 12 | // ID is the ID of the Account. 13 | ID bson.ObjectId `bson:"_id"` 14 | 15 | // Name is display name for the Account. 16 | Name *string `bson:"name,omitempty"` 17 | 18 | // Key is the secret key to authenticate the Account ID. 19 | Key string `bson:"key"` 20 | 21 | // Deleted 22 | Deleted bool `bson:"deleted"` 23 | } 24 | 25 | // NewAccount creates a new Account. 26 | func (b *Base) NewAccount(name *string) (account *Account, err error) { 27 | account = &Account{ 28 | ID: bson.NewObjectId(), 29 | Name: name, 30 | Key: randKey(32), 31 | } 32 | err = b.db.C("accounts").Insert(account) 33 | _, err = b.ShouldRefreshSession(err) 34 | return 35 | } 36 | 37 | // UpdateAccount updates an Account. 38 | func (b *Base) UpdateAccount(accountID bson.ObjectId, name *string) (account *Account, err error) { 39 | if name == nil { 40 | return b.GetAccount(accountID) 41 | } 42 | change := mgo.Change{ 43 | Update: bson.M{ 44 | "$set": bson.M{ 45 | "name": name, 46 | }, 47 | }, 48 | ReturnNew: true, 49 | } 50 | query := bson.M{ 51 | "_id": accountID, 52 | } 53 | account = &Account{} 54 | _, err = b.db.C("accounts").Find(query).Apply(change, account) 55 | _, err = b.ShouldRefreshSession(err) 56 | return 57 | } 58 | 59 | // GetAccount returns an Account given its ID. 60 | func (b *Base) GetAccount(accountID bson.ObjectId) (account *Account, err error) { 61 | query := bson.M{ 62 | "_id": accountID, 63 | "deleted": false, 64 | } 65 | account = &Account{} 66 | err = b.db.C("accounts").Find(query).One(account) 67 | _, err = b.ShouldRefreshSession(err) 68 | if err == mgo.ErrNotFound { 69 | err = nil 70 | account = nil 71 | } 72 | return 73 | } 74 | 75 | // DeleteAccount deletes an Account given its ID. 76 | func (b *Base) DeleteAccount(account bson.ObjectId) (err error) { 77 | update := bson.M{ 78 | "$set": bson.M{ 79 | "deleted": true, 80 | }, 81 | } 82 | 83 | query := bson.M{ 84 | "account": account, 85 | } 86 | _, err = b.db.C("attempts").UpdateAll(query, update) 87 | _, err = b.ShouldRefreshSession(err) 88 | if err != nil { 89 | return 90 | } 91 | _, err = b.db.C("queues").UpdateAll(query, update) 92 | _, err = b.ShouldRefreshSession(err) 93 | if err != nil { 94 | return 95 | } 96 | _, err = b.db.C("tasks").UpdateAll(query, update) 97 | _, err = b.ShouldRefreshSession(err) 98 | if err != nil { 99 | return 100 | } 101 | _, err = b.db.C("applications").UpdateAll(query, update) 102 | _, err = b.ShouldRefreshSession(err) 103 | if err != nil { 104 | return 105 | } 106 | 107 | err = b.db.C("accounts").UpdateId(account, update) 108 | _, err = b.ShouldRefreshSession(err) 109 | return 110 | } 111 | 112 | // GetAccounts returns a list of Accounts. 113 | func (b *Base) GetAccounts(lp ListParams, lr *ListResult) (err error) { 114 | query := bson.M{ 115 | "deleted": false, 116 | } 117 | return b.getItems("accounts", query, lp, lr) 118 | } 119 | 120 | //AuthenticateAccount authenticates an Account. 121 | func (b *Base) AuthenticateAccount(account bson.ObjectId, key string) (bool, error) { 122 | query := bson.M{ 123 | "_id": account, 124 | "key": key, 125 | "deleted": false, 126 | } 127 | n, err := b.db.C("accounts").Find(query).Count() 128 | if err != nil { 129 | _, err = b.ShouldRefreshSession(err) 130 | return false, err 131 | } 132 | return n == 1, nil 133 | } 134 | 135 | var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") 136 | 137 | func randKey(n int) string { 138 | b := make([]rune, n) 139 | for i := range b { 140 | b[i] = chars[rand.Intn(len(chars))] 141 | } 142 | return string(b) 143 | } 144 | -------------------------------------------------------------------------------- /models/httpauth.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // HTTPAuth is used for HTTP Basic Auth. 4 | type HTTPAuth struct { 5 | Username string `json:"username,omitempty" bson:"username,omitempty"` 6 | Password string `json:"password,omitempty" bson:"password,omitempty"` 7 | } 8 | -------------------------------------------------------------------------------- /models/listresult.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "math" 5 | "reflect" 6 | 7 | "gopkg.in/mgo.v2/bson" 8 | ) 9 | 10 | // ListResult is the structure used for listing collections. 11 | type ListResult struct { 12 | List interface{} `json:"list"` 13 | HasMore bool `json:"hasMore"` 14 | Total int `json:"total"` 15 | Count int `json:"count"` 16 | Page int `json:"page"` 17 | Pages int `json:"pages"` 18 | } 19 | 20 | func (b *Base) getItems(collection string, query bson.M, lp ListParams, lr *ListResult) (err error) { 21 | skip := lp.Limit * (lp.Page - 1) 22 | if lr.Total, err = b.db.C(collection).Find(query).Count(); err != nil { 23 | _, err = b.ShouldRefreshSession(err) 24 | return 25 | } 26 | lr.Page = lp.Page 27 | lr.Pages = int(math.Ceil(float64(lr.Total) / float64(lp.Limit))) 28 | if lr.Page > lr.Pages { 29 | lr.Page = lr.Pages 30 | } 31 | if skip < lr.Total { 32 | if err = b.db.C(collection).Find(query).Sort("-_id").Skip(skip).Limit(lp.Limit).All(lr.List); err != nil { 33 | return 34 | } 35 | lr.Count = reflect.ValueOf(lr.List).Elem().Len() 36 | lr.HasMore = lr.Total > lr.Count+skip 37 | } 38 | return 39 | } 40 | -------------------------------------------------------------------------------- /models/retry.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | var ( 11 | // ErrMaxAttemptsExceeded indicates that the maximum retries has been 12 | // excedeed. Usually to consider a service unreachable/unavailable. 13 | ErrMaxAttemptsExceeded = errors.New("maximum of attempts exceeded") 14 | ) 15 | 16 | func init() { 17 | rand.Seed(time.Now().UTC().UnixNano()) 18 | } 19 | 20 | type Retry struct { 21 | // Attempts is the current number of attempts we did. 22 | Attempts int `bson:"attempts" json:"attempts"` 23 | // MaxAttempts is the maximum number of attempts we will try. 24 | MaxAttempts int `bson:"max_attempts" json:"maxAttempts"` 25 | // Factor is factor to increase the duration between each attempts. 26 | Factor float64 `bson:"factor" json:"factor"` 27 | // Min is the minimum duration between each attempts in seconds. 28 | Min int `bson:"min" json:"min"` 29 | // Max is the maximum duration between each attempts in seconds. 30 | Max int `bson:"max" json:"max"` 31 | } 32 | 33 | func (r *Retry) NextAttempt(now int64) (int64, error) { 34 | if r.MaxAttempts > 0 && r.Attempts+1 >= r.MaxAttempts { 35 | return 0, ErrMaxAttemptsExceeded 36 | } 37 | 38 | var next float64 39 | var min = float64(r.Min) 40 | var max = float64(r.Max) 41 | d := min * math.Pow(r.Factor, float64(r.Attempts)) 42 | if d > max { 43 | next = max 44 | } else { 45 | next = d 46 | } 47 | // Randomize next run from 0% up to 20% of next interval. 48 | next += rand.Float64() * next / 5 49 | r.Attempts++ 50 | return now + int64(next*1000000000), nil 51 | } 52 | 53 | func (r *Retry) SetDefault() { 54 | if r.MaxAttempts == 0 { 55 | r.MaxAttempts = 10 56 | } 57 | if r.Factor == 0 { 58 | r.Factor = 2 59 | } 60 | if r.Min == 0 { 61 | r.Min = 10 62 | } 63 | if r.Max == 0 { 64 | r.Max = 300 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /restapi/auth.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | "log" 7 | "net/http" 8 | "strings" 9 | 10 | "github.com/ant0ine/go-json-rest/rest" 11 | ) 12 | 13 | // AuthBasicMiddleware provides a simple AuthBasic implementation. On failure, a 401 HTTP response 14 | //is returned. On success, the wrapped middleware is called, and the userId is made available as 15 | // request.Env["REMOTE_USER"].(string) 16 | type AuthBasicMiddleware struct { 17 | 18 | // Realm name to display to the user. Required. 19 | Realm string 20 | 21 | // Callback function that should perform the authentication of the user based on userId and 22 | // password. Must return true on success, false on failure. Required. 23 | Authenticator func(userId string, password string, request *rest.Request) bool 24 | 25 | // Callback function that should perform the authorization of the authenticated user. Called 26 | // only after an authentication success. Must return true on success, false on failure. 27 | // Optional, default to success. 28 | Authorizator func(userId string, request *rest.Request) bool 29 | } 30 | 31 | // MiddlewareFunc makes AuthBasicMiddleware implement the Middleware interface. 32 | func (mw *AuthBasicMiddleware) MiddlewareFunc(handler rest.HandlerFunc) rest.HandlerFunc { 33 | 34 | if mw.Realm == "" { 35 | log.Fatal("Realm is required") 36 | } 37 | 38 | if mw.Authenticator == nil { 39 | log.Fatal("Authenticator is required") 40 | } 41 | 42 | if mw.Authorizator == nil { 43 | mw.Authorizator = func(userId string, request *rest.Request) bool { 44 | return true 45 | } 46 | } 47 | 48 | return func(writer rest.ResponseWriter, request *rest.Request) { 49 | 50 | authHeader := request.Header.Get("Authorization") 51 | if authHeader == "" { 52 | mw.unauthorized(writer) 53 | return 54 | } 55 | 56 | providedUserID, providedPassword, err := mw.decodeBasicAuthHeader(authHeader) 57 | 58 | if err != nil { 59 | rest.Error(writer, "Invalid authentication", http.StatusBadRequest) 60 | return 61 | } 62 | 63 | if !mw.Authenticator(providedUserID, providedPassword, request) { 64 | mw.unauthorized(writer) 65 | return 66 | } 67 | 68 | if !mw.Authorizator(providedUserID, request) { 69 | mw.unauthorized(writer) 70 | return 71 | } 72 | 73 | request.Env["REMOTE_USER"] = providedUserID 74 | 75 | handler(writer, request) 76 | } 77 | } 78 | 79 | func (mw *AuthBasicMiddleware) unauthorized(writer rest.ResponseWriter) { 80 | writer.Header().Set("WWW-Authenticate", "Basic realm="+mw.Realm) 81 | rest.Error(writer, "Not Authorized", http.StatusUnauthorized) 82 | } 83 | 84 | func (mw *AuthBasicMiddleware) decodeBasicAuthHeader(header string) (user string, password string, err error) { 85 | 86 | parts := strings.SplitN(header, " ", 2) 87 | if !(len(parts) == 2 && parts[0] == "Basic") { 88 | return "", "", errors.New("Invalid authentication") 89 | } 90 | 91 | decoded, err := base64.StdEncoding.DecodeString(parts[1]) 92 | if err != nil { 93 | return "", "", errors.New("Invalid base64") 94 | } 95 | 96 | creds := strings.SplitN(string(decoded), ":", 2) 97 | if len(creds) != 2 { 98 | return "", "", errors.New("Invalid authentication") 99 | } 100 | 101 | return creds[0], creds[1], nil 102 | } 103 | -------------------------------------------------------------------------------- /restapi/status.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import ( 4 | "expvar" 5 | 6 | "github.com/ant0ine/go-json-rest/rest" 7 | ) 8 | 9 | func GetStatus(w rest.ResponseWriter, r *rest.Request) { 10 | // b := GetBase(r) 11 | status := make(map[string]string) 12 | status["status"] = "ok" 13 | status["attemptsError"] = expvar.Get("attemptsError").String() 14 | status["attemptsSuccess"] = expvar.Get("attemptsSuccess").String() 15 | w.WriteJson(status) 16 | } 17 | -------------------------------------------------------------------------------- /restapi/utils.go: -------------------------------------------------------------------------------- 1 | package restapi 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "time" 7 | 8 | "github.com/ant0ine/go-json-rest/rest" 9 | "github.com/sebest/hooky/models" 10 | ) 11 | 12 | // UnixToRFC3339 converts a Unix timestamp to a human readable format. 13 | func UnixToRFC3339(ts int64) string { 14 | if ts > 0 { 15 | return time.Unix(ts, 0).UTC().Format(time.RFC3339) 16 | } 17 | return "" 18 | } 19 | 20 | func parseListQuery(r *rest.Request) models.ListParams { 21 | q := r.URL.Query() 22 | l := models.ListParams{} 23 | // Filters 24 | items := strings.Split(q.Get("filters"), ",") 25 | l.Filters = make(map[string]string, len(items)) 26 | for _, item := range items { 27 | p := strings.SplitN(item, ":", 2) 28 | if len(p) == 2 { 29 | l.Filters[p[0]] = p[1] 30 | } 31 | } 32 | // Fields 33 | l.Fields = strings.Split(q.Get("fields"), ",") 34 | // Sort 35 | items = strings.Split(q.Get("sort"), ",") 36 | l.Sort = make(map[string]string, len(items)) 37 | for _, item := range items { 38 | p := strings.SplitN(item, ":", 1) 39 | if len(p) == 2 { 40 | l.Sort[p[0]] = p[1] 41 | } 42 | } 43 | // Page 44 | var err error 45 | l.Page, err = strconv.Atoi(q.Get("page")) 46 | if err != nil || l.Page < 1 { 47 | l.Page = 1 48 | } 49 | // Limit 50 | l.Limit, err = strconv.Atoi(q.Get("limit")) 51 | if err != nil || l.Limit > 100 { 52 | l.Limit = 100 53 | } 54 | return l 55 | } 56 | -------------------------------------------------------------------------------- /store/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/mgo.v2" 7 | ) 8 | 9 | type Store struct { 10 | session *mgo.Session 11 | } 12 | 13 | func New(url string) (*Store, error) { 14 | session, err := mgo.Dial(url) 15 | if err != nil { 16 | return nil, err 17 | } 18 | session.SetSocketTimeout(20 * time.Second) 19 | session.SetSafe(&mgo.Safe{}) 20 | return &Store{ 21 | session: session, 22 | }, nil 23 | } 24 | 25 | func (s *Store) DB() *mgo.Database { 26 | return s.session.Copy().DB("") 27 | } 28 | --------------------------------------------------------------------------------