├── .gitignore
├── .travis.yml
├── Godeps
├── Godeps.json
└── Readme
├── LICENSE
├── Procfile
├── README.md
├── UNLICENSE
├── api
├── error_handlers.go
└── get_ip.go
├── app.json
├── main.go
├── models
└── models.go
└── vendor
└── github.com
├── julienschmidt
└── httprouter
│ ├── .travis.yml
│ ├── LICENSE
│ ├── README.md
│ ├── path.go
│ ├── router.go
│ └── tree.go
└── rs
└── cors
├── .travis.yml
├── LICENSE
├── README.md
├── cors.go
└── utils.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Elastic Beanstalk Files
2 | .elasticbeanstalk/*
3 | !.elasticbeanstalk/*.cfg.yml
4 | !.elasticbeanstalk/*.global.yml
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: go
3 | go:
4 | - 1.8
5 | before_script: go get github.com/mitchellh/gox
6 | script: go build
7 | after_success: gox -osarch="linux/amd64" && echo $TRAVIS_TEST_RESULT
8 |
--------------------------------------------------------------------------------
/Godeps/Godeps.json:
--------------------------------------------------------------------------------
1 | {
2 | "ImportPath": "github.com/rdegges/ipify-api",
3 | "GoVersion": "go1.8",
4 | "GodepVersion": "v79",
5 | "Deps": [
6 | {
7 | "ImportPath": "github.com/julienschmidt/httprouter",
8 | "Comment": "v1.1-7-g9034cff",
9 | "Rev": "9034cff70da2ab5888417e40d23cd213a1587f16"
10 | },
11 | {
12 | "ImportPath": "github.com/rs/cors",
13 | "Rev": "eb527c8097e0f19a3ff7b253a3fe70545070f420"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/Godeps/Readme:
--------------------------------------------------------------------------------
1 | This directory tree is generated automatically by godep.
2 |
3 | Please do not edit.
4 |
5 | See https://github.com/tools/godep for more information.
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Randall Degges
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: ipify-api
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ipify-api
2 |
3 | *A Simple Public IP Address API*
4 |
5 |
6 | This repository contains the source code for [ipify](https://www.ipify.org), one
7 | of the largest and most popular IP address API services on the internet. ipify
8 | serves over 30 billion requests per month!
9 |
10 |
11 | ## What does ipify do?
12 |
13 | Have you ever needed to pragmatically get your public IP address? This is quite
14 | common for developers provisioning cloud servers, for instance, where you might
15 | be creating servers and running bootstrapping software on them without access to
16 | server metadata.
17 |
18 | Being able to quickly and reliably get access to your public IP address is
19 | essential for configuring DNS, managing external services, and a number of other
20 | operationally related tasks.
21 |
22 | In general, there are a number of uses for public IP address information.
23 |
24 |
25 | ## What is ipify?
26 |
27 | ipify is a free API service anyone can use to get their public IP address. It is
28 | highly reliable (built on top of [Heroku](https://www.heroku.com/)) and fast.
29 | Typical response times (server side) are between 1ms and 10ms.
30 |
31 | ipify is also fully funded -- it's been running for years and isn't going
32 | anywhere. The people behind ipify cover all expenses and maintenance, so you
33 | can feel safe integrating with it knowing it won't be disappearing.
34 |
35 | If you'd like to use ipify in your application, no permission is needed. You can
36 | immediately start using the service without any restrictions. Simply visit our
37 | [public website](https://www.ipify.org) for more information.
38 |
39 |
40 | ## What is this project?
41 |
42 | This project is the source code that powers the ipify service. ipify is written
43 | in the Go programming language for speed and efficiency purposes. You can read
44 | an [article](https://www.rdegges.com/2018/to-30-billion-and-beyond/) written by
45 | ipify's creator, [Randall Degges](https://twitter.com/rdegges), if you'd like
46 | more information.
47 |
48 | If you'd like to contribute to ipify's development, you can do so here. Pull
49 | requests are encouraged.
50 |
51 | Finally, if you'd like to deploy your own instance of ipify, you can easily do
52 | so. Compiling this project will produce a single statically linked binary that
53 | is designed to be run on Heroku. With minor modification, ipify can be ran on
54 | any web hosting platform.
55 |
56 | Please contact [Randall](mailto:r@rdegges.com) if you need assistance deploying
57 | your own copy of ipify onto a non-Heroku host.
58 |
59 |
60 | ## Building ipify
61 |
62 | To develop and build ipify, you'll need to have the Go programming language
63 | setup on your computer. If you don't, you can read more about it here:
64 | https://golang.org/
65 |
66 | Once you have Go installed, you'll need to clone this project into your
67 | computer's GOPATH. For me, this means I'll typically do something like:
68 |
69 | ```bash
70 | $ git clone https://github.com/rdegges/ipify-api.git ~/go/src/github.com/rdegges/ipify-api
71 | ```
72 |
73 | To build the project, change to the project directory and run:
74 |
75 | ```bash
76 | $ go build
77 | ```
78 |
79 | This will create the `ipify-api` binary in the current directory that you can
80 | use for testing.
81 |
82 |
83 | ## Deploying ipify
84 |
85 | If you'd like to deploy your own version of ipify to Heroku, you can do so
86 | easily by clicking the button below. This will take you to Heroku and let you
87 | instantly provision your own copy of the ipify service.
88 |
89 | [](https://heroku.com/deploy)
90 |
91 |
92 | ## Questions?
93 |
94 | Got a question? Please create a Github issue!
95 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/api/error_handlers.go:
--------------------------------------------------------------------------------
1 | // ipify-api/error_handlers
2 | //
3 | // This package holds our API error handlers which we use to service REST API
4 | // errors.
5 |
6 | package api
7 |
8 | import (
9 | "net/http"
10 | )
11 |
12 | // MethodNotAllowed renders a method not allowed response for invalid request
13 | // types.
14 | func MethodNotAllowed(w http.ResponseWriter, r *http.Request) {
15 | w.WriteHeader(405)
16 | }
17 |
18 | // NotFound renders a not found response for invalid API endpoints.
19 | func NotFound(w http.ResponseWriter, r *http.Request) {
20 | w.WriteHeader(404)
21 | }
22 |
--------------------------------------------------------------------------------
/api/get_ip.go:
--------------------------------------------------------------------------------
1 | // ipify-api/api
2 | //
3 | // This package holds our API handlers which we use to service REST API
4 | // requests.
5 |
6 | package api
7 |
8 | import (
9 | "encoding/json"
10 | "fmt"
11 | "github.com/julienschmidt/httprouter"
12 | "github.com/rdegges/ipify-api/models"
13 | "net"
14 | "net/http"
15 | "strings"
16 | )
17 |
18 | // GetIP returns a user's public facing IP address (IPv4 OR IPv6).
19 | //
20 | // By default, it will return the IP address in plain text, but can also return
21 | // data in both JSON and JSONP if requested to.
22 | func GetIP(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
23 |
24 | err := r.ParseForm()
25 | if err != nil {
26 | panic(err)
27 | }
28 |
29 | // We'll always grab the first IP address in the X-Forwarded-For header
30 | // list. We do this because this is always the *origin* IP address, which
31 | // is the *true* IP of the user. For more information on this, see the
32 | // Wikipedia page: https://en.wikipedia.org/wiki/X-Forwarded-For
33 | ip := net.ParseIP(strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0]).String()
34 |
35 | // If the user specifies a 'format' querystring, we'll try to return the
36 | // user's IP address in the specified format.
37 | if format, ok := r.Form["format"]; ok && len(format) > 0 {
38 | jsonStr, _ := json.Marshal(models.IPAddress{ip})
39 |
40 | switch format[0] {
41 | case "json":
42 | w.Header().Set("Content-Type", "application/json")
43 | fmt.Fprintf(w, string(jsonStr))
44 | return
45 | case "jsonp":
46 | // If the user specifies a 'callback' parameter, we'll use that as
47 | // the name of our JSONP callback.
48 | callback := "callback"
49 | if val, ok := r.Form["callback"]; ok && len(val) > 0 {
50 | callback = val[0]
51 | }
52 |
53 | w.Header().Set("Content-Type", "application/javascript")
54 | fmt.Fprintf(w, callback+"("+string(jsonStr)+");")
55 | return
56 | }
57 | }
58 |
59 | // If no 'format' querystring was specified, we'll default to returning the
60 | // IP in plain text.
61 | w.Header().Set("Content-Type", "text/plain")
62 | fmt.Fprintf(w, ip)
63 | }
64 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ipify-api",
3 | "description": "A simple public IP address API.",
4 | "repository": "https://github.com/rdegges/ipify-api",
5 | "logo": "https://www.ipify.org/static/images/globe.png",
6 | "keywords": ["go", "ipify", "ip", "address", "api"]
7 | }
8 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // ipify-api
2 | //
3 | // This is the main package which starts up and runs our REST API service.
4 | //
5 | // ipify is a simple API service which returns a user's public IP address (it
6 | // supports handling both IPv4 and IPv6 addresses).
7 |
8 | package main
9 |
10 | import (
11 | "github.com/julienschmidt/httprouter"
12 | "github.com/rdegges/ipify-api/api"
13 | "github.com/rs/cors"
14 | "log"
15 | "net/http"
16 | "os"
17 | )
18 |
19 | // main launches our web server which runs indefinitely.
20 | func main() {
21 |
22 | // Setup all routes. We only service API requests, so this is basic.
23 | router := httprouter.New()
24 | router.GET("/", api.GetIP)
25 |
26 | // Setup 404 / 405 handlers.
27 | router.NotFound = http.HandlerFunc(api.NotFound)
28 | router.MethodNotAllowed = http.HandlerFunc(api.MethodNotAllowed)
29 |
30 | // Setup middlewares. For this we're basically adding:
31 | // - Support for CORS to make JSONP work.
32 | handler := cors.Default().Handler(router)
33 |
34 | // Start the server.
35 | port := os.Getenv("PORT")
36 | if port == "" {
37 | port = "3000"
38 | }
39 |
40 | log.Println("Starting HTTP server on port:", port)
41 | log.Fatal(http.ListenAndServe(":"+port, handler))
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/models/models.go:
--------------------------------------------------------------------------------
1 | // ipify-api/models
2 | //
3 | // This package contains all models used in the ipify service.
4 |
5 | package models
6 |
7 | // IPAddress is a struct we use to represent JSON API responses.
8 | type IPAddress struct {
9 | IP string `json:"ip"`
10 | }
11 |
--------------------------------------------------------------------------------
/vendor/github.com/julienschmidt/httprouter/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: go
3 | go:
4 | - 1.1
5 | - 1.2
6 | - 1.3
7 | - 1.4
8 | - 1.5
9 | - tip
10 |
--------------------------------------------------------------------------------
/vendor/github.com/julienschmidt/httprouter/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Julien Schmidt. All rights reserved.
2 |
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * The names of the contributors may not be used to endorse or promote
12 | products derived from this software without specific prior written
13 | permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL JULIEN SCHMIDT BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/vendor/github.com/julienschmidt/httprouter/README.md:
--------------------------------------------------------------------------------
1 | # HttpRouter [](https://travis-ci.org/julienschmidt/httprouter) [](http://gocover.io/github.com/julienschmidt/httprouter) [](http://godoc.org/github.com/julienschmidt/httprouter)
2 |
3 | HttpRouter is a lightweight high performance HTTP request router
4 | (also called *multiplexer* or just *mux* for short) for [Go](http://golang.org/).
5 |
6 | In contrast to the [default mux](http://golang.org/pkg/net/http/#ServeMux) of Go's net/http package, this router supports
7 | variables in the routing pattern and matches against the request method.
8 | It also scales better.
9 |
10 | The router is optimized for high performance and a small memory footprint.
11 | It scales well even with very long paths and a large number of routes.
12 | A compressing dynamic trie (radix tree) structure is used for efficient matching.
13 |
14 | ## Features
15 | **Only explicit matches:** With other routers, like [http.ServeMux](http://golang.org/pkg/net/http/#ServeMux),
16 | a requested URL path could match multiple patterns. Therefore they have some
17 | awkward pattern priority rules, like *longest match* or *first registered,
18 | first matched*. By design of this router, a request can only match exactly one
19 | or no route. As a result, there are also no unintended matches, which makes it
20 | great for SEO and improves the user experience.
21 |
22 | **Stop caring about trailing slashes:** Choose the URL style you like, the
23 | router automatically redirects the client if a trailing slash is missing or if
24 | there is one extra. Of course it only does so, if the new path has a handler.
25 | If you don't like it, you can [turn off this behavior](http://godoc.org/github.com/julienschmidt/httprouter#Router.RedirectTrailingSlash).
26 |
27 | **Path auto-correction:** Besides detecting the missing or additional trailing
28 | slash at no extra cost, the router can also fix wrong cases and remove
29 | superfluous path elements (like `../` or `//`).
30 | Is [CAPTAIN CAPS LOCK](http://www.urbandictionary.com/define.php?term=Captain+Caps+Lock) one of your users?
31 | HttpRouter can help him by making a case-insensitive look-up and redirecting him
32 | to the correct URL.
33 |
34 | **Parameters in your routing pattern:** Stop parsing the requested URL path,
35 | just give the path segment a name and the router delivers the dynamic value to
36 | you. Because of the design of the router, path parameters are very cheap.
37 |
38 | **Zero Garbage:** The matching and dispatching process generates zero bytes of
39 | garbage. In fact, the only heap allocations that are made, is by building the
40 | slice of the key-value pairs for path parameters. If the request path contains
41 | no parameters, not a single heap allocation is necessary.
42 |
43 | **Best Performance:** [Benchmarks speak for themselves](https://github.com/julienschmidt/go-http-routing-benchmark).
44 | See below for technical details of the implementation.
45 |
46 | **No more server crashes:** You can set a [Panic handler](http://godoc.org/github.com/julienschmidt/httprouter#Router.PanicHandler) to deal with panics
47 | occurring during handling a HTTP request. The router then recovers and lets the
48 | PanicHandler log what happened and deliver a nice error page.
49 |
50 | Of course you can also set **custom [NotFound](http://godoc.org/github.com/julienschmidt/httprouter#Router.NotFound) and [MethodNotAllowed](http://godoc.org/github.com/julienschmidt/httprouter#Router.MethodNotAllowed) handlers** and [**serve static files**](http://godoc.org/github.com/julienschmidt/httprouter#Router.ServeFiles).
51 |
52 | ## Usage
53 | This is just a quick introduction, view the [GoDoc](http://godoc.org/github.com/julienschmidt/httprouter) for details.
54 |
55 | Let's start with a trivial example:
56 | ```go
57 | package main
58 |
59 | import (
60 | "fmt"
61 | "github.com/julienschmidt/httprouter"
62 | "net/http"
63 | "log"
64 | )
65 |
66 | func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
67 | fmt.Fprint(w, "Welcome!\n")
68 | }
69 |
70 | func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
71 | fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
72 | }
73 |
74 | func main() {
75 | router := httprouter.New()
76 | router.GET("/", Index)
77 | router.GET("/hello/:name", Hello)
78 |
79 | log.Fatal(http.ListenAndServe(":8080", router))
80 | }
81 | ```
82 |
83 | ### Named parameters
84 | As you can see, `:name` is a *named parameter*.
85 | The values are accessible via `httprouter.Params`, which is just a slice of `httprouter.Param`s.
86 | You can get the value of a parameter either by its index in the slice, or by using the `ByName(name)` method:
87 | `:name` can be retrived by `ByName("name")`.
88 |
89 | Named parameters only match a single path segment:
90 | ```
91 | Pattern: /user/:user
92 |
93 | /user/gordon match
94 | /user/you match
95 | /user/gordon/profile no match
96 | /user/ no match
97 | ```
98 |
99 | **Note:** Since this router has only explicit matches, you can not register static routes and parameters for the same path segment. For example you can not register the patterns `/user/new` and `/user/:user` for the same request method at the same time. The routing of different request methods is independent from each other.
100 |
101 | ### Catch-All parameters
102 | The second type are *catch-all* parameters and have the form `*name`.
103 | Like the name suggests, they match everything.
104 | Therefore they must always be at the **end** of the pattern:
105 | ```
106 | Pattern: /src/*filepath
107 |
108 | /src/ match
109 | /src/somefile.go match
110 | /src/subdir/somefile.go match
111 | ```
112 |
113 | ## How does it work?
114 | The router relies on a tree structure which makes heavy use of *common prefixes*,
115 | it is basically a *compact* [*prefix tree*](http://en.wikipedia.org/wiki/Trie)
116 | (or just [*Radix tree*](http://en.wikipedia.org/wiki/Radix_tree)).
117 | Nodes with a common prefix also share a common parent. Here is a short example
118 | what the routing tree for the `GET` request method could look like:
119 |
120 | ```
121 | Priority Path Handle
122 | 9 \ *<1>
123 | 3 ├s nil
124 | 2 |├earch\ *<2>
125 | 1 |└upport\ *<3>
126 | 2 ├blog\ *<4>
127 | 1 | └:post nil
128 | 1 | └\ *<5>
129 | 2 ├about-us\ *<6>
130 | 1 | └team\ *<7>
131 | 1 └contact\ *<8>
132 | ```
133 | Every `*` represents the memory address of a handler function (a pointer).
134 | If you follow a path trough the tree from the root to the leaf, you get the
135 | complete route path, e.g `\blog\:post\`, where `:post` is just a placeholder
136 | ([*parameter*](#named-parameters)) for an actual post name. Unlike hash-maps, a
137 | tree structure also allows us to use dynamic parts like the `:post` parameter,
138 | since we actually match against the routing patterns instead of just comparing
139 | hashes. [As benchmarks show](https://github.com/julienschmidt/go-http-routing-benchmark),
140 | this works very well and efficient.
141 |
142 | Since URL paths have a hierarchical structure and make use only of a limited set
143 | of characters (byte values), it is very likely that there are a lot of common
144 | prefixes. This allows us to easily reduce the routing into ever smaller problems.
145 | Moreover the router manages a separate tree for every request method.
146 | For one thing it is more space efficient than holding a method->handle map in
147 | every single node, for another thing is also allows us to greatly reduce the
148 | routing problem before even starting the look-up in the prefix-tree.
149 |
150 | For even better scalability, the child nodes on each tree level are ordered by
151 | priority, where the priority is just the number of handles registered in sub
152 | nodes (children, grandchildren, and so on..).
153 | This helps in two ways:
154 |
155 | 1. Nodes which are part of the most routing paths are evaluated first. This
156 | helps to make as much routes as possible to be reachable as fast as possible.
157 | 2. It is some sort of cost compensation. The longest reachable path (highest
158 | cost) can always be evaluated first. The following scheme visualizes the tree
159 | structure. Nodes are evaluated from top to bottom and from left to right.
160 |
161 | ```
162 | ├------------
163 | ├---------
164 | ├-----
165 | ├----
166 | ├--
167 | ├--
168 | └-
169 | ```
170 |
171 |
172 | ## Why doesn't this work with http.Handler?
173 | **It does!** The router itself implements the http.Handler interface.
174 | Moreover the router provides convenient [adapters for http.Handler](http://godoc.org/github.com/julienschmidt/httprouter#Router.Handler)s and [http.HandlerFunc](http://godoc.org/github.com/julienschmidt/httprouter#Router.HandlerFunc)s
175 | which allows them to be used as a [httprouter.Handle](http://godoc.org/github.com/julienschmidt/httprouter#Router.Handle) when registering a route.
176 | The only disadvantage is, that no parameter values can be retrieved when a
177 | http.Handler or http.HandlerFunc is used, since there is no efficient way to
178 | pass the values with the existing function parameters.
179 | Therefore [httprouter.Handle](http://godoc.org/github.com/julienschmidt/httprouter#Router.Handle) has a third function parameter.
180 |
181 | Just try it out for yourself, the usage of HttpRouter is very straightforward. The package is compact and minimalistic, but also probably one of the easiest routers to set up.
182 |
183 |
184 | ## Where can I find Middleware *X*?
185 | This package just provides a very efficient request router with a few extra
186 | features. The router is just a [http.Handler](http://golang.org/pkg/net/http/#Handler),
187 | you can chain any http.Handler compatible middleware before the router,
188 | for example the [Gorilla handlers](http://www.gorillatoolkit.org/pkg/handlers).
189 | Or you could [just write your own](http://justinas.org/writing-http-middleware-in-go/),
190 | it's very easy!
191 |
192 | Alternatively, you could try [a web framework based on HttpRouter](#web-frameworks-based-on-httprouter).
193 |
194 | ### Multi-domain / Sub-domains
195 | Here is a quick example: Does your server serve multiple domains / hosts?
196 | You want to use sub-domains?
197 | Define a router per host!
198 | ```go
199 | // We need an object that implements the http.Handler interface.
200 | // Therefore we need a type for which we implement the ServeHTTP method.
201 | // We just use a map here, in which we map host names (with port) to http.Handlers
202 | type HostSwitch map[string]http.Handler
203 |
204 | // Implement the ServerHTTP method on our new type
205 | func (hs HostSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
206 | // Check if a http.Handler is registered for the given host.
207 | // If yes, use it to handle the request.
208 | if handler := hs[r.Host]; handler != nil {
209 | handler.ServeHTTP(w, r)
210 | } else {
211 | // Handle host names for wich no handler is registered
212 | http.Error(w, "Forbidden", 403) // Or Redirect?
213 | }
214 | }
215 |
216 | func main() {
217 | // Initialize a router as usual
218 | router := httprouter.New()
219 | router.GET("/", Index)
220 | router.GET("/hello/:name", Hello)
221 |
222 | // Make a new HostSwitch and insert the router (our http handler)
223 | // for example.com and port 12345
224 | hs := make(HostSwitch)
225 | hs["example.com:12345"] = router
226 |
227 | // Use the HostSwitch to listen and serve on port 12345
228 | log.Fatal(http.ListenAndServe(":12345", hs))
229 | }
230 | ```
231 |
232 | ### Basic Authentication
233 | Another quick example: Basic Authentication (RFC 2617) for handles:
234 |
235 | ```go
236 | package main
237 |
238 | import (
239 | "bytes"
240 | "encoding/base64"
241 | "fmt"
242 | "github.com/julienschmidt/httprouter"
243 | "net/http"
244 | "log"
245 | "strings"
246 | )
247 |
248 | func BasicAuth(h httprouter.Handle, user, pass []byte) httprouter.Handle {
249 | return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
250 | const basicAuthPrefix string = "Basic "
251 |
252 | // Get the Basic Authentication credentials
253 | auth := r.Header.Get("Authorization")
254 | if strings.HasPrefix(auth, basicAuthPrefix) {
255 | // Check credentials
256 | payload, err := base64.StdEncoding.DecodeString(auth[len(basicAuthPrefix):])
257 | if err == nil {
258 | pair := bytes.SplitN(payload, []byte(":"), 2)
259 | if len(pair) == 2 &&
260 | bytes.Equal(pair[0], user) &&
261 | bytes.Equal(pair[1], pass) {
262 |
263 | // Delegate request to the given handle
264 | h(w, r, ps)
265 | return
266 | }
267 | }
268 | }
269 |
270 | // Request Basic Authentication otherwise
271 | w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
272 | http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
273 | }
274 | }
275 |
276 | func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
277 | fmt.Fprint(w, "Not protected!\n")
278 | }
279 |
280 | func Protected(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
281 | fmt.Fprint(w, "Protected!\n")
282 | }
283 |
284 | func main() {
285 | user := []byte("gordon")
286 | pass := []byte("secret!")
287 |
288 | router := httprouter.New()
289 | router.GET("/", Index)
290 | router.GET("/protected/", BasicAuth(Protected, user, pass))
291 |
292 | log.Fatal(http.ListenAndServe(":8080", router))
293 | }
294 | ```
295 |
296 | ## Chaining with the NotFound handler
297 |
298 | **NOTE: It might be required to set [Router.HandleMethodNotAllowed](http://godoc.org/github.com/julienschmidt/httprouter#Router.HandleMethodNotAllowed) to `false` to avoid problems.**
299 |
300 | You can use another [http.Handler](http://golang.org/pkg/net/http/#Handler), for example another router, to handle requests which could not be matched by this router by using the [Router.NotFound](http://godoc.org/github.com/julienschmidt/httprouter#Router.NotFound) handler. This allows chaining.
301 |
302 | ### Static files
303 | The `NotFound` handler can for example be used to serve static files from the root path `/` (like an index.html file along with other assets):
304 | ```go
305 | // Serve static files from the ./public directory
306 | router.NotFound = http.FileServer(http.Dir("public")).ServeHTTP
307 | ```
308 |
309 | But this approach sidesteps the strict core rules of this router to avoid routing problems. A cleaner approach is to use a distinct sub-path for serving files, like `/static/*filepath` or `/files/*filepath`.
310 |
311 | ## Web Frameworks based on HttpRouter
312 | If the HttpRouter is a bit too minimalistic for you, you might try one of the following more high-level 3rd-party web frameworks building upon the HttpRouter package:
313 | * [Ace](https://github.com/plimble/ace): Blazing fast Go Web Framework
314 | * [api2go](https://github.com/univedo/api2go): A JSON API Implementation for Go
315 | * [Gin](https://github.com/gin-gonic/gin): Features a martini-like API with much better performance
316 | * [Goat](https://github.com/bahlo/goat): A minimalistic REST API server in Go
317 | * [Hikaru](https://github.com/najeira/hikaru): Supports standalone and Google AppEngine
318 | * [Hitch](https://github.com/nbio/hitch): Hitch ties httprouter, [httpcontext](https://github.com/nbio/httpcontext), and middleware up in a bow
319 | * [kami](https://github.com/guregu/kami): A tiny web framework using x/net/context
320 | * [Medeina](https://github.com/imdario/medeina): Inspired by Ruby's Roda and Cuba
321 | * [Neko](https://github.com/rocwong/neko): A lightweight web application framework for Golang
322 | * [Roxanna](https://github.com/iamthemuffinman/Roxanna): An amalgamation of httprouter, better logging, and hot reload
323 | * [siesta](https://github.com/VividCortex/siesta): Composable HTTP handlers with contexts
324 |
--------------------------------------------------------------------------------
/vendor/github.com/julienschmidt/httprouter/path.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Julien Schmidt. All rights reserved.
2 | // Based on the path package, Copyright 2009 The Go Authors.
3 | // Use of this source code is governed by a BSD-style license that can be found
4 | // in the LICENSE file.
5 |
6 | package httprouter
7 |
8 | // CleanPath is the URL version of path.Clean, it returns a canonical URL path
9 | // for p, eliminating . and .. elements.
10 | //
11 | // The following rules are applied iteratively until no further processing can
12 | // be done:
13 | // 1. Replace multiple slashes with a single slash.
14 | // 2. Eliminate each . path name element (the current directory).
15 | // 3. Eliminate each inner .. path name element (the parent directory)
16 | // along with the non-.. element that precedes it.
17 | // 4. Eliminate .. elements that begin a rooted path:
18 | // that is, replace "/.." by "/" at the beginning of a path.
19 | //
20 | // If the result of this process is an empty string, "/" is returned
21 | func CleanPath(p string) string {
22 | // Turn empty string into "/"
23 | if p == "" {
24 | return "/"
25 | }
26 |
27 | n := len(p)
28 | var buf []byte
29 |
30 | // Invariants:
31 | // reading from path; r is index of next byte to process.
32 | // writing to buf; w is index of next byte to write.
33 |
34 | // path must start with '/'
35 | r := 1
36 | w := 1
37 |
38 | if p[0] != '/' {
39 | r = 0
40 | buf = make([]byte, n+1)
41 | buf[0] = '/'
42 | }
43 |
44 | trailing := n > 2 && p[n-1] == '/'
45 |
46 | // A bit more clunky without a 'lazybuf' like the path package, but the loop
47 | // gets completely inlined (bufApp). So in contrast to the path package this
48 | // loop has no expensive function calls (except 1x make)
49 |
50 | for r < n {
51 | switch {
52 | case p[r] == '/':
53 | // empty path element, trailing slash is added after the end
54 | r++
55 |
56 | case p[r] == '.' && r+1 == n:
57 | trailing = true
58 | r++
59 |
60 | case p[r] == '.' && p[r+1] == '/':
61 | // . element
62 | r++
63 |
64 | case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'):
65 | // .. element: remove to last /
66 | r += 2
67 |
68 | if w > 1 {
69 | // can backtrack
70 | w--
71 |
72 | if buf == nil {
73 | for w > 1 && p[w] != '/' {
74 | w--
75 | }
76 | } else {
77 | for w > 1 && buf[w] != '/' {
78 | w--
79 | }
80 | }
81 | }
82 |
83 | default:
84 | // real path element.
85 | // add slash if needed
86 | if w > 1 {
87 | bufApp(&buf, p, w, '/')
88 | w++
89 | }
90 |
91 | // copy element
92 | for r < n && p[r] != '/' {
93 | bufApp(&buf, p, w, p[r])
94 | w++
95 | r++
96 | }
97 | }
98 | }
99 |
100 | // re-append trailing slash
101 | if trailing && w > 1 {
102 | bufApp(&buf, p, w, '/')
103 | w++
104 | }
105 |
106 | if buf == nil {
107 | return p[:w]
108 | }
109 | return string(buf[:w])
110 | }
111 |
112 | // internal helper to lazily create a buffer if necessary
113 | func bufApp(buf *[]byte, s string, w int, c byte) {
114 | if *buf == nil {
115 | if s[w] == c {
116 | return
117 | }
118 |
119 | *buf = make([]byte, len(s))
120 | copy(*buf, s[:w])
121 | }
122 | (*buf)[w] = c
123 | }
124 |
--------------------------------------------------------------------------------
/vendor/github.com/julienschmidt/httprouter/router.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Julien Schmidt. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be found
3 | // in the LICENSE file.
4 |
5 | // Package httprouter is a trie based high performance HTTP request router.
6 | //
7 | // A trivial example is:
8 | //
9 | // package main
10 | //
11 | // import (
12 | // "fmt"
13 | // "github.com/julienschmidt/httprouter"
14 | // "net/http"
15 | // "log"
16 | // )
17 | //
18 | // func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
19 | // fmt.Fprint(w, "Welcome!\n")
20 | // }
21 | //
22 | // func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
23 | // fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
24 | // }
25 | //
26 | // func main() {
27 | // router := httprouter.New()
28 | // router.GET("/", Index)
29 | // router.GET("/hello/:name", Hello)
30 | //
31 | // log.Fatal(http.ListenAndServe(":8080", router))
32 | // }
33 | //
34 | // The router matches incoming requests by the request method and the path.
35 | // If a handle is registered for this path and method, the router delegates the
36 | // request to that function.
37 | // For the methods GET, POST, PUT, PATCH and DELETE shortcut functions exist to
38 | // register handles, for all other methods router.Handle can be used.
39 | //
40 | // The registered path, against which the router matches incoming requests, can
41 | // contain two types of parameters:
42 | // Syntax Type
43 | // :name named parameter
44 | // *name catch-all parameter
45 | //
46 | // Named parameters are dynamic path segments. They match anything until the
47 | // next '/' or the path end:
48 | // Path: /blog/:category/:post
49 | //
50 | // Requests:
51 | // /blog/go/request-routers match: category="go", post="request-routers"
52 | // /blog/go/request-routers/ no match, but the router would redirect
53 | // /blog/go/ no match
54 | // /blog/go/request-routers/comments no match
55 | //
56 | // Catch-all parameters match anything until the path end, including the
57 | // directory index (the '/' before the catch-all). Since they match anything
58 | // until the end, catch-all parameters must always be the final path element.
59 | // Path: /files/*filepath
60 | //
61 | // Requests:
62 | // /files/ match: filepath="/"
63 | // /files/LICENSE match: filepath="/LICENSE"
64 | // /files/templates/article.html match: filepath="/templates/article.html"
65 | // /files no match, but the router would redirect
66 | //
67 | // The value of parameters is saved as a slice of the Param struct, consisting
68 | // each of a key and a value. The slice is passed to the Handle func as a third
69 | // parameter.
70 | // There are two ways to retrieve the value of a parameter:
71 | // // by the name of the parameter
72 | // user := ps.ByName("user") // defined by :user or *user
73 | //
74 | // // by the index of the parameter. This way you can also get the name (key)
75 | // thirdKey := ps[2].Key // the name of the 3rd parameter
76 | // thirdValue := ps[2].Value // the value of the 3rd parameter
77 | package httprouter
78 |
79 | import (
80 | "net/http"
81 | )
82 |
83 | // Handle is a function that can be registered to a route to handle HTTP
84 | // requests. Like http.HandlerFunc, but has a third parameter for the values of
85 | // wildcards (variables).
86 | type Handle func(http.ResponseWriter, *http.Request, Params)
87 |
88 | // Param is a single URL parameter, consisting of a key and a value.
89 | type Param struct {
90 | Key string
91 | Value string
92 | }
93 |
94 | // Params is a Param-slice, as returned by the router.
95 | // The slice is ordered, the first URL parameter is also the first slice value.
96 | // It is therefore safe to read values by the index.
97 | type Params []Param
98 |
99 | // ByName returns the value of the first Param which key matches the given name.
100 | // If no matching Param is found, an empty string is returned.
101 | func (ps Params) ByName(name string) string {
102 | for i := range ps {
103 | if ps[i].Key == name {
104 | return ps[i].Value
105 | }
106 | }
107 | return ""
108 | }
109 |
110 | // Router is a http.Handler which can be used to dispatch requests to different
111 | // handler functions via configurable routes
112 | type Router struct {
113 | trees map[string]*node
114 |
115 | // Enables automatic redirection if the current route can't be matched but a
116 | // handler for the path with (without) the trailing slash exists.
117 | // For example if /foo/ is requested but a route only exists for /foo, the
118 | // client is redirected to /foo with http status code 301 for GET requests
119 | // and 307 for all other request methods.
120 | RedirectTrailingSlash bool
121 |
122 | // If enabled, the router tries to fix the current request path, if no
123 | // handle is registered for it.
124 | // First superfluous path elements like ../ or // are removed.
125 | // Afterwards the router does a case-insensitive lookup of the cleaned path.
126 | // If a handle can be found for this route, the router makes a redirection
127 | // to the corrected path with status code 301 for GET requests and 307 for
128 | // all other request methods.
129 | // For example /FOO and /..//Foo could be redirected to /foo.
130 | // RedirectTrailingSlash is independent of this option.
131 | RedirectFixedPath bool
132 |
133 | // If enabled, the router checks if another method is allowed for the
134 | // current route, if the current request can not be routed.
135 | // If this is the case, the request is answered with 'Method Not Allowed'
136 | // and HTTP status code 405.
137 | // If no other Method is allowed, the request is delegated to the NotFound
138 | // handler.
139 | HandleMethodNotAllowed bool
140 |
141 | // Configurable http.Handler which is called when no matching route is
142 | // found. If it is not set, http.NotFound is used.
143 | NotFound http.Handler
144 |
145 | // Configurable http.Handler which is called when a request
146 | // cannot be routed and HandleMethodNotAllowed is true.
147 | // If it is not set, http.Error with http.StatusMethodNotAllowed is used.
148 | MethodNotAllowed http.Handler
149 |
150 | // Function to handle panics recovered from http handlers.
151 | // It should be used to generate a error page and return the http error code
152 | // 500 (Internal Server Error).
153 | // The handler can be used to keep your server from crashing because of
154 | // unrecovered panics.
155 | PanicHandler func(http.ResponseWriter, *http.Request, interface{})
156 | }
157 |
158 | // Make sure the Router conforms with the http.Handler interface
159 | var _ http.Handler = New()
160 |
161 | // New returns a new initialized Router.
162 | // Path auto-correction, including trailing slashes, is enabled by default.
163 | func New() *Router {
164 | return &Router{
165 | RedirectTrailingSlash: true,
166 | RedirectFixedPath: true,
167 | HandleMethodNotAllowed: true,
168 | }
169 | }
170 |
171 | // GET is a shortcut for router.Handle("GET", path, handle)
172 | func (r *Router) GET(path string, handle Handle) {
173 | r.Handle("GET", path, handle)
174 | }
175 |
176 | // HEAD is a shortcut for router.Handle("HEAD", path, handle)
177 | func (r *Router) HEAD(path string, handle Handle) {
178 | r.Handle("HEAD", path, handle)
179 | }
180 |
181 | // OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
182 | func (r *Router) OPTIONS(path string, handle Handle) {
183 | r.Handle("OPTIONS", path, handle)
184 | }
185 |
186 | // POST is a shortcut for router.Handle("POST", path, handle)
187 | func (r *Router) POST(path string, handle Handle) {
188 | r.Handle("POST", path, handle)
189 | }
190 |
191 | // PUT is a shortcut for router.Handle("PUT", path, handle)
192 | func (r *Router) PUT(path string, handle Handle) {
193 | r.Handle("PUT", path, handle)
194 | }
195 |
196 | // PATCH is a shortcut for router.Handle("PATCH", path, handle)
197 | func (r *Router) PATCH(path string, handle Handle) {
198 | r.Handle("PATCH", path, handle)
199 | }
200 |
201 | // DELETE is a shortcut for router.Handle("DELETE", path, handle)
202 | func (r *Router) DELETE(path string, handle Handle) {
203 | r.Handle("DELETE", path, handle)
204 | }
205 |
206 | // Handle registers a new request handle with the given path and method.
207 | //
208 | // For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
209 | // functions can be used.
210 | //
211 | // This function is intended for bulk loading and to allow the usage of less
212 | // frequently used, non-standardized or custom methods (e.g. for internal
213 | // communication with a proxy).
214 | func (r *Router) Handle(method, path string, handle Handle) {
215 | if path[0] != '/' {
216 | panic("path must begin with '/' in path '" + path + "'")
217 | }
218 |
219 | if r.trees == nil {
220 | r.trees = make(map[string]*node)
221 | }
222 |
223 | root := r.trees[method]
224 | if root == nil {
225 | root = new(node)
226 | r.trees[method] = root
227 | }
228 |
229 | root.addRoute(path, handle)
230 | }
231 |
232 | // Handler is an adapter which allows the usage of an http.Handler as a
233 | // request handle.
234 | func (r *Router) Handler(method, path string, handler http.Handler) {
235 | r.Handle(method, path,
236 | func(w http.ResponseWriter, req *http.Request, _ Params) {
237 | handler.ServeHTTP(w, req)
238 | },
239 | )
240 | }
241 |
242 | // HandlerFunc is an adapter which allows the usage of an http.HandlerFunc as a
243 | // request handle.
244 | func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
245 | r.Handler(method, path, handler)
246 | }
247 |
248 | // ServeFiles serves files from the given file system root.
249 | // The path must end with "/*filepath", files are then served from the local
250 | // path /defined/root/dir/*filepath.
251 | // For example if root is "/etc" and *filepath is "passwd", the local file
252 | // "/etc/passwd" would be served.
253 | // Internally a http.FileServer is used, therefore http.NotFound is used instead
254 | // of the Router's NotFound handler.
255 | // To use the operating system's file system implementation,
256 | // use http.Dir:
257 | // router.ServeFiles("/src/*filepath", http.Dir("/var/www"))
258 | func (r *Router) ServeFiles(path string, root http.FileSystem) {
259 | if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
260 | panic("path must end with /*filepath in path '" + path + "'")
261 | }
262 |
263 | fileServer := http.FileServer(root)
264 |
265 | r.GET(path, func(w http.ResponseWriter, req *http.Request, ps Params) {
266 | req.URL.Path = ps.ByName("filepath")
267 | fileServer.ServeHTTP(w, req)
268 | })
269 | }
270 |
271 | func (r *Router) recv(w http.ResponseWriter, req *http.Request) {
272 | if rcv := recover(); rcv != nil {
273 | r.PanicHandler(w, req, rcv)
274 | }
275 | }
276 |
277 | // Lookup allows the manual lookup of a method + path combo.
278 | // This is e.g. useful to build a framework around this router.
279 | // If the path was found, it returns the handle function and the path parameter
280 | // values. Otherwise the third return value indicates whether a redirection to
281 | // the same path with an extra / without the trailing slash should be performed.
282 | func (r *Router) Lookup(method, path string) (Handle, Params, bool) {
283 | if root := r.trees[method]; root != nil {
284 | return root.getValue(path)
285 | }
286 | return nil, nil, false
287 | }
288 |
289 | // ServeHTTP makes the router implement the http.Handler interface.
290 | func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
291 | if r.PanicHandler != nil {
292 | defer r.recv(w, req)
293 | }
294 |
295 | if root := r.trees[req.Method]; root != nil {
296 | path := req.URL.Path
297 |
298 | if handle, ps, tsr := root.getValue(path); handle != nil {
299 | handle(w, req, ps)
300 | return
301 | } else if req.Method != "CONNECT" && path != "/" {
302 | code := 301 // Permanent redirect, request with GET method
303 | if req.Method != "GET" {
304 | // Temporary redirect, request with same method
305 | // As of Go 1.3, Go does not support status code 308.
306 | code = 307
307 | }
308 |
309 | if tsr && r.RedirectTrailingSlash {
310 | if len(path) > 1 && path[len(path)-1] == '/' {
311 | req.URL.Path = path[:len(path)-1]
312 | } else {
313 | req.URL.Path = path + "/"
314 | }
315 | http.Redirect(w, req, req.URL.String(), code)
316 | return
317 | }
318 |
319 | // Try to fix the request path
320 | if r.RedirectFixedPath {
321 | fixedPath, found := root.findCaseInsensitivePath(
322 | CleanPath(path),
323 | r.RedirectTrailingSlash,
324 | )
325 | if found {
326 | req.URL.Path = string(fixedPath)
327 | http.Redirect(w, req, req.URL.String(), code)
328 | return
329 | }
330 | }
331 | }
332 | }
333 |
334 | // Handle 405
335 | if r.HandleMethodNotAllowed {
336 | for method := range r.trees {
337 | // Skip the requested method - we already tried this one
338 | if method == req.Method {
339 | continue
340 | }
341 |
342 | handle, _, _ := r.trees[method].getValue(req.URL.Path)
343 | if handle != nil {
344 | if r.MethodNotAllowed != nil {
345 | r.MethodNotAllowed.ServeHTTP(w, req)
346 | } else {
347 | http.Error(w,
348 | http.StatusText(http.StatusMethodNotAllowed),
349 | http.StatusMethodNotAllowed,
350 | )
351 | }
352 | return
353 | }
354 | }
355 | }
356 |
357 | // Handle 404
358 | if r.NotFound != nil {
359 | r.NotFound.ServeHTTP(w, req)
360 | } else {
361 | http.NotFound(w, req)
362 | }
363 | }
364 |
--------------------------------------------------------------------------------
/vendor/github.com/julienschmidt/httprouter/tree.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Julien Schmidt. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be found
3 | // in the LICENSE file.
4 |
5 | package httprouter
6 |
7 | import (
8 | "strings"
9 | "unicode"
10 | )
11 |
12 | func min(a, b int) int {
13 | if a <= b {
14 | return a
15 | }
16 | return b
17 | }
18 |
19 | func countParams(path string) uint8 {
20 | var n uint
21 | for i := 0; i < len(path); i++ {
22 | if path[i] != ':' && path[i] != '*' {
23 | continue
24 | }
25 | n++
26 | }
27 | if n >= 255 {
28 | return 255
29 | }
30 | return uint8(n)
31 | }
32 |
33 | type nodeType uint8
34 |
35 | const (
36 | static nodeType = 0
37 | param nodeType = 1
38 | catchAll nodeType = 2
39 | )
40 |
41 | type node struct {
42 | path string
43 | wildChild bool
44 | nType nodeType
45 | maxParams uint8
46 | indices string
47 | children []*node
48 | handle Handle
49 | priority uint32
50 | }
51 |
52 | // increments priority of the given child and reorders if necessary
53 | func (n *node) incrementChildPrio(pos int) int {
54 | n.children[pos].priority++
55 | prio := n.children[pos].priority
56 |
57 | // adjust position (move to front)
58 | newPos := pos
59 | for newPos > 0 && n.children[newPos-1].priority < prio {
60 | // swap node positions
61 | tmpN := n.children[newPos-1]
62 | n.children[newPos-1] = n.children[newPos]
63 | n.children[newPos] = tmpN
64 |
65 | newPos--
66 | }
67 |
68 | // build new index char string
69 | if newPos != pos {
70 | n.indices = n.indices[:newPos] + // unchanged prefix, might be empty
71 | n.indices[pos:pos+1] + // the index char we move
72 | n.indices[newPos:pos] + n.indices[pos+1:] // rest without char at 'pos'
73 | }
74 |
75 | return newPos
76 | }
77 |
78 | // addRoute adds a node with the given handle to the path.
79 | // Not concurrency-safe!
80 | func (n *node) addRoute(path string, handle Handle) {
81 | fullPath := path
82 | n.priority++
83 | numParams := countParams(path)
84 |
85 | // non-empty tree
86 | if len(n.path) > 0 || len(n.children) > 0 {
87 | walk:
88 | for {
89 | // Update maxParams of the current node
90 | if numParams > n.maxParams {
91 | n.maxParams = numParams
92 | }
93 |
94 | // Find the longest common prefix.
95 | // This also implies that the common prefix contains no ':' or '*'
96 | // since the existing key can't contain those chars.
97 | i := 0
98 | max := min(len(path), len(n.path))
99 | for i < max && path[i] == n.path[i] {
100 | i++
101 | }
102 |
103 | // Split edge
104 | if i < len(n.path) {
105 | child := node{
106 | path: n.path[i:],
107 | wildChild: n.wildChild,
108 | indices: n.indices,
109 | children: n.children,
110 | handle: n.handle,
111 | priority: n.priority - 1,
112 | }
113 |
114 | // Update maxParams (max of all children)
115 | for i := range child.children {
116 | if child.children[i].maxParams > child.maxParams {
117 | child.maxParams = child.children[i].maxParams
118 | }
119 | }
120 |
121 | n.children = []*node{&child}
122 | // []byte for proper unicode char conversion, see #65
123 | n.indices = string([]byte{n.path[i]})
124 | n.path = path[:i]
125 | n.handle = nil
126 | n.wildChild = false
127 | }
128 |
129 | // Make new node a child of this node
130 | if i < len(path) {
131 | path = path[i:]
132 |
133 | if n.wildChild {
134 | n = n.children[0]
135 | n.priority++
136 |
137 | // Update maxParams of the child node
138 | if numParams > n.maxParams {
139 | n.maxParams = numParams
140 | }
141 | numParams--
142 |
143 | // Check if the wildcard matches
144 | if len(path) >= len(n.path) && n.path == path[:len(n.path)] {
145 | // check for longer wildcard, e.g. :name and :names
146 | if len(n.path) >= len(path) || path[len(n.path)] == '/' {
147 | continue walk
148 | }
149 | }
150 |
151 | panic("path segment '" + path +
152 | "' conflicts with existing wildcard '" + n.path +
153 | "' in path '" + fullPath + "'")
154 | }
155 |
156 | c := path[0]
157 |
158 | // slash after param
159 | if n.nType == param && c == '/' && len(n.children) == 1 {
160 | n = n.children[0]
161 | n.priority++
162 | continue walk
163 | }
164 |
165 | // Check if a child with the next path byte exists
166 | for i := 0; i < len(n.indices); i++ {
167 | if c == n.indices[i] {
168 | i = n.incrementChildPrio(i)
169 | n = n.children[i]
170 | continue walk
171 | }
172 | }
173 |
174 | // Otherwise insert it
175 | if c != ':' && c != '*' {
176 | // []byte for proper unicode char conversion, see #65
177 | n.indices += string([]byte{c})
178 | child := &node{
179 | maxParams: numParams,
180 | }
181 | n.children = append(n.children, child)
182 | n.incrementChildPrio(len(n.indices) - 1)
183 | n = child
184 | }
185 | n.insertChild(numParams, path, fullPath, handle)
186 | return
187 |
188 | } else if i == len(path) { // Make node a (in-path) leaf
189 | if n.handle != nil {
190 | panic("a handle is already registered for path ''" + fullPath + "'")
191 | }
192 | n.handle = handle
193 | }
194 | return
195 | }
196 | } else { // Empty tree
197 | n.insertChild(numParams, path, fullPath, handle)
198 | }
199 | }
200 |
201 | func (n *node) insertChild(numParams uint8, path, fullPath string, handle Handle) {
202 | var offset int // already handled bytes of the path
203 |
204 | // find prefix until first wildcard (beginning with ':'' or '*'')
205 | for i, max := 0, len(path); numParams > 0; i++ {
206 | c := path[i]
207 | if c != ':' && c != '*' {
208 | continue
209 | }
210 |
211 | // find wildcard end (either '/' or path end)
212 | end := i + 1
213 | for end < max && path[end] != '/' {
214 | switch path[end] {
215 | // the wildcard name must not contain ':' and '*'
216 | case ':', '*':
217 | panic("only one wildcard per path segment is allowed, has: '" +
218 | path[i:] + "' in path '" + fullPath + "'")
219 | default:
220 | end++
221 | }
222 | }
223 |
224 | // check if this Node existing children which would be
225 | // unreachable if we insert the wildcard here
226 | if len(n.children) > 0 {
227 | panic("wildcard route '" + path[i:end] +
228 | "' conflicts with existing children in path '" + fullPath + "'")
229 | }
230 |
231 | // check if the wildcard has a name
232 | if end-i < 2 {
233 | panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
234 | }
235 |
236 | if c == ':' { // param
237 | // split path at the beginning of the wildcard
238 | if i > 0 {
239 | n.path = path[offset:i]
240 | offset = i
241 | }
242 |
243 | child := &node{
244 | nType: param,
245 | maxParams: numParams,
246 | }
247 | n.children = []*node{child}
248 | n.wildChild = true
249 | n = child
250 | n.priority++
251 | numParams--
252 |
253 | // if the path doesn't end with the wildcard, then there
254 | // will be another non-wildcard subpath starting with '/'
255 | if end < max {
256 | n.path = path[offset:end]
257 | offset = end
258 |
259 | child := &node{
260 | maxParams: numParams,
261 | priority: 1,
262 | }
263 | n.children = []*node{child}
264 | n = child
265 | }
266 |
267 | } else { // catchAll
268 | if end != max || numParams > 1 {
269 | panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
270 | }
271 |
272 | if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
273 | panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
274 | }
275 |
276 | // currently fixed width 1 for '/'
277 | i--
278 | if path[i] != '/' {
279 | panic("no / before catch-all in path '" + fullPath + "'")
280 | }
281 |
282 | n.path = path[offset:i]
283 |
284 | // first node: catchAll node with empty path
285 | child := &node{
286 | wildChild: true,
287 | nType: catchAll,
288 | maxParams: 1,
289 | }
290 | n.children = []*node{child}
291 | n.indices = string(path[i])
292 | n = child
293 | n.priority++
294 |
295 | // second node: node holding the variable
296 | child = &node{
297 | path: path[i:],
298 | nType: catchAll,
299 | maxParams: 1,
300 | handle: handle,
301 | priority: 1,
302 | }
303 | n.children = []*node{child}
304 |
305 | return
306 | }
307 | }
308 |
309 | // insert remaining path part and handle to the leaf
310 | n.path = path[offset:]
311 | n.handle = handle
312 | }
313 |
314 | // Returns the handle registered with the given path (key). The values of
315 | // wildcards are saved to a map.
316 | // If no handle can be found, a TSR (trailing slash redirect) recommendation is
317 | // made if a handle exists with an extra (without the) trailing slash for the
318 | // given path.
319 | func (n *node) getValue(path string) (handle Handle, p Params, tsr bool) {
320 | walk: // Outer loop for walking the tree
321 | for {
322 | if len(path) > len(n.path) {
323 | if path[:len(n.path)] == n.path {
324 | path = path[len(n.path):]
325 | // If this node does not have a wildcard (param or catchAll)
326 | // child, we can just look up the next child node and continue
327 | // to walk down the tree
328 | if !n.wildChild {
329 | c := path[0]
330 | for i := 0; i < len(n.indices); i++ {
331 | if c == n.indices[i] {
332 | n = n.children[i]
333 | continue walk
334 | }
335 | }
336 |
337 | // Nothing found.
338 | // We can recommend to redirect to the same URL without a
339 | // trailing slash if a leaf exists for that path.
340 | tsr = (path == "/" && n.handle != nil)
341 | return
342 |
343 | }
344 |
345 | // handle wildcard child
346 | n = n.children[0]
347 | switch n.nType {
348 | case param:
349 | // find param end (either '/' or path end)
350 | end := 0
351 | for end < len(path) && path[end] != '/' {
352 | end++
353 | }
354 |
355 | // save param value
356 | if p == nil {
357 | // lazy allocation
358 | p = make(Params, 0, n.maxParams)
359 | }
360 | i := len(p)
361 | p = p[:i+1] // expand slice within preallocated capacity
362 | p[i].Key = n.path[1:]
363 | p[i].Value = path[:end]
364 |
365 | // we need to go deeper!
366 | if end < len(path) {
367 | if len(n.children) > 0 {
368 | path = path[end:]
369 | n = n.children[0]
370 | continue walk
371 | }
372 |
373 | // ... but we can't
374 | tsr = (len(path) == end+1)
375 | return
376 | }
377 |
378 | if handle = n.handle; handle != nil {
379 | return
380 | } else if len(n.children) == 1 {
381 | // No handle found. Check if a handle for this path + a
382 | // trailing slash exists for TSR recommendation
383 | n = n.children[0]
384 | tsr = (n.path == "/" && n.handle != nil)
385 | }
386 |
387 | return
388 |
389 | case catchAll:
390 | // save param value
391 | if p == nil {
392 | // lazy allocation
393 | p = make(Params, 0, n.maxParams)
394 | }
395 | i := len(p)
396 | p = p[:i+1] // expand slice within preallocated capacity
397 | p[i].Key = n.path[2:]
398 | p[i].Value = path
399 |
400 | handle = n.handle
401 | return
402 |
403 | default:
404 | panic("invalid node type")
405 | }
406 | }
407 | } else if path == n.path {
408 | // We should have reached the node containing the handle.
409 | // Check if this node has a handle registered.
410 | if handle = n.handle; handle != nil {
411 | return
412 | }
413 |
414 | // No handle found. Check if a handle for this path + a
415 | // trailing slash exists for trailing slash recommendation
416 | for i := 0; i < len(n.indices); i++ {
417 | if n.indices[i] == '/' {
418 | n = n.children[i]
419 | tsr = (len(n.path) == 1 && n.handle != nil) ||
420 | (n.nType == catchAll && n.children[0].handle != nil)
421 | return
422 | }
423 | }
424 |
425 | return
426 | }
427 |
428 | // Nothing found. We can recommend to redirect to the same URL with an
429 | // extra trailing slash if a leaf exists for that path
430 | tsr = (path == "/") ||
431 | (len(n.path) == len(path)+1 && n.path[len(path)] == '/' &&
432 | path == n.path[:len(n.path)-1] && n.handle != nil)
433 | return
434 | }
435 | }
436 |
437 | // Makes a case-insensitive lookup of the given path and tries to find a handler.
438 | // It can optionally also fix trailing slashes.
439 | // It returns the case-corrected path and a bool indicating whether the lookup
440 | // was successful.
441 | func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPath []byte, found bool) {
442 | ciPath = make([]byte, 0, len(path)+1) // preallocate enough memory
443 |
444 | // Outer loop for walking the tree
445 | for len(path) >= len(n.path) && strings.ToLower(path[:len(n.path)]) == strings.ToLower(n.path) {
446 | path = path[len(n.path):]
447 | ciPath = append(ciPath, n.path...)
448 |
449 | if len(path) > 0 {
450 | // If this node does not have a wildcard (param or catchAll) child,
451 | // we can just look up the next child node and continue to walk down
452 | // the tree
453 | if !n.wildChild {
454 | r := unicode.ToLower(rune(path[0]))
455 | for i, index := range n.indices {
456 | // must use recursive approach since both index and
457 | // ToLower(index) could exist. We must check both.
458 | if r == unicode.ToLower(index) {
459 | out, found := n.children[i].findCaseInsensitivePath(path, fixTrailingSlash)
460 | if found {
461 | return append(ciPath, out...), true
462 | }
463 | }
464 | }
465 |
466 | // Nothing found. We can recommend to redirect to the same URL
467 | // without a trailing slash if a leaf exists for that path
468 | found = (fixTrailingSlash && path == "/" && n.handle != nil)
469 | return
470 | }
471 |
472 | n = n.children[0]
473 | switch n.nType {
474 | case param:
475 | // find param end (either '/' or path end)
476 | k := 0
477 | for k < len(path) && path[k] != '/' {
478 | k++
479 | }
480 |
481 | // add param value to case insensitive path
482 | ciPath = append(ciPath, path[:k]...)
483 |
484 | // we need to go deeper!
485 | if k < len(path) {
486 | if len(n.children) > 0 {
487 | path = path[k:]
488 | n = n.children[0]
489 | continue
490 | }
491 |
492 | // ... but we can't
493 | if fixTrailingSlash && len(path) == k+1 {
494 | return ciPath, true
495 | }
496 | return
497 | }
498 |
499 | if n.handle != nil {
500 | return ciPath, true
501 | } else if fixTrailingSlash && len(n.children) == 1 {
502 | // No handle found. Check if a handle for this path + a
503 | // trailing slash exists
504 | n = n.children[0]
505 | if n.path == "/" && n.handle != nil {
506 | return append(ciPath, '/'), true
507 | }
508 | }
509 | return
510 |
511 | case catchAll:
512 | return append(ciPath, path...), true
513 |
514 | default:
515 | panic("invalid node type")
516 | }
517 | } else {
518 | // We should have reached the node containing the handle.
519 | // Check if this node has a handle registered.
520 | if n.handle != nil {
521 | return ciPath, true
522 | }
523 |
524 | // No handle found.
525 | // Try to fix the path by adding a trailing slash
526 | if fixTrailingSlash {
527 | for i := 0; i < len(n.indices); i++ {
528 | if n.indices[i] == '/' {
529 | n = n.children[i]
530 | if (len(n.path) == 1 && n.handle != nil) ||
531 | (n.nType == catchAll && n.children[0].handle != nil) {
532 | return append(ciPath, '/'), true
533 | }
534 | return
535 | }
536 | }
537 | }
538 | return
539 | }
540 | }
541 |
542 | // Nothing found.
543 | // Try to fix the path by adding / removing a trailing slash
544 | if fixTrailingSlash {
545 | if path == "/" {
546 | return ciPath, true
547 | }
548 | if len(path)+1 == len(n.path) && n.path[len(path)] == '/' &&
549 | strings.ToLower(path) == strings.ToLower(n.path[:len(path)]) &&
550 | n.handle != nil {
551 | return append(ciPath, n.path...), true
552 | }
553 | }
554 | return
555 | }
556 |
--------------------------------------------------------------------------------
/vendor/github.com/rs/cors/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - 1.3
4 | - 1.4
5 |
--------------------------------------------------------------------------------
/vendor/github.com/rs/cors/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Olivier Poitrey
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/vendor/github.com/rs/cors/README.md:
--------------------------------------------------------------------------------
1 | # Go CORS handler [](https://godoc.org/github.com/rs/cors) [](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [](https://travis-ci.org/rs/cors)
2 |
3 | CORS is a `net/http` handler implementing [Cross Origin Resource Sharing W3 specification](http://www.w3.org/TR/cors/) in Golang.
4 |
5 | ## Getting Started
6 |
7 | 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`.
8 |
9 | ```go
10 | package main
11 |
12 | import (
13 | "net/http"
14 |
15 | "github.com/rs/cors"
16 | )
17 |
18 | func main() {
19 | mux := http.NewServeMux()
20 | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
21 | w.Header().Set("Content-Type", "application/json")
22 | w.Write([]byte("{\"hello\": \"world\"}"))
23 | })
24 |
25 | // cors.Default() setup the middleware with default options being
26 | // all origins accepted with simple methods (GET, POST). See
27 | // documentation below for more options.
28 | handler := cors.Default().Handler(mux)
29 | http.ListenAndServe(":8080", handler)
30 | }
31 | ```
32 |
33 | Install `cors`:
34 |
35 | go get github.com/rs/cors
36 |
37 | Then run your server:
38 |
39 | go run server.go
40 |
41 | The server now runs on `localhost:8080`:
42 |
43 | $ curl -D - -H 'Origin: http://foo.com' http://localhost:8080/
44 | HTTP/1.1 200 OK
45 | Access-Control-Allow-Origin: foo.com
46 | Content-Type: application/json
47 | Date: Sat, 25 Oct 2014 03:43:57 GMT
48 | Content-Length: 18
49 |
50 | {"hello": "world"}
51 |
52 | ### More Examples
53 |
54 | * `net/http`: [examples/nethttp/server.go](https://github.com/rs/cors/blob/master/examples/nethttp/server.go)
55 | * [Goji](https://goji.io): [examples/goji/server.go](https://github.com/rs/cors/blob/master/examples/goji/server.go)
56 | * [Martini](http://martini.codegangsta.io): [examples/martini/server.go](https://github.com/rs/cors/blob/master/examples/martini/server.go)
57 | * [Negroni](https://github.com/codegangsta/negroni): [examples/negroni/server.go](https://github.com/rs/cors/blob/master/examples/negroni/server.go)
58 | * [Alice](https://github.com/justinas/alice): [examples/alice/server.go](https://github.com/rs/cors/blob/master/examples/alice/server.go)
59 |
60 | ## Parameters
61 |
62 | Parameters are passed to the middleware thru the `cors.New` method as follow:
63 |
64 | ```go
65 | c := cors.New(cors.Options{
66 | AllowedOrigins: []string{"http://foo.com"},
67 | AllowCredentials: true,
68 | })
69 |
70 | // Insert the middleware
71 | handler = c.Handler(handler)
72 | ```
73 |
74 | * **AllowedOrigins** `[]string`: A list of origins a cross-domain request can be executed from. If the special `*` value is present in the list, all origins will be allowed. An origin may contain a wildcard (`*`) to replace 0 or more characters (i.e.: `http://*.domain.com`). Usage of wildcards implies a small performance penality. Only one wildcard can be used per origin. The default value is `*`.
75 | * **AllowOriginFunc** `func (origin string) bool`: A custom function to validate the origin. It take the origin as argument and returns true if allowed or false otherwise. If this option is set, the content of `AllowedOrigins` is ignored
76 | * **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests. Default value is simple methods (`GET` and `POST`).
77 | * **AllowedHeaders** `[]string`: A list of non simple headers the client is allowed to use with cross-domain requests.
78 | * **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification
79 | * **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`.
80 | * **MaxAge** `int`: Indicates how long (in seconds) the results of a preflight request can be cached. The default is `0` which stands for no max age.
81 | * **OptionsPassthrough** `bool`: Instructs preflight to let other potential next handlers to process the `OPTIONS` method. Turn this on if your application handles `OPTIONS`.
82 | * **Debug** `bool`: Debugging flag adds additional output to debug server side CORS issues.
83 |
84 | See [API documentation](http://godoc.org/github.com/rs/cors) for more info.
85 |
86 | ## Benchmarks
87 |
88 | BenchmarkWithout 20000000 64.6 ns/op 8 B/op 1 allocs/op
89 | BenchmarkDefault 3000000 469 ns/op 114 B/op 2 allocs/op
90 | BenchmarkAllowedOrigin 3000000 608 ns/op 114 B/op 2 allocs/op
91 | BenchmarkPreflight 20000000 73.2 ns/op 0 B/op 0 allocs/op
92 | BenchmarkPreflightHeader 20000000 73.6 ns/op 0 B/op 0 allocs/op
93 | BenchmarkParseHeaderList 2000000 847 ns/op 184 B/op 6 allocs/op
94 | BenchmarkParse…Single 5000000 290 ns/op 32 B/op 3 allocs/op
95 | BenchmarkParse…Normalized 2000000 776 ns/op 160 B/op 6 allocs/op
96 |
97 | ## Licenses
98 |
99 | All source code is licensed under the [MIT License](https://raw.github.com/rs/cors/master/LICENSE).
100 |
--------------------------------------------------------------------------------
/vendor/github.com/rs/cors/cors.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package cors is net/http handler to handle CORS related requests
3 | as defined by http://www.w3.org/TR/cors/
4 |
5 | You can configure it by passing an option struct to cors.New:
6 |
7 | c := cors.New(cors.Options{
8 | AllowedOrigins: []string{"foo.com"},
9 | AllowedMethods: []string{"GET", "POST", "DELETE"},
10 | AllowCredentials: true,
11 | })
12 |
13 | Then insert the handler in the chain:
14 |
15 | handler = c.Handler(handler)
16 |
17 | See Options documentation for more options.
18 |
19 | The resulting handler is a standard net/http handler.
20 | */
21 | package cors
22 |
23 | import (
24 | "log"
25 | "net/http"
26 | "os"
27 | "strconv"
28 | "strings"
29 | )
30 |
31 | // Options is a configuration container to setup the CORS middleware.
32 | type Options struct {
33 | // AllowedOrigins is a list of origins a cross-domain request can be executed from.
34 | // If the special "*" value is present in the list, all origins will be allowed.
35 | // An origin may contain a wildcard (*) to replace 0 or more characters
36 | // (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
37 | // Only one wildcard can be used per origin.
38 | // Default value is ["*"]
39 | AllowedOrigins []string
40 | // AllowOriginFunc is a custom function to validate the origin. It take the origin
41 | // as argument and returns true if allowed or false otherwise. If this option is
42 | // set, the content of AllowedOrigins is ignored.
43 | AllowOriginFunc func(origin string) bool
44 | // AllowedMethods is a list of methods the client is allowed to use with
45 | // cross-domain requests. Default value is simple methods (GET and POST)
46 | AllowedMethods []string
47 | // AllowedHeaders is list of non simple headers the client is allowed to use with
48 | // cross-domain requests.
49 | // If the special "*" value is present in the list, all headers will be allowed.
50 | // Default value is [] but "Origin" is always appended to the list.
51 | AllowedHeaders []string
52 | // ExposedHeaders indicates which headers are safe to expose to the API of a CORS
53 | // API specification
54 | ExposedHeaders []string
55 | // AllowCredentials indicates whether the request can include user credentials like
56 | // cookies, HTTP authentication or client side SSL certificates.
57 | AllowCredentials bool
58 | // MaxAge indicates how long (in seconds) the results of a preflight request
59 | // can be cached
60 | MaxAge int
61 | // OptionsPassthrough instructs preflight to let other potential next handlers to
62 | // process the OPTIONS method. Turn this on if your application handles OPTIONS.
63 | OptionsPassthrough bool
64 | // Debugging flag adds additional output to debug server side CORS issues
65 | Debug bool
66 | }
67 |
68 | // Cors http handler
69 | type Cors struct {
70 | // Debug logger
71 | log *log.Logger
72 | // Set to true when allowed origins contains a "*"
73 | allowedOriginsAll bool
74 | // Normalized list of plain allowed origins
75 | allowedOrigins []string
76 | // List of allowed origins containing wildcards
77 | allowedWOrigins []wildcard
78 | // Optional origin validator function
79 | allowOriginFunc func(origin string) bool
80 | // Set to true when allowed headers contains a "*"
81 | allowedHeadersAll bool
82 | // Normalized list of allowed headers
83 | allowedHeaders []string
84 | // Normalized list of allowed methods
85 | allowedMethods []string
86 | // Normalized list of exposed headers
87 | exposedHeaders []string
88 | allowCredentials bool
89 | maxAge int
90 | optionPassthrough bool
91 | }
92 |
93 | // New creates a new Cors handler with the provided options.
94 | func New(options Options) *Cors {
95 | c := &Cors{
96 | exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey),
97 | allowOriginFunc: options.AllowOriginFunc,
98 | allowCredentials: options.AllowCredentials,
99 | maxAge: options.MaxAge,
100 | optionPassthrough: options.OptionsPassthrough,
101 | }
102 | if options.Debug {
103 | c.log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
104 | }
105 |
106 | // Normalize options
107 | // Note: for origins and methods matching, the spec requires a case-sensitive matching.
108 | // As it may error prone, we chose to ignore the spec here.
109 |
110 | // Allowed Origins
111 | if len(options.AllowedOrigins) == 0 {
112 | // Default is all origins
113 | c.allowedOriginsAll = true
114 | } else {
115 | c.allowedOrigins = []string{}
116 | c.allowedWOrigins = []wildcard{}
117 | for _, origin := range options.AllowedOrigins {
118 | // Normalize
119 | origin = strings.ToLower(origin)
120 | if origin == "*" {
121 | // If "*" is present in the list, turn the whole list into a match all
122 | c.allowedOriginsAll = true
123 | c.allowedOrigins = nil
124 | c.allowedWOrigins = nil
125 | break
126 | } else if i := strings.IndexByte(origin, '*'); i >= 0 {
127 | // Split the origin in two: start and end string without the *
128 | w := wildcard{origin[0:i], origin[i+1 : len(origin)]}
129 | c.allowedWOrigins = append(c.allowedWOrigins, w)
130 | } else {
131 | c.allowedOrigins = append(c.allowedOrigins, origin)
132 | }
133 | }
134 | }
135 |
136 | // Allowed Headers
137 | if len(options.AllowedHeaders) == 0 {
138 | // Use sensible defaults
139 | c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"}
140 | } else {
141 | // Origin is always appended as some browsers will always request for this header at preflight
142 | c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey)
143 | for _, h := range options.AllowedHeaders {
144 | if h == "*" {
145 | c.allowedHeadersAll = true
146 | c.allowedHeaders = nil
147 | break
148 | }
149 | }
150 | }
151 |
152 | // Allowed Methods
153 | if len(options.AllowedMethods) == 0 {
154 | // Default is spec's "simple" methods
155 | c.allowedMethods = []string{"GET", "POST"}
156 | } else {
157 | c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
158 | }
159 |
160 | return c
161 | }
162 |
163 | // Default creates a new Cors handler with default options
164 | func Default() *Cors {
165 | return New(Options{})
166 | }
167 |
168 | // Handler apply the CORS specification on the request, and add relevant CORS headers
169 | // as necessary.
170 | func (c *Cors) Handler(h http.Handler) http.Handler {
171 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
172 | if r.Method == "OPTIONS" {
173 | c.logf("Handler: Preflight request")
174 | c.handlePreflight(w, r)
175 | // Preflight requests are standalone and should stop the chain as some other
176 | // middleware may not handle OPTIONS requests correctly. One typical example
177 | // is authentication middleware ; OPTIONS requests won't carry authentication
178 | // headers (see #1)
179 | if c.optionPassthrough {
180 | h.ServeHTTP(w, r)
181 | }
182 | } else {
183 | c.logf("Handler: Actual request")
184 | c.handleActualRequest(w, r)
185 | h.ServeHTTP(w, r)
186 | }
187 | })
188 | }
189 |
190 | // HandlerFunc provides Martini compatible handler
191 | func (c *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
192 | if r.Method == "OPTIONS" {
193 | c.logf("HandlerFunc: Preflight request")
194 | c.handlePreflight(w, r)
195 | } else {
196 | c.logf("HandlerFunc: Actual request")
197 | c.handleActualRequest(w, r)
198 | }
199 | }
200 |
201 | // Negroni compatible interface
202 | func (c *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
203 | if r.Method == "OPTIONS" {
204 | c.logf("ServeHTTP: Preflight request")
205 | c.handlePreflight(w, r)
206 | // Preflight requests are standalone and should stop the chain as some other
207 | // middleware may not handle OPTIONS requests correctly. One typical example
208 | // is authentication middleware ; OPTIONS requests won't carry authentication
209 | // headers (see #1)
210 | if c.optionPassthrough {
211 | next(w, r)
212 | }
213 | } else {
214 | c.logf("ServeHTTP: Actual request")
215 | c.handleActualRequest(w, r)
216 | next(w, r)
217 | }
218 | }
219 |
220 | // handlePreflight handles pre-flight CORS requests
221 | func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
222 | headers := w.Header()
223 | origin := r.Header.Get("Origin")
224 |
225 | if r.Method != "OPTIONS" {
226 | c.logf(" Preflight aborted: %s!=OPTIONS", r.Method)
227 | return
228 | }
229 | // Always set Vary headers
230 | // see https://github.com/rs/cors/issues/10,
231 | // https://github.com/rs/cors/commit/dbdca4d95feaa7511a46e6f1efb3b3aa505bc43f#commitcomment-12352001
232 | headers.Add("Vary", "Origin")
233 | headers.Add("Vary", "Access-Control-Request-Method")
234 | headers.Add("Vary", "Access-Control-Request-Headers")
235 |
236 | if origin == "" {
237 | c.logf(" Preflight aborted: empty origin")
238 | return
239 | }
240 | if !c.isOriginAllowed(origin) {
241 | c.logf(" Preflight aborted: origin '%s' not allowed", origin)
242 | return
243 | }
244 |
245 | reqMethod := r.Header.Get("Access-Control-Request-Method")
246 | if !c.isMethodAllowed(reqMethod) {
247 | c.logf(" Preflight aborted: method '%s' not allowed", reqMethod)
248 | return
249 | }
250 | reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers"))
251 | if !c.areHeadersAllowed(reqHeaders) {
252 | c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders)
253 | return
254 | }
255 | headers.Set("Access-Control-Allow-Origin", origin)
256 | // Spec says: Since the list of methods can be unbounded, simply returning the method indicated
257 | // by Access-Control-Request-Method (if supported) can be enough
258 | headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
259 | if len(reqHeaders) > 0 {
260 |
261 | // Spec says: Since the list of headers can be unbounded, simply returning supported headers
262 | // from Access-Control-Request-Headers can be enough
263 | headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
264 | }
265 | if c.allowCredentials {
266 | headers.Set("Access-Control-Allow-Credentials", "true")
267 | }
268 | if c.maxAge > 0 {
269 | headers.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge))
270 | }
271 | c.logf(" Preflight response headers: %v", headers)
272 | }
273 |
274 | // handleActualRequest handles simple cross-origin requests, actual request or redirects
275 | func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
276 | headers := w.Header()
277 | origin := r.Header.Get("Origin")
278 |
279 | if r.Method == "OPTIONS" {
280 | c.logf(" Actual request no headers added: method == %s", r.Method)
281 | return
282 | }
283 | // Always set Vary, see https://github.com/rs/cors/issues/10
284 | headers.Add("Vary", "Origin")
285 | if origin == "" {
286 | c.logf(" Actual request no headers added: missing origin")
287 | return
288 | }
289 | if !c.isOriginAllowed(origin) {
290 | c.logf(" Actual request no headers added: origin '%s' not allowed", origin)
291 | return
292 | }
293 |
294 | // Note that spec does define a way to specifically disallow a simple method like GET or
295 | // POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
296 | // spec doesn't instruct to check the allowed methods for simple cross-origin requests.
297 | // We think it's a nice feature to be able to have control on those methods though.
298 | if !c.isMethodAllowed(r.Method) {
299 | if c.log != nil {
300 | c.logf(" Actual request no headers added: method '%s' not allowed",
301 | r.Method)
302 | }
303 |
304 | return
305 | }
306 | headers.Set("Access-Control-Allow-Origin", origin)
307 | if len(c.exposedHeaders) > 0 {
308 | headers.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", "))
309 | }
310 | if c.allowCredentials {
311 | headers.Set("Access-Control-Allow-Credentials", "true")
312 | }
313 | c.logf(" Actual response added headers: %v", headers)
314 | }
315 |
316 | // convenience method. checks if debugging is turned on before printing
317 | func (c *Cors) logf(format string, a ...interface{}) {
318 | if c.log != nil {
319 | c.log.Printf(format, a...)
320 | }
321 | }
322 |
323 | // isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
324 | // on the endpoint
325 | func (c *Cors) isOriginAllowed(origin string) bool {
326 | if c.allowOriginFunc != nil {
327 | return c.allowOriginFunc(origin)
328 | }
329 | if c.allowedOriginsAll {
330 | return true
331 | }
332 | origin = strings.ToLower(origin)
333 | for _, o := range c.allowedOrigins {
334 | if o == origin {
335 | return true
336 | }
337 | }
338 | for _, w := range c.allowedWOrigins {
339 | if w.match(origin) {
340 | return true
341 | }
342 | }
343 | return false
344 | }
345 |
346 | // isMethodAllowed checks if a given method can be used as part of a cross-domain request
347 | // on the endpoing
348 | func (c *Cors) isMethodAllowed(method string) bool {
349 | if len(c.allowedMethods) == 0 {
350 | // If no method allowed, always return false, even for preflight request
351 | return false
352 | }
353 | method = strings.ToUpper(method)
354 | if method == "OPTIONS" {
355 | // Always allow preflight requests
356 | return true
357 | }
358 | for _, m := range c.allowedMethods {
359 | if m == method {
360 | return true
361 | }
362 | }
363 | return false
364 | }
365 |
366 | // areHeadersAllowed checks if a given list of headers are allowed to used within
367 | // a cross-domain request.
368 | func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
369 | if c.allowedHeadersAll || len(requestedHeaders) == 0 {
370 | return true
371 | }
372 | for _, header := range requestedHeaders {
373 | header = http.CanonicalHeaderKey(header)
374 | found := false
375 | for _, h := range c.allowedHeaders {
376 | if h == header {
377 | found = true
378 | }
379 | }
380 | if !found {
381 | return false
382 | }
383 | }
384 | return true
385 | }
386 |
--------------------------------------------------------------------------------
/vendor/github.com/rs/cors/utils.go:
--------------------------------------------------------------------------------
1 | package cors
2 |
3 | import "strings"
4 |
5 | const toLower = 'a' - 'A'
6 |
7 | type converter func(string) string
8 |
9 | type wildcard struct {
10 | prefix string
11 | suffix string
12 | }
13 |
14 | func (w wildcard) match(s string) bool {
15 | return len(s) >= len(w.prefix+w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix)
16 | }
17 |
18 | // convert converts a list of string using the passed converter function
19 | func convert(s []string, c converter) []string {
20 | out := []string{}
21 | for _, i := range s {
22 | out = append(out, c(i))
23 | }
24 | return out
25 | }
26 |
27 | // parseHeaderList tokenize + normalize a string containing a list of headers
28 | func parseHeaderList(headerList string) []string {
29 | l := len(headerList)
30 | h := make([]byte, 0, l)
31 | upper := true
32 | // Estimate the number headers in order to allocate the right splice size
33 | t := 0
34 | for i := 0; i < l; i++ {
35 | if headerList[i] == ',' {
36 | t++
37 | }
38 | }
39 | headers := make([]string, 0, t)
40 | for i := 0; i < l; i++ {
41 | b := headerList[i]
42 | if b >= 'a' && b <= 'z' {
43 | if upper {
44 | h = append(h, b-toLower)
45 | } else {
46 | h = append(h, b)
47 | }
48 | } else if b >= 'A' && b <= 'Z' {
49 | if !upper {
50 | h = append(h, b+toLower)
51 | } else {
52 | h = append(h, b)
53 | }
54 | } else if b == '-' || (b >= '0' && b <= '9') {
55 | h = append(h, b)
56 | }
57 |
58 | if b == ' ' || b == ',' || i == l-1 {
59 | if len(h) > 0 {
60 | // Flush the found header
61 | headers = append(headers, string(h))
62 | h = h[:0]
63 | upper = true
64 | }
65 | } else {
66 | upper = b == '-'
67 | }
68 | }
69 | return headers
70 | }
71 |
--------------------------------------------------------------------------------