From d6d889a7f29849fa0581ca1b4255ff6eb75c8736 Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Thu, 21 Jul 2022 16:29:35 +0200 Subject: [PATCH 01/17] Add go modules; add martini to project --- go.mod | 7 + go.sum | 4 + .../github.com/codegangsta/inject/.gitignore | 2 + vendor/github.com/codegangsta/inject/LICENSE | 20 + .../github.com/codegangsta/inject/README.md | 92 ++++ .../github.com/codegangsta/inject/inject.go | 187 ++++++++ .../codegangsta/inject/update_readme.sh | 3 + .../github.com/go-martini/martini/.gitignore | 56 +++ vendor/github.com/go-martini/martini/LICENSE | 20 + .../github.com/go-martini/martini/README.md | 400 +++++++++++++++++ vendor/github.com/go-martini/martini/env.go | 31 ++ .../go-martini/martini/go_version.go | 7 + .../github.com/go-martini/martini/logger.go | 29 ++ .../github.com/go-martini/martini/martini.go | 189 ++++++++ .../github.com/go-martini/martini/recovery.go | 144 ++++++ .../go-martini/martini/response_writer.go | 107 +++++ .../go-martini/martini/return_handler.go | 43 ++ .../github.com/go-martini/martini/router.go | 425 ++++++++++++++++++ .../github.com/go-martini/martini/static.go | 135 ++++++ .../github.com/go-martini/martini/wercker.yml | 1 + vendor/modules.txt | 6 + 21 files changed, 1908 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 vendor/github.com/codegangsta/inject/.gitignore create mode 100644 vendor/github.com/codegangsta/inject/LICENSE create mode 100644 vendor/github.com/codegangsta/inject/README.md create mode 100644 vendor/github.com/codegangsta/inject/inject.go create mode 100644 vendor/github.com/codegangsta/inject/update_readme.sh create mode 100644 vendor/github.com/go-martini/martini/.gitignore create mode 100644 vendor/github.com/go-martini/martini/LICENSE create mode 100644 vendor/github.com/go-martini/martini/README.md create mode 100644 vendor/github.com/go-martini/martini/env.go create mode 100644 vendor/github.com/go-martini/martini/go_version.go create mode 100644 vendor/github.com/go-martini/martini/logger.go create mode 100644 vendor/github.com/go-martini/martini/martini.go create mode 100644 vendor/github.com/go-martini/martini/recovery.go create mode 100644 vendor/github.com/go-martini/martini/response_writer.go create mode 100644 vendor/github.com/go-martini/martini/return_handler.go create mode 100644 vendor/github.com/go-martini/martini/router.go create mode 100644 vendor/github.com/go-martini/martini/static.go create mode 100644 vendor/github.com/go-martini/martini/wercker.yml create mode 100644 vendor/modules.txt diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cdcbee3 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module throttle + +go 1.18 + +require github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab + +require github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d9e117f --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= diff --git a/vendor/github.com/codegangsta/inject/.gitignore b/vendor/github.com/codegangsta/inject/.gitignore new file mode 100644 index 0000000..df3df8a --- /dev/null +++ b/vendor/github.com/codegangsta/inject/.gitignore @@ -0,0 +1,2 @@ +inject +inject.test diff --git a/vendor/github.com/codegangsta/inject/LICENSE b/vendor/github.com/codegangsta/inject/LICENSE new file mode 100644 index 0000000..eb68a0e --- /dev/null +++ b/vendor/github.com/codegangsta/inject/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Jeremy Saenz + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/codegangsta/inject/README.md b/vendor/github.com/codegangsta/inject/README.md new file mode 100644 index 0000000..679abe0 --- /dev/null +++ b/vendor/github.com/codegangsta/inject/README.md @@ -0,0 +1,92 @@ +# inject +-- + import "github.com/codegangsta/inject" + +Package inject provides utilities for mapping and injecting dependencies in +various ways. + +Language Translations: +* [简体中文](translations/README_zh_cn.md) + +## Usage + +#### func InterfaceOf + +```go +func InterfaceOf(value interface{}) reflect.Type +``` +InterfaceOf dereferences a pointer to an Interface type. It panics if value is +not an pointer to an interface. + +#### type Applicator + +```go +type Applicator interface { + // Maps dependencies in the Type map to each field in the struct + // that is tagged with 'inject'. Returns an error if the injection + // fails. + Apply(interface{}) error +} +``` + +Applicator represents an interface for mapping dependencies to a struct. + +#### type Injector + +```go +type Injector interface { + Applicator + Invoker + TypeMapper + // SetParent sets the parent of the injector. If the injector cannot find a + // dependency in its Type map it will check its parent before returning an + // error. + SetParent(Injector) +} +``` + +Injector represents an interface for mapping and injecting dependencies into +structs and function arguments. + +#### func New + +```go +func New() Injector +``` +New returns a new Injector. + +#### type Invoker + +```go +type Invoker interface { + // Invoke attempts to call the interface{} provided as a function, + // providing dependencies for function arguments based on Type. Returns + // a slice of reflect.Value representing the returned values of the function. + // Returns an error if the injection fails. + Invoke(interface{}) ([]reflect.Value, error) +} +``` + +Invoker represents an interface for calling functions via reflection. + +#### type TypeMapper + +```go +type TypeMapper interface { + // Maps the interface{} value based on its immediate type from reflect.TypeOf. + Map(interface{}) TypeMapper + // Maps the interface{} value based on the pointer of an Interface provided. + // This is really only useful for mapping a value as an interface, as interfaces + // cannot at this time be referenced directly without a pointer. + MapTo(interface{}, interface{}) TypeMapper + // Provides a possibility to directly insert a mapping based on type and value. + // This makes it possible to directly map type arguments not possible to instantiate + // with reflect like unidirectional channels. + Set(reflect.Type, reflect.Value) TypeMapper + // Returns the Value that is mapped to the current type. Returns a zeroed Value if + // the Type has not been mapped. + Get(reflect.Type) reflect.Value +} +``` + +TypeMapper represents an interface for mapping interface{} values based on type. diff --git a/vendor/github.com/codegangsta/inject/inject.go b/vendor/github.com/codegangsta/inject/inject.go new file mode 100644 index 0000000..3ff713c --- /dev/null +++ b/vendor/github.com/codegangsta/inject/inject.go @@ -0,0 +1,187 @@ +// Package inject provides utilities for mapping and injecting dependencies in various ways. +package inject + +import ( + "fmt" + "reflect" +) + +// Injector represents an interface for mapping and injecting dependencies into structs +// and function arguments. +type Injector interface { + Applicator + Invoker + TypeMapper + // SetParent sets the parent of the injector. If the injector cannot find a + // dependency in its Type map it will check its parent before returning an + // error. + SetParent(Injector) +} + +// Applicator represents an interface for mapping dependencies to a struct. +type Applicator interface { + // Maps dependencies in the Type map to each field in the struct + // that is tagged with 'inject'. Returns an error if the injection + // fails. + Apply(interface{}) error +} + +// Invoker represents an interface for calling functions via reflection. +type Invoker interface { + // Invoke attempts to call the interface{} provided as a function, + // providing dependencies for function arguments based on Type. Returns + // a slice of reflect.Value representing the returned values of the function. + // Returns an error if the injection fails. + Invoke(interface{}) ([]reflect.Value, error) +} + +// TypeMapper represents an interface for mapping interface{} values based on type. +type TypeMapper interface { + // Maps the interface{} value based on its immediate type from reflect.TypeOf. + Map(interface{}) TypeMapper + // Maps the interface{} value based on the pointer of an Interface provided. + // This is really only useful for mapping a value as an interface, as interfaces + // cannot at this time be referenced directly without a pointer. + MapTo(interface{}, interface{}) TypeMapper + // Provides a possibility to directly insert a mapping based on type and value. + // This makes it possible to directly map type arguments not possible to instantiate + // with reflect like unidirectional channels. + Set(reflect.Type, reflect.Value) TypeMapper + // Returns the Value that is mapped to the current type. Returns a zeroed Value if + // the Type has not been mapped. + Get(reflect.Type) reflect.Value +} + +type injector struct { + values map[reflect.Type]reflect.Value + parent Injector +} + +// InterfaceOf dereferences a pointer to an Interface type. +// It panics if value is not an pointer to an interface. +func InterfaceOf(value interface{}) reflect.Type { + t := reflect.TypeOf(value) + + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + + if t.Kind() != reflect.Interface { + panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)") + } + + return t +} + +// New returns a new Injector. +func New() Injector { + return &injector{ + values: make(map[reflect.Type]reflect.Value), + } +} + +// Invoke attempts to call the interface{} provided as a function, +// providing dependencies for function arguments based on Type. +// Returns a slice of reflect.Value representing the returned values of the function. +// Returns an error if the injection fails. +// It panics if f is not a function +func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { + t := reflect.TypeOf(f) + + var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func + for i := 0; i < t.NumIn(); i++ { + argType := t.In(i) + val := inj.Get(argType) + if !val.IsValid() { + return nil, fmt.Errorf("Value not found for type %v", argType) + } + + in[i] = val + } + + return reflect.ValueOf(f).Call(in), nil +} + +// Maps dependencies in the Type map to each field in the struct +// that is tagged with 'inject'. +// Returns an error if the injection fails. +func (inj *injector) Apply(val interface{}) error { + v := reflect.ValueOf(val) + + for v.Kind() == reflect.Ptr { + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + return nil // Should not panic here ? + } + + t := v.Type() + + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + structField := t.Field(i) + if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") { + ft := f.Type() + v := inj.Get(ft) + if !v.IsValid() { + return fmt.Errorf("Value not found for type %v", ft) + } + + f.Set(v) + } + + } + + return nil +} + +// Maps the concrete value of val to its dynamic type using reflect.TypeOf, +// It returns the TypeMapper registered in. +func (i *injector) Map(val interface{}) TypeMapper { + i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) + return i +} + +func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { + i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) + return i +} + +// Maps the given reflect.Type to the given reflect.Value and returns +// the Typemapper the mapping has been registered in. +func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { + i.values[typ] = val + return i +} + +func (i *injector) Get(t reflect.Type) reflect.Value { + val := i.values[t] + + if val.IsValid() { + return val + } + + // no concrete types found, try to find implementors + // if t is an interface + if t.Kind() == reflect.Interface { + for k, v := range i.values { + if k.Implements(t) { + val = v + break + } + } + } + + // Still no type found, try to look it up on the parent + if !val.IsValid() && i.parent != nil { + val = i.parent.Get(t) + } + + return val + +} + +func (i *injector) SetParent(parent Injector) { + i.parent = parent +} diff --git a/vendor/github.com/codegangsta/inject/update_readme.sh b/vendor/github.com/codegangsta/inject/update_readme.sh new file mode 100644 index 0000000..497f9a5 --- /dev/null +++ b/vendor/github.com/codegangsta/inject/update_readme.sh @@ -0,0 +1,3 @@ +#!/bin/bash +go get github.com/robertkrimen/godocdown/godocdown +godocdown > README.md diff --git a/vendor/github.com/go-martini/martini/.gitignore b/vendor/github.com/go-martini/martini/.gitignore new file mode 100644 index 0000000..fb6d4ac --- /dev/null +++ b/vendor/github.com/go-martini/martini/.gitignore @@ -0,0 +1,56 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test + +/.godeps +/.envrc + +# Godeps +Godeps/_workspace +Godeps/Readme + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +.idea/ + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/vendor/github.com/go-martini/martini/LICENSE b/vendor/github.com/go-martini/martini/LICENSE new file mode 100644 index 0000000..d3fefb8 --- /dev/null +++ b/vendor/github.com/go-martini/martini/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jeremy Saenz + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/go-martini/martini/README.md b/vendor/github.com/go-martini/martini/README.md new file mode 100644 index 0000000..d7adb8f --- /dev/null +++ b/vendor/github.com/go-martini/martini/README.md @@ -0,0 +1,400 @@ +# Martini [![wercker status](https://app.wercker.com/status/9b7dbc6e2654b604cd694d191c3d5487/s/master "wercker status")](https://app.wercker.com/project/bykey/9b7dbc6e2654b604cd694d191c3d5487)[![GoDoc](https://godoc.org/github.com/go-martini/martini?status.png)](http://godoc.org/github.com/go-martini/martini) + +### **NOTE:** The martini framework is no longer maintained. + +Martini is a powerful package for quickly writing modular web applications/services in Golang. + +Language Translations: +* [繁體中文](translations/README_zh_tw.md) +* [简体中文](translations/README_zh_cn.md) +* [Português Brasileiro (pt_BR)](translations/README_pt_br.md) +* [Español](translations/README_es_ES.md) +* [한국어 번역](translations/README_ko_kr.md) +* [Русский](translations/README_ru_RU.md) +* [日本語](translations/README_ja_JP.md) +* [French](translations/README_fr_FR.md) +* [Turkish](translations/README_tr_TR.md) +* [German](translations/README_de_DE.md) +* [Polski](translations/README_pl_PL.md) + +## Getting Started + +After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`. + +~~~ go +package main + +import "github.com/go-martini/martini" + +func main() { + m := martini.Classic() + m.Get("/", func() string { + return "Hello world!" + }) + m.Run() +} +~~~ + +Then install the Martini package (**go 1.1** or greater is required): +~~~ +go get github.com/go-martini/martini +~~~ + +Then run your server: +~~~ +go run server.go +~~~ + +You will now have a Martini webserver running on `localhost:3000`. + +## Getting Help + +Join the [Mailing list](https://groups.google.com/forum/#!forum/martini-go) + +Watch the [Demo Video](http://martini.codegangsta.io/#demo) + +Ask questions on Stackoverflow using the [martini tag](http://stackoverflow.com/questions/tagged/martini) + +GoDoc [documentation](http://godoc.org/github.com/go-martini/martini) + + +## Features +* Extremely simple to use. +* Non-intrusive design. +* Plays nice with other Golang packages. +* Awesome path matching and routing. +* Modular design - Easy to add functionality, easy to rip stuff out. +* Lots of good handlers/middlewares to use. +* Great 'out of the box' feature set. +* **Fully compatible with the [http.HandlerFunc](http://godoc.org/net/http#HandlerFunc) interface.** +* Default document serving (e.g., for serving AngularJS apps in HTML5 mode). + +## More Middleware +For more middleware and functionality, check out the repositories in the [martini-contrib](https://github.com/martini-contrib) organization. + +## Table of Contents +* [Classic Martini](#classic-martini) + * [Handlers](#handlers) + * [Routing](#routing) + * [Services](#services) + * [Serving Static Files](#serving-static-files) +* [Middleware Handlers](#middleware-handlers) + * [Next()](#next) +* [Martini Env](#martini-env) +* [FAQ](#faq) + +## Classic Martini +To get up and running quickly, [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic) provides some reasonable defaults that work well for most web applications: +~~~ go + m := martini.Classic() + // ... middleware and routing goes here + m.Run() +~~~ + +Below is some of the functionality [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic) pulls in automatically: + * Request/Response Logging - [martini.Logger](http://godoc.org/github.com/go-martini/martini#Logger) + * Panic Recovery - [martini.Recovery](http://godoc.org/github.com/go-martini/martini#Recovery) + * Static File serving - [martini.Static](http://godoc.org/github.com/go-martini/martini#Static) + * Routing - [martini.Router](http://godoc.org/github.com/go-martini/martini#Router) + +### Handlers +Handlers are the heart and soul of Martini. A handler is basically any kind of callable function: +~~~ go +m.Get("/", func() { + println("hello world") +}) +~~~ + +#### Return Values +If a handler returns something, Martini will write the result to the current [http.ResponseWriter](http://godoc.org/net/http#ResponseWriter) as a string: +~~~ go +m.Get("/", func() string { + return "hello world" // HTTP 200 : "hello world" +}) +~~~ + +You can also optionally return a status code: +~~~ go +m.Get("/", func() (int, string) { + return 418, "i'm a teapot" // HTTP 418 : "i'm a teapot" +}) +~~~ + +#### Service Injection +Handlers are invoked via reflection. Martini makes use of *Dependency Injection* to resolve dependencies in a Handlers argument list. **This makes Martini completely compatible with golang's `http.HandlerFunc` interface.** + +If you add an argument to your Handler, Martini will search its list of services and attempt to resolve the dependency via type assertion: +~~~ go +m.Get("/", func(res http.ResponseWriter, req *http.Request) { // res and req are injected by Martini + res.WriteHeader(200) // HTTP 200 +}) +~~~ + +The following services are included with [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic): + * [*log.Logger](http://godoc.org/log#Logger) - Global logger for Martini. + * [martini.Context](http://godoc.org/github.com/go-martini/martini#Context) - http request context. + * [martini.Params](http://godoc.org/github.com/go-martini/martini#Params) - `map[string]string` of named params found by route matching. + * [martini.Routes](http://godoc.org/github.com/go-martini/martini#Routes) - Route helper service. + * [martini.Route](http://godoc.org/github.com/go-martini/martini#Route) - Current active route. + * [http.ResponseWriter](http://godoc.org/net/http/#ResponseWriter) - http Response writer interface. + * [*http.Request](http://godoc.org/net/http/#Request) - http Request. + +### Routing +In Martini, a route is an HTTP method paired with a URL-matching pattern. +Each route can take one or more handler methods: +~~~ go +m.Get("/", func() { + // show something +}) + +m.Patch("/", func() { + // update something +}) + +m.Post("/", func() { + // create something +}) + +m.Put("/", func() { + // replace something +}) + +m.Delete("/", func() { + // destroy something +}) + +m.Options("/", func() { + // http options +}) + +m.NotFound(func() { + // handle 404 +}) +~~~ + +Routes are matched in the order they are defined. The first route that +matches the request is invoked. + +Route patterns may include named parameters, accessible via the [martini.Params](http://godoc.org/github.com/go-martini/martini#Params) service: +~~~ go +m.Get("/hello/:name", func(params martini.Params) string { + return "Hello " + params["name"] +}) +~~~ + +Routes can be matched with globs: +~~~ go +m.Get("/hello/**", func(params martini.Params) string { + return "Hello " + params["_1"] +}) +~~~ + +Regular expressions can be used as well: +~~~go +m.Get("/hello/(?P[a-zA-Z]+)", func(params martini.Params) string { + return fmt.Sprintf ("Hello %s", params["name"]) +}) +~~~ +Take a look at the [Go documentation](http://golang.org/pkg/regexp/syntax/) for more info about regular expressions syntax . + +Route handlers can be stacked on top of each other, which is useful for things like authentication and authorization: +~~~ go +m.Get("/secret", authorize, func() { + // this will execute as long as authorize doesn't write a response +}) +~~~ + +Route groups can be added too using the Group method. +~~~ go +m.Group("/books", func(r martini.Router) { + r.Get("/:id", GetBooks) + r.Post("/new", NewBook) + r.Put("/update/:id", UpdateBook) + r.Delete("/delete/:id", DeleteBook) +}) +~~~ + +Just like you can pass middlewares to a handler you can pass middlewares to groups. +~~~ go +m.Group("/books", func(r martini.Router) { + r.Get("/:id", GetBooks) + r.Post("/new", NewBook) + r.Put("/update/:id", UpdateBook) + r.Delete("/delete/:id", DeleteBook) +}, MyMiddleware1, MyMiddleware2) +~~~ + +### Services +Services are objects that are available to be injected into a Handler's argument list. You can map a service on a *Global* or *Request* level. + +#### Global Mapping +A Martini instance implements the inject.Injector interface, so mapping a service is easy: +~~~ go +db := &MyDatabase{} +m := martini.Classic() +m.Map(db) // the service will be available to all handlers as *MyDatabase +// ... +m.Run() +~~~ + +#### Request-Level Mapping +Mapping on the request level can be done in a handler via [martini.Context](http://godoc.org/github.com/go-martini/martini#Context): +~~~ go +func MyCustomLoggerHandler(c martini.Context, req *http.Request) { + logger := &MyCustomLogger{req} + c.Map(logger) // mapped as *MyCustomLogger +} +~~~ + +#### Mapping values to Interfaces +One of the most powerful parts about services is the ability to map a service to an interface. For instance, if you wanted to override the [http.ResponseWriter](http://godoc.org/net/http#ResponseWriter) with an object that wrapped it and performed extra operations, you can write the following handler: +~~~ go +func WrapResponseWriter(res http.ResponseWriter, c martini.Context) { + rw := NewSpecialResponseWriter(res) + c.MapTo(rw, (*http.ResponseWriter)(nil)) // override ResponseWriter with our wrapper ResponseWriter +} +~~~ + +### Serving Static Files +A [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic) instance automatically serves static files from the "public" directory in the root of your server. +You can serve from more directories by adding more [martini.Static](http://godoc.org/github.com/go-martini/martini#Static) handlers. +~~~ go +m.Use(martini.Static("assets")) // serve from the "assets" directory as well +~~~ + +#### Serving a Default Document +You can specify the URL of a local file to serve when the requested URL is not +found. You can also specify an exclusion prefix so that certain URLs are ignored. +This is useful for servers that serve both static files and have additional +handlers defined (e.g., REST API). When doing so, it's useful to define the +static handler as a part of the NotFound chain. + +The following example serves the `/index.html` file whenever any URL is +requested that does not match any local file and does not start with `/api/v`: +~~~ go +static := martini.Static("assets", martini.StaticOptions{Fallback: "/index.html", Exclude: "/api/v"}) +m.NotFound(static, http.NotFound) +~~~ + +## Middleware Handlers +Middleware Handlers sit between the incoming http request and the router. In essence they are no different than any other Handler in Martini. You can add a middleware handler to the stack like so: +~~~ go +m.Use(func() { + // do some middleware stuff +}) +~~~ + +You can have full control over the middleware stack with the `Handlers` function. This will replace any handlers that have been previously set: +~~~ go +m.Handlers( + Middleware1, + Middleware2, + Middleware3, +) +~~~ + +Middleware Handlers work really well for things like logging, authorization, authentication, sessions, gzipping, error pages and any other operations that must happen before or after an http request: +~~~ go +// validate an api key +m.Use(func(res http.ResponseWriter, req *http.Request) { + if req.Header.Get("X-API-KEY") != "secret123" { + res.WriteHeader(http.StatusUnauthorized) + } +}) +~~~ + +### Next() +[Context.Next()](http://godoc.org/github.com/go-martini/martini#Context) is an optional function that Middleware Handlers can call to yield the until after the other Handlers have been executed. This works really well for any operations that must happen after an http request: +~~~ go +// log before and after a request +m.Use(func(c martini.Context, log *log.Logger){ + log.Println("before a request") + + c.Next() + + log.Println("after a request") +}) +~~~ + +## Martini Env + +Some Martini handlers make use of the `martini.Env` global variable to provide special functionality for development environments vs production environments. It is recommended that the `MARTINI_ENV=production` environment variable to be set when deploying a Martini server into a production environment. + +## FAQ + +### Where do I find middleware X? + +Start by looking in the [martini-contrib](https://github.com/martini-contrib) projects. If it is not there feel free to contact a martini-contrib team member about adding a new repo to the organization. + +* [acceptlang](https://github.com/martini-contrib/acceptlang) - Handler for parsing the `Accept-Language` HTTP header. +* [accessflags](https://github.com/martini-contrib/accessflags) - Handler to enable Access Control. +* [auth](https://github.com/martini-contrib/auth) - Handlers for authentication. +* [binding](https://github.com/martini-contrib/binding) - Handler for mapping/validating a raw request into a structure. +* [cors](https://github.com/martini-contrib/cors) - Handler that enables CORS support. +* [csrf](https://github.com/martini-contrib/csrf) - CSRF protection for applications +* [encoder](https://github.com/martini-contrib/encoder) - Encoder service for rendering data in several formats and content negotiation. +* [gzip](https://github.com/martini-contrib/gzip) - Handler for adding gzip compress to requests +* [gorelic](https://github.com/martini-contrib/gorelic) - NewRelic middleware +* [logstasher](https://github.com/martini-contrib/logstasher) - Middleware that prints logstash-compatible JSON +* [method](https://github.com/martini-contrib/method) - HTTP method overriding via Header or form fields. +* [oauth2](https://github.com/martini-contrib/oauth2) - Handler that provides OAuth 2.0 login for Martini apps. Google Sign-in, Facebook Connect and Github login is supported. +* [permissions2](https://github.com/xyproto/permissions2) - Handler for keeping track of users, login states and permissions. +* [render](https://github.com/martini-contrib/render) - Handler that provides a service for easily rendering JSON and HTML templates. +* [secure](https://github.com/martini-contrib/secure) - Implements a few quick security wins. +* [sessions](https://github.com/martini-contrib/sessions) - Handler that provides a Session service. +* [sessionauth](https://github.com/martini-contrib/sessionauth) - Handler that provides a simple way to make routes require a login, and to handle user logins in the session +* [strict](https://github.com/martini-contrib/strict) - Strict Mode +* [strip](https://github.com/martini-contrib/strip) - URL Prefix stripping. +* [staticbin](https://github.com/martini-contrib/staticbin) - Handler for serving static files from binary data +* [throttle](https://github.com/martini-contrib/throttle) - Request rate throttling middleware. +* [vauth](https://github.com/rafecolton/vauth) - Handlers for vender webhook authentication (currently GitHub and TravisCI) +* [web](https://github.com/martini-contrib/web) - hoisie web.go's Context + +### How do I integrate with existing servers? + +A Martini instance implements `http.Handler`, so it can easily be used to serve subtrees +on existing Go servers. For example this is a working Martini app for Google App Engine: + +~~~ go +package hello + +import ( + "net/http" + "github.com/go-martini/martini" +) + +func init() { + m := martini.Classic() + m.Get("/", func() string { + return "Hello world!" + }) + http.Handle("/", m) +} +~~~ + +### How do I change the port/host? + +Martini's `Run` function looks for the PORT and HOST environment variables and uses those. Otherwise Martini will default to localhost:3000. +To have more flexibility over port and host, use the `martini.RunOnAddr` function instead. + +~~~ go + m := martini.Classic() + // ... + m.RunOnAddr(":8080") +~~~ + +### Live code reload? + +[gin](https://github.com/codegangsta/gin) and [fresh](https://github.com/pilu/fresh) both live reload martini apps. + +## Contributing +Martini is meant to be kept tiny and clean. Most contributions should end up in a repository in the [martini-contrib](https://github.com/martini-contrib) organization. If you do have a contribution for the core of Martini feel free to put up a Pull Request. + +## License +Martini is distributed by The MIT License, see LICENSE + +## About + +Inspired by [express](https://github.com/visionmedia/express) and [sinatra](https://github.com/sinatra/sinatra) + +Martini is obsessively designed by none other than the [Code Gangsta](http://codegangsta.io/) diff --git a/vendor/github.com/go-martini/martini/env.go b/vendor/github.com/go-martini/martini/env.go new file mode 100644 index 0000000..54d5857 --- /dev/null +++ b/vendor/github.com/go-martini/martini/env.go @@ -0,0 +1,31 @@ +package martini + +import ( + "os" +) + +// Envs +const ( + Dev string = "development" + Prod string = "production" + Test string = "test" +) + +// Env is the environment that Martini is executing in. The MARTINI_ENV is read on initialization to set this variable. +var Env = Dev +var Root string + +func setENV(e string) { + if len(e) > 0 { + Env = e + } +} + +func init() { + setENV(os.Getenv("MARTINI_ENV")) + var err error + Root, err = os.Getwd() + if err != nil { + panic(err) + } +} diff --git a/vendor/github.com/go-martini/martini/go_version.go b/vendor/github.com/go-martini/martini/go_version.go new file mode 100644 index 0000000..bd271a8 --- /dev/null +++ b/vendor/github.com/go-martini/martini/go_version.go @@ -0,0 +1,7 @@ +// +build !go1.1 + +package martini + +func MartiniDoesNotSupportGo1Point0() { + "Martini requires Go 1.1 or greater." +} diff --git a/vendor/github.com/go-martini/martini/logger.go b/vendor/github.com/go-martini/martini/logger.go new file mode 100644 index 0000000..d01107c --- /dev/null +++ b/vendor/github.com/go-martini/martini/logger.go @@ -0,0 +1,29 @@ +package martini + +import ( + "log" + "net/http" + "time" +) + +// Logger returns a middleware handler that logs the request as it goes in and the response as it goes out. +func Logger() Handler { + return func(res http.ResponseWriter, req *http.Request, c Context, log *log.Logger) { + start := time.Now() + + addr := req.Header.Get("X-Real-IP") + if addr == "" { + addr = req.Header.Get("X-Forwarded-For") + if addr == "" { + addr = req.RemoteAddr + } + } + + log.Printf("Started %s %s for %s", req.Method, req.URL.Path, addr) + + rw := res.(ResponseWriter) + c.Next() + + log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start)) + } +} diff --git a/vendor/github.com/go-martini/martini/martini.go b/vendor/github.com/go-martini/martini/martini.go new file mode 100644 index 0000000..0ce4f3d --- /dev/null +++ b/vendor/github.com/go-martini/martini/martini.go @@ -0,0 +1,189 @@ +// Package martini is a powerful package for quickly writing modular web applications/services in Golang. +// +// For a full guide visit http://github.com/go-martini/martini +// +// package main +// +// import "github.com/go-martini/martini" +// +// func main() { +// m := martini.Classic() +// +// m.Get("/", func() string { +// return "Hello world!" +// }) +// +// m.Run() +// } +package martini + +import ( + "log" + "net/http" + "os" + "reflect" + + "github.com/codegangsta/inject" +) + +// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level. +type Martini struct { + inject.Injector + handlers []Handler + action Handler + logger *log.Logger +} + +// New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used. +func New() *Martini { + m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)} + m.Map(m.logger) + m.Map(defaultReturnHandler()) + return m +} + +// Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers. +// Will panic if any of the handlers is not a callable function +func (m *Martini) Handlers(handlers ...Handler) { + m.handlers = make([]Handler, 0) + for _, handler := range handlers { + m.Use(handler) + } +} + +// Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic(). +func (m *Martini) Action(handler Handler) { + validateHandler(handler) + m.action = handler +} + +// Logger sets the logger +func (m *Martini) Logger(logger *log.Logger) { + m.logger = logger + m.Map(m.logger) +} + +// Use adds a middleware Handler to the stack. Will panic if the handler is not a callable func. Middleware Handlers are invoked in the order that they are added. +func (m *Martini) Use(handler Handler) { + validateHandler(handler) + + m.handlers = append(m.handlers, handler) +} + +// ServeHTTP is the HTTP Entry point for a Martini instance. Useful if you want to control your own HTTP server. +func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) { + m.createContext(res, req).run() +} + +// Run the http server on a given host and port. +func (m *Martini) RunOnAddr(addr string) { + // TODO: Should probably be implemented using a new instance of http.Server in place of + // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use. + // This would also allow to improve testing when a custom host and port are passed. + + logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger) + logger.Printf("listening on %s (%s)\n", addr, Env) + logger.Fatalln(http.ListenAndServe(addr, m)) +} + +// Run the http server. Listening on os.GetEnv("PORT") or 3000 by default. +func (m *Martini) Run() { + port := os.Getenv("PORT") + if len(port) == 0 { + port = "3000" + } + + host := os.Getenv("HOST") + + m.RunOnAddr(host + ":" + port) +} + +func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context { + c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0} + c.SetParent(m) + c.MapTo(c, (*Context)(nil)) + c.MapTo(c.rw, (*http.ResponseWriter)(nil)) + c.Map(req) + return c +} + +// ClassicMartini represents a Martini with some reasonable defaults. Embeds the router functions for convenience. +type ClassicMartini struct { + *Martini + Router +} + +// Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery and martini.Static. +// Classic also maps martini.Routes as a service. +func Classic() *ClassicMartini { + r := NewRouter() + m := New() + m.Use(Logger()) + m.Use(Recovery()) + m.Use(Static("public")) + m.MapTo(r, (*Routes)(nil)) + m.Action(r.Handle) + return &ClassicMartini{m, r} +} + +// Handler can be any callable function. Martini attempts to inject services into the handler's argument list. +// Martini will panic if an argument could not be fullfilled via dependency injection. +type Handler interface{} + +func validateHandler(handler Handler) { + if reflect.TypeOf(handler).Kind() != reflect.Func { + panic("martini handler must be a callable func") + } +} + +// Context represents a request context. Services can be mapped on the request level from this interface. +type Context interface { + inject.Injector + // Next is an optional function that Middleware Handlers can call to yield the until after + // the other Handlers have been executed. This works really well for any operations that must + // happen after an http request + Next() + // Written returns whether or not the response for this context has been written. + Written() bool +} + +type context struct { + inject.Injector + handlers []Handler + action Handler + rw ResponseWriter + index int +} + +func (c *context) handler() Handler { + if c.index < len(c.handlers) { + return c.handlers[c.index] + } + if c.index == len(c.handlers) { + return c.action + } + panic("invalid index for context handler") +} + +func (c *context) Next() { + c.index += 1 + c.run() +} + +func (c *context) Written() bool { + return c.rw.Written() +} + +func (c *context) run() { + for c.index <= len(c.handlers) { + _, err := c.Invoke(c.handler()) + if err != nil { + panic(err) + } + c.index += 1 + + if c.Written() { + return + } + } +} diff --git a/vendor/github.com/go-martini/martini/recovery.go b/vendor/github.com/go-martini/martini/recovery.go new file mode 100644 index 0000000..fe0d918 --- /dev/null +++ b/vendor/github.com/go-martini/martini/recovery.go @@ -0,0 +1,144 @@ +package martini + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "net/http" + "runtime" + + "github.com/codegangsta/inject" +) + +const ( + panicHtml = ` +PANIC: %s + + +

