server: Add an inspect endpoint for containers
Signed-off-by: Mrunal Patel <mpatel@redhat.com>
This commit is contained in:
parent
d634468da6
commit
58bc35ab40
11 changed files with 876 additions and 0 deletions
|
@ -412,6 +412,13 @@ func main() {
|
||||||
service.StartExitMonitor()
|
service.StartExitMonitor()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := service.StartInspectEndpoint()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("Failed to start container inspect endpoint: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
err = s.Serve(lis)
|
err = s.Serve(lis)
|
||||||
if graceful && strings.Contains(strings.ToLower(err.Error()), "use of closed network connection") {
|
if graceful && strings.Contains(strings.ToLower(err.Error()), "use of closed network connection") {
|
||||||
err = nil
|
err = nil
|
||||||
|
|
56
server/inspect.go
Normal file
56
server/inspect.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-zoo/bone"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerInfo stores information about containers
|
||||||
|
type ContainerInfo struct {
|
||||||
|
Pid int `json:"pid"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
CreatedTime int64 `json:"created_time"`
|
||||||
|
Labels map[string]string `json:"labels"`
|
||||||
|
Annotations map[string]string `json:"annotations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartInspectEndpoint starts a http server that
|
||||||
|
// serves container information requests
|
||||||
|
func (s *Server) StartInspectEndpoint() error {
|
||||||
|
mux := bone.New()
|
||||||
|
|
||||||
|
mux.Get("/containers/:id", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
containerID := bone.GetValue(req, "id")
|
||||||
|
ctr := s.GetContainer(containerID)
|
||||||
|
if ctr == nil {
|
||||||
|
http.Error(w, fmt.Sprintf("container with id: %s not found", containerID), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctrState := ctr.State()
|
||||||
|
if ctrState == nil {
|
||||||
|
http.Error(w, fmt.Sprintf("container %s state is nil", containerID), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ci := ContainerInfo{
|
||||||
|
Pid: ctrState.Pid,
|
||||||
|
Image: ctr.Image(),
|
||||||
|
CreatedTime: ctrState.Created.UnixNano(),
|
||||||
|
Labels: ctr.Labels(),
|
||||||
|
Annotations: ctr.Annotations(),
|
||||||
|
}
|
||||||
|
js, err := json.Marshal(ci)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write(js)
|
||||||
|
}))
|
||||||
|
|
||||||
|
// TODO: Make this configurable
|
||||||
|
return http.ListenAndServe("localhost:7373", mux)
|
||||||
|
}
|
|
@ -97,3 +97,4 @@ github.com/prometheus/procfs 65c1f6f8f0fc1e2185eb9863a3bc751496404259
|
||||||
github.com/matttproud/golang_protobuf_extensions fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
|
github.com/matttproud/golang_protobuf_extensions fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
|
||||||
github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4
|
github.com/beorn7/perks 3ac7bf7a47d159a033b107610db8a1b6575507a4
|
||||||
github.com/containerd/cgroups 7a5fdd8330119dc70d850260db8f3594d89d6943
|
github.com/containerd/cgroups 7a5fdd8330119dc70d850260db8f3594d89d6943
|
||||||
|
github.com/go-zoo/bone 031b4005dfe248ccba241a0c9de0f9e112fd6b7c
|
||||||
|
|
22
vendor/github.com/go-zoo/bone/LICENSE
generated
vendored
Normal file
22
vendor/github.com/go-zoo/bone/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 CodingFerret
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
81
vendor/github.com/go-zoo/bone/README.md
generated
vendored
Normal file
81
vendor/github.com/go-zoo/bone/README.md
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
bone [![GoDoc](https://godoc.org/github.com/squiidz/bone?status.png)](http://godoc.org/github.com/go-zoo/bone) [![Build Status](https://travis-ci.org/go-zoo/bone.svg)](https://travis-ci.org/go-zoo/bone) [![Go Report Card](https://goreportcard.com/badge/go-zoo/bone)](https://goreportcard.com/report/go-zoo/bone) [![Sourcegraph](https://sourcegraph.com/github.com/go-zoo/bone/-/badge.svg)](https://sourcegraph.com/github.com/go-zoo/bone?badge)
|
||||||
|
=======
|
||||||
|
|
||||||
|
## What is bone ?
|
||||||
|
|
||||||
|
Bone is a lightweight and lightning fast HTTP Multiplexer for Golang. It support :
|
||||||
|
|
||||||
|
- URL Parameters
|
||||||
|
- REGEX Parameters
|
||||||
|
- Wildcard routes
|
||||||
|
- Router Prefix
|
||||||
|
- Sub Router, `mux.SubRoute()`, support most standard router (bone, gorilla/mux, httpRouter etc...)
|
||||||
|
- Http method declaration
|
||||||
|
- Support for `http.Handler` and `http.HandlerFunc`
|
||||||
|
- Custom NotFound handler
|
||||||
|
- Respect the Go standard `http.Handler` interface
|
||||||
|
|
||||||
|
![alt tag](https://c2.staticflickr.com/2/1070/540747396_5542b42cca_z.jpg)
|
||||||
|
|
||||||
|
## Speed
|
||||||
|
|
||||||
|
```
|
||||||
|
- BenchmarkBoneMux 10000000 118 ns/op
|
||||||
|
- BenchmarkZeusMux 100000 144 ns/op
|
||||||
|
- BenchmarkHttpRouterMux 10000000 134 ns/op
|
||||||
|
- BenchmarkNetHttpMux 3000000 580 ns/op
|
||||||
|
- BenchmarkGorillaMux 300000 3333 ns/op
|
||||||
|
- BenchmarkGorillaPatMux 1000000 1889 ns/op
|
||||||
|
```
|
||||||
|
|
||||||
|
These test are just for fun, all these router are great and really efficient.
|
||||||
|
Bone do not pretend to be the fastest router for every job.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
``` go
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import(
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-zoo/bone"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main () {
|
||||||
|
mux := bone.New()
|
||||||
|
|
||||||
|
// mux.Get, Post, etc ... takes http.Handler
|
||||||
|
mux.Get("/home/:id", http.HandlerFunc(HomeHandler))
|
||||||
|
mux.Get("/profil/:id/:var", http.HandlerFunc(ProfilHandler))
|
||||||
|
mux.Post("/data", http.HandlerFunc(DataHandler))
|
||||||
|
|
||||||
|
// Support REGEX Route params
|
||||||
|
mux.Get("/index/#id^[0-9]$", http.HandlerFunc(IndexHandler))
|
||||||
|
|
||||||
|
// Handle take http.Handler
|
||||||
|
mux.Handle("/", http.HandlerFunc(RootHandler))
|
||||||
|
|
||||||
|
// GetFunc, PostFunc etc ... takes http.HandlerFunc
|
||||||
|
mux.GetFunc("/test", Handler)
|
||||||
|
|
||||||
|
http.ListenAndServe(":8080", mux)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Handler(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
// Get the value of the "id" parameters.
|
||||||
|
val := bone.GetValue(req, "id")
|
||||||
|
|
||||||
|
rw.Write([]byte(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Blog Posts
|
||||||
|
- http://www.peterbe.com/plog/my-favorite-go-multiplexer
|
||||||
|
- https://harshladha.xyz/my-first-library-in-go-language-hasty-791b8e2b9e69
|
||||||
|
|
||||||
|
## Libs
|
||||||
|
- Errors dump for Go : [Trash](https://github.com/go-zoo/trash)
|
||||||
|
- Middleware Chaining module : [Claw](https://github.com/go-zoo/claw)
|
74
vendor/github.com/go-zoo/bone/bone.go
generated
vendored
Normal file
74
vendor/github.com/go-zoo/bone/bone.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/********************************
|
||||||
|
*** Multiplexer for Go ***
|
||||||
|
*** Bone is under MIT license ***
|
||||||
|
*** Code by CodingFerret ***
|
||||||
|
*** github.com/go-zoo ***
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
package bone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mux have routes and a notFound handler
|
||||||
|
// Route: all the registred route
|
||||||
|
// notFound: 404 handler, default http.NotFound if not provided
|
||||||
|
type Mux struct {
|
||||||
|
Routes map[string][]*Route
|
||||||
|
prefix string
|
||||||
|
notFound http.Handler
|
||||||
|
Serve func(rw http.ResponseWriter, req *http.Request)
|
||||||
|
CaseSensitive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
static = "static"
|
||||||
|
method = []string{"GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS"}
|
||||||
|
)
|
||||||
|
|
||||||
|
type adapter func(*Mux) *Mux
|
||||||
|
|
||||||
|
// New create a pointer to a Mux instance
|
||||||
|
func New(adapters ...adapter) *Mux {
|
||||||
|
m := &Mux{Routes: make(map[string][]*Route), Serve: nil, CaseSensitive: true}
|
||||||
|
for _, adap := range adapters {
|
||||||
|
adap(m)
|
||||||
|
}
|
||||||
|
if m.Serve == nil {
|
||||||
|
m.Serve = m.DefaultServe
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix set a default prefix for all routes registred on the router
|
||||||
|
func (m *Mux) Prefix(p string) *Mux {
|
||||||
|
m.prefix = strings.TrimSuffix(p, "/")
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultServe is the default http request handler
|
||||||
|
func (m *Mux) DefaultServe(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
// Check if a route match
|
||||||
|
if !m.parse(rw, req) {
|
||||||
|
// Check if it's a static ressource
|
||||||
|
if !m.staticRoute(rw, req) {
|
||||||
|
// Check if the request path doesn't end with /
|
||||||
|
if !m.validate(rw, req) {
|
||||||
|
// Check if same route exists for another HTTP method
|
||||||
|
if !m.otherMethods(rw, req) {
|
||||||
|
m.HandleNotFound(rw, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP pass the request to the serve method of Mux
|
||||||
|
func (m *Mux) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if !m.CaseSensitive {
|
||||||
|
req.URL.Path = strings.ToLower(req.URL.Path)
|
||||||
|
}
|
||||||
|
m.Serve(rw, req)
|
||||||
|
}
|
169
vendor/github.com/go-zoo/bone/helper.go
generated
vendored
Normal file
169
vendor/github.com/go-zoo/bone/helper.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/********************************
|
||||||
|
*** Multiplexer for Go ***
|
||||||
|
*** Bone is under MIT license ***
|
||||||
|
*** Code by CodingFerret ***
|
||||||
|
*** github.com/go-zoo ***
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
package bone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *Mux) ListenAndServe(port string) error {
|
||||||
|
return http.ListenAndServe(port, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mux) parse(rw http.ResponseWriter, req *http.Request) bool {
|
||||||
|
for _, r := range m.Routes[req.Method] {
|
||||||
|
ok := r.parse(rw, req)
|
||||||
|
if ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If no HEAD method, default to GET
|
||||||
|
if req.Method == "HEAD" {
|
||||||
|
for _, r := range m.Routes["GET"] {
|
||||||
|
ok := r.parse(rw, req)
|
||||||
|
if ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticRoute check if the request path is for Static route
|
||||||
|
func (m *Mux) staticRoute(rw http.ResponseWriter, req *http.Request) bool {
|
||||||
|
for _, s := range m.Routes[static] {
|
||||||
|
if len(req.URL.Path) >= s.Size {
|
||||||
|
if req.URL.Path[:s.Size] == s.Path {
|
||||||
|
s.Handler.ServeHTTP(rw, req)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleNotFound handle when a request does not match a registered handler.
|
||||||
|
func (m *Mux) HandleNotFound(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if m.notFound != nil {
|
||||||
|
m.notFound.ServeHTTP(rw, req)
|
||||||
|
} else {
|
||||||
|
http.NotFound(rw, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the path don't end with a /
|
||||||
|
func (m *Mux) validate(rw http.ResponseWriter, req *http.Request) bool {
|
||||||
|
plen := len(req.URL.Path)
|
||||||
|
if plen > 1 && req.URL.Path[plen-1:] == "/" {
|
||||||
|
cleanURL(&req.URL.Path)
|
||||||
|
rw.Header().Set("Location", req.URL.String())
|
||||||
|
rw.WriteHeader(http.StatusFound)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Retry to find a route that match
|
||||||
|
return m.parse(rw, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func valid(path string) bool {
|
||||||
|
plen := len(path)
|
||||||
|
if plen > 1 && path[plen-1:] == "/" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean url path
|
||||||
|
func cleanURL(url *string) {
|
||||||
|
ulen := len((*url))
|
||||||
|
if ulen > 1 {
|
||||||
|
if (*url)[ulen-1:] == "/" {
|
||||||
|
*url = (*url)[:ulen-1]
|
||||||
|
cleanURL(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValue return the key value, of the current *http.Request
|
||||||
|
func GetValue(req *http.Request, key string) string {
|
||||||
|
return GetAllValues(req)[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequestRoute returns the route of given Request
|
||||||
|
func (m *Mux) GetRequestRoute(req *http.Request) string {
|
||||||
|
cleanURL(&req.URL.Path)
|
||||||
|
for _, r := range m.Routes[req.Method] {
|
||||||
|
if r.Atts != 0 {
|
||||||
|
if r.Atts&SUB != 0 {
|
||||||
|
return r.Handler.(*Mux).GetRequestRoute(req)
|
||||||
|
}
|
||||||
|
if r.Match(req) {
|
||||||
|
return r.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.URL.Path == r.Path {
|
||||||
|
return r.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range m.Routes[static] {
|
||||||
|
if len(req.URL.Path) >= s.Size {
|
||||||
|
if req.URL.Path[:s.Size] == s.Path {
|
||||||
|
return s.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "NotFound"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQuery return the key value, of the current *http.Request query
|
||||||
|
func GetQuery(req *http.Request, key string) []string {
|
||||||
|
if ok, value := extractQueries(req); ok {
|
||||||
|
return value[key]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllQueries return all queries of the current *http.Request
|
||||||
|
func GetAllQueries(req *http.Request) map[string][]string {
|
||||||
|
if ok, values := extractQueries(req); ok {
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractQueries(req *http.Request) (bool, map[string][]string) {
|
||||||
|
if q, err := url.ParseQuery(req.URL.RawQuery); err == nil {
|
||||||
|
var queries = make(map[string][]string)
|
||||||
|
for k, v := range q {
|
||||||
|
for _, item := range v {
|
||||||
|
values := strings.Split(item, ",")
|
||||||
|
queries[k] = append(queries[k], values...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, queries
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mux) otherMethods(rw http.ResponseWriter, req *http.Request) bool {
|
||||||
|
for _, met := range method {
|
||||||
|
if met != req.Method {
|
||||||
|
for _, r := range m.Routes[met] {
|
||||||
|
ok := r.exists(rw, req)
|
||||||
|
if ok {
|
||||||
|
rw.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
45
vendor/github.com/go-zoo/bone/helper_15.go
generated
vendored
Normal file
45
vendor/github.com/go-zoo/bone/helper_15.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// +build !go1.7
|
||||||
|
|
||||||
|
/********************************
|
||||||
|
*** Multiplexer for Go ***
|
||||||
|
*** Bone is under MIT license ***
|
||||||
|
*** Code by CodingFerret ***
|
||||||
|
*** github.com/go-zoo ***
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
package bone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var globalVars = struct {
|
||||||
|
sync.RWMutex
|
||||||
|
v map[*http.Request]map[string]string
|
||||||
|
}{v: make(map[*http.Request]map[string]string)}
|
||||||
|
|
||||||
|
// GetAllValues return the req PARAMs
|
||||||
|
func GetAllValues(req *http.Request) map[string]string {
|
||||||
|
globalVars.RLock()
|
||||||
|
values := globalVars.v[req]
|
||||||
|
globalVars.RUnlock()
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveMatchedRequest is an extension point for Route which allows us to conditionally compile for
|
||||||
|
// go1.7 and <go1.7
|
||||||
|
func (r *Route) serveMatchedRequest(rw http.ResponseWriter, req *http.Request, vars map[string]string) {
|
||||||
|
globalVars.Lock()
|
||||||
|
globalVars.v[req] = vars
|
||||||
|
globalVars.Unlock()
|
||||||
|
|
||||||
|
// Regardless if ServeHTTP panics (and potentially recovers) we can make sure to not leak
|
||||||
|
// memory in globalVars for this request
|
||||||
|
defer func() {
|
||||||
|
globalVars.Lock()
|
||||||
|
delete(globalVars.v, req)
|
||||||
|
globalVars.Unlock()
|
||||||
|
}()
|
||||||
|
r.Handler.ServeHTTP(rw, req)
|
||||||
|
}
|
39
vendor/github.com/go-zoo/bone/helper_17.go
generated
vendored
Normal file
39
vendor/github.com/go-zoo/bone/helper_17.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
/********************************
|
||||||
|
*** Multiplexer for Go ***
|
||||||
|
*** Bone is under MIT license ***
|
||||||
|
*** Code by CodingFerret ***
|
||||||
|
*** github.com/go-zoo ***
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
package bone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// contextKeyType is a private struct that is used for storing bone values in net.Context
|
||||||
|
type contextKeyType struct{}
|
||||||
|
|
||||||
|
// contextKey is the key that is used to store bone values in the net.Context for each request
|
||||||
|
var contextKey = contextKeyType{}
|
||||||
|
|
||||||
|
// GetAllValues return the req PARAMs
|
||||||
|
func GetAllValues(req *http.Request) map[string]string {
|
||||||
|
values, ok := req.Context().Value(contextKey).(map[string]string)
|
||||||
|
if ok {
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveMatchedRequest is an extension point for Route which allows us to conditionally compile for
|
||||||
|
// go1.7 and <go1.7
|
||||||
|
func (r *Route) serveMatchedRequest(rw http.ResponseWriter, req *http.Request, vars map[string]string) {
|
||||||
|
ctx := context.WithValue(req.Context(), contextKey, vars)
|
||||||
|
newReq := req.WithContext(ctx)
|
||||||
|
r.Handler.ServeHTTP(rw, newReq)
|
||||||
|
}
|
137
vendor/github.com/go-zoo/bone/mux.go
generated
vendored
Normal file
137
vendor/github.com/go-zoo/bone/mux.go
generated
vendored
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/********************************
|
||||||
|
*** Multiplexer for Go ***
|
||||||
|
*** Bone is under MIT license ***
|
||||||
|
*** Code by CodingFerret ***
|
||||||
|
*** github.com/go-zoo ***
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
package bone
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Router is the same as a http.Handler
|
||||||
|
type Router interface {
|
||||||
|
ServeHTTP(http.ResponseWriter, *http.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the route in the router
|
||||||
|
func (m *Mux) Register(method string, path string, handler http.Handler) *Route {
|
||||||
|
return m.register(method, path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFunc add a new route to the Mux with the Get method
|
||||||
|
func (m *Mux) GetFunc(path string, handler http.HandlerFunc) *Route {
|
||||||
|
return m.register("GET", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostFunc add a new route to the Mux with the Post method
|
||||||
|
func (m *Mux) PostFunc(path string, handler http.HandlerFunc) *Route {
|
||||||
|
return m.register("POST", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutFunc add a new route to the Mux with the Put method
|
||||||
|
func (m *Mux) PutFunc(path string, handler http.HandlerFunc) *Route {
|
||||||
|
return m.register("PUT", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFunc add a new route to the Mux with the Delete method
|
||||||
|
func (m *Mux) DeleteFunc(path string, handler http.HandlerFunc) *Route {
|
||||||
|
return m.register("DELETE", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeadFunc add a new route to the Mux with the Head method
|
||||||
|
func (m *Mux) HeadFunc(path string, handler http.HandlerFunc) *Route {
|
||||||
|
return m.register("HEAD", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PatchFunc add a new route to the Mux with the Patch method
|
||||||
|
func (m *Mux) PatchFunc(path string, handler http.HandlerFunc) *Route {
|
||||||
|
return m.register("PATCH", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionsFunc add a new route to the Mux with the Options method
|
||||||
|
func (m *Mux) OptionsFunc(path string, handler http.HandlerFunc) *Route {
|
||||||
|
return m.register("OPTIONS", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFoundFunc the mux custom 404 handler
|
||||||
|
func (m *Mux) NotFoundFunc(handler http.HandlerFunc) {
|
||||||
|
m.notFound = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle add a new route to the Mux without a HTTP method
|
||||||
|
func (m *Mux) Handle(path string, handler http.Handler) {
|
||||||
|
for _, mt := range method {
|
||||||
|
m.register(mt, path, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleFunc is use to pass a func(http.ResponseWriter, *Http.Request) instead of http.Handler
|
||||||
|
func (m *Mux) HandleFunc(path string, handler http.HandlerFunc) {
|
||||||
|
m.Handle(path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get add a new route to the Mux with the Get method
|
||||||
|
func (m *Mux) Get(path string, handler http.Handler) *Route {
|
||||||
|
return m.register("GET", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post add a new route to the Mux with the Post method
|
||||||
|
func (m *Mux) Post(path string, handler http.Handler) *Route {
|
||||||
|
return m.register("POST", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put add a new route to the Mux with the Put method
|
||||||
|
func (m *Mux) Put(path string, handler http.Handler) *Route {
|
||||||
|
return m.register("PUT", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete add a new route to the Mux with the Delete method
|
||||||
|
func (m *Mux) Delete(path string, handler http.Handler) *Route {
|
||||||
|
return m.register("DELETE", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head add a new route to the Mux with the Head method
|
||||||
|
func (m *Mux) Head(path string, handler http.Handler) *Route {
|
||||||
|
return m.register("HEAD", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch add a new route to the Mux with the Patch method
|
||||||
|
func (m *Mux) Patch(path string, handler http.Handler) *Route {
|
||||||
|
return m.register("PATCH", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options add a new route to the Mux with the Options method
|
||||||
|
func (m *Mux) Options(path string, handler http.Handler) *Route {
|
||||||
|
return m.register("OPTIONS", path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFound the mux custom 404 handler
|
||||||
|
func (m *Mux) NotFound(handler http.Handler) {
|
||||||
|
m.notFound = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the new route in the router with the provided method and handler
|
||||||
|
func (m *Mux) register(method string, path string, handler http.Handler) *Route {
|
||||||
|
r := NewRoute(m.prefix+path, handler)
|
||||||
|
r.Method = method
|
||||||
|
if valid(path) {
|
||||||
|
m.Routes[method] = append(m.Routes[method], r)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
m.Routes[static] = append(m.Routes[static], r)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubRoute register a router as a SubRouter of bone
|
||||||
|
func (m *Mux) SubRoute(path string, router Router) *Route {
|
||||||
|
r := NewRoute(m.prefix+path, router)
|
||||||
|
if valid(path) {
|
||||||
|
r.Atts += SUB
|
||||||
|
for _, mt := range method {
|
||||||
|
m.Routes[mt] = append(m.Routes[mt], r)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
245
vendor/github.com/go-zoo/bone/route.go
generated
vendored
Normal file
245
vendor/github.com/go-zoo/bone/route.go
generated
vendored
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
/********************************
|
||||||
|
*** Multiplexer for Go ***
|
||||||
|
*** Bone is under MIT license ***
|
||||||
|
*** Code by CodingFerret ***
|
||||||
|
*** github.com/go-zoo ***
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
package bone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
//PARAM value store in Atts if the route have parameters
|
||||||
|
PARAM = 2
|
||||||
|
//SUB value store in Atts if the route is a sub router
|
||||||
|
SUB = 4
|
||||||
|
//WC value store in Atts if the route have wildcard
|
||||||
|
WC = 8
|
||||||
|
//REGEX value store in Atts if the route contains regex
|
||||||
|
REGEX = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
// Route content the required information for a valid route
|
||||||
|
// Path: is the Route URL
|
||||||
|
// Size: is the length of the path
|
||||||
|
// Token: is the value of each part of the path, split by /
|
||||||
|
// Pattern: is content information about the route, if it's have a route variable
|
||||||
|
// handler: is the handler who handle this route
|
||||||
|
// Method: define HTTP method on the route
|
||||||
|
type Route struct {
|
||||||
|
Path string
|
||||||
|
Method string
|
||||||
|
Size int
|
||||||
|
Atts int
|
||||||
|
wildPos int
|
||||||
|
Token Token
|
||||||
|
Pattern map[int]string
|
||||||
|
Compile map[int]*regexp.Regexp
|
||||||
|
Tag map[int]string
|
||||||
|
Handler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token content all value of a spliting route path
|
||||||
|
// Tokens: string value of each token
|
||||||
|
// size: number of token
|
||||||
|
type Token struct {
|
||||||
|
raw []int
|
||||||
|
Tokens []string
|
||||||
|
Size int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRoute return a pointer to a Route instance and call save() on it
|
||||||
|
func NewRoute(url string, h http.Handler) *Route {
|
||||||
|
r := &Route{Path: url, Handler: h}
|
||||||
|
r.save()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save, set automatically the the Route.Size and Route.Pattern value
|
||||||
|
func (r *Route) save() {
|
||||||
|
r.Size = len(r.Path)
|
||||||
|
r.Token.Tokens = strings.Split(r.Path, "/")
|
||||||
|
for i, s := range r.Token.Tokens {
|
||||||
|
if len(s) >= 1 {
|
||||||
|
switch s[:1] {
|
||||||
|
case ":":
|
||||||
|
if r.Pattern == nil {
|
||||||
|
r.Pattern = make(map[int]string)
|
||||||
|
}
|
||||||
|
r.Pattern[i] = s[1:]
|
||||||
|
r.Atts |= PARAM
|
||||||
|
case "#":
|
||||||
|
if r.Compile == nil {
|
||||||
|
r.Compile = make(map[int]*regexp.Regexp)
|
||||||
|
r.Tag = make(map[int]string)
|
||||||
|
}
|
||||||
|
tmp := strings.Split(s, "^")
|
||||||
|
r.Tag[i] = tmp[0][1:]
|
||||||
|
r.Compile[i] = regexp.MustCompile("^" + tmp[1][:len(tmp[1])-1])
|
||||||
|
r.Atts |= REGEX
|
||||||
|
case "*":
|
||||||
|
r.wildPos = i
|
||||||
|
r.Atts |= WC
|
||||||
|
default:
|
||||||
|
r.Token.raw = append(r.Token.raw, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.Token.Size++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match check if the request match the route Pattern
|
||||||
|
func (r *Route) Match(req *http.Request) bool {
|
||||||
|
ok, _ := r.matchAndParse(req)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchAndParse check if the request matches the route Pattern and returns a map of the parsed
|
||||||
|
// variables if it matches
|
||||||
|
func (r *Route) matchAndParse(req *http.Request) (bool, map[string]string) {
|
||||||
|
ss := strings.Split(req.URL.EscapedPath(), "/")
|
||||||
|
if r.matchRawTokens(&ss) {
|
||||||
|
if len(ss) == r.Token.Size || r.Atts&WC != 0 {
|
||||||
|
totalSize := len(r.Pattern)
|
||||||
|
if r.Atts®EX != 0 {
|
||||||
|
totalSize += len(r.Compile)
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := make(map[string]string, totalSize)
|
||||||
|
for k, v := range r.Pattern {
|
||||||
|
vars[v] = ss[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Atts®EX != 0 {
|
||||||
|
for k, v := range r.Compile {
|
||||||
|
if !v.MatchString(ss[k]) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
vars[r.Tag[k]] = ss[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, vars
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) parse(rw http.ResponseWriter, req *http.Request) bool {
|
||||||
|
if r.Atts != 0 {
|
||||||
|
if r.Atts&SUB != 0 {
|
||||||
|
if len(req.URL.Path) >= r.Size {
|
||||||
|
if req.URL.Path[:r.Size] == r.Path {
|
||||||
|
req.URL.Path = req.URL.Path[r.Size:]
|
||||||
|
r.Handler.ServeHTTP(rw, req)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok, vars := r.matchAndParse(req); ok {
|
||||||
|
r.serveMatchedRequest(rw, req, vars)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.URL.Path == r.Path {
|
||||||
|
r.Handler.ServeHTTP(rw, req)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) matchRawTokens(ss *[]string) bool {
|
||||||
|
if len(*ss) >= r.Token.Size {
|
||||||
|
for i, v := range r.Token.raw {
|
||||||
|
if (*ss)[v] != r.Token.Tokens[v] {
|
||||||
|
if r.Atts&WC != 0 && r.wildPos == i {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) exists(rw http.ResponseWriter, req *http.Request) bool {
|
||||||
|
if r.Atts != 0 {
|
||||||
|
if r.Atts&SUB != 0 {
|
||||||
|
if len(req.URL.Path) >= r.Size {
|
||||||
|
if req.URL.Path[:r.Size] == r.Path {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok, _ := r.matchAndParse(req); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if req.URL.Path == r.Path {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get set the route method to Get
|
||||||
|
func (r *Route) Get() *Route {
|
||||||
|
r.Method = "GET"
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post set the route method to Post
|
||||||
|
func (r *Route) Post() *Route {
|
||||||
|
r.Method = "POST"
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put set the route method to Put
|
||||||
|
func (r *Route) Put() *Route {
|
||||||
|
r.Method = "PUT"
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete set the route method to Delete
|
||||||
|
func (r *Route) Delete() *Route {
|
||||||
|
r.Method = "DELETE"
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head set the route method to Head
|
||||||
|
func (r *Route) Head() *Route {
|
||||||
|
r.Method = "HEAD"
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch set the route method to Patch
|
||||||
|
func (r *Route) Patch() *Route {
|
||||||
|
r.Method = "PATCH"
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options set the route method to Options
|
||||||
|
func (r *Route) Options() *Route {
|
||||||
|
r.Method = "OPTIONS"
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if r.Method != "" {
|
||||||
|
if req.Method == r.Method {
|
||||||
|
r.Handler.ServeHTTP(rw, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.NotFound(rw, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Handler.ServeHTTP(rw, req)
|
||||||
|
}
|
Loading…
Reference in a new issue