PANIC

+
%s
+
%s
+ +` +) + +var ( + dunno = []byte("???") + centerDot = []byte("·") + dot = []byte(".") + slash = []byte("/") +) + +// stack returns a nicely formated stack frame, skipping skip frames +func stack(skip int) []byte { + buf := new(bytes.Buffer) // the returned data + // As we loop, we open files and read them. These variables record the currently + // loaded file. + var lines [][]byte + var lastFile string + for i := skip; ; i++ { // Skip the expected number of frames + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + // Print this much at least. If we can't find the source, it won't show. + fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) + if file != lastFile { + data, err := ioutil.ReadFile(file) + if err != nil { + continue + } + lines = bytes.Split(data, []byte{'\n'}) + lastFile = file + } + fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) + } + return buf.Bytes() +} + +// source returns a space-trimmed slice of the n'th line. +func source(lines [][]byte, n int) []byte { + n-- // in stack trace, lines are 1-indexed but our array is 0-indexed + if n < 0 || n >= len(lines) { + return dunno + } + return bytes.TrimSpace(lines[n]) +} + +// function returns, if possible, the name of the function containing the PC. +func function(pc uintptr) []byte { + fn := runtime.FuncForPC(pc) + if fn == nil { + return dunno + } + name := []byte(fn.Name()) + // The name includes the path name to the package, which is unnecessary + // since the file name is already included. Plus, it has center dots. + // That is, we see + // runtime/debug.*T·ptrmethod + // and want + // *T.ptrmethod + // Also the package path might contains dot (e.g. code.google.com/...), + // so first eliminate the path prefix + if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { + name = name[lastslash+1:] + } + if period := bytes.Index(name, dot); period >= 0 { + name = name[period+1:] + } + name = bytes.Replace(name, centerDot, dot, -1) + return name +} + +// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. +// While Martini is in development mode, Recovery will also output the panic as HTML. +func Recovery() Handler { + return func(c Context, log *log.Logger) { + defer func() { + if err := recover(); err != nil { + stack := stack(3) + log.Printf("PANIC: %s\n%s", err, stack) + + // Lookup the current responsewriter + val := c.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) + res := val.Interface().(http.ResponseWriter) + + // respond with panic message while in development mode + var body []byte + if Env == Dev { + res.Header().Set("Content-Type", "text/html") + body = []byte(fmt.Sprintf(panicHtml, err, err, stack)) + } else { + body = []byte("500 Internal Server Error") + } + + res.WriteHeader(http.StatusInternalServerError) + if nil != body { + res.Write(body) + } + } + }() + + c.Next() + } +} diff --git a/vendor/github.com/go-martini/martini/response_writer.go b/vendor/github.com/go-martini/martini/response_writer.go new file mode 100644 index 0000000..12574b1 --- /dev/null +++ b/vendor/github.com/go-martini/martini/response_writer.go @@ -0,0 +1,107 @@ +package martini + +import ( + "bufio" + "fmt" + "net" + "net/http" +) + +// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about +// the response. It is recommended that middleware handlers use this construct to wrap a responsewriter +// if the functionality calls for it. +type ResponseWriter interface { + http.ResponseWriter + http.Flusher + http.Hijacker + // Status returns the status code of the response or 0 if the response has not been written. + Status() int + // Written returns whether or not the ResponseWriter has been written. + Written() bool + // Size returns the size of the response body. + Size() int + // Before allows for a function to be called before the ResponseWriter has been written to. This is + // useful for setting headers or any other operations that must happen before a response has been written. + Before(BeforeFunc) +} + +// BeforeFunc is a function that is called before the ResponseWriter has been written to. +type BeforeFunc func(ResponseWriter) + +// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter +func NewResponseWriter(rw http.ResponseWriter) ResponseWriter { + newRw := responseWriter{rw, 0, 0, nil} + if cn, ok := rw.(http.CloseNotifier); ok { + return &closeNotifyResponseWriter{newRw, cn} + } + return &newRw +} + +type responseWriter struct { + http.ResponseWriter + status int + size int + beforeFuncs []BeforeFunc +} + +func (rw *responseWriter) WriteHeader(s int) { + rw.callBefore() + rw.ResponseWriter.WriteHeader(s) + rw.status = s +} + +func (rw *responseWriter) Write(b []byte) (int, error) { + if !rw.Written() { + // The status will be StatusOK if WriteHeader has not been called yet + rw.WriteHeader(http.StatusOK) + } + size, err := rw.ResponseWriter.Write(b) + rw.size += size + return size, err +} + +func (rw *responseWriter) Status() int { + return rw.status +} + +func (rw *responseWriter) Size() int { + return rw.size +} + +func (rw *responseWriter) Written() bool { + return rw.status != 0 +} + +func (rw *responseWriter) Before(before BeforeFunc) { + rw.beforeFuncs = append(rw.beforeFuncs, before) +} + +func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hijacker, ok := rw.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") + } + return hijacker.Hijack() +} + +func (rw *responseWriter) callBefore() { + for i := len(rw.beforeFuncs) - 1; i >= 0; i-- { + rw.beforeFuncs[i](rw) + } +} + +func (rw *responseWriter) Flush() { + flusher, ok := rw.ResponseWriter.(http.Flusher) + if ok { + flusher.Flush() + } +} + +type closeNotifyResponseWriter struct { + responseWriter + closeNotifier http.CloseNotifier +} + +func (rw *closeNotifyResponseWriter) CloseNotify() <-chan bool { + return rw.closeNotifier.CloseNotify() +} diff --git a/vendor/github.com/go-martini/martini/return_handler.go b/vendor/github.com/go-martini/martini/return_handler.go new file mode 100644 index 0000000..4ea8f34 --- /dev/null +++ b/vendor/github.com/go-martini/martini/return_handler.go @@ -0,0 +1,43 @@ +package martini + +import ( + "github.com/codegangsta/inject" + "net/http" + "reflect" +) + +// ReturnHandler is a service that Martini provides that is called +// when a route handler returns something. The ReturnHandler is +// responsible for writing to the ResponseWriter based on the values +// that are passed into this function. +type ReturnHandler func(Context, []reflect.Value) + +func defaultReturnHandler() ReturnHandler { + return func(ctx Context, vals []reflect.Value) { + rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) + res := rv.Interface().(http.ResponseWriter) + var responseVal reflect.Value + if len(vals) > 1 && vals[0].Kind() == reflect.Int { + res.WriteHeader(int(vals[0].Int())) + responseVal = vals[1] + } else if len(vals) > 0 { + responseVal = vals[0] + } + if canDeref(responseVal) { + responseVal = responseVal.Elem() + } + if isByteSlice(responseVal) { + res.Write(responseVal.Bytes()) + } else { + res.Write([]byte(responseVal.String())) + } + } +} + +func isByteSlice(val reflect.Value) bool { + return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 +} + +func canDeref(val reflect.Value) bool { + return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr +} diff --git a/vendor/github.com/go-martini/martini/router.go b/vendor/github.com/go-martini/martini/router.go new file mode 100644 index 0000000..3abbabb --- /dev/null +++ b/vendor/github.com/go-martini/martini/router.go @@ -0,0 +1,425 @@ +package martini + +import ( + "fmt" + "net/http" + "reflect" + "regexp" + "strconv" + "sync" +) + +// Params is a map of name/value pairs for named routes. An instance of martini.Params is available to be injected into any route handler. +type Params map[string]string + +// Router is Martini's de-facto routing interface. Supports HTTP verbs, stacked handlers, and dependency injection. +type Router interface { + Routes + + // Group adds a group where related routes can be added. + Group(string, func(Router), ...Handler) + // Get adds a route for a HTTP GET request to the specified matching pattern. + Get(string, ...Handler) Route + // Patch adds a route for a HTTP PATCH request to the specified matching pattern. + Patch(string, ...Handler) Route + // Post adds a route for a HTTP POST request to the specified matching pattern. + Post(string, ...Handler) Route + // Put adds a route for a HTTP PUT request to the specified matching pattern. + Put(string, ...Handler) Route + // Delete adds a route for a HTTP DELETE request to the specified matching pattern. + Delete(string, ...Handler) Route + // Options adds a route for a HTTP OPTIONS request to the specified matching pattern. + Options(string, ...Handler) Route + // Head adds a route for a HTTP HEAD request to the specified matching pattern. + Head(string, ...Handler) Route + // Any adds a route for any HTTP method request to the specified matching pattern. + Any(string, ...Handler) Route + // AddRoute adds a route for a given HTTP method request to the specified matching pattern. + AddRoute(string, string, ...Handler) Route + + // NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default. + NotFound(...Handler) + + // Handle is the entry point for routing. This is used as a martini.Handler + Handle(http.ResponseWriter, *http.Request, Context) +} + +type router struct { + routes []*route + notFounds []Handler + groups []group + routesLock sync.RWMutex +} + +type group struct { + pattern string + handlers []Handler +} + +// NewRouter creates a new Router instance. +// If you aren't using ClassicMartini, then you can add Routes as a +// service with: +// +// m := martini.New() +// r := martini.NewRouter() +// m.MapTo(r, (*martini.Routes)(nil)) +// +// If you are using ClassicMartini, then this is done for you. +func NewRouter() Router { + return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)} +} + +func (r *router) Group(pattern string, fn func(Router), h ...Handler) { + r.groups = append(r.groups, group{pattern, h}) + fn(r) + r.groups = r.groups[:len(r.groups)-1] +} + +func (r *router) Get(pattern string, h ...Handler) Route { + return r.addRoute("GET", pattern, h) +} + +func (r *router) Patch(pattern string, h ...Handler) Route { + return r.addRoute("PATCH", pattern, h) +} + +func (r *router) Post(pattern string, h ...Handler) Route { + return r.addRoute("POST", pattern, h) +} + +func (r *router) Put(pattern string, h ...Handler) Route { + return r.addRoute("PUT", pattern, h) +} + +func (r *router) Delete(pattern string, h ...Handler) Route { + return r.addRoute("DELETE", pattern, h) +} + +func (r *router) Options(pattern string, h ...Handler) Route { + return r.addRoute("OPTIONS", pattern, h) +} + +func (r *router) Head(pattern string, h ...Handler) Route { + return r.addRoute("HEAD", pattern, h) +} + +func (r *router) Any(pattern string, h ...Handler) Route { + return r.addRoute("*", pattern, h) +} + +func (r *router) AddRoute(method, pattern string, h ...Handler) Route { + return r.addRoute(method, pattern, h) +} + +func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) { + bestMatch := NoMatch + var bestVals map[string]string + var bestRoute *route + for _, route := range r.getRoutes() { + match, vals := route.Match(req.Method, req.URL.Path) + if match.BetterThan(bestMatch) { + bestMatch = match + bestVals = vals + bestRoute = route + if match == ExactMatch { + break + } + } + } + if bestMatch != NoMatch { + params := Params(bestVals) + context.Map(params) + bestRoute.Handle(context, res) + return + } + + // no routes exist, 404 + c := &routeContext{context, 0, r.notFounds} + context.MapTo(c, (*Context)(nil)) + c.run() +} + +func (r *router) NotFound(handler ...Handler) { + r.notFounds = handler +} + +func (r *router) addRoute(method string, pattern string, handlers []Handler) *route { + if len(r.groups) > 0 { + groupPattern := "" + h := make([]Handler, 0) + for _, g := range r.groups { + groupPattern += g.pattern + h = append(h, g.handlers...) + } + + pattern = groupPattern + pattern + h = append(h, handlers...) + handlers = h + } + + route := newRoute(method, pattern, handlers) + route.Validate() + r.appendRoute(route) + return route +} + +func (r *router) appendRoute(rt *route) { + r.routesLock.Lock() + defer r.routesLock.Unlock() + r.routes = append(r.routes, rt) +} + +func (r *router) getRoutes() []*route { + r.routesLock.RLock() + defer r.routesLock.RUnlock() + return r.routes[:] +} + +func (r *router) findRoute(name string) *route { + for _, route := range r.getRoutes() { + if route.name == name { + return route + } + } + + return nil +} + +// Route is an interface representing a Route in Martini's routing layer. +type Route interface { + // URLWith returns a rendering of the Route's url with the given string params. + URLWith([]string) string + // Name sets a name for the route. + Name(string) + // GetName returns the name of the route. + GetName() string + // Pattern returns the pattern of the route. + Pattern() string + // Method returns the method of the route. + Method() string +} + +type route struct { + method string + regex *regexp.Regexp + handlers []Handler + pattern string + name string +} + +var routeReg1 = regexp.MustCompile(`:[^/#?()\.\\]+`) +var routeReg2 = regexp.MustCompile(`\*\*`) + +func newRoute(method string, pattern string, handlers []Handler) *route { + route := route{method, nil, handlers, pattern, ""} + pattern = routeReg1.ReplaceAllStringFunc(pattern, func(m string) string { + return fmt.Sprintf(`(?P<%s>[^/#?]+)`, m[1:]) + }) + var index int + pattern = routeReg2.ReplaceAllStringFunc(pattern, func(m string) string { + index++ + return fmt.Sprintf(`(?P<_%d>[^#?]*)`, index) + }) + pattern += `\/?` + route.regex = regexp.MustCompile(pattern) + return &route +} + +type RouteMatch int + +const ( + NoMatch RouteMatch = iota + StarMatch + OverloadMatch + ExactMatch +) + +//Higher number = better match +func (r RouteMatch) BetterThan(o RouteMatch) bool { + return r > o +} + +func (r route) MatchMethod(method string) RouteMatch { + switch { + case method == r.method: + return ExactMatch + case method == "HEAD" && r.method == "GET": + return OverloadMatch + case r.method == "*": + return StarMatch + default: + return NoMatch + } +} + +func (r route) Match(method string, path string) (RouteMatch, map[string]string) { + // add Any method matching support + match := r.MatchMethod(method) + if match == NoMatch { + return match, nil + } + + matches := r.regex.FindStringSubmatch(path) + if len(matches) > 0 && matches[0] == path { + params := make(map[string]string) + for i, name := range r.regex.SubexpNames() { + if len(name) > 0 { + params[name] = matches[i] + } + } + return match, params + } + return NoMatch, nil +} + +func (r *route) Validate() { + for _, handler := range r.handlers { + validateHandler(handler) + } +} + +func (r *route) Handle(c Context, res http.ResponseWriter) { + context := &routeContext{c, 0, r.handlers} + c.MapTo(context, (*Context)(nil)) + c.MapTo(r, (*Route)(nil)) + context.run() +} + +var urlReg = regexp.MustCompile(`:[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`) + +// URLWith returns the url pattern replacing the parameters for its values +func (r *route) URLWith(args []string) string { + if len(args) > 0 { + argCount := len(args) + i := 0 + url := urlReg.ReplaceAllStringFunc(r.pattern, func(m string) string { + var val interface{} + if i < argCount { + val = args[i] + } else { + val = m + } + i += 1 + return fmt.Sprintf(`%v`, val) + }) + + return url + } + return r.pattern +} + +func (r *route) Name(name string) { + r.name = name +} + +func (r *route) GetName() string { + return r.name +} + +func (r *route) Pattern() string { + return r.pattern +} + +func (r *route) Method() string { + return r.method +} + +// Routes is a helper service for Martini's routing layer. +type Routes interface { + // URLFor returns a rendered URL for the given route. Optional params can be passed to fulfill named parameters in the route. + URLFor(name string, params ...interface{}) string + // MethodsFor returns an array of methods available for the path + MethodsFor(path string) []string + // All returns an array with all the routes in the router. + All() []Route +} + +// URLFor returns the url for the given route name. +func (r *router) URLFor(name string, params ...interface{}) string { + route := r.findRoute(name) + + if route == nil { + panic("route not found") + } + + var args []string + for _, param := range params { + switch v := param.(type) { + case int: + args = append(args, strconv.FormatInt(int64(v), 10)) + case string: + args = append(args, v) + default: + if v != nil { + panic("Arguments passed to URLFor must be integers or strings") + } + } + } + + return route.URLWith(args) +} + +func (r *router) All() []Route { + routes := r.getRoutes() + var ri = make([]Route, len(routes)) + + for i, route := range routes { + ri[i] = Route(route) + } + + return ri +} + +func hasMethod(methods []string, method string) bool { + for _, v := range methods { + if v == method { + return true + } + } + return false +} + +// MethodsFor returns all methods available for path +func (r *router) MethodsFor(path string) []string { + methods := []string{} + for _, route := range r.getRoutes() { + matches := route.regex.FindStringSubmatch(path) + if len(matches) > 0 && matches[0] == path && !hasMethod(methods, route.method) { + methods = append(methods, route.method) + } + } + return methods +} + +type routeContext struct { + Context + index int + handlers []Handler +} + +func (r *routeContext) Next() { + r.index += 1 + r.run() +} + +func (r *routeContext) run() { + for r.index < len(r.handlers) { + handler := r.handlers[r.index] + vals, err := r.Invoke(handler) + if err != nil { + panic(err) + } + r.index += 1 + + // if the handler returned something, write it to the http response + if len(vals) > 0 { + ev := r.Get(reflect.TypeOf(ReturnHandler(nil))) + handleReturn := ev.Interface().(ReturnHandler) + handleReturn(r, vals) + } + + if r.Written() { + return + } + } +} diff --git a/vendor/github.com/go-martini/martini/static.go b/vendor/github.com/go-martini/martini/static.go new file mode 100644 index 0000000..51af6cf --- /dev/null +++ b/vendor/github.com/go-martini/martini/static.go @@ -0,0 +1,135 @@ +package martini + +import ( + "log" + "net/http" + "net/url" + "path" + "path/filepath" + "strings" +) + +// StaticOptions is a struct for specifying configuration options for the martini.Static middleware. +type StaticOptions struct { + // Prefix is the optional prefix used to serve the static directory content + Prefix string + // SkipLogging will disable [Static] log messages when a static file is served. + SkipLogging bool + // IndexFile defines which file to serve as index if it exists. + IndexFile string + // Expires defines which user-defined function to use for producing a HTTP Expires Header + // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching + Expires func() string + // Fallback defines a default URL to serve when the requested resource was + // not found. + Fallback string + // Exclude defines a pattern for URLs this handler should never process. + Exclude string +} + +func prepareStaticOptions(options []StaticOptions) StaticOptions { + var opt StaticOptions + if len(options) > 0 { + opt = options[0] + } + + // Defaults + if len(opt.IndexFile) == 0 { + opt.IndexFile = "index.html" + } + // Normalize the prefix if provided + if opt.Prefix != "" { + // Ensure we have a leading '/' + if opt.Prefix[0] != '/' { + opt.Prefix = "/" + opt.Prefix + } + // Remove any trailing '/' + opt.Prefix = strings.TrimRight(opt.Prefix, "/") + } + return opt +} + +// Static returns a middleware handler that serves static files in the given directory. +func Static(directory string, staticOpt ...StaticOptions) Handler { + if !filepath.IsAbs(directory) { + directory = filepath.Join(Root, directory) + } + dir := http.Dir(directory) + opt := prepareStaticOptions(staticOpt) + + return func(res http.ResponseWriter, req *http.Request, log *log.Logger) { + if req.Method != "GET" && req.Method != "HEAD" { + return + } + if opt.Exclude != "" && strings.HasPrefix(req.URL.Path, opt.Exclude) { + return + } + file := req.URL.Path + // if we have a prefix, filter requests by stripping the prefix + if opt.Prefix != "" { + if !strings.HasPrefix(file, opt.Prefix) { + return + } + file = file[len(opt.Prefix):] + if file != "" && file[0] != '/' { + return + } + } + f, err := dir.Open(file) + if err != nil { + // try any fallback before giving up + if opt.Fallback != "" { + file = opt.Fallback // so that logging stays true + f, err = dir.Open(opt.Fallback) + } + + if err != nil { + // discard the error? + return + } + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return + } + + // try to serve index file + if fi.IsDir() { + // redirect if missing trailing slash + if !strings.HasSuffix(req.URL.Path, "/") { + dest := url.URL{ + Path: req.URL.Path + "/", + RawQuery: req.URL.RawQuery, + Fragment: req.URL.Fragment, + } + http.Redirect(res, req, dest.String(), http.StatusFound) + return + } + + file = path.Join(file, opt.IndexFile) + f, err = dir.Open(file) + if err != nil { + return + } + defer f.Close() + + fi, err = f.Stat() + if err != nil || fi.IsDir() { + return + } + } + + if !opt.SkipLogging { + log.Println("[Static] Serving " + file) + } + + // Add an Expires header to the static content + if opt.Expires != nil { + res.Header().Set("Expires", opt.Expires()) + } + + http.ServeContent(res, req, file, fi.ModTime(), f) + } +} diff --git a/vendor/github.com/go-martini/martini/wercker.yml b/vendor/github.com/go-martini/martini/wercker.yml new file mode 100644 index 0000000..f8bf918 --- /dev/null +++ b/vendor/github.com/go-martini/martini/wercker.yml @@ -0,0 +1 @@ +box: wercker/golang@1.1.1 \ No newline at end of file diff --git a/vendor/modules.txt b/vendor/modules.txt new file mode 100644 index 0000000..dcbda9e --- /dev/null +++ b/vendor/modules.txt @@ -0,0 +1,6 @@ +# github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 +## explicit +github.com/codegangsta/inject +# github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab +## explicit +github.com/go-martini/martini From 7667750b51e4fc068cde340c7540f7facc9aa21b Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Thu, 21 Jul 2022 16:31:14 +0200 Subject: [PATCH 02/17] Add SkipCountFunction to Options --- throttle.go | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/throttle.go b/throttle.go index ebfc8a2..99fda58 100644 --- a/throttle.go +++ b/throttle.go @@ -10,6 +10,8 @@ import ( "strings" "sync" "time" + + "github.com/go-martini/martini" ) const ( @@ -55,6 +57,11 @@ type Options struct { // If the throttle is disabled or not // defaults to false Disabled bool + + // If this function returns true, the request will not be counted towards the access count. + // You can set it to provide your own conditions for a request to be counted based on the request, the response or + // something else stored in the context + SkipCountFunction func(res http.ResponseWriter, req *http.Request, c martini.Context) bool } // KeyValueStorer is the required interface for the Store Option @@ -224,6 +231,14 @@ func (o *Options) Identify(req *http.Request) string { return o.IdentificationFunction(req) } +func (o *Options) SkipCount(res http.ResponseWriter, req *http.Request, c martini.Context) bool { + if o.SkipCountFunction == nil { + return false + } + + return o.SkipCountFunction(res, req, c) +} + // A throttling Policy // Takes two arguments, one required: // First is a Quota (A Limit with an associated time). When the given Limit @@ -231,7 +246,7 @@ func (o *Options) Identify(req *http.Request) string { // access to resources will be denied to this user // Second is Options to use with this policy. For further information on options, // see Options further above. -func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, req *http.Request) { +func Policy(quota *Quota, options ...*Options) martini.Handler { o := newOptions(options) if o.Disabled { return func(resp http.ResponseWriter, req *http.Request) {} @@ -239,7 +254,7 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re controller := newController(quota, o.Store) - return func(resp http.ResponseWriter, req *http.Request) { + return func(resp http.ResponseWriter, req *http.Request, c martini.Context) { id := makeKey(o.KeyPrefix, quota.KeyId(), o.Identify(req)) if controller.DeniesAccess(id) { @@ -248,11 +263,13 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re resp.WriteHeader(msg.StatusCode) resp.Write([]byte(msg.Message)) return - } else { - controller.RegisterAccess(id) - setRateLimitHeaders(resp, controller, id) } + if o.SkipCount(resp, req, c) { + return + } + controller.RegisterAccess(id) + setRateLimitHeaders(resp, controller, id) } } From 55525b434a0653a6530a43b4e8f30d2ea2ec4c31 Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Thu, 21 Jul 2022 16:31:32 +0200 Subject: [PATCH 03/17] Ignore .idea folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file From 3118efa92252bf3dabe11b5dbf6970d1a6f3b15e Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 09:01:52 +0200 Subject: [PATCH 04/17] Undo adding martini --- throttle.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/throttle.go b/throttle.go index 99fda58..ab44afa 100644 --- a/throttle.go +++ b/throttle.go @@ -10,8 +10,6 @@ import ( "strings" "sync" "time" - - "github.com/go-martini/martini" ) const ( @@ -61,7 +59,7 @@ type Options struct { // If this function returns true, the request will not be counted towards the access count. // You can set it to provide your own conditions for a request to be counted based on the request, the response or // something else stored in the context - SkipCountFunction func(res http.ResponseWriter, req *http.Request, c martini.Context) bool + SkipCountFunction func(res http.ResponseWriter, req *http.Request) bool } // KeyValueStorer is the required interface for the Store Option @@ -231,12 +229,12 @@ func (o *Options) Identify(req *http.Request) string { return o.IdentificationFunction(req) } -func (o *Options) SkipCount(res http.ResponseWriter, req *http.Request, c martini.Context) bool { +func (o *Options) SkipCount(res http.ResponseWriter, req *http.Request) bool { if o.SkipCountFunction == nil { return false } - return o.SkipCountFunction(res, req, c) + return o.SkipCountFunction(res, req) } // A throttling Policy @@ -246,7 +244,7 @@ func (o *Options) SkipCount(res http.ResponseWriter, req *http.Request, c martin // access to resources will be denied to this user // Second is Options to use with this policy. For further information on options, // see Options further above. -func Policy(quota *Quota, options ...*Options) martini.Handler { +func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, req *http.Request) { o := newOptions(options) if o.Disabled { return func(resp http.ResponseWriter, req *http.Request) {} @@ -254,7 +252,7 @@ func Policy(quota *Quota, options ...*Options) martini.Handler { controller := newController(quota, o.Store) - return func(resp http.ResponseWriter, req *http.Request, c martini.Context) { + return func(resp http.ResponseWriter, req *http.Request) { id := makeKey(o.KeyPrefix, quota.KeyId(), o.Identify(req)) if controller.DeniesAccess(id) { @@ -265,7 +263,7 @@ func Policy(quota *Quota, options ...*Options) martini.Handler { return } - if o.SkipCount(resp, req, c) { + if o.SkipCount(resp, req) { return } controller.RegisterAccess(id) From f29f712833030929eed0ac0a8f5b5f38361e9337 Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 09:02:18 +0200 Subject: [PATCH 05/17] Remove go modules --- go.mod | 7 - go.sum | 4 - .../github.com/codegangsta/inject/.gitignore | 2 - vendor/github.com/codegangsta/inject/LICENSE | 20 - .../github.com/codegangsta/inject/README.md | 92 ---- .../github.com/codegangsta/inject/inject.go | 187 -------- .../codegangsta/inject/update_readme.sh | 3 - .../github.com/go-martini/martini/.gitignore | 56 --- vendor/github.com/go-martini/martini/LICENSE | 20 - .../github.com/go-martini/martini/README.md | 400 ----------------- vendor/github.com/go-martini/martini/env.go | 31 -- .../go-martini/martini/go_version.go | 7 - .../github.com/go-martini/martini/logger.go | 29 -- .../github.com/go-martini/martini/martini.go | 189 -------- .../github.com/go-martini/martini/recovery.go | 144 ------ .../go-martini/martini/response_writer.go | 107 ----- .../go-martini/martini/return_handler.go | 43 -- .../github.com/go-martini/martini/router.go | 425 ------------------ .../github.com/go-martini/martini/static.go | 135 ------ .../github.com/go-martini/martini/wercker.yml | 1 - vendor/modules.txt | 6 - 21 files changed, 1908 deletions(-) delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 vendor/github.com/codegangsta/inject/.gitignore delete mode 100644 vendor/github.com/codegangsta/inject/LICENSE delete mode 100644 vendor/github.com/codegangsta/inject/README.md delete mode 100644 vendor/github.com/codegangsta/inject/inject.go delete mode 100644 vendor/github.com/codegangsta/inject/update_readme.sh delete mode 100644 vendor/github.com/go-martini/martini/.gitignore delete mode 100644 vendor/github.com/go-martini/martini/LICENSE delete mode 100644 vendor/github.com/go-martini/martini/README.md delete mode 100644 vendor/github.com/go-martini/martini/env.go delete mode 100644 vendor/github.com/go-martini/martini/go_version.go delete mode 100644 vendor/github.com/go-martini/martini/logger.go delete mode 100644 vendor/github.com/go-martini/martini/martini.go delete mode 100644 vendor/github.com/go-martini/martini/recovery.go delete mode 100644 vendor/github.com/go-martini/martini/response_writer.go delete mode 100644 vendor/github.com/go-martini/martini/return_handler.go delete mode 100644 vendor/github.com/go-martini/martini/router.go delete mode 100644 vendor/github.com/go-martini/martini/static.go delete mode 100644 vendor/github.com/go-martini/martini/wercker.yml delete mode 100644 vendor/modules.txt diff --git a/go.mod b/go.mod deleted file mode 100644 index cdcbee3..0000000 --- a/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module throttle - -go 1.18 - -require github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab - -require github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect diff --git a/go.sum b/go.sum deleted file mode 100644 index d9e117f..0000000 --- a/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= diff --git a/vendor/github.com/codegangsta/inject/.gitignore b/vendor/github.com/codegangsta/inject/.gitignore deleted file mode 100644 index df3df8a..0000000 --- a/vendor/github.com/codegangsta/inject/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -inject -inject.test diff --git a/vendor/github.com/codegangsta/inject/LICENSE b/vendor/github.com/codegangsta/inject/LICENSE deleted file mode 100644 index eb68a0e..0000000 --- a/vendor/github.com/codegangsta/inject/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Jeremy Saenz - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/codegangsta/inject/README.md b/vendor/github.com/codegangsta/inject/README.md deleted file mode 100644 index 679abe0..0000000 --- a/vendor/github.com/codegangsta/inject/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# inject --- - import "github.com/codegangsta/inject" - -Package inject provides utilities for mapping and injecting dependencies in -various ways. - -Language Translations: -* [简体中文](translations/README_zh_cn.md) - -## Usage - -#### func InterfaceOf - -```go -func InterfaceOf(value interface{}) reflect.Type -``` -InterfaceOf dereferences a pointer to an Interface type. It panics if value is -not an pointer to an interface. - -#### type Applicator - -```go -type Applicator interface { - // Maps dependencies in the Type map to each field in the struct - // that is tagged with 'inject'. Returns an error if the injection - // fails. - Apply(interface{}) error -} -``` - -Applicator represents an interface for mapping dependencies to a struct. - -#### type Injector - -```go -type Injector interface { - Applicator - Invoker - TypeMapper - // SetParent sets the parent of the injector. If the injector cannot find a - // dependency in its Type map it will check its parent before returning an - // error. - SetParent(Injector) -} -``` - -Injector represents an interface for mapping and injecting dependencies into -structs and function arguments. - -#### func New - -```go -func New() Injector -``` -New returns a new Injector. - -#### type Invoker - -```go -type Invoker interface { - // Invoke attempts to call the interface{} provided as a function, - // providing dependencies for function arguments based on Type. Returns - // a slice of reflect.Value representing the returned values of the function. - // Returns an error if the injection fails. - Invoke(interface{}) ([]reflect.Value, error) -} -``` - -Invoker represents an interface for calling functions via reflection. - -#### type TypeMapper - -```go -type TypeMapper interface { - // Maps the interface{} value based on its immediate type from reflect.TypeOf. - Map(interface{}) TypeMapper - // Maps the interface{} value based on the pointer of an Interface provided. - // This is really only useful for mapping a value as an interface, as interfaces - // cannot at this time be referenced directly without a pointer. - MapTo(interface{}, interface{}) TypeMapper - // Provides a possibility to directly insert a mapping based on type and value. - // This makes it possible to directly map type arguments not possible to instantiate - // with reflect like unidirectional channels. - Set(reflect.Type, reflect.Value) TypeMapper - // Returns the Value that is mapped to the current type. Returns a zeroed Value if - // the Type has not been mapped. - Get(reflect.Type) reflect.Value -} -``` - -TypeMapper represents an interface for mapping interface{} values based on type. diff --git a/vendor/github.com/codegangsta/inject/inject.go b/vendor/github.com/codegangsta/inject/inject.go deleted file mode 100644 index 3ff713c..0000000 --- a/vendor/github.com/codegangsta/inject/inject.go +++ /dev/null @@ -1,187 +0,0 @@ -// Package inject provides utilities for mapping and injecting dependencies in various ways. -package inject - -import ( - "fmt" - "reflect" -) - -// Injector represents an interface for mapping and injecting dependencies into structs -// and function arguments. -type Injector interface { - Applicator - Invoker - TypeMapper - // SetParent sets the parent of the injector. If the injector cannot find a - // dependency in its Type map it will check its parent before returning an - // error. - SetParent(Injector) -} - -// Applicator represents an interface for mapping dependencies to a struct. -type Applicator interface { - // Maps dependencies in the Type map to each field in the struct - // that is tagged with 'inject'. Returns an error if the injection - // fails. - Apply(interface{}) error -} - -// Invoker represents an interface for calling functions via reflection. -type Invoker interface { - // Invoke attempts to call the interface{} provided as a function, - // providing dependencies for function arguments based on Type. Returns - // a slice of reflect.Value representing the returned values of the function. - // Returns an error if the injection fails. - Invoke(interface{}) ([]reflect.Value, error) -} - -// TypeMapper represents an interface for mapping interface{} values based on type. -type TypeMapper interface { - // Maps the interface{} value based on its immediate type from reflect.TypeOf. - Map(interface{}) TypeMapper - // Maps the interface{} value based on the pointer of an Interface provided. - // This is really only useful for mapping a value as an interface, as interfaces - // cannot at this time be referenced directly without a pointer. - MapTo(interface{}, interface{}) TypeMapper - // Provides a possibility to directly insert a mapping based on type and value. - // This makes it possible to directly map type arguments not possible to instantiate - // with reflect like unidirectional channels. - Set(reflect.Type, reflect.Value) TypeMapper - // Returns the Value that is mapped to the current type. Returns a zeroed Value if - // the Type has not been mapped. - Get(reflect.Type) reflect.Value -} - -type injector struct { - values map[reflect.Type]reflect.Value - parent Injector -} - -// InterfaceOf dereferences a pointer to an Interface type. -// It panics if value is not an pointer to an interface. -func InterfaceOf(value interface{}) reflect.Type { - t := reflect.TypeOf(value) - - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - - if t.Kind() != reflect.Interface { - panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)") - } - - return t -} - -// New returns a new Injector. -func New() Injector { - return &injector{ - values: make(map[reflect.Type]reflect.Value), - } -} - -// Invoke attempts to call the interface{} provided as a function, -// providing dependencies for function arguments based on Type. -// Returns a slice of reflect.Value representing the returned values of the function. -// Returns an error if the injection fails. -// It panics if f is not a function -func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { - t := reflect.TypeOf(f) - - var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func - for i := 0; i < t.NumIn(); i++ { - argType := t.In(i) - val := inj.Get(argType) - if !val.IsValid() { - return nil, fmt.Errorf("Value not found for type %v", argType) - } - - in[i] = val - } - - return reflect.ValueOf(f).Call(in), nil -} - -// Maps dependencies in the Type map to each field in the struct -// that is tagged with 'inject'. -// Returns an error if the injection fails. -func (inj *injector) Apply(val interface{}) error { - v := reflect.ValueOf(val) - - for v.Kind() == reflect.Ptr { - v = v.Elem() - } - - if v.Kind() != reflect.Struct { - return nil // Should not panic here ? - } - - t := v.Type() - - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - structField := t.Field(i) - if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") { - ft := f.Type() - v := inj.Get(ft) - if !v.IsValid() { - return fmt.Errorf("Value not found for type %v", ft) - } - - f.Set(v) - } - - } - - return nil -} - -// Maps the concrete value of val to its dynamic type using reflect.TypeOf, -// It returns the TypeMapper registered in. -func (i *injector) Map(val interface{}) TypeMapper { - i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) - return i -} - -func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { - i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) - return i -} - -// Maps the given reflect.Type to the given reflect.Value and returns -// the Typemapper the mapping has been registered in. -func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { - i.values[typ] = val - return i -} - -func (i *injector) Get(t reflect.Type) reflect.Value { - val := i.values[t] - - if val.IsValid() { - return val - } - - // no concrete types found, try to find implementors - // if t is an interface - if t.Kind() == reflect.Interface { - for k, v := range i.values { - if k.Implements(t) { - val = v - break - } - } - } - - // Still no type found, try to look it up on the parent - if !val.IsValid() && i.parent != nil { - val = i.parent.Get(t) - } - - return val - -} - -func (i *injector) SetParent(parent Injector) { - i.parent = parent -} diff --git a/vendor/github.com/codegangsta/inject/update_readme.sh b/vendor/github.com/codegangsta/inject/update_readme.sh deleted file mode 100644 index 497f9a5..0000000 --- a/vendor/github.com/codegangsta/inject/update_readme.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -go get github.com/robertkrimen/godocdown/godocdown -godocdown > README.md diff --git a/vendor/github.com/go-martini/martini/.gitignore b/vendor/github.com/go-martini/martini/.gitignore deleted file mode 100644 index fb6d4ac..0000000 --- a/vendor/github.com/go-martini/martini/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test - -/.godeps -/.envrc - -# Godeps -Godeps/_workspace -Godeps/Readme - -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -.idea/ - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties diff --git a/vendor/github.com/go-martini/martini/LICENSE b/vendor/github.com/go-martini/martini/LICENSE deleted file mode 100644 index d3fefb8..0000000 --- a/vendor/github.com/go-martini/martini/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Jeremy Saenz - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/go-martini/martini/README.md b/vendor/github.com/go-martini/martini/README.md deleted file mode 100644 index d7adb8f..0000000 --- a/vendor/github.com/go-martini/martini/README.md +++ /dev/null @@ -1,400 +0,0 @@ -# Martini [![wercker status](https://app.wercker.com/status/9b7dbc6e2654b604cd694d191c3d5487/s/master "wercker status")](https://app.wercker.com/project/bykey/9b7dbc6e2654b604cd694d191c3d5487)[![GoDoc](https://godoc.org/github.com/go-martini/martini?status.png)](http://godoc.org/github.com/go-martini/martini) - -### **NOTE:** The martini framework is no longer maintained. - -Martini is a powerful package for quickly writing modular web applications/services in Golang. - -Language Translations: -* [繁體中文](translations/README_zh_tw.md) -* [简体中文](translations/README_zh_cn.md) -* [Português Brasileiro (pt_BR)](translations/README_pt_br.md) -* [Español](translations/README_es_ES.md) -* [한국어 번역](translations/README_ko_kr.md) -* [Русский](translations/README_ru_RU.md) -* [日本語](translations/README_ja_JP.md) -* [French](translations/README_fr_FR.md) -* [Turkish](translations/README_tr_TR.md) -* [German](translations/README_de_DE.md) -* [Polski](translations/README_pl_PL.md) - -## Getting Started - -After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`. - -~~~ go -package main - -import "github.com/go-martini/martini" - -func main() { - m := martini.Classic() - m.Get("/", func() string { - return "Hello world!" - }) - m.Run() -} -~~~ - -Then install the Martini package (**go 1.1** or greater is required): -~~~ -go get github.com/go-martini/martini -~~~ - -Then run your server: -~~~ -go run server.go -~~~ - -You will now have a Martini webserver running on `localhost:3000`. - -## Getting Help - -Join the [Mailing list](https://groups.google.com/forum/#!forum/martini-go) - -Watch the [Demo Video](http://martini.codegangsta.io/#demo) - -Ask questions on Stackoverflow using the [martini tag](http://stackoverflow.com/questions/tagged/martini) - -GoDoc [documentation](http://godoc.org/github.com/go-martini/martini) - - -## Features -* Extremely simple to use. -* Non-intrusive design. -* Plays nice with other Golang packages. -* Awesome path matching and routing. -* Modular design - Easy to add functionality, easy to rip stuff out. -* Lots of good handlers/middlewares to use. -* Great 'out of the box' feature set. -* **Fully compatible with the [http.HandlerFunc](http://godoc.org/net/http#HandlerFunc) interface.** -* Default document serving (e.g., for serving AngularJS apps in HTML5 mode). - -## More Middleware -For more middleware and functionality, check out the repositories in the [martini-contrib](https://github.com/martini-contrib) organization. - -## Table of Contents -* [Classic Martini](#classic-martini) - * [Handlers](#handlers) - * [Routing](#routing) - * [Services](#services) - * [Serving Static Files](#serving-static-files) -* [Middleware Handlers](#middleware-handlers) - * [Next()](#next) -* [Martini Env](#martini-env) -* [FAQ](#faq) - -## Classic Martini -To get up and running quickly, [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic) provides some reasonable defaults that work well for most web applications: -~~~ go - m := martini.Classic() - // ... middleware and routing goes here - m.Run() -~~~ - -Below is some of the functionality [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic) pulls in automatically: - * Request/Response Logging - [martini.Logger](http://godoc.org/github.com/go-martini/martini#Logger) - * Panic Recovery - [martini.Recovery](http://godoc.org/github.com/go-martini/martini#Recovery) - * Static File serving - [martini.Static](http://godoc.org/github.com/go-martini/martini#Static) - * Routing - [martini.Router](http://godoc.org/github.com/go-martini/martini#Router) - -### Handlers -Handlers are the heart and soul of Martini. A handler is basically any kind of callable function: -~~~ go -m.Get("/", func() { - println("hello world") -}) -~~~ - -#### Return Values -If a handler returns something, Martini will write the result to the current [http.ResponseWriter](http://godoc.org/net/http#ResponseWriter) as a string: -~~~ go -m.Get("/", func() string { - return "hello world" // HTTP 200 : "hello world" -}) -~~~ - -You can also optionally return a status code: -~~~ go -m.Get("/", func() (int, string) { - return 418, "i'm a teapot" // HTTP 418 : "i'm a teapot" -}) -~~~ - -#### Service Injection -Handlers are invoked via reflection. Martini makes use of *Dependency Injection* to resolve dependencies in a Handlers argument list. **This makes Martini completely compatible with golang's `http.HandlerFunc` interface.** - -If you add an argument to your Handler, Martini will search its list of services and attempt to resolve the dependency via type assertion: -~~~ go -m.Get("/", func(res http.ResponseWriter, req *http.Request) { // res and req are injected by Martini - res.WriteHeader(200) // HTTP 200 -}) -~~~ - -The following services are included with [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic): - * [*log.Logger](http://godoc.org/log#Logger) - Global logger for Martini. - * [martini.Context](http://godoc.org/github.com/go-martini/martini#Context) - http request context. - * [martini.Params](http://godoc.org/github.com/go-martini/martini#Params) - `map[string]string` of named params found by route matching. - * [martini.Routes](http://godoc.org/github.com/go-martini/martini#Routes) - Route helper service. - * [martini.Route](http://godoc.org/github.com/go-martini/martini#Route) - Current active route. - * [http.ResponseWriter](http://godoc.org/net/http/#ResponseWriter) - http Response writer interface. - * [*http.Request](http://godoc.org/net/http/#Request) - http Request. - -### Routing -In Martini, a route is an HTTP method paired with a URL-matching pattern. -Each route can take one or more handler methods: -~~~ go -m.Get("/", func() { - // show something -}) - -m.Patch("/", func() { - // update something -}) - -m.Post("/", func() { - // create something -}) - -m.Put("/", func() { - // replace something -}) - -m.Delete("/", func() { - // destroy something -}) - -m.Options("/", func() { - // http options -}) - -m.NotFound(func() { - // handle 404 -}) -~~~ - -Routes are matched in the order they are defined. The first route that -matches the request is invoked. - -Route patterns may include named parameters, accessible via the [martini.Params](http://godoc.org/github.com/go-martini/martini#Params) service: -~~~ go -m.Get("/hello/:name", func(params martini.Params) string { - return "Hello " + params["name"] -}) -~~~ - -Routes can be matched with globs: -~~~ go -m.Get("/hello/**", func(params martini.Params) string { - return "Hello " + params["_1"] -}) -~~~ - -Regular expressions can be used as well: -~~~go -m.Get("/hello/(?P[a-zA-Z]+)", func(params martini.Params) string { - return fmt.Sprintf ("Hello %s", params["name"]) -}) -~~~ -Take a look at the [Go documentation](http://golang.org/pkg/regexp/syntax/) for more info about regular expressions syntax . - -Route handlers can be stacked on top of each other, which is useful for things like authentication and authorization: -~~~ go -m.Get("/secret", authorize, func() { - // this will execute as long as authorize doesn't write a response -}) -~~~ - -Route groups can be added too using the Group method. -~~~ go -m.Group("/books", func(r martini.Router) { - r.Get("/:id", GetBooks) - r.Post("/new", NewBook) - r.Put("/update/:id", UpdateBook) - r.Delete("/delete/:id", DeleteBook) -}) -~~~ - -Just like you can pass middlewares to a handler you can pass middlewares to groups. -~~~ go -m.Group("/books", func(r martini.Router) { - r.Get("/:id", GetBooks) - r.Post("/new", NewBook) - r.Put("/update/:id", UpdateBook) - r.Delete("/delete/:id", DeleteBook) -}, MyMiddleware1, MyMiddleware2) -~~~ - -### Services -Services are objects that are available to be injected into a Handler's argument list. You can map a service on a *Global* or *Request* level. - -#### Global Mapping -A Martini instance implements the inject.Injector interface, so mapping a service is easy: -~~~ go -db := &MyDatabase{} -m := martini.Classic() -m.Map(db) // the service will be available to all handlers as *MyDatabase -// ... -m.Run() -~~~ - -#### Request-Level Mapping -Mapping on the request level can be done in a handler via [martini.Context](http://godoc.org/github.com/go-martini/martini#Context): -~~~ go -func MyCustomLoggerHandler(c martini.Context, req *http.Request) { - logger := &MyCustomLogger{req} - c.Map(logger) // mapped as *MyCustomLogger -} -~~~ - -#### Mapping values to Interfaces -One of the most powerful parts about services is the ability to map a service to an interface. For instance, if you wanted to override the [http.ResponseWriter](http://godoc.org/net/http#ResponseWriter) with an object that wrapped it and performed extra operations, you can write the following handler: -~~~ go -func WrapResponseWriter(res http.ResponseWriter, c martini.Context) { - rw := NewSpecialResponseWriter(res) - c.MapTo(rw, (*http.ResponseWriter)(nil)) // override ResponseWriter with our wrapper ResponseWriter -} -~~~ - -### Serving Static Files -A [martini.Classic()](http://godoc.org/github.com/go-martini/martini#Classic) instance automatically serves static files from the "public" directory in the root of your server. -You can serve from more directories by adding more [martini.Static](http://godoc.org/github.com/go-martini/martini#Static) handlers. -~~~ go -m.Use(martini.Static("assets")) // serve from the "assets" directory as well -~~~ - -#### Serving a Default Document -You can specify the URL of a local file to serve when the requested URL is not -found. You can also specify an exclusion prefix so that certain URLs are ignored. -This is useful for servers that serve both static files and have additional -handlers defined (e.g., REST API). When doing so, it's useful to define the -static handler as a part of the NotFound chain. - -The following example serves the `/index.html` file whenever any URL is -requested that does not match any local file and does not start with `/api/v`: -~~~ go -static := martini.Static("assets", martini.StaticOptions{Fallback: "/index.html", Exclude: "/api/v"}) -m.NotFound(static, http.NotFound) -~~~ - -## Middleware Handlers -Middleware Handlers sit between the incoming http request and the router. In essence they are no different than any other Handler in Martini. You can add a middleware handler to the stack like so: -~~~ go -m.Use(func() { - // do some middleware stuff -}) -~~~ - -You can have full control over the middleware stack with the `Handlers` function. This will replace any handlers that have been previously set: -~~~ go -m.Handlers( - Middleware1, - Middleware2, - Middleware3, -) -~~~ - -Middleware Handlers work really well for things like logging, authorization, authentication, sessions, gzipping, error pages and any other operations that must happen before or after an http request: -~~~ go -// validate an api key -m.Use(func(res http.ResponseWriter, req *http.Request) { - if req.Header.Get("X-API-KEY") != "secret123" { - res.WriteHeader(http.StatusUnauthorized) - } -}) -~~~ - -### Next() -[Context.Next()](http://godoc.org/github.com/go-martini/martini#Context) is an optional function that Middleware Handlers can call to yield the until after the other Handlers have been executed. This works really well for any operations that must happen after an http request: -~~~ go -// log before and after a request -m.Use(func(c martini.Context, log *log.Logger){ - log.Println("before a request") - - c.Next() - - log.Println("after a request") -}) -~~~ - -## Martini Env - -Some Martini handlers make use of the `martini.Env` global variable to provide special functionality for development environments vs production environments. It is recommended that the `MARTINI_ENV=production` environment variable to be set when deploying a Martini server into a production environment. - -## FAQ - -### Where do I find middleware X? - -Start by looking in the [martini-contrib](https://github.com/martini-contrib) projects. If it is not there feel free to contact a martini-contrib team member about adding a new repo to the organization. - -* [acceptlang](https://github.com/martini-contrib/acceptlang) - Handler for parsing the `Accept-Language` HTTP header. -* [accessflags](https://github.com/martini-contrib/accessflags) - Handler to enable Access Control. -* [auth](https://github.com/martini-contrib/auth) - Handlers for authentication. -* [binding](https://github.com/martini-contrib/binding) - Handler for mapping/validating a raw request into a structure. -* [cors](https://github.com/martini-contrib/cors) - Handler that enables CORS support. -* [csrf](https://github.com/martini-contrib/csrf) - CSRF protection for applications -* [encoder](https://github.com/martini-contrib/encoder) - Encoder service for rendering data in several formats and content negotiation. -* [gzip](https://github.com/martini-contrib/gzip) - Handler for adding gzip compress to requests -* [gorelic](https://github.com/martini-contrib/gorelic) - NewRelic middleware -* [logstasher](https://github.com/martini-contrib/logstasher) - Middleware that prints logstash-compatible JSON -* [method](https://github.com/martini-contrib/method) - HTTP method overriding via Header or form fields. -* [oauth2](https://github.com/martini-contrib/oauth2) - Handler that provides OAuth 2.0 login for Martini apps. Google Sign-in, Facebook Connect and Github login is supported. -* [permissions2](https://github.com/xyproto/permissions2) - Handler for keeping track of users, login states and permissions. -* [render](https://github.com/martini-contrib/render) - Handler that provides a service for easily rendering JSON and HTML templates. -* [secure](https://github.com/martini-contrib/secure) - Implements a few quick security wins. -* [sessions](https://github.com/martini-contrib/sessions) - Handler that provides a Session service. -* [sessionauth](https://github.com/martini-contrib/sessionauth) - Handler that provides a simple way to make routes require a login, and to handle user logins in the session -* [strict](https://github.com/martini-contrib/strict) - Strict Mode -* [strip](https://github.com/martini-contrib/strip) - URL Prefix stripping. -* [staticbin](https://github.com/martini-contrib/staticbin) - Handler for serving static files from binary data -* [throttle](https://github.com/martini-contrib/throttle) - Request rate throttling middleware. -* [vauth](https://github.com/rafecolton/vauth) - Handlers for vender webhook authentication (currently GitHub and TravisCI) -* [web](https://github.com/martini-contrib/web) - hoisie web.go's Context - -### How do I integrate with existing servers? - -A Martini instance implements `http.Handler`, so it can easily be used to serve subtrees -on existing Go servers. For example this is a working Martini app for Google App Engine: - -~~~ go -package hello - -import ( - "net/http" - "github.com/go-martini/martini" -) - -func init() { - m := martini.Classic() - m.Get("/", func() string { - return "Hello world!" - }) - http.Handle("/", m) -} -~~~ - -### How do I change the port/host? - -Martini's `Run` function looks for the PORT and HOST environment variables and uses those. Otherwise Martini will default to localhost:3000. -To have more flexibility over port and host, use the `martini.RunOnAddr` function instead. - -~~~ go - m := martini.Classic() - // ... - m.RunOnAddr(":8080") -~~~ - -### Live code reload? - -[gin](https://github.com/codegangsta/gin) and [fresh](https://github.com/pilu/fresh) both live reload martini apps. - -## Contributing -Martini is meant to be kept tiny and clean. Most contributions should end up in a repository in the [martini-contrib](https://github.com/martini-contrib) organization. If you do have a contribution for the core of Martini feel free to put up a Pull Request. - -## License -Martini is distributed by The MIT License, see LICENSE - -## About - -Inspired by [express](https://github.com/visionmedia/express) and [sinatra](https://github.com/sinatra/sinatra) - -Martini is obsessively designed by none other than the [Code Gangsta](http://codegangsta.io/) diff --git a/vendor/github.com/go-martini/martini/env.go b/vendor/github.com/go-martini/martini/env.go deleted file mode 100644 index 54d5857..0000000 --- a/vendor/github.com/go-martini/martini/env.go +++ /dev/null @@ -1,31 +0,0 @@ -package martini - -import ( - "os" -) - -// Envs -const ( - Dev string = "development" - Prod string = "production" - Test string = "test" -) - -// Env is the environment that Martini is executing in. The MARTINI_ENV is read on initialization to set this variable. -var Env = Dev -var Root string - -func setENV(e string) { - if len(e) > 0 { - Env = e - } -} - -func init() { - setENV(os.Getenv("MARTINI_ENV")) - var err error - Root, err = os.Getwd() - if err != nil { - panic(err) - } -} diff --git a/vendor/github.com/go-martini/martini/go_version.go b/vendor/github.com/go-martini/martini/go_version.go deleted file mode 100644 index bd271a8..0000000 --- a/vendor/github.com/go-martini/martini/go_version.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build !go1.1 - -package martini - -func MartiniDoesNotSupportGo1Point0() { - "Martini requires Go 1.1 or greater." -} diff --git a/vendor/github.com/go-martini/martini/logger.go b/vendor/github.com/go-martini/martini/logger.go deleted file mode 100644 index d01107c..0000000 --- a/vendor/github.com/go-martini/martini/logger.go +++ /dev/null @@ -1,29 +0,0 @@ -package martini - -import ( - "log" - "net/http" - "time" -) - -// Logger returns a middleware handler that logs the request as it goes in and the response as it goes out. -func Logger() Handler { - return func(res http.ResponseWriter, req *http.Request, c Context, log *log.Logger) { - start := time.Now() - - addr := req.Header.Get("X-Real-IP") - if addr == "" { - addr = req.Header.Get("X-Forwarded-For") - if addr == "" { - addr = req.RemoteAddr - } - } - - log.Printf("Started %s %s for %s", req.Method, req.URL.Path, addr) - - rw := res.(ResponseWriter) - c.Next() - - log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start)) - } -} diff --git a/vendor/github.com/go-martini/martini/martini.go b/vendor/github.com/go-martini/martini/martini.go deleted file mode 100644 index 0ce4f3d..0000000 --- a/vendor/github.com/go-martini/martini/martini.go +++ /dev/null @@ -1,189 +0,0 @@ -// Package martini is a powerful package for quickly writing modular web applications/services in Golang. -// -// For a full guide visit http://github.com/go-martini/martini -// -// package main -// -// import "github.com/go-martini/martini" -// -// func main() { -// m := martini.Classic() -// -// m.Get("/", func() string { -// return "Hello world!" -// }) -// -// m.Run() -// } -package martini - -import ( - "log" - "net/http" - "os" - "reflect" - - "github.com/codegangsta/inject" -) - -// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level. -type Martini struct { - inject.Injector - handlers []Handler - action Handler - logger *log.Logger -} - -// New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used. -func New() *Martini { - m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)} - m.Map(m.logger) - m.Map(defaultReturnHandler()) - return m -} - -// Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers. -// Will panic if any of the handlers is not a callable function -func (m *Martini) Handlers(handlers ...Handler) { - m.handlers = make([]Handler, 0) - for _, handler := range handlers { - m.Use(handler) - } -} - -// Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic(). -func (m *Martini) Action(handler Handler) { - validateHandler(handler) - m.action = handler -} - -// Logger sets the logger -func (m *Martini) Logger(logger *log.Logger) { - m.logger = logger - m.Map(m.logger) -} - -// Use adds a middleware Handler to the stack. Will panic if the handler is not a callable func. Middleware Handlers are invoked in the order that they are added. -func (m *Martini) Use(handler Handler) { - validateHandler(handler) - - m.handlers = append(m.handlers, handler) -} - -// ServeHTTP is the HTTP Entry point for a Martini instance. Useful if you want to control your own HTTP server. -func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) { - m.createContext(res, req).run() -} - -// Run the http server on a given host and port. -func (m *Martini) RunOnAddr(addr string) { - // TODO: Should probably be implemented using a new instance of http.Server in place of - // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use. - // This would also allow to improve testing when a custom host and port are passed. - - logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger) - logger.Printf("listening on %s (%s)\n", addr, Env) - logger.Fatalln(http.ListenAndServe(addr, m)) -} - -// Run the http server. Listening on os.GetEnv("PORT") or 3000 by default. -func (m *Martini) Run() { - port := os.Getenv("PORT") - if len(port) == 0 { - port = "3000" - } - - host := os.Getenv("HOST") - - m.RunOnAddr(host + ":" + port) -} - -func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context { - c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0} - c.SetParent(m) - c.MapTo(c, (*Context)(nil)) - c.MapTo(c.rw, (*http.ResponseWriter)(nil)) - c.Map(req) - return c -} - -// ClassicMartini represents a Martini with some reasonable defaults. Embeds the router functions for convenience. -type ClassicMartini struct { - *Martini - Router -} - -// Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery and martini.Static. -// Classic also maps martini.Routes as a service. -func Classic() *ClassicMartini { - r := NewRouter() - m := New() - m.Use(Logger()) - m.Use(Recovery()) - m.Use(Static("public")) - m.MapTo(r, (*Routes)(nil)) - m.Action(r.Handle) - return &ClassicMartini{m, r} -} - -// Handler can be any callable function. Martini attempts to inject services into the handler's argument list. -// Martini will panic if an argument could not be fullfilled via dependency injection. -type Handler interface{} - -func validateHandler(handler Handler) { - if reflect.TypeOf(handler).Kind() != reflect.Func { - panic("martini handler must be a callable func") - } -} - -// Context represents a request context. Services can be mapped on the request level from this interface. -type Context interface { - inject.Injector - // Next is an optional function that Middleware Handlers can call to yield the until after - // the other Handlers have been executed. This works really well for any operations that must - // happen after an http request - Next() - // Written returns whether or not the response for this context has been written. - Written() bool -} - -type context struct { - inject.Injector - handlers []Handler - action Handler - rw ResponseWriter - index int -} - -func (c *context) handler() Handler { - if c.index < len(c.handlers) { - return c.handlers[c.index] - } - if c.index == len(c.handlers) { - return c.action - } - panic("invalid index for context handler") -} - -func (c *context) Next() { - c.index += 1 - c.run() -} - -func (c *context) Written() bool { - return c.rw.Written() -} - -func (c *context) run() { - for c.index <= len(c.handlers) { - _, err := c.Invoke(c.handler()) - if err != nil { - panic(err) - } - c.index += 1 - - if c.Written() { - return - } - } -} diff --git a/vendor/github.com/go-martini/martini/recovery.go b/vendor/github.com/go-martini/martini/recovery.go deleted file mode 100644 index fe0d918..0000000 --- a/vendor/github.com/go-martini/martini/recovery.go +++ /dev/null @@ -1,144 +0,0 @@ -package martini - -import ( - "bytes" - "fmt" - "io/ioutil" - "log" - "net/http" - "runtime" - - "github.com/codegangsta/inject" -) - -const ( - panicHtml = ` -PANIC: %s - - -

PANIC

-
%s
-
%s
- -` -) - -var ( - dunno = []byte("???") - centerDot = []byte("·") - dot = []byte(".") - slash = []byte("/") -) - -// stack returns a nicely formated stack frame, skipping skip frames -func stack(skip int) []byte { - buf := new(bytes.Buffer) // the returned data - // As we loop, we open files and read them. These variables record the currently - // loaded file. - var lines [][]byte - var lastFile string - for i := skip; ; i++ { // Skip the expected number of frames - pc, file, line, ok := runtime.Caller(i) - if !ok { - break - } - // Print this much at least. If we can't find the source, it won't show. - fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) - if file != lastFile { - data, err := ioutil.ReadFile(file) - if err != nil { - continue - } - lines = bytes.Split(data, []byte{'\n'}) - lastFile = file - } - fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) - } - return buf.Bytes() -} - -// source returns a space-trimmed slice of the n'th line. -func source(lines [][]byte, n int) []byte { - n-- // in stack trace, lines are 1-indexed but our array is 0-indexed - if n < 0 || n >= len(lines) { - return dunno - } - return bytes.TrimSpace(lines[n]) -} - -// function returns, if possible, the name of the function containing the PC. -func function(pc uintptr) []byte { - fn := runtime.FuncForPC(pc) - if fn == nil { - return dunno - } - name := []byte(fn.Name()) - // The name includes the path name to the package, which is unnecessary - // since the file name is already included. Plus, it has center dots. - // That is, we see - // runtime/debug.*T·ptrmethod - // and want - // *T.ptrmethod - // Also the package path might contains dot (e.g. code.google.com/...), - // so first eliminate the path prefix - if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { - name = name[lastslash+1:] - } - if period := bytes.Index(name, dot); period >= 0 { - name = name[period+1:] - } - name = bytes.Replace(name, centerDot, dot, -1) - return name -} - -// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. -// While Martini is in development mode, Recovery will also output the panic as HTML. -func Recovery() Handler { - return func(c Context, log *log.Logger) { - defer func() { - if err := recover(); err != nil { - stack := stack(3) - log.Printf("PANIC: %s\n%s", err, stack) - - // Lookup the current responsewriter - val := c.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) - res := val.Interface().(http.ResponseWriter) - - // respond with panic message while in development mode - var body []byte - if Env == Dev { - res.Header().Set("Content-Type", "text/html") - body = []byte(fmt.Sprintf(panicHtml, err, err, stack)) - } else { - body = []byte("500 Internal Server Error") - } - - res.WriteHeader(http.StatusInternalServerError) - if nil != body { - res.Write(body) - } - } - }() - - c.Next() - } -} diff --git a/vendor/github.com/go-martini/martini/response_writer.go b/vendor/github.com/go-martini/martini/response_writer.go deleted file mode 100644 index 12574b1..0000000 --- a/vendor/github.com/go-martini/martini/response_writer.go +++ /dev/null @@ -1,107 +0,0 @@ -package martini - -import ( - "bufio" - "fmt" - "net" - "net/http" -) - -// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about -// the response. It is recommended that middleware handlers use this construct to wrap a responsewriter -// if the functionality calls for it. -type ResponseWriter interface { - http.ResponseWriter - http.Flusher - http.Hijacker - // Status returns the status code of the response or 0 if the response has not been written. - Status() int - // Written returns whether or not the ResponseWriter has been written. - Written() bool - // Size returns the size of the response body. - Size() int - // Before allows for a function to be called before the ResponseWriter has been written to. This is - // useful for setting headers or any other operations that must happen before a response has been written. - Before(BeforeFunc) -} - -// BeforeFunc is a function that is called before the ResponseWriter has been written to. -type BeforeFunc func(ResponseWriter) - -// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter -func NewResponseWriter(rw http.ResponseWriter) ResponseWriter { - newRw := responseWriter{rw, 0, 0, nil} - if cn, ok := rw.(http.CloseNotifier); ok { - return &closeNotifyResponseWriter{newRw, cn} - } - return &newRw -} - -type responseWriter struct { - http.ResponseWriter - status int - size int - beforeFuncs []BeforeFunc -} - -func (rw *responseWriter) WriteHeader(s int) { - rw.callBefore() - rw.ResponseWriter.WriteHeader(s) - rw.status = s -} - -func (rw *responseWriter) Write(b []byte) (int, error) { - if !rw.Written() { - // The status will be StatusOK if WriteHeader has not been called yet - rw.WriteHeader(http.StatusOK) - } - size, err := rw.ResponseWriter.Write(b) - rw.size += size - return size, err -} - -func (rw *responseWriter) Status() int { - return rw.status -} - -func (rw *responseWriter) Size() int { - return rw.size -} - -func (rw *responseWriter) Written() bool { - return rw.status != 0 -} - -func (rw *responseWriter) Before(before BeforeFunc) { - rw.beforeFuncs = append(rw.beforeFuncs, before) -} - -func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hijacker, ok := rw.ResponseWriter.(http.Hijacker) - if !ok { - return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") - } - return hijacker.Hijack() -} - -func (rw *responseWriter) callBefore() { - for i := len(rw.beforeFuncs) - 1; i >= 0; i-- { - rw.beforeFuncs[i](rw) - } -} - -func (rw *responseWriter) Flush() { - flusher, ok := rw.ResponseWriter.(http.Flusher) - if ok { - flusher.Flush() - } -} - -type closeNotifyResponseWriter struct { - responseWriter - closeNotifier http.CloseNotifier -} - -func (rw *closeNotifyResponseWriter) CloseNotify() <-chan bool { - return rw.closeNotifier.CloseNotify() -} diff --git a/vendor/github.com/go-martini/martini/return_handler.go b/vendor/github.com/go-martini/martini/return_handler.go deleted file mode 100644 index 4ea8f34..0000000 --- a/vendor/github.com/go-martini/martini/return_handler.go +++ /dev/null @@ -1,43 +0,0 @@ -package martini - -import ( - "github.com/codegangsta/inject" - "net/http" - "reflect" -) - -// ReturnHandler is a service that Martini provides that is called -// when a route handler returns something. The ReturnHandler is -// responsible for writing to the ResponseWriter based on the values -// that are passed into this function. -type ReturnHandler func(Context, []reflect.Value) - -func defaultReturnHandler() ReturnHandler { - return func(ctx Context, vals []reflect.Value) { - rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) - res := rv.Interface().(http.ResponseWriter) - var responseVal reflect.Value - if len(vals) > 1 && vals[0].Kind() == reflect.Int { - res.WriteHeader(int(vals[0].Int())) - responseVal = vals[1] - } else if len(vals) > 0 { - responseVal = vals[0] - } - if canDeref(responseVal) { - responseVal = responseVal.Elem() - } - if isByteSlice(responseVal) { - res.Write(responseVal.Bytes()) - } else { - res.Write([]byte(responseVal.String())) - } - } -} - -func isByteSlice(val reflect.Value) bool { - return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 -} - -func canDeref(val reflect.Value) bool { - return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr -} diff --git a/vendor/github.com/go-martini/martini/router.go b/vendor/github.com/go-martini/martini/router.go deleted file mode 100644 index 3abbabb..0000000 --- a/vendor/github.com/go-martini/martini/router.go +++ /dev/null @@ -1,425 +0,0 @@ -package martini - -import ( - "fmt" - "net/http" - "reflect" - "regexp" - "strconv" - "sync" -) - -// Params is a map of name/value pairs for named routes. An instance of martini.Params is available to be injected into any route handler. -type Params map[string]string - -// Router is Martini's de-facto routing interface. Supports HTTP verbs, stacked handlers, and dependency injection. -type Router interface { - Routes - - // Group adds a group where related routes can be added. - Group(string, func(Router), ...Handler) - // Get adds a route for a HTTP GET request to the specified matching pattern. - Get(string, ...Handler) Route - // Patch adds a route for a HTTP PATCH request to the specified matching pattern. - Patch(string, ...Handler) Route - // Post adds a route for a HTTP POST request to the specified matching pattern. - Post(string, ...Handler) Route - // Put adds a route for a HTTP PUT request to the specified matching pattern. - Put(string, ...Handler) Route - // Delete adds a route for a HTTP DELETE request to the specified matching pattern. - Delete(string, ...Handler) Route - // Options adds a route for a HTTP OPTIONS request to the specified matching pattern. - Options(string, ...Handler) Route - // Head adds a route for a HTTP HEAD request to the specified matching pattern. - Head(string, ...Handler) Route - // Any adds a route for any HTTP method request to the specified matching pattern. - Any(string, ...Handler) Route - // AddRoute adds a route for a given HTTP method request to the specified matching pattern. - AddRoute(string, string, ...Handler) Route - - // NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default. - NotFound(...Handler) - - // Handle is the entry point for routing. This is used as a martini.Handler - Handle(http.ResponseWriter, *http.Request, Context) -} - -type router struct { - routes []*route - notFounds []Handler - groups []group - routesLock sync.RWMutex -} - -type group struct { - pattern string - handlers []Handler -} - -// NewRouter creates a new Router instance. -// If you aren't using ClassicMartini, then you can add Routes as a -// service with: -// -// m := martini.New() -// r := martini.NewRouter() -// m.MapTo(r, (*martini.Routes)(nil)) -// -// If you are using ClassicMartini, then this is done for you. -func NewRouter() Router { - return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)} -} - -func (r *router) Group(pattern string, fn func(Router), h ...Handler) { - r.groups = append(r.groups, group{pattern, h}) - fn(r) - r.groups = r.groups[:len(r.groups)-1] -} - -func (r *router) Get(pattern string, h ...Handler) Route { - return r.addRoute("GET", pattern, h) -} - -func (r *router) Patch(pattern string, h ...Handler) Route { - return r.addRoute("PATCH", pattern, h) -} - -func (r *router) Post(pattern string, h ...Handler) Route { - return r.addRoute("POST", pattern, h) -} - -func (r *router) Put(pattern string, h ...Handler) Route { - return r.addRoute("PUT", pattern, h) -} - -func (r *router) Delete(pattern string, h ...Handler) Route { - return r.addRoute("DELETE", pattern, h) -} - -func (r *router) Options(pattern string, h ...Handler) Route { - return r.addRoute("OPTIONS", pattern, h) -} - -func (r *router) Head(pattern string, h ...Handler) Route { - return r.addRoute("HEAD", pattern, h) -} - -func (r *router) Any(pattern string, h ...Handler) Route { - return r.addRoute("*", pattern, h) -} - -func (r *router) AddRoute(method, pattern string, h ...Handler) Route { - return r.addRoute(method, pattern, h) -} - -func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) { - bestMatch := NoMatch - var bestVals map[string]string - var bestRoute *route - for _, route := range r.getRoutes() { - match, vals := route.Match(req.Method, req.URL.Path) - if match.BetterThan(bestMatch) { - bestMatch = match - bestVals = vals - bestRoute = route - if match == ExactMatch { - break - } - } - } - if bestMatch != NoMatch { - params := Params(bestVals) - context.Map(params) - bestRoute.Handle(context, res) - return - } - - // no routes exist, 404 - c := &routeContext{context, 0, r.notFounds} - context.MapTo(c, (*Context)(nil)) - c.run() -} - -func (r *router) NotFound(handler ...Handler) { - r.notFounds = handler -} - -func (r *router) addRoute(method string, pattern string, handlers []Handler) *route { - if len(r.groups) > 0 { - groupPattern := "" - h := make([]Handler, 0) - for _, g := range r.groups { - groupPattern += g.pattern - h = append(h, g.handlers...) - } - - pattern = groupPattern + pattern - h = append(h, handlers...) - handlers = h - } - - route := newRoute(method, pattern, handlers) - route.Validate() - r.appendRoute(route) - return route -} - -func (r *router) appendRoute(rt *route) { - r.routesLock.Lock() - defer r.routesLock.Unlock() - r.routes = append(r.routes, rt) -} - -func (r *router) getRoutes() []*route { - r.routesLock.RLock() - defer r.routesLock.RUnlock() - return r.routes[:] -} - -func (r *router) findRoute(name string) *route { - for _, route := range r.getRoutes() { - if route.name == name { - return route - } - } - - return nil -} - -// Route is an interface representing a Route in Martini's routing layer. -type Route interface { - // URLWith returns a rendering of the Route's url with the given string params. - URLWith([]string) string - // Name sets a name for the route. - Name(string) - // GetName returns the name of the route. - GetName() string - // Pattern returns the pattern of the route. - Pattern() string - // Method returns the method of the route. - Method() string -} - -type route struct { - method string - regex *regexp.Regexp - handlers []Handler - pattern string - name string -} - -var routeReg1 = regexp.MustCompile(`:[^/#?()\.\\]+`) -var routeReg2 = regexp.MustCompile(`\*\*`) - -func newRoute(method string, pattern string, handlers []Handler) *route { - route := route{method, nil, handlers, pattern, ""} - pattern = routeReg1.ReplaceAllStringFunc(pattern, func(m string) string { - return fmt.Sprintf(`(?P<%s>[^/#?]+)`, m[1:]) - }) - var index int - pattern = routeReg2.ReplaceAllStringFunc(pattern, func(m string) string { - index++ - return fmt.Sprintf(`(?P<_%d>[^#?]*)`, index) - }) - pattern += `\/?` - route.regex = regexp.MustCompile(pattern) - return &route -} - -type RouteMatch int - -const ( - NoMatch RouteMatch = iota - StarMatch - OverloadMatch - ExactMatch -) - -//Higher number = better match -func (r RouteMatch) BetterThan(o RouteMatch) bool { - return r > o -} - -func (r route) MatchMethod(method string) RouteMatch { - switch { - case method == r.method: - return ExactMatch - case method == "HEAD" && r.method == "GET": - return OverloadMatch - case r.method == "*": - return StarMatch - default: - return NoMatch - } -} - -func (r route) Match(method string, path string) (RouteMatch, map[string]string) { - // add Any method matching support - match := r.MatchMethod(method) - if match == NoMatch { - return match, nil - } - - matches := r.regex.FindStringSubmatch(path) - if len(matches) > 0 && matches[0] == path { - params := make(map[string]string) - for i, name := range r.regex.SubexpNames() { - if len(name) > 0 { - params[name] = matches[i] - } - } - return match, params - } - return NoMatch, nil -} - -func (r *route) Validate() { - for _, handler := range r.handlers { - validateHandler(handler) - } -} - -func (r *route) Handle(c Context, res http.ResponseWriter) { - context := &routeContext{c, 0, r.handlers} - c.MapTo(context, (*Context)(nil)) - c.MapTo(r, (*Route)(nil)) - context.run() -} - -var urlReg = regexp.MustCompile(`:[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`) - -// URLWith returns the url pattern replacing the parameters for its values -func (r *route) URLWith(args []string) string { - if len(args) > 0 { - argCount := len(args) - i := 0 - url := urlReg.ReplaceAllStringFunc(r.pattern, func(m string) string { - var val interface{} - if i < argCount { - val = args[i] - } else { - val = m - } - i += 1 - return fmt.Sprintf(`%v`, val) - }) - - return url - } - return r.pattern -} - -func (r *route) Name(name string) { - r.name = name -} - -func (r *route) GetName() string { - return r.name -} - -func (r *route) Pattern() string { - return r.pattern -} - -func (r *route) Method() string { - return r.method -} - -// Routes is a helper service for Martini's routing layer. -type Routes interface { - // URLFor returns a rendered URL for the given route. Optional params can be passed to fulfill named parameters in the route. - URLFor(name string, params ...interface{}) string - // MethodsFor returns an array of methods available for the path - MethodsFor(path string) []string - // All returns an array with all the routes in the router. - All() []Route -} - -// URLFor returns the url for the given route name. -func (r *router) URLFor(name string, params ...interface{}) string { - route := r.findRoute(name) - - if route == nil { - panic("route not found") - } - - var args []string - for _, param := range params { - switch v := param.(type) { - case int: - args = append(args, strconv.FormatInt(int64(v), 10)) - case string: - args = append(args, v) - default: - if v != nil { - panic("Arguments passed to URLFor must be integers or strings") - } - } - } - - return route.URLWith(args) -} - -func (r *router) All() []Route { - routes := r.getRoutes() - var ri = make([]Route, len(routes)) - - for i, route := range routes { - ri[i] = Route(route) - } - - return ri -} - -func hasMethod(methods []string, method string) bool { - for _, v := range methods { - if v == method { - return true - } - } - return false -} - -// MethodsFor returns all methods available for path -func (r *router) MethodsFor(path string) []string { - methods := []string{} - for _, route := range r.getRoutes() { - matches := route.regex.FindStringSubmatch(path) - if len(matches) > 0 && matches[0] == path && !hasMethod(methods, route.method) { - methods = append(methods, route.method) - } - } - return methods -} - -type routeContext struct { - Context - index int - handlers []Handler -} - -func (r *routeContext) Next() { - r.index += 1 - r.run() -} - -func (r *routeContext) run() { - for r.index < len(r.handlers) { - handler := r.handlers[r.index] - vals, err := r.Invoke(handler) - if err != nil { - panic(err) - } - r.index += 1 - - // if the handler returned something, write it to the http response - if len(vals) > 0 { - ev := r.Get(reflect.TypeOf(ReturnHandler(nil))) - handleReturn := ev.Interface().(ReturnHandler) - handleReturn(r, vals) - } - - if r.Written() { - return - } - } -} diff --git a/vendor/github.com/go-martini/martini/static.go b/vendor/github.com/go-martini/martini/static.go deleted file mode 100644 index 51af6cf..0000000 --- a/vendor/github.com/go-martini/martini/static.go +++ /dev/null @@ -1,135 +0,0 @@ -package martini - -import ( - "log" - "net/http" - "net/url" - "path" - "path/filepath" - "strings" -) - -// StaticOptions is a struct for specifying configuration options for the martini.Static middleware. -type StaticOptions struct { - // Prefix is the optional prefix used to serve the static directory content - Prefix string - // SkipLogging will disable [Static] log messages when a static file is served. - SkipLogging bool - // IndexFile defines which file to serve as index if it exists. - IndexFile string - // Expires defines which user-defined function to use for producing a HTTP Expires Header - // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching - Expires func() string - // Fallback defines a default URL to serve when the requested resource was - // not found. - Fallback string - // Exclude defines a pattern for URLs this handler should never process. - Exclude string -} - -func prepareStaticOptions(options []StaticOptions) StaticOptions { - var opt StaticOptions - if len(options) > 0 { - opt = options[0] - } - - // Defaults - if len(opt.IndexFile) == 0 { - opt.IndexFile = "index.html" - } - // Normalize the prefix if provided - if opt.Prefix != "" { - // Ensure we have a leading '/' - if opt.Prefix[0] != '/' { - opt.Prefix = "/" + opt.Prefix - } - // Remove any trailing '/' - opt.Prefix = strings.TrimRight(opt.Prefix, "/") - } - return opt -} - -// Static returns a middleware handler that serves static files in the given directory. -func Static(directory string, staticOpt ...StaticOptions) Handler { - if !filepath.IsAbs(directory) { - directory = filepath.Join(Root, directory) - } - dir := http.Dir(directory) - opt := prepareStaticOptions(staticOpt) - - return func(res http.ResponseWriter, req *http.Request, log *log.Logger) { - if req.Method != "GET" && req.Method != "HEAD" { - return - } - if opt.Exclude != "" && strings.HasPrefix(req.URL.Path, opt.Exclude) { - return - } - file := req.URL.Path - // if we have a prefix, filter requests by stripping the prefix - if opt.Prefix != "" { - if !strings.HasPrefix(file, opt.Prefix) { - return - } - file = file[len(opt.Prefix):] - if file != "" && file[0] != '/' { - return - } - } - f, err := dir.Open(file) - if err != nil { - // try any fallback before giving up - if opt.Fallback != "" { - file = opt.Fallback // so that logging stays true - f, err = dir.Open(opt.Fallback) - } - - if err != nil { - // discard the error? - return - } - } - defer f.Close() - - fi, err := f.Stat() - if err != nil { - return - } - - // try to serve index file - if fi.IsDir() { - // redirect if missing trailing slash - if !strings.HasSuffix(req.URL.Path, "/") { - dest := url.URL{ - Path: req.URL.Path + "/", - RawQuery: req.URL.RawQuery, - Fragment: req.URL.Fragment, - } - http.Redirect(res, req, dest.String(), http.StatusFound) - return - } - - file = path.Join(file, opt.IndexFile) - f, err = dir.Open(file) - if err != nil { - return - } - defer f.Close() - - fi, err = f.Stat() - if err != nil || fi.IsDir() { - return - } - } - - if !opt.SkipLogging { - log.Println("[Static] Serving " + file) - } - - // Add an Expires header to the static content - if opt.Expires != nil { - res.Header().Set("Expires", opt.Expires()) - } - - http.ServeContent(res, req, file, fi.ModTime(), f) - } -} diff --git a/vendor/github.com/go-martini/martini/wercker.yml b/vendor/github.com/go-martini/martini/wercker.yml deleted file mode 100644 index f8bf918..0000000 --- a/vendor/github.com/go-martini/martini/wercker.yml +++ /dev/null @@ -1 +0,0 @@ -box: wercker/golang@1.1.1 \ No newline at end of file diff --git a/vendor/modules.txt b/vendor/modules.txt deleted file mode 100644 index dcbda9e..0000000 --- a/vendor/modules.txt +++ /dev/null @@ -1,6 +0,0 @@ -# github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 -## explicit -github.com/codegangsta/inject -# github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab -## explicit -github.com/go-martini/martini From fb94e27cd0c6d69b3e573003b311b0f96d05b80b Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 09:05:03 +0200 Subject: [PATCH 06/17] Rename SkipCountFunction to SkipRegisterFunction --- throttle.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/throttle.go b/throttle.go index ab44afa..d5ca7cf 100644 --- a/throttle.go +++ b/throttle.go @@ -59,7 +59,7 @@ type Options struct { // If this function returns true, the request will not be counted towards the access count. // You can set it to provide your own conditions for a request to be counted based on the request, the response or // something else stored in the context - SkipCountFunction func(res http.ResponseWriter, req *http.Request) bool + SkipRegisterFunction func(res http.ResponseWriter, req *http.Request) bool } // KeyValueStorer is the required interface for the Store Option @@ -229,12 +229,12 @@ func (o *Options) Identify(req *http.Request) string { return o.IdentificationFunction(req) } -func (o *Options) SkipCount(res http.ResponseWriter, req *http.Request) bool { - if o.SkipCountFunction == nil { +func (o *Options) SkipRegister(res http.ResponseWriter, req *http.Request) bool { + if o.SkipRegisterFunction == nil { return false } - return o.SkipCountFunction(res, req) + return o.SkipRegisterFunction(res, req) } // A throttling Policy @@ -263,7 +263,7 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re return } - if o.SkipCount(resp, req) { + if o.SkipRegister(resp, req) { return } controller.RegisterAccess(id) From 61b67f699138af52c23e8702f26e95ebdfa8904b Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 11:57:14 +0200 Subject: [PATCH 07/17] Rename instances of http.ResponseWriter to resp for consistency --- throttle.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/throttle.go b/throttle.go index d5ca7cf..db3c8b3 100644 --- a/throttle.go +++ b/throttle.go @@ -59,7 +59,7 @@ type Options struct { // If this function returns true, the request will not be counted towards the access count. // You can set it to provide your own conditions for a request to be counted based on the request, the response or // something else stored in the context - SkipRegisterFunction func(res http.ResponseWriter, req *http.Request) bool + SkipRegisterFunction func(resp http.ResponseWriter, req *http.Request) bool } // KeyValueStorer is the required interface for the Store Option @@ -229,12 +229,12 @@ func (o *Options) Identify(req *http.Request) string { return o.IdentificationFunction(req) } -func (o *Options) SkipRegister(res http.ResponseWriter, req *http.Request) bool { +func (o *Options) SkipRegister(resp http.ResponseWriter, req *http.Request) bool { if o.SkipRegisterFunction == nil { return false } - return o.SkipRegisterFunction(res, req) + return o.SkipRegisterFunction(resp, req) } // A throttling Policy From 38b36d03e5c2f890f525a138b285fbac2923802a Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 12:01:53 +0200 Subject: [PATCH 08/17] Introduce a default SkipRegisterFunction --- throttle.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/throttle.go b/throttle.go index db3c8b3..be6f4ec 100644 --- a/throttle.go +++ b/throttle.go @@ -230,10 +230,6 @@ func (o *Options) Identify(req *http.Request) string { } func (o *Options) SkipRegister(resp http.ResponseWriter, req *http.Request) bool { - if o.SkipRegisterFunction == nil { - return false - } - return o.SkipRegisterFunction(resp, req) } @@ -294,6 +290,10 @@ func defaultIdentify(req *http.Request) string { return ip } +func defaultSkipRegister(http.ResponseWriter, *http.Request) bool { + return false +} + // Make a key from various parts for use in the key value store func makeKey(parts ...string) string { return strings.Join(parts, "_") @@ -308,6 +308,7 @@ func newOptions(options []*Options) *Options { KeyPrefix: defaultKeyPrefix, Store: nil, Disabled: defaultDisabled, + SkipRegisterFunction: defaultSkipRegister, } // when all defaults, return it From aca426f3df86a9801db458e2e260a728194a7cfd Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 12:02:31 +0200 Subject: [PATCH 09/17] Remove .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 723ef36..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.idea \ No newline at end of file From 74bd1cac36c066a158a3304c6dc565c5dd1c5a4f Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 13:37:27 +0200 Subject: [PATCH 10/17] Always set rate limit headers --- throttle.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/throttle.go b/throttle.go index be6f4ec..c0081d4 100644 --- a/throttle.go +++ b/throttle.go @@ -251,19 +251,18 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re return func(resp http.ResponseWriter, req *http.Request) { id := makeKey(o.KeyPrefix, quota.KeyId(), o.Identify(req)) + setRateLimitHeaders(resp, controller, id) + if controller.DeniesAccess(id) { msg := newAccessMessage(o.StatusCode, o.Message) - setRateLimitHeaders(resp, controller, id) resp.WriteHeader(msg.StatusCode) resp.Write([]byte(msg.Message)) return } - if o.SkipRegister(resp, req) { - return + if !o.SkipRegister(resp, req) { + controller.RegisterAccess(id) } - controller.RegisterAccess(id) - setRateLimitHeaders(resp, controller, id) } } From 964e534f116491d1895403dd37dbd340a1162339 Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 14:06:54 +0200 Subject: [PATCH 11/17] Make sure current request is included in the rate limit headers --- throttle.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/throttle.go b/throttle.go index c0081d4..756f294 100644 --- a/throttle.go +++ b/throttle.go @@ -251,10 +251,9 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re return func(resp http.ResponseWriter, req *http.Request) { id := makeKey(o.KeyPrefix, quota.KeyId(), o.Identify(req)) - setRateLimitHeaders(resp, controller, id) - if controller.DeniesAccess(id) { msg := newAccessMessage(o.StatusCode, o.Message) + setRateLimitHeaders(resp, controller, id) resp.WriteHeader(msg.StatusCode) resp.Write([]byte(msg.Message)) return @@ -263,6 +262,7 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re if !o.SkipRegister(resp, req) { controller.RegisterAccess(id) } + setRateLimitHeaders(resp, controller, id) } } From 0f1ff6b70104cbd9176893906913eefe8ae268b2 Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 16:56:15 +0200 Subject: [PATCH 12/17] Add SkipAccessFunction to Options --- throttle.go | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/throttle.go b/throttle.go index 756f294..ed019d9 100644 --- a/throttle.go +++ b/throttle.go @@ -60,6 +60,13 @@ type Options struct { // You can set it to provide your own conditions for a request to be counted based on the request, the response or // something else stored in the context SkipRegisterFunction func(resp http.ResponseWriter, req *http.Request) bool + + // If this function returns true, the request will not checked for access, the policy will be ignored. + // You can set it to provide your own conditions for a request or the response to be allowed, for example to + // whitelist an IP address + // Note: You can't delay processing here with something like c.Next() until after the request because that will make + // the access check to happen after executing the controller handler + SkipAccessCheckFunction func(resp http.ResponseWriter, req *http.Request) bool } // KeyValueStorer is the required interface for the Store Option @@ -233,6 +240,10 @@ func (o *Options) SkipRegister(resp http.ResponseWriter, req *http.Request) bool return o.SkipRegisterFunction(resp, req) } +func (o *Options) SkipAccessCheck(resp http.ResponseWriter, req *http.Request) bool { + return o.SkipAccessCheckFunction(resp, req) +} + // A throttling Policy // Takes two arguments, one required: // First is a Quota (A Limit with an associated time). When the given Limit @@ -251,9 +262,16 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re return func(resp http.ResponseWriter, req *http.Request) { id := makeKey(o.KeyPrefix, quota.KeyId(), o.Identify(req)) + // Already set rate limit headers in case the SkipRegister method calls some delay method like c.Next() and we + // can't set the headers again. + setRateLimitHeaders(resp, controller, id) + + if o.SkipAccessCheck(resp, req) { + return + } + if controller.DeniesAccess(id) { msg := newAccessMessage(o.StatusCode, o.Message) - setRateLimitHeaders(resp, controller, id) resp.WriteHeader(msg.StatusCode) resp.Write([]byte(msg.Message)) return @@ -262,6 +280,8 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re if !o.SkipRegister(resp, req) { controller.RegisterAccess(id) } + + // Set the headers again setRateLimitHeaders(resp, controller, id) } } @@ -293,6 +313,10 @@ func defaultSkipRegister(http.ResponseWriter, *http.Request) bool { return false } +func defaultSkipAccess(http.ResponseWriter, *http.Request) bool { + return false +} + // Make a key from various parts for use in the key value store func makeKey(parts ...string) string { return strings.Join(parts, "_") @@ -301,13 +325,14 @@ func makeKey(parts ...string) string { // Creates new default options and assigns any given options func newOptions(options []*Options) *Options { o := Options{ - StatusCode: defaultStatusCode, - Message: defaultMessage, - IdentificationFunction: defaultIdentify, - KeyPrefix: defaultKeyPrefix, - Store: nil, - Disabled: defaultDisabled, - SkipRegisterFunction: defaultSkipRegister, + StatusCode: defaultStatusCode, + Message: defaultMessage, + IdentificationFunction: defaultIdentify, + KeyPrefix: defaultKeyPrefix, + Store: nil, + Disabled: defaultDisabled, + SkipRegisterFunction: defaultSkipRegister, + SkipAccessCheckFunction: defaultSkipAccess, } // when all defaults, return it From 57c2bc9c2d994622894d1a439de187014cb54258 Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Mon, 25 Jul 2022 16:56:30 +0200 Subject: [PATCH 13/17] Add example to comment --- throttle.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/throttle.go b/throttle.go index ed019d9..d4f9f64 100644 --- a/throttle.go +++ b/throttle.go @@ -57,8 +57,8 @@ type Options struct { Disabled bool // If this function returns true, the request will not be counted towards the access count. - // You can set it to provide your own conditions for a request to be counted based on the request, the response or - // something else stored in the context + // You can set it to provide your own conditions for a request to be counted based on the request or the response, + // for example to exclude success responses from the count SkipRegisterFunction func(resp http.ResponseWriter, req *http.Request) bool // If this function returns true, the request will not checked for access, the policy will be ignored. From ac7413c3e4b53f7ede206e55b8d59d672f9efc8b Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Tue, 26 Jul 2022 10:27:00 +0200 Subject: [PATCH 14/17] Only set headers again when necessary --- throttle.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/throttle.go b/throttle.go index d4f9f64..041a15a 100644 --- a/throttle.go +++ b/throttle.go @@ -279,10 +279,10 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re if !o.SkipRegister(resp, req) { controller.RegisterAccess(id) - } - // Set the headers again - setRateLimitHeaders(resp, controller, id) + // Set the headers again + setRateLimitHeaders(resp, controller, id) + } } } From c7c79f99d14eb20c45a8dba577519c91d4c66d88 Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Tue, 26 Jul 2022 10:39:52 +0200 Subject: [PATCH 15/17] Refactor comment --- throttle.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/throttle.go b/throttle.go index 041a15a..2628805 100644 --- a/throttle.go +++ b/throttle.go @@ -62,8 +62,8 @@ type Options struct { SkipRegisterFunction func(resp http.ResponseWriter, req *http.Request) bool // If this function returns true, the request will not checked for access, the policy will be ignored. - // You can set it to provide your own conditions for a request or the response to be allowed, for example to - // whitelist an IP address + // You can set it to provide your own conditions for a request or the response to be allowed, for example to skip + // throttling on an IP allowlist // Note: You can't delay processing here with something like c.Next() until after the request because that will make // the access check to happen after executing the controller handler SkipAccessCheckFunction func(resp http.ResponseWriter, req *http.Request) bool From c7facbd788ddbbcc3209cd504a7d5d57380db214 Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Tue, 26 Jul 2022 10:48:13 +0200 Subject: [PATCH 16/17] Refactor comments --- throttle.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/throttle.go b/throttle.go index 2628805..83a24be 100644 --- a/throttle.go +++ b/throttle.go @@ -58,14 +58,15 @@ type Options struct { // If this function returns true, the request will not be counted towards the access count. // You can set it to provide your own conditions for a request to be counted based on the request or the response, - // for example to exclude success responses from the count + // for example to exclude success responses from the count. SkipRegisterFunction func(resp http.ResponseWriter, req *http.Request) bool - // If this function returns true, the request will not checked for access, the policy will be ignored. - // You can set it to provide your own conditions for a request or the response to be allowed, for example to skip - // throttling on an IP allowlist - // Note: You can't delay processing here with something like c.Next() until after the request because that will make - // the access check to happen after executing the controller handler + // If this function returns true, the request will not be checked for access, the policy will be ignored. + // You can set it to provide your own conditions for a request or a response to be allowed, for example to skip + // throttling on an IP allowlist. + // Note: You can't delay processing here with something like c.Next() until after the request, because that will + // make the access check to happen after executing the controller handler. Because of this, be aware that resp might + // not contain what you want yet. SkipAccessCheckFunction func(resp http.ResponseWriter, req *http.Request) bool } From a8d8b4d0e46fc2a3ef96fb6a2bbbad0c484f176b Mon Sep 17 00:00:00 2001 From: "wim.de.jager" Date: Tue, 26 Jul 2022 11:00:24 +0200 Subject: [PATCH 17/17] Refactor comments --- throttle.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/throttle.go b/throttle.go index 83a24be..b98352a 100644 --- a/throttle.go +++ b/throttle.go @@ -264,7 +264,7 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re id := makeKey(o.KeyPrefix, quota.KeyId(), o.Identify(req)) // Already set rate limit headers in case the SkipRegister method calls some delay method like c.Next() and we - // can't set the headers again. + // might not be able to set the headers again in that case, because the response has already been written. setRateLimitHeaders(resp, controller, id) if o.SkipAccessCheck(resp, req) { @@ -281,7 +281,8 @@ func Policy(quota *Quota, options ...*Options) func(resp http.ResponseWriter, re if !o.SkipRegister(resp, req) { controller.RegisterAccess(id) - // Set the headers again + // Set the headers again because the rate limit values have been changed at this point due to calling + // RegisterAccess. setRateLimitHeaders(resp, controller, id) } }