├── .gitignore ├── go.mod ├── go.sum ├── cors.go ├── vendor ├── vendor.json └── github.com │ └── pressly │ └── chi │ ├── LICENSE │ ├── CONTRIBUTING.md │ ├── chain.go │ ├── CHANGELOG.md │ ├── chi.go │ ├── context.go │ ├── tree.go │ ├── mux.go │ └── README.md ├── README.md ├── html.go └── server.go /.gitignore: -------------------------------------------------------------------------------- 1 | termux-api-server 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/imikod/termux-api-server 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/go-chi/chi v4.0.2+incompatible 7 | ) 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= 2 | github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= 3 | -------------------------------------------------------------------------------- /cors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | func cors(h http.Handler) http.Handler { 8 | fn := func(w http.ResponseWriter, r *http.Request) { 9 | w.Header().Set("Access-Control-Allow-Origin", "*") 10 | h.ServeHTTP(w, r) 11 | } 12 | 13 | return http.HandlerFunc(fn) 14 | } -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "ZfO0bvDS6WOAG+mKN90UAS+tuMA=", 7 | "path": "github.com/pressly/chi", 8 | "revision": "5a8c2b83d3dbb4628a361cf2e8355a9ae6eba5a4", 9 | "revisionTime": "2016-12-16T23:31:48Z" 10 | } 11 | ], 12 | "rootPath": "github.com/imikod/termux-api-server" 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Termux API Server 2 | ================= 3 | Server for [termux-api](https://github.com/termux/termux-api) 4 | 5 | Usage 6 | ===== 7 | ``` 8 | $ termux-api-server 9 | ``` 10 | Server starts with default port `8000` 11 | You can change the port to `8991` like so: 12 | ``` 13 | $ termux-api-server -p 8991 14 | ``` 15 | and open [http://localhost:8991](http://localhost:8991) 16 | 17 | Build from source 18 | ================= 19 | - Install go 20 | ``` 21 | $ apt install golang 22 | ``` 23 | - Set GOPATH 24 | ``` 25 | $ cd ~ 26 | $ mkdir go go/bin go/pkg go/src 27 | $ export GOPATH=~/go 28 | ``` 29 | - Install termux-api-server 30 | ``` 31 | $ go get github.com/imikod/termux-api-server 32 | ``` 33 | 34 | Security Warning 35 | ================ 36 | 37 | Make sure your device has no data connection and it is only connected to your own secure wlan. 38 | Otherwise sensitive information could be exposed to the internet. 39 | -------------------------------------------------------------------------------- /vendor/github.com/pressly/chi/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka) 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/pressly/chi/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Prerequisites 4 | 5 | 1. [Install Go][go-install]. 6 | 2. Download the sources and switch the working directory: 7 | 8 | ```bash 9 | go get -u -d github.com/pressly/chi 10 | cd $GOPATH/src/github.com/pressly/chi 11 | ``` 12 | 13 | ## Submitting a Pull Request 14 | 15 | A typical workflow is: 16 | 17 | 1. [Fork the repository.][fork] [This tip maybe also helpful.][go-fork-tip] 18 | 2. [Create a topic branch.][branch] 19 | 3. Add tests for your change. 20 | 4. Run `go test`. If your tests pass, return to the step 3. 21 | 5. Implement the change and ensure the steps from the previous step pass. 22 | 6. Run `goimports -w .`, to ensure the new code conforms to Go formatting guideline. 23 | 7. [Add, commit and push your changes.][git-help] 24 | 8. [Submit a pull request.][pull-req] 25 | 26 | [go-install]: https://golang.org/doc/install 27 | [go-fork-tip]: http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html 28 | [fork]: https://help.github.com/articles/fork-a-repo 29 | [branch]: http://learn.github.com/p/branching.html 30 | [git-help]: https://guides.github.com 31 | [pull-req]: https://help.github.com/articles/using-pull-requests 32 | -------------------------------------------------------------------------------- /vendor/github.com/pressly/chi/chain.go: -------------------------------------------------------------------------------- 1 | package chi 2 | 3 | import "net/http" 4 | 5 | // Chain returns a Middlewares type from a slice of middleware handlers. 6 | func Chain(middlewares ...func(http.Handler) http.Handler) Middlewares { 7 | return Middlewares(middlewares) 8 | } 9 | 10 | // Handler builds and returns a http.Handler from the chain of middlewares, 11 | // with `h http.Handler` as the final handler. 12 | func (mws Middlewares) Handler(h http.Handler) http.Handler { 13 | return &ChainHandler{mws, h, chain(mws, h)} 14 | } 15 | 16 | // HandlerFunc builds and returns a http.Handler from the chain of middlewares, 17 | // with `h http.Handler` as the final handler. 18 | func (mws Middlewares) HandlerFunc(h http.HandlerFunc) http.Handler { 19 | return &ChainHandler{mws, h, chain(mws, h)} 20 | } 21 | 22 | type ChainHandler struct { 23 | Middlewares Middlewares 24 | Endpoint http.Handler 25 | chain http.Handler 26 | } 27 | 28 | func (c *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 29 | c.chain.ServeHTTP(w, r) 30 | } 31 | 32 | // chain builds a http.Handler composed of an inline middleware stack and endpoint 33 | // handler in the order they are passed. 34 | func chain(middlewares []func(http.Handler) http.Handler, endpoint http.Handler) http.Handler { 35 | // Return ahead of time if there aren't any middlewares for the chain 36 | if len(middlewares) == 0 { 37 | return endpoint 38 | } 39 | 40 | // Wrap the end handler with the middleware chain 41 | h := middlewares[len(middlewares)-1](endpoint) 42 | for i := len(middlewares) - 2; i >= 0; i-- { 43 | h = middlewares[i](h) 44 | } 45 | 46 | return h 47 | } 48 | -------------------------------------------------------------------------------- /vendor/github.com/pressly/chi/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v2.0.0rc1 (2016-07-26) 4 | 5 | - Huge update! chi v2 is a large refactor targetting Go 1.7+. As of Go 1.7, the popular 6 | community `"net/context"` package has been included in the standard library as `"context"` and 7 | utilized by `"net/http"` and `http.Request` to managing deadlines, cancelation signals and other 8 | request-scoped values. We're very excited about the new context addition and are proud to 9 | introduce chi v2, a minimal and powerful routing package for building large HTTP services, 10 | with zero external dependencies. Chi focuses on idiomatic design and encourages the use of 11 | stdlib HTTP handlers and middlwares. 12 | - chi v2 deprecates its `chi.Handler` interface and requires `http.Handler` or `http.HandlerFunc` 13 | - chi v2 stores URL routing parameters and patterns in the standard request context: `r.Context()` 14 | - chi v2 lower-level routing context is accessible by `chi.RouteContext(r.Context()) *chi.Context`, 15 | which provides direct access to URL routing parameters, the routing path and the matching 16 | routing patterns. 17 | - Users upgrading from chi v1 to v2, need to: 18 | 1. Update the old chi.Handler signature, `func(ctx context.Context, w http.ResponseWriter, r *http.Request)` to 19 | the standard http.Handler: `func(w http.ResponseWriter, r *http.Request)` 20 | 2. Use `chi.URLParam(r *http.Request, paramKey string) string` 21 | or `URLParamFromCtx(ctx context.Context, paramKey string) string` to access a url parameter value 22 | 23 | ## v1.0.0 (2016-07-01) 24 | 25 | - Released chi v1 stable https://github.com/pressly/chi/tree/v1.0.0 for Go 1.6 and older. 26 | 27 | ## v0.9.0 (2016-03-31) 28 | 29 | - Reuse context objects via sync.Pool for zero-allocation routing [#33](https://github.com/pressly/chi/pull/33) 30 | - BREAKING NOTE: due to subtle API changes, previously `chi.URLParams(ctx)["id"]` used to access url parameters 31 | has changed to: `chi.URLParam(ctx, "id")` 32 | -------------------------------------------------------------------------------- /vendor/github.com/pressly/chi/chi.go: -------------------------------------------------------------------------------- 1 | // 2 | // Package chi is a small, idiomatic and composable router for building HTTP services. 3 | // 4 | // chi requires Go 1.7 or newer. 5 | // 6 | // Example: 7 | // package main 8 | // 9 | // import ( 10 | // "net/http" 11 | // 12 | // "github.com/pressly/chi" 13 | // "github.com/pressly/chi/middleware" 14 | // ) 15 | // 16 | // func main() { 17 | // r := chi.NewRouter() 18 | // r.Use(middleware.Logger) 19 | // r.Use(middleware.Recoverer) 20 | // 21 | // r.Get("/", func(w http.ResponseWriter, r *http.Request) { 22 | // w.Write([]byte("root.")) 23 | // }) 24 | // 25 | // http.ListenAndServe(":3333", r) 26 | // } 27 | // 28 | // See github.com/pressly/chi/_examples/ for more in-depth examples. 29 | // 30 | package chi 31 | 32 | import "net/http" 33 | 34 | // NewRouter returns a new Mux object that implements the Router interface. 35 | func NewRouter() *Mux { 36 | return NewMux() 37 | } 38 | 39 | // Router consisting of the core routing methods used by chi's Mux, 40 | // using only the standard net/http. 41 | type Router interface { 42 | http.Handler 43 | Routes 44 | 45 | // Use appends one of more middlewares onto the Router stack. 46 | Use(middlewares ...func(http.Handler) http.Handler) 47 | 48 | // With adds inline middlewares for an endpoint handler. 49 | With(middlewares ...func(http.Handler) http.Handler) Router 50 | 51 | // Group adds a new inline-Router along the current routing 52 | // path, with a fresh middleware stack for the inline-Router. 53 | Group(fn func(r Router)) Router 54 | 55 | // Route mounts a sub-Router along a `pattern`` string. 56 | Route(pattern string, fn func(r Router)) Router 57 | 58 | // Mount attaches another http.Handler along ./pattern/* 59 | Mount(pattern string, h http.Handler) 60 | 61 | // Handle and HandleFunc adds routes for `pattern` that matches 62 | // all HTTP methods. 63 | Handle(pattern string, h http.Handler) 64 | HandleFunc(pattern string, h http.HandlerFunc) 65 | 66 | // HTTP-method routing along `pattern` 67 | Connect(pattern string, h http.HandlerFunc) 68 | Delete(pattern string, h http.HandlerFunc) 69 | Get(pattern string, h http.HandlerFunc) 70 | Head(pattern string, h http.HandlerFunc) 71 | Options(pattern string, h http.HandlerFunc) 72 | Patch(pattern string, h http.HandlerFunc) 73 | Post(pattern string, h http.HandlerFunc) 74 | Put(pattern string, h http.HandlerFunc) 75 | Trace(pattern string, h http.HandlerFunc) 76 | 77 | // NotFound defines a handler to respond whenever a route could 78 | // not be found. 79 | NotFound(h http.HandlerFunc) 80 | } 81 | 82 | // Routes interface adds two methods for router traversal, which is also 83 | // used by the `docgen` subpackage to generation documentation for Routers. 84 | type Routes interface { 85 | // Routes returns the routing tree in an easily traversable structure. 86 | Routes() []Route 87 | 88 | // Middlewares returns the list of middlewares in use by the router. 89 | Middlewares() Middlewares 90 | } 91 | 92 | // Middlewares type is a slice of standard middleware handlers with methods 93 | // to compose middleware chains and http.Handler's. 94 | type Middlewares []func(http.Handler) http.Handler 95 | -------------------------------------------------------------------------------- /html.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var index = ` 4 | 5 | 6 | termux-api-server 7 | 41 | 42 | 43 |
44 | 45 |

Termux Services

46 | Simple server to expose the 47 | termux-api 48 | with CORS 49 |

50 | 51 |

termux-battery-status

52 |
GET /battery-status
53 | Get the status of the device battery. 54 |

55 | 56 |

termux-camera-info

57 |
GET /camera-info
58 | Get information about device camera(s). 59 |

60 | 61 |

termux-camera-photo

62 |
GET /camera-photo?c={cameraID}
63 | Take a photo and view it in JPEG format. 64 |

65 | 66 |

termux-contact-list

67 |
GET /contact-list
68 | List all contacts. 69 |

70 | 71 |

termux-infrared-frequencies

72 |
GET /infrared-frequencies
73 | Query the infrared transmitter's supported carrier frequencies. 74 |

75 | 76 |

termux-location

77 |
GET /location?p={provider}
78 | Get the device location. 79 |

80 | 81 |

termux-notification

82 |
GET /notification?c={content}&i={id}&t={title}&u={url}
83 | Display a system notification. 84 |

85 | 86 |

termux-sms-inbox

87 |
GET /sms-inbox?d={true}&l={limit}&n={true}&o={offset}
88 | List received SMS messages. 89 |

90 | 91 |

termux-sms-send

92 |
GET /sms-send?n={number1}&n={number2}&n={number3}&t={text}
93 | Send a SMS message to the specified recipient number(s). 94 |

95 | 96 |

termux-telephony-cellinfo

97 |
GET /telephony-cellinfo
98 | Get information about all observed cell information from all radios 99 | on the device including the primary and neighboring cells. 100 |

101 | 102 |

termux-telephony-deviceinfo

103 |
GET /telephony-deviceinfo
104 | Get information about the telephony device. 105 |

106 | 107 |

termux-tts-engines

108 |
GET /tts-engines
109 | Get information about the available text-to-speech (TTS) engines. 110 | The name of an engine may be given to the termux-tts-speak command using the -e option. 111 |

112 | 113 |

termux-tts-speak

114 |
GET /tts-speak?e={engine}&l={language}&p={pitch}&r={rate}&s={stream}&t={text}
115 | Speak text with a system text-to-speech (TTS) engine. 116 |

117 | 118 |

termux-vibrate

119 |
GET /vibrate?d={duration}&f={true}
120 | Vibrate the device. 121 |

122 | 123 |
124 | 125 | 126 | ` 127 | -------------------------------------------------------------------------------- /vendor/github.com/pressly/chi/context.go: -------------------------------------------------------------------------------- 1 | package chi 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | ) 8 | 9 | var ( 10 | RouteCtxKey = &contextKey{"RouteContext"} 11 | ) 12 | 13 | // Context is the default routing context set on the root node of a 14 | // request context to track URL parameters and an optional routing path. 15 | type Context struct { 16 | // URL routing parameter key and values. 17 | URLParams params 18 | 19 | // Routing path override used by subrouters. 20 | RoutePath string 21 | 22 | // Routing pattern matching the path. 23 | RoutePattern string 24 | 25 | // Routing patterns throughout the lifecycle of the request, 26 | // across all connected routers. 27 | RoutePatterns []string 28 | } 29 | 30 | // NewRouteContext returns a new routing Context object. 31 | func NewRouteContext() *Context { 32 | return &Context{} 33 | } 34 | 35 | // reset a routing context to its initial state. 36 | func (x *Context) reset() { 37 | x.URLParams = x.URLParams[:0] 38 | x.RoutePath = "" 39 | x.RoutePattern = "" 40 | x.RoutePatterns = x.RoutePatterns[:0] 41 | } 42 | 43 | // RouteContext returns chi's routing Context object from a 44 | // http.Request Context. 45 | func RouteContext(ctx context.Context) *Context { 46 | return ctx.Value(RouteCtxKey).(*Context) 47 | } 48 | 49 | // URLParam returns the url parameter from a http.Request object. 50 | func URLParam(r *http.Request, key string) string { 51 | if rctx := RouteContext(r.Context()); rctx != nil { 52 | return rctx.URLParams.Get(key) 53 | } 54 | return "" 55 | } 56 | 57 | // URLParamFromCtx returns the url parameter from a http.Request Context. 58 | func URLParamFromCtx(ctx context.Context, key string) string { 59 | if rctx := RouteContext(ctx); rctx != nil { 60 | return rctx.URLParams.Get(key) 61 | } 62 | return "" 63 | } 64 | 65 | type param struct { 66 | Key, Value string 67 | } 68 | 69 | type params []param 70 | 71 | func (ps *params) Add(key string, value string) { 72 | *ps = append(*ps, param{key, value}) 73 | } 74 | 75 | func (ps params) Get(key string) string { 76 | for _, p := range ps { 77 | if p.Key == key { 78 | return p.Value 79 | } 80 | } 81 | return "" 82 | } 83 | 84 | func (ps *params) Set(key string, value string) { 85 | idx := -1 86 | for i, p := range *ps { 87 | if p.Key == key { 88 | idx = i 89 | break 90 | } 91 | } 92 | if idx < 0 { 93 | (*ps).Add(key, value) 94 | } else { 95 | (*ps)[idx] = param{key, value} 96 | } 97 | } 98 | 99 | func (ps *params) Del(key string) string { 100 | for i, p := range *ps { 101 | if p.Key == key { 102 | *ps = append((*ps)[:i], (*ps)[i+1:]...) 103 | return p.Value 104 | } 105 | } 106 | return "" 107 | } 108 | 109 | // ServerBaseContext wraps an http.Handler to set the request context to the 110 | // `baseCtx`. 111 | func ServerBaseContext(h http.Handler, baseCtx context.Context) http.Handler { 112 | fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 113 | ctx := r.Context() 114 | 115 | // Copy over default net/http server context keys 116 | if v, ok := ctx.Value(http.ServerContextKey).(*http.Server); ok { 117 | baseCtx = context.WithValue(baseCtx, http.ServerContextKey, v) 118 | } 119 | if v, ok := ctx.Value(http.LocalAddrContextKey).(net.Addr); ok { 120 | baseCtx = context.WithValue(baseCtx, http.LocalAddrContextKey, v) 121 | } 122 | 123 | h.ServeHTTP(w, r.WithContext(baseCtx)) 124 | }) 125 | return fn 126 | } 127 | 128 | // contextKey is a value for use with context.WithValue. It's used as 129 | // a pointer so it fits in an interface{} without allocation. This technique 130 | // for defining context keys was copied from Go 1.7's new use of context in net/http. 131 | type contextKey struct { 132 | name string 133 | } 134 | 135 | func (k *contextKey) String() string { 136 | return "chi context value " + k.name 137 | } 138 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "os/exec" 9 | "strings" 10 | 11 | "github.com/go-chi/chi" 12 | "github.com/go-chi/chi/middleware" 13 | ) 14 | 15 | var jsonCommands = [...]string{ 16 | "termux-battery-status", 17 | "termux-camera-info", 18 | "termux-contact-list", 19 | "termux-infrared-frequencies", 20 | "termux-location", 21 | "termux-sms-inbox", 22 | "termux-telephony-cellinfo", 23 | "termux-telephony-deviceinfo", 24 | "termux-tts-engines", 25 | } 26 | 27 | func json(w http.ResponseWriter, r *http.Request) { 28 | var arg []string 29 | cmd := chi.URLParam(r, "cmd") 30 | if !strings.HasPrefix(cmd, "termux-") { 31 | cmd = "termux-" + cmd 32 | } 33 | cmdFound := false 34 | for _, c := range jsonCommands { 35 | if cmd == c { 36 | cmdFound = true 37 | break 38 | } 39 | } 40 | if !cmdFound { 41 | http.Error(w, "not supported", 404) 42 | return 43 | } 44 | 45 | if cmd == "termux-location" { 46 | p := r.FormValue("p") 47 | if p == "network" || p == "passive" { 48 | arg = append(arg, "-p", p) 49 | } 50 | } 51 | if cmd == "termux-sms-inbox" { 52 | if dates := r.FormValue("d"); dates == "true" { 53 | arg = append(arg, "-d") 54 | } 55 | if limit := r.FormValue("l"); limit != "" { 56 | arg = append(arg, "-l", limit) 57 | } 58 | if numbers := r.FormValue("n"); numbers == "true" { 59 | arg = append(arg, "-n") 60 | } 61 | if offset := r.FormValue("o"); offset != "" { 62 | arg = append(arg, "-o", offset) 63 | } 64 | } 65 | 66 | out, err := exec.Command(cmd, arg...).Output() 67 | if err != nil { 68 | http.Error(w, err.Error(), 500) 69 | return 70 | } 71 | w.Header().Set("Content-Type", "application/json; charset=UTF-8") 72 | w.Write(out) 73 | } 74 | 75 | func camera(w http.ResponseWriter, r *http.Request) { 76 | photo := "termux-photo.jpg" 77 | cameraID := r.FormValue("c") 78 | if cameraID == "" { 79 | cameraID = "0" 80 | } 81 | 82 | out, err := exec.Command("termux-camera-photo", 83 | "-c", cameraID, photo).Output() 84 | if err != nil { 85 | http.Error(w, err.Error(), 500) 86 | return 87 | } 88 | if string(out) != "" { 89 | http.Error(w, string(out), 500) 90 | return 91 | } 92 | 93 | jpg, err := ioutil.ReadFile(photo) 94 | if err != nil { 95 | http.Error(w, err.Error(), 500) 96 | return 97 | } 98 | w.Header().Set("Content-Type", "image/jpeg") 99 | w.Write(jpg) 100 | } 101 | 102 | func notification(w http.ResponseWriter, r *http.Request) { 103 | var arg []string 104 | 105 | if content := r.FormValue("c"); content != "" { 106 | arg = append(arg, "-c", content) 107 | } 108 | if id := r.FormValue("i"); id != "" { 109 | arg = append(arg, "-i", id) 110 | } 111 | if title := r.FormValue("t"); title != "" { 112 | arg = append(arg, "-t", title) 113 | } 114 | if url := r.FormValue("u"); url != "" { 115 | arg = append(arg, "-u", url) 116 | } 117 | 118 | out, err := exec.Command("termux-notification", arg...).Output() 119 | if err != nil { 120 | http.Error(w, err.Error(), 500) 121 | return 122 | } 123 | if string(out) != "" { 124 | http.Error(w, string(out), 500) 125 | return 126 | } 127 | w.Write([]byte("OK")) 128 | } 129 | 130 | func sendSMS(w http.ResponseWriter, r *http.Request) { 131 | text := r.FormValue("t") 132 | r.ParseForm() 133 | var numbers string 134 | for _, number := range r.Form["n"] { 135 | numbers = numbers + number + "," 136 | } 137 | 138 | out, err := exec.Command("termux-sms-send", "-n", numbers, text).Output() 139 | if err != nil { 140 | http.Error(w, err.Error(), 500) 141 | return 142 | } 143 | if string(out) != "" { 144 | http.Error(w, string(out), 500) 145 | return 146 | } 147 | w.Write([]byte("OK")) 148 | } 149 | 150 | func speak(w http.ResponseWriter, r *http.Request) { 151 | var arg []string 152 | 153 | if engine := r.FormValue("e"); engine != "" { 154 | arg = append(arg, "-e", engine) 155 | } 156 | if language := r.FormValue("l"); language != "" { 157 | arg = append(arg, "-l", language) 158 | } 159 | if pitch := r.FormValue("p"); pitch != "" { 160 | arg = append(arg, "-p", pitch) 161 | } 162 | if rate := r.FormValue("r"); rate != "" { 163 | arg = append(arg, "-r", rate) 164 | } 165 | if stream := r.FormValue("s"); stream != "" { 166 | arg = append(arg, "-s", stream) 167 | } 168 | if text := r.FormValue("t"); text != "" { 169 | arg = append(arg, text) 170 | } 171 | 172 | out, err := exec.Command("termux-tts-speak", arg...).Output() 173 | if err != nil { 174 | http.Error(w, err.Error(), 500) 175 | return 176 | } 177 | if string(out) != "" { 178 | http.Error(w, string(out), 500) 179 | return 180 | } 181 | w.Write([]byte("OK")) 182 | } 183 | 184 | func vibrate(w http.ResponseWriter, r *http.Request) { 185 | var arg []string 186 | 187 | if duration := r.FormValue("d"); duration != "" { 188 | arg = append(arg, "-d", duration) 189 | } 190 | if force := r.FormValue("d"); force == "true" { 191 | arg = append(arg, "-f") 192 | } 193 | 194 | out, err := exec.Command("termux-vibrate", arg...).Output() 195 | if err != nil { 196 | http.Error(w, err.Error(), 500) 197 | return 198 | } 199 | if string(out) != "" { 200 | http.Error(w, string(out), 500) 201 | return 202 | } 203 | w.Write([]byte("OK")) 204 | } 205 | 206 | func main() { 207 | var port string 208 | flag.StringVar(&port, "p", "8000", "port") 209 | flag.Parse() 210 | 211 | r := chi.NewRouter() 212 | r.Use(cors) 213 | r.Use(middleware.Logger) 214 | 215 | r.Get("/", func(w http.ResponseWriter, r *http.Request) { 216 | w.Write([]byte(index)) 217 | }) 218 | r.Get("/camera-photo", camera) 219 | r.Get("/notification", notification) 220 | r.Get("/sms-send", sendSMS) 221 | r.Get("/tts-speak", speak) 222 | r.Get("/vibrate", vibrate) 223 | r.Get("/:cmd", json) 224 | 225 | log.Fatal(http.ListenAndServe("0.0.0.0:"+port, r)) 226 | } 227 | -------------------------------------------------------------------------------- /vendor/github.com/pressly/chi/tree.go: -------------------------------------------------------------------------------- 1 | package chi 2 | 3 | // Radix tree implementation below is a based on the original work by 4 | // Armon Dadgar in https://github.com/armon/go-radix/blob/master/radix.go 5 | // (MIT licensed). It's been heavily modified for use as a HTTP routing tree. 6 | 7 | import ( 8 | "net/http" 9 | "sort" 10 | "strings" 11 | ) 12 | 13 | type methodTyp int 14 | 15 | const ( 16 | mCONNECT methodTyp = 1 << iota 17 | mDELETE 18 | mGET 19 | mHEAD 20 | mOPTIONS 21 | mPATCH 22 | mPOST 23 | mPUT 24 | mTRACE 25 | mSTUB 26 | 27 | mALL methodTyp = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS | 28 | mPATCH | mPOST | mPUT | mTRACE 29 | ) 30 | 31 | var methodMap = map[string]methodTyp{ 32 | "CONNECT": mCONNECT, 33 | "DELETE": mDELETE, 34 | "GET": mGET, 35 | "HEAD": mHEAD, 36 | "OPTIONS": mOPTIONS, 37 | "PATCH": mPATCH, 38 | "POST": mPOST, 39 | "PUT": mPUT, 40 | "TRACE": mTRACE, 41 | } 42 | 43 | type nodeTyp uint8 44 | 45 | const ( 46 | ntStatic nodeTyp = iota // /home 47 | ntRegexp // /:id([0-9]+) or #id^[0-9]+$ 48 | ntParam // /:user 49 | ntCatchAll // /api/v1/* 50 | ) 51 | 52 | type node struct { 53 | // node type 54 | typ nodeTyp 55 | 56 | // first byte of the prefix 57 | label byte 58 | 59 | // prefix is the common prefix we ignore 60 | prefix string 61 | 62 | // pattern is the computed path of prefixes 63 | pattern string 64 | 65 | // HTTP handler on the leaf node 66 | handlers methodHandlers 67 | 68 | // chi subroutes on the leaf node 69 | subroutes Routes 70 | 71 | // Child nodes should be stored in-order for iteration, 72 | // in groups of the node type. 73 | children [ntCatchAll + 1]nodes 74 | } 75 | 76 | func (n *node) FindRoute(rctx *Context, path string) methodHandlers { 77 | // Reset the context routing pattern 78 | rctx.RoutePattern = "" 79 | 80 | // Find the routing handlers for the path 81 | rn := n.findRoute(rctx, path) 82 | if rn == nil { 83 | return nil 84 | } 85 | 86 | // Record the routing pattern in the request lifecycle 87 | if rn.pattern != "" { 88 | rctx.RoutePattern = rn.pattern 89 | rctx.RoutePatterns = append(rctx.RoutePatterns, rctx.RoutePattern) 90 | } 91 | 92 | return rn.handlers 93 | } 94 | 95 | func (n *node) InsertRoute(method methodTyp, pattern string, handler http.Handler) *node { 96 | var parent *node 97 | search := pattern 98 | 99 | for { 100 | // Handle key exhaustion 101 | if len(search) == 0 { 102 | // Insert or update the node's leaf handler 103 | n.setHandler(method, handler) 104 | n.pattern = pattern 105 | return n 106 | } 107 | 108 | // Look for the edge 109 | parent = n 110 | n = n.getEdge(search[0]) 111 | 112 | // No edge, create one 113 | if n == nil { 114 | cn := &node{label: search[0], prefix: search, pattern: pattern} 115 | cn.setHandler(method, handler) 116 | parent.addChild(pattern, cn) 117 | return cn 118 | } 119 | 120 | if n.typ > ntStatic { 121 | // We found a wildcard node, meaning search path starts with 122 | // a wild prefix. Trim off the wildcard search path and continue. 123 | p := strings.Index(search, "/") 124 | if p < 0 { 125 | p = len(search) 126 | } 127 | search = search[p:] 128 | continue 129 | } 130 | 131 | // Static nodes fall below here. 132 | // Determine longest prefix of the search key on match. 133 | commonPrefix := n.longestPrefix(search, n.prefix) 134 | if commonPrefix == len(n.prefix) { 135 | // the common prefix is as long as the current node's prefix we're attempting to insert. 136 | // keep the search going. 137 | search = search[commonPrefix:] 138 | continue 139 | } 140 | 141 | // Split the node 142 | child := &node{ 143 | typ: ntStatic, 144 | prefix: search[:commonPrefix], 145 | } 146 | parent.replaceChild(search[0], child) 147 | 148 | // Restore the existing node 149 | n.label = n.prefix[commonPrefix] 150 | n.prefix = n.prefix[commonPrefix:] 151 | child.addChild(pattern, n) 152 | 153 | // If the new key is a subset, add to to this node 154 | search = search[commonPrefix:] 155 | if len(search) == 0 { 156 | child.setHandler(method, handler) 157 | child.pattern = pattern 158 | return child 159 | } 160 | 161 | // Create a new edge for the node 162 | subchild := &node{ 163 | typ: ntStatic, 164 | label: search[0], 165 | prefix: search, 166 | pattern: pattern, 167 | } 168 | subchild.setHandler(method, handler) 169 | child.addChild(pattern, subchild) 170 | return subchild 171 | } 172 | } 173 | 174 | func (n *node) findPattern(pattern string) *node { 175 | nn := n 176 | for _, nds := range nn.children { 177 | if len(nds) == 0 { 178 | continue 179 | } 180 | 181 | n = nn.getEdge(pattern[0]) 182 | if n == nil { 183 | continue 184 | } 185 | 186 | idx := n.longestPrefix(pattern, n.prefix) 187 | xpattern := pattern[idx:] 188 | 189 | if len(xpattern) == 0 { 190 | return n 191 | } else if xpattern[0] == '/' && idx < len(n.prefix) { 192 | continue 193 | } 194 | 195 | return n.findPattern(xpattern) 196 | } 197 | return nil 198 | } 199 | 200 | func (n *node) isLeaf() bool { 201 | return n.handlers != nil 202 | } 203 | 204 | func (n *node) addChild(pattern string, child *node) { 205 | search := child.prefix 206 | 207 | // Find any wildcard segments 208 | p := strings.IndexAny(search, ":*") 209 | 210 | // Determine new node type 211 | ntyp := child.typ 212 | if p >= 0 { 213 | switch search[p] { 214 | case ':': 215 | ntyp = ntParam 216 | case '*': 217 | ntyp = ntCatchAll 218 | } 219 | } 220 | 221 | if p == 0 { 222 | // Path starts with a wildcard 223 | 224 | handlers := child.handlers 225 | child.typ = ntyp 226 | 227 | if ntyp == ntCatchAll { 228 | p = -1 229 | } else { 230 | p = strings.IndexByte(search, '/') 231 | } 232 | if p < 0 { 233 | p = len(search) 234 | } 235 | child.prefix = search[:p] 236 | 237 | if p != len(search) { 238 | // add edge for the remaining part, split the end. 239 | child.handlers = nil 240 | 241 | search = search[p:] 242 | 243 | child.addChild(pattern, &node{ 244 | typ: ntStatic, 245 | label: search[0], // this will always start with / 246 | prefix: search, 247 | pattern: pattern, 248 | handlers: handlers, 249 | }) 250 | } 251 | 252 | } else if p > 0 { 253 | // Path has some wildcard 254 | 255 | // starts with a static segment 256 | handlers := child.handlers 257 | child.typ = ntStatic 258 | child.prefix = search[:p] 259 | child.handlers = nil 260 | 261 | // add the wild edge node 262 | search = search[p:] 263 | 264 | child.addChild(pattern, &node{ 265 | typ: ntyp, 266 | label: search[0], 267 | prefix: search, 268 | pattern: pattern, 269 | handlers: handlers, 270 | }) 271 | 272 | } else { 273 | // Path is all static 274 | child.typ = ntyp 275 | 276 | } 277 | 278 | n.children[child.typ] = append(n.children[child.typ], child) 279 | n.children[child.typ].Sort() 280 | } 281 | 282 | func (n *node) replaceChild(label byte, child *node) { 283 | for i := 0; i < len(n.children[child.typ]); i++ { 284 | if n.children[child.typ][i].label == label { 285 | n.children[child.typ][i] = child 286 | n.children[child.typ][i].label = label 287 | return 288 | } 289 | } 290 | 291 | panic("chi: replacing missing child") 292 | } 293 | 294 | func (n *node) getEdge(label byte) *node { 295 | for _, nds := range n.children { 296 | num := len(nds) 297 | for i := 0; i < num; i++ { 298 | if nds[i].label == label { 299 | return nds[i] 300 | } 301 | } 302 | } 303 | return nil 304 | } 305 | 306 | func (n *node) findEdge(ntyp nodeTyp, label byte) *node { 307 | nds := n.children[ntyp] 308 | num := len(nds) 309 | idx := 0 310 | 311 | switch ntyp { 312 | case ntStatic: 313 | i, j := 0, num-1 314 | for i <= j { 315 | idx = i + (j-i)/2 316 | if label > nds[idx].label { 317 | i = idx + 1 318 | } else if label < nds[idx].label { 319 | j = idx - 1 320 | } else { 321 | i = num // breaks cond 322 | } 323 | } 324 | if nds[idx].label != label { 325 | return nil 326 | } 327 | return nds[idx] 328 | 329 | default: // wild nodes 330 | // TODO: right now we match them all.. but regexp should 331 | // run through regexp matcher 332 | return nds[idx] 333 | } 334 | } 335 | 336 | // Recursive edge traversal by checking all nodeTyp groups along the way. 337 | // It's like searching through a multi-dimensional radix trie. 338 | func (n *node) findRoute(rctx *Context, path string) *node { 339 | nn := n 340 | search := path 341 | 342 | for t, nds := range nn.children { 343 | ntyp := nodeTyp(t) 344 | if len(nds) == 0 { 345 | continue 346 | } 347 | 348 | // search subset of edges of the index for a matching node 349 | var label byte 350 | if search != "" { 351 | label = search[0] 352 | } 353 | 354 | xn := nn.findEdge(ntyp, label) // next node 355 | if xn == nil { 356 | continue 357 | } 358 | 359 | // Prepare next search path by trimming prefix from requested path 360 | xsearch := search 361 | if xn.typ > ntStatic { 362 | p := -1 363 | if xn.typ < ntCatchAll { 364 | p = strings.IndexByte(xsearch, '/') 365 | } 366 | if p < 0 { 367 | p = len(xsearch) 368 | } 369 | 370 | if xn.typ == ntCatchAll { 371 | rctx.URLParams.Add("*", xsearch) 372 | } else { 373 | rctx.URLParams.Add(xn.prefix[1:], xsearch[:p]) 374 | } 375 | 376 | xsearch = xsearch[p:] 377 | } else if strings.HasPrefix(xsearch, xn.prefix) { 378 | xsearch = xsearch[len(xn.prefix):] 379 | } else { 380 | continue // no match 381 | } 382 | 383 | // did we find it yet? 384 | if len(xsearch) == 0 { 385 | if xn.isLeaf() { 386 | return xn 387 | } 388 | } 389 | 390 | // recursively find the next node.. 391 | fin := xn.findRoute(rctx, xsearch) 392 | if fin != nil { 393 | // found a node, return it 394 | return fin 395 | } 396 | 397 | // Did not found final handler, let's remove the param here if it was set 398 | if xn.typ > ntStatic { 399 | if xn.typ == ntCatchAll { 400 | rctx.URLParams.Del("*") 401 | } else { 402 | rctx.URLParams.Del(xn.prefix[1:]) 403 | } 404 | } 405 | } 406 | 407 | return nil 408 | } 409 | 410 | // longestPrefix finds the length of the shared prefix 411 | // of two strings 412 | func (n *node) longestPrefix(k1, k2 string) int { 413 | max := len(k1) 414 | if l := len(k2); l < max { 415 | max = l 416 | } 417 | var i int 418 | for i = 0; i < max; i++ { 419 | if k1[i] != k2[i] { 420 | break 421 | } 422 | } 423 | return i 424 | } 425 | 426 | func (n *node) setHandler(method methodTyp, handler http.Handler) { 427 | if n.handlers == nil { 428 | n.handlers = make(methodHandlers, 0) 429 | } 430 | if method&mSTUB == mSTUB { 431 | n.handlers[mSTUB] = handler 432 | } else { 433 | n.handlers[mSTUB] = nil 434 | } 435 | if method&mALL == mALL { 436 | n.handlers[mALL] = handler 437 | for _, m := range methodMap { 438 | n.handlers[m] = handler 439 | } 440 | } else { 441 | n.handlers[method] = handler 442 | } 443 | } 444 | 445 | func (n *node) isEmpty() bool { 446 | for _, nds := range n.children { 447 | if len(nds) > 0 { 448 | return false 449 | } 450 | } 451 | return true 452 | } 453 | 454 | func (n *node) routes() []Route { 455 | rts := []Route{} 456 | 457 | n.walkRoutes(n.prefix, n, func(pattern string, handlers methodHandlers, subroutes Routes) bool { 458 | if handlers[mSTUB] != nil && subroutes == nil { 459 | return false 460 | } 461 | 462 | if subroutes != nil && len(pattern) > 2 { 463 | pattern = pattern[:len(pattern)-2] 464 | } 465 | 466 | var hs = make(map[string]http.Handler, 0) 467 | if handlers[mALL] != nil { 468 | hs["*"] = handlers[mALL] 469 | } 470 | for mt, h := range handlers { 471 | if h == nil { 472 | continue 473 | } 474 | m := methodTypString(mt) 475 | if m == "" { 476 | continue 477 | } 478 | hs[m] = h 479 | } 480 | 481 | rt := Route{pattern, hs, subroutes} 482 | rts = append(rts, rt) 483 | return false 484 | }) 485 | 486 | return rts 487 | } 488 | 489 | func (n *node) walkRoutes(pattern string, nd *node, fn walkFn) bool { 490 | pattern = nd.pattern 491 | 492 | // Visit the leaf values if any 493 | if (nd.handlers != nil || nd.subroutes != nil) && fn(pattern, nd.handlers, nd.subroutes) { 494 | return true 495 | } 496 | 497 | // Recurse on the children 498 | for _, nds := range nd.children { 499 | for _, nd := range nds { 500 | if n.walkRoutes(pattern, nd, fn) { 501 | return true 502 | } 503 | } 504 | } 505 | return false 506 | } 507 | 508 | func methodTypString(method methodTyp) string { 509 | for s, t := range methodMap { 510 | if method == t { 511 | return s 512 | } 513 | } 514 | return "" 515 | } 516 | 517 | type walkFn func(pattern string, handlers methodHandlers, subroutes Routes) bool 518 | 519 | // methodHandlers is a mapping of http method constants to handlers 520 | // for a given route. 521 | type methodHandlers map[methodTyp]http.Handler 522 | 523 | type nodes []*node 524 | 525 | // Sort the list of nodes by label 526 | func (ns nodes) Len() int { return len(ns) } 527 | func (ns nodes) Less(i, j int) bool { return ns[i].label < ns[j].label } 528 | func (ns nodes) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } 529 | func (ns nodes) Sort() { sort.Sort(ns) } 530 | 531 | type Route struct { 532 | Pattern string 533 | Handlers map[string]http.Handler 534 | SubRoutes Routes 535 | } 536 | -------------------------------------------------------------------------------- /vendor/github.com/pressly/chi/mux.go: -------------------------------------------------------------------------------- 1 | package chi 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | var _ Router = &Mux{} 12 | 13 | // Mux is a simple HTTP route multiplexer that parses a request path, 14 | // records any URL params, and executes an end handler. It implements 15 | // the http.Handler interface and is friendly with the standard library. 16 | // 17 | // Mux is designed to be fast, minimal and offer a powerful API for building 18 | // modular and composable HTTP services with a large set of handlers. It's 19 | // particularly useful for writing large REST API services that break a handler 20 | // into many smaller parts composed of middlewares and end handlers. 21 | type Mux struct { 22 | // The radix trie router 23 | tree *node 24 | 25 | // The middleware stack 26 | middlewares []func(http.Handler) http.Handler 27 | 28 | // Controls the behaviour of middleware chain generation when a mux 29 | // is registered as an inline group inside another mux. 30 | inline bool 31 | 32 | // The computed mux handler made of the chained middleware stack and 33 | // the tree router 34 | handler http.Handler 35 | 36 | // Routing context pool 37 | pool sync.Pool 38 | 39 | // Custom route not found handler 40 | notFoundHandler http.HandlerFunc 41 | 42 | // Custom method not allowed handler 43 | methodNotAllowedHandler http.HandlerFunc 44 | } 45 | 46 | // NewMux returns a newly initialized Mux object that implements the Router 47 | // interface. 48 | func NewMux() *Mux { 49 | mux := &Mux{tree: &node{}} 50 | mux.pool.New = func() interface{} { 51 | return NewRouteContext() 52 | } 53 | return mux 54 | } 55 | 56 | // ServeHTTP is the single method of the http.Handler interface that makes 57 | // Mux interoperable with the standard library. It uses a sync.Pool to get and 58 | // reuse routing contexts for each request. 59 | func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { 60 | // Ensure the mux has some routes defined on the mux 61 | if mx.handler == nil { 62 | panic("chi: attempting to route to a mux with no handlers.") 63 | } 64 | 65 | // Check if a routing context already exists from a parent router. 66 | rctx, _ := r.Context().Value(RouteCtxKey).(*Context) 67 | if rctx != nil { 68 | mx.handler.ServeHTTP(w, r) 69 | return 70 | } 71 | 72 | // Fetch a RouteContext object from the sync pool, and call the computed 73 | // mx.handler that is comprised of mx.middlewares + mx.routeHTTP. 74 | // Once the request is finished, reset the routing context and put it back 75 | // into the pool for reuse from another request. 76 | rctx = mx.pool.Get().(*Context) 77 | rctx.reset() 78 | r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, rctx)) 79 | mx.handler.ServeHTTP(w, r) 80 | mx.pool.Put(rctx) 81 | } 82 | 83 | // Use appends a middleware handler to the Mux middleware stack. 84 | // 85 | // The middleware stack for any Mux will execute before searching for a matching 86 | // route to a specific handler, which provides opportunity to respond early, 87 | // change the course of the request execution, or set request-scoped values for 88 | // the next http.Handler. 89 | func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler) { 90 | if mx.handler != nil { 91 | panic("chi: all middlewares must be defined before routes on a mux") 92 | } 93 | mx.middlewares = append(mx.middlewares, middlewares...) 94 | } 95 | 96 | // Handle adds the route `pattern` that matches any http method to 97 | // execute the `handler` http.Handler. 98 | func (mx *Mux) Handle(pattern string, handler http.Handler) { 99 | mx.handle(mALL, pattern, handler) 100 | } 101 | 102 | // HandleFunc adds the route `pattern` that matches any http method to 103 | // execute the `handlerFn` http.HandlerFunc. 104 | func (mx *Mux) HandleFunc(pattern string, handlerFn http.HandlerFunc) { 105 | mx.handle(mALL, pattern, handlerFn) 106 | } 107 | 108 | // Connect adds the route `pattern` that matches a CONNECT http method to 109 | // execute the `handlerFn` http.HandlerFunc. 110 | func (mx *Mux) Connect(pattern string, handlerFn http.HandlerFunc) { 111 | mx.handle(mCONNECT, pattern, handlerFn) 112 | } 113 | 114 | // Delete adds the route `pattern` that matches a DELETE http method to 115 | // execute the `handlerFn` http.HandlerFunc. 116 | func (mx *Mux) Delete(pattern string, handlerFn http.HandlerFunc) { 117 | mx.handle(mDELETE, pattern, handlerFn) 118 | } 119 | 120 | // Get adds the route `pattern` that matches a GET http method to 121 | // execute the `handlerFn` http.HandlerFunc. 122 | func (mx *Mux) Get(pattern string, handlerFn http.HandlerFunc) { 123 | mx.handle(mGET, pattern, handlerFn) 124 | } 125 | 126 | // Head adds the route `pattern` that matches a HEAD http method to 127 | // execute the `handlerFn` http.HandlerFunc. 128 | func (mx *Mux) Head(pattern string, handlerFn http.HandlerFunc) { 129 | mx.handle(mHEAD, pattern, handlerFn) 130 | } 131 | 132 | // Options adds the route `pattern` that matches a OPTIONS http method to 133 | // execute the `handlerFn` http.HandlerFunc. 134 | func (mx *Mux) Options(pattern string, handlerFn http.HandlerFunc) { 135 | mx.handle(mOPTIONS, pattern, handlerFn) 136 | } 137 | 138 | // Patch adds the route `pattern` that matches a PATCH http method to 139 | // execute the `handlerFn` http.HandlerFunc. 140 | func (mx *Mux) Patch(pattern string, handlerFn http.HandlerFunc) { 141 | mx.handle(mPATCH, pattern, handlerFn) 142 | } 143 | 144 | // Post adds the route `pattern` that matches a POST http method to 145 | // execute the `handlerFn` http.HandlerFunc. 146 | func (mx *Mux) Post(pattern string, handlerFn http.HandlerFunc) { 147 | mx.handle(mPOST, pattern, handlerFn) 148 | } 149 | 150 | // Put adds the route `pattern` that matches a PUT http method to 151 | // execute the `handlerFn` http.HandlerFunc. 152 | func (mx *Mux) Put(pattern string, handlerFn http.HandlerFunc) { 153 | mx.handle(mPUT, pattern, handlerFn) 154 | } 155 | 156 | // Trace adds the route `pattern` that matches a TRACE http method to 157 | // execute the `handlerFn` http.HandlerFunc. 158 | func (mx *Mux) Trace(pattern string, handlerFn http.HandlerFunc) { 159 | mx.handle(mTRACE, pattern, handlerFn) 160 | } 161 | 162 | // NotFound sets a custom http.HandlerFunc for routing paths that could 163 | // not be found. The default 404 handler is `http.NotFound`. 164 | func (mx *Mux) NotFound(handlerFn http.HandlerFunc) { 165 | mx.notFoundHandler = handlerFn 166 | 167 | mx.updateSubRoutes(func(subMux *Mux) { 168 | if subMux.notFoundHandler == nil { 169 | subMux.NotFound(handlerFn) 170 | } 171 | }) 172 | } 173 | 174 | // MethodNotAllowed sets a custom http.HandlerFunc for routing paths where the 175 | // method is unresolved. The default handler returns a 405 with an empty body. 176 | func (mx *Mux) MethodNotAllowed(handlerFn http.HandlerFunc) { 177 | mx.methodNotAllowedHandler = handlerFn 178 | 179 | mx.updateSubRoutes(func(subMux *Mux) { 180 | if subMux.methodNotAllowedHandler == nil { 181 | subMux.MethodNotAllowed(handlerFn) 182 | } 183 | }) 184 | } 185 | 186 | // With adds inline middlewares for an endpoint handler. 187 | func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router { 188 | // Similarly as in handle(), we must build the mux handler once further 189 | // middleware registration isn't allowed for this stack, like now. 190 | if !mx.inline && mx.handler == nil { 191 | mx.buildRouteHandler() 192 | } 193 | 194 | // Copy middlewares from parent inline muxs 195 | var mws Middlewares 196 | if mx.inline { 197 | mws = make(Middlewares, len(mx.middlewares)) 198 | copy(mws, mx.middlewares) 199 | } 200 | mws = append(mws, middlewares...) 201 | 202 | im := &Mux{inline: true, tree: mx.tree, middlewares: mws} 203 | return im 204 | } 205 | 206 | // Group creates a new inline-Mux with a fresh middleware stack. It's useful 207 | // for a group of handlers along the same routing path that use an additional 208 | // set of middlewares. See _examples/. 209 | func (mx *Mux) Group(fn func(r Router)) Router { 210 | im := mx.With().(*Mux) 211 | if fn != nil { 212 | fn(im) 213 | } 214 | return im 215 | } 216 | 217 | // Route creates a new Mux with a fresh middleware stack and mounts it 218 | // along the `pattern` as a subrouter. Effectively, this is a short-hand 219 | // call to Mount. See _examples/. 220 | func (mx *Mux) Route(pattern string, fn func(r Router)) Router { 221 | subRouter := NewRouter() 222 | if fn != nil { 223 | fn(subRouter) 224 | } 225 | mx.Mount(pattern, subRouter) 226 | return subRouter 227 | } 228 | 229 | // Mount attaches another http.Handler or chi Router as a subrouter along a routing 230 | // path. It's very useful to split up a large API as many independent routers and 231 | // compose them as a single service using Mount. See _examples/. 232 | // 233 | // Note that Mount() simply sets a wildcard along the `pattern` that will continue 234 | // routing at the `handler`, which in most cases is another chi.Router. As a result, 235 | // if you define two Mount() routes on the exact same pattern the mount will panic. 236 | func (mx *Mux) Mount(pattern string, handler http.Handler) { 237 | // Provide runtime safety for ensuring a pattern isn't mounted on an existing 238 | // routing pattern. 239 | if mx.tree.findPattern(pattern+"*") != nil || mx.tree.findPattern(pattern+"/*") != nil { 240 | panic(fmt.Sprintf("chi: attempting to Mount() a handler on an existing path, '%s'", pattern)) 241 | } 242 | 243 | // Assign sub-Router's with the parent not found handler if not specified. 244 | subr, ok := handler.(*Mux) 245 | if ok && subr.notFoundHandler == nil && mx.notFoundHandler != nil { 246 | subr.NotFound(mx.notFoundHandler) 247 | } 248 | 249 | // Wrap the sub-router in a handlerFunc to scope the request path for routing. 250 | subHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 251 | rctx := RouteContext(r.Context()) 252 | rctx.RoutePath = "/" + rctx.URLParams.Del("*") 253 | handler.ServeHTTP(w, r) 254 | }) 255 | 256 | if pattern == "" || pattern[len(pattern)-1] != '/' { 257 | mx.handle(mALL|mSTUB, pattern, subHandler) 258 | mx.handle(mALL|mSTUB, pattern+"/", mx.NotFoundHandler()) 259 | pattern += "/" 260 | } 261 | 262 | method := mALL 263 | subroutes, _ := handler.(Routes) 264 | if subroutes != nil { 265 | method |= mSTUB 266 | } 267 | n := mx.handle(method, pattern+"*", subHandler) 268 | 269 | if subroutes != nil { 270 | n.subroutes = subroutes 271 | } 272 | } 273 | 274 | func (mx *Mux) Middlewares() Middlewares { 275 | return mx.middlewares 276 | } 277 | 278 | func (mx *Mux) Routes() []Route { 279 | return mx.tree.routes() 280 | } 281 | 282 | // FileServer conveniently sets up a http.FileServer handler to serve 283 | // static files from a http.FileSystem. 284 | func (mx *Mux) FileServer(path string, root http.FileSystem) { 285 | if strings.ContainsAny(path, ":*") { 286 | panic("chi: FileServer does not permit URL parameters.") 287 | } 288 | 289 | fs := http.StripPrefix(path, http.FileServer(root)) 290 | 291 | if path != "/" && path[len(path)-1] != '/' { 292 | mx.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP) 293 | path += "/" 294 | } 295 | path += "*" 296 | 297 | mx.Get(path, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 298 | fs.ServeHTTP(w, r) 299 | })) 300 | } 301 | 302 | // NotFoundHandler returns the default Mux 404 responder whenever a route 303 | // cannot be found. 304 | func (mx *Mux) NotFoundHandler() http.HandlerFunc { 305 | if mx.notFoundHandler != nil { 306 | return mx.notFoundHandler 307 | } 308 | return http.NotFound 309 | } 310 | 311 | // MethodNotAllowedHandler returns the default Mux 405 responder whenever 312 | // a method cannot be resolved for a route. 313 | func (mx *Mux) MethodNotAllowedHandler() http.HandlerFunc { 314 | if mx.methodNotAllowedHandler != nil { 315 | return mx.methodNotAllowedHandler 316 | } 317 | return methodNotAllowedHandler 318 | } 319 | 320 | // buildRouteHandler builds the single mux handler that is a chain of the middleware 321 | // stack, as defined by calls to Use(), and the tree router (Mux) itself. After this 322 | // point, no other middlewares can be registered on this Mux's stack. But you can still 323 | // compose additional middlewares via Group()'s or using a chained middleware handler. 324 | func (mx *Mux) buildRouteHandler() { 325 | mx.handler = chain(mx.middlewares, http.HandlerFunc(mx.routeHTTP)) 326 | } 327 | 328 | // handle registers a http.Handler in the routing tree for a particular http method 329 | // and routing pattern. 330 | func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *node { 331 | if len(pattern) == 0 || pattern[0] != '/' { 332 | panic(fmt.Sprintf("chi: routing pattern must begin with '/' in '%s'", pattern)) 333 | } 334 | 335 | // Build the final routing handler for this Mux. 336 | if !mx.inline && mx.handler == nil { 337 | mx.buildRouteHandler() 338 | } 339 | 340 | // Build endpoint handler with inline middlewares for the route 341 | var h http.Handler 342 | if mx.inline { 343 | mx.handler = http.HandlerFunc(mx.routeHTTP) 344 | h = Chain(mx.middlewares...).Handler(handler) 345 | } else { 346 | h = handler 347 | } 348 | 349 | // Add the endpoint to the tree and return the node 350 | return mx.tree.InsertRoute(method, pattern, h) 351 | } 352 | 353 | // routeHTTP routes a http.Request through the Mux routing tree to serve 354 | // the matching handler for a particular http method. 355 | func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) { 356 | // Grab the route context object 357 | rctx := r.Context().Value(RouteCtxKey).(*Context) 358 | 359 | // The request routing path 360 | routePath := rctx.RoutePath 361 | if routePath == "" { 362 | routePath = r.URL.Path 363 | } 364 | 365 | // Check if method is supported by chi 366 | method, ok := methodMap[r.Method] 367 | if !ok { 368 | mx.MethodNotAllowedHandler().ServeHTTP(w, r) 369 | return 370 | } 371 | 372 | // Find the route 373 | hs := mx.tree.FindRoute(rctx, routePath) 374 | if hs == nil { 375 | mx.NotFoundHandler().ServeHTTP(w, r) 376 | return 377 | } 378 | 379 | h, ok := hs[method] 380 | if !ok { 381 | mx.MethodNotAllowedHandler().ServeHTTP(w, r) 382 | return 383 | } 384 | 385 | // Serve it up 386 | h.ServeHTTP(w, r) 387 | } 388 | 389 | // Recursively update data on child routers. 390 | func (mx *Mux) updateSubRoutes(fn func(subMux *Mux)) { 391 | for _, r := range mx.tree.routes() { 392 | subMux, ok := r.SubRoutes.(*Mux) 393 | if !ok { 394 | continue 395 | } 396 | fn(subMux) 397 | } 398 | } 399 | 400 | // methodNotAllowedHandler is a helper function to respond with a 405, 401 | // method not allowed. 402 | func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) { 403 | w.WriteHeader(405) 404 | w.Write(nil) 405 | } 406 | -------------------------------------------------------------------------------- /vendor/github.com/pressly/chi/README.md: -------------------------------------------------------------------------------- 1 | chi 2 | === 3 | 4 | [![GoDoc Widget]][GoDoc] [![Travis Widget]][Travis] 5 | 6 | `chi` is a lightweight, idiomatic and composable router for building Go 1.7+ HTTP services. It's 7 | especially good at helping you write large REST API services that are kept maintainable as your 8 | project grows and changes. `chi` is built on the new `context` package introduced in Go 1.7 to 9 | handle signaling, cancelation and request-scoped values across a handler chain. 10 | 11 | The focus of the project has been to seek out an elegant and comfortable design for writing 12 | REST API servers, written during the development of the Pressly API service that powers our 13 | public API service, which in turn powers all of our client-side applications. 14 | 15 | The key considerations of chi's design are: project structure, maintainability, standard http 16 | handlers (stdlib-only), developer productivity, and deconstructing a large system into many small 17 | parts. The core router `github.com/pressly/chi` is quite small (less than 1000 LOC), but we've also 18 | included some useful/optional subpackages: `middleware`, `render` and `docgen`. We hope you enjoy it too! 19 | 20 | 21 | ## Features 22 | 23 | * **Lightweight** - cloc'd in <1000 LOC for the chi router 24 | * **Fast** - yes, see [benchmarks](#benchmarks) 25 | * **100% compatible with net/http** - use any http or middleware pkg in the ecosystem that is also compat with `net/http` 26 | * **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and subrouter mounting 27 | * **Context control** - built on new `context` package, providing value chaining, cancelations and timeouts 28 | * **Robust** - tested / used in production at Pressly.com, and many others 29 | * **Doc generation** - `docgen` auto-generates routing documentation from your source to JSON or Markdown 30 | * **No external dependencies** - plain ol' Go 1.7+ stdlib + net/http 31 | 32 | 33 | ## Examples 34 | 35 | * [rest](https://github.com/pressly/chi/blob/master/_examples/rest/main.go) - REST APIs made easy, productive and maintainable 36 | * [logging](https://github.com/pressly/chi/blob/master/_examples/logging/main.go) - Easy structured logging for any backend 37 | * [limits](https://github.com/pressly/chi/blob/master/_examples/limits/main.go) - Timeouts and Throttling 38 | * [todos-resource](https://github.com/pressly/chi/blob/master/_examples/todos-resource/main.go) - Struct routers/handlers, an example of another code layout style 39 | * [versions](https://github.com/pressly/chi/blob/master/_examples/versions/main.go) - Demo of `chi/render` subpkg 40 | * [fileserver](https://github.com/pressly/chi/blob/master/_examples/fileserver/main.go) - Easily serve static files 41 | * [graceful](https://github.com/pressly/chi/blob/master/_examples/graceful/main.go) - Graceful context signaling and server shutdown 42 | 43 | 44 | **As easy as:** 45 | 46 | ```go 47 | package main 48 | 49 | import ( 50 | "net/http" 51 | "github.com/pressly/chi" 52 | ) 53 | 54 | func main() { 55 | r := chi.NewRouter() 56 | r.Get("/", func(w http.ResponseWriter, r *http.Request) { 57 | w.Write([]byte("welcome")) 58 | }) 59 | http.ListenAndServe(":3000", r) 60 | } 61 | ``` 62 | 63 | **REST Preview:** 64 | 65 | Here is a little preview of how routing looks like with chi. Also take a look at the generated routing docs 66 | in JSON ([routes.json](https://github.com/pressly/chi/blob/master/_examples/rest/routes.json)) and in 67 | Markdown ([routes.md](https://github.com/pressly/chi/blob/master/_examples/rest/routes.md)). 68 | 69 | I highly recommend reading the source of the [examples](#examples) listed above, they will show you all the features 70 | of chi and serve as a good form of documentation. 71 | 72 | ```go 73 | import ( 74 | //... 75 | "context" 76 | "github.com/pressly/chi" 77 | "github.com/pressly/chi/middleware" 78 | ) 79 | 80 | func main() { 81 | r := chi.NewRouter() 82 | 83 | // A good base middleware stack 84 | r.Use(middleware.RequestID) 85 | r.Use(middleware.RealIP) 86 | r.Use(middleware.Logger) 87 | r.Use(middleware.Recoverer) 88 | 89 | // When a client closes their connection midway through a request, the 90 | // http.CloseNotifier will cancel the request context (ctx). 91 | r.Use(middleware.CloseNotify) 92 | 93 | // Set a timeout value on the request context (ctx), that will signal 94 | // through ctx.Done() that the request has timed out and further 95 | // processing should be stopped. 96 | r.Use(middleware.Timeout(60 * time.Second)) 97 | 98 | r.Get("/", func(w http.ResponseWriter, r *http.Request) { 99 | w.Write([]byte("hi")) 100 | }) 101 | 102 | // RESTy routes for "articles" resource 103 | r.Route("/articles", func(r chi.Router) { 104 | r.With(paginate).Get("/", listArticles) // GET /articles 105 | r.Post("/", createArticle) // POST /articles 106 | r.Get("/search", searchArticles) // GET /articles/search 107 | 108 | r.Route("/:articleID", func(r chi.Router) { 109 | r.Use(ArticleCtx) 110 | r.Get("/", getArticle) // GET /articles/123 111 | r.Put("/", updateArticle) // PUT /articles/123 112 | r.Delete("/", deleteArticle) // DELETE /articles/123 113 | }) 114 | }) 115 | 116 | // Mount the admin sub-router 117 | r.Mount("/admin", adminRouter()) 118 | 119 | http.ListenAndServe(":3333", r) 120 | } 121 | 122 | func ArticleCtx(next http.Handler) http.Handler { 123 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 124 | articleID := chi.URLParam(r, "articleID") 125 | article, err := dbGetArticle(articleID) 126 | if err != nil { 127 | http.Error(w, http.StatusText(404), 404) 128 | return 129 | } 130 | ctx := context.WithValue(r.Context(), "article", article) 131 | next.ServeHTTP(w, r.WithContext(ctx)) 132 | }) 133 | } 134 | 135 | func getArticle(w http.ResponseWriter, r *http.Request) { 136 | ctx := r.Context() 137 | article, ok := ctx.Value("article").(*Article) 138 | if !ok { 139 | http.Error(w, http.StatusText(422), 422) 140 | return 141 | } 142 | w.Write([]byte(fmt.Sprintf("title:%s", article.Title))) 143 | } 144 | 145 | // A completely separate router for administrator routes 146 | func adminRouter() http.Handler { 147 | r := chi.NewRouter() 148 | r.Use(AdminOnly) 149 | r.Get("/", adminIndex) 150 | r.Get("/accounts", adminListAccounts) 151 | return r 152 | } 153 | 154 | func AdminOnly(next http.Handler) http.Handler { 155 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 156 | ctx := r.Context() 157 | perm, ok := ctx.Value("acl.permission").(YourPermissionType) 158 | if !ok || !perm.IsAdmin() { 159 | http.Error(w, http.StatusText(403), 403) 160 | return 161 | } 162 | next.ServeHTTP(w, r) 163 | }) 164 | } 165 | ``` 166 | 167 | 168 | ## Router design 169 | 170 | chi's router is based on a kind of [Patricia Radix trie](https://en.wikipedia.org/wiki/Radix_tree). 171 | The router is fully compatible with `net/http`. 172 | 173 | Built on top of the tree is the `Router` interface: 174 | 175 | ```go 176 | // Router consisting of the core routing methods used by chi's Mux, 177 | // using only the standard net/http. 178 | type Router interface { 179 | http.Handler 180 | Routes 181 | 182 | // Use appends one of more middlewares onto the Router stack. 183 | Use(middlewares ...func(http.Handler) http.Handler) 184 | 185 | // With adds inline middlewares for an endpoint handler. 186 | With(middlewares ...func(http.Handler) http.Handler) Router 187 | 188 | // Group adds a new inline-Router along the current routing 189 | // path, with a fresh middleware stack for the inline-Router. 190 | Group(fn func(r Router)) Router 191 | 192 | // Route mounts a sub-Router along a `pattern`` string. 193 | Route(pattern string, fn func(r Router)) Router 194 | 195 | // Mount attaches another http.Handler along ./pattern/* 196 | Mount(pattern string, h http.Handler) 197 | 198 | // Handle and HandleFunc adds routes for `pattern` that matches 199 | // all HTTP methods. 200 | Handle(pattern string, h http.Handler) 201 | HandleFunc(pattern string, h http.HandlerFunc) 202 | 203 | // HTTP-method routing along `pattern` 204 | Connect(pattern string, h http.HandlerFunc) 205 | Delete(pattern string, h http.HandlerFunc) 206 | Get(pattern string, h http.HandlerFunc) 207 | Head(pattern string, h http.HandlerFunc) 208 | Options(pattern string, h http.HandlerFunc) 209 | Patch(pattern string, h http.HandlerFunc) 210 | Post(pattern string, h http.HandlerFunc) 211 | Put(pattern string, h http.HandlerFunc) 212 | Trace(pattern string, h http.HandlerFunc) 213 | 214 | // NotFound defines a handler to respond whenever a route could 215 | // not be found. 216 | NotFound(h http.HandlerFunc) 217 | } 218 | 219 | // Routes interface adds two methods for router traversal, which is also 220 | // used by the `docgen` subpackage to generation documentation for Routers. 221 | type Routes interface { 222 | // Routes returns the routing tree in an easily traversable structure. 223 | Routes() []Route 224 | 225 | // Middlewares returns the list of middlewares in use by the router. 226 | Middlewares() Middlewares 227 | } 228 | ``` 229 | 230 | Each routing method accepts a URL `pattern` and chain of `handlers`. The URL pattern 231 | supports named params (ie. `/users/:userID`) and wildcards (ie. `/admin/*`). 232 | 233 | 234 | ### Middleware handlers 235 | 236 | chi's middlewares are just stdlib net/http middleware handlers. There is nothing special 237 | about them, which means the router and all the tooling is designed to be compatible and 238 | friendly with any middleware in the community. This offers much better extensibility and reuse 239 | of packages and is at the heart of chi's purpose. 240 | 241 | Here is an example of a standard net/http middleware handler using the new request context 242 | available in Go 1.7+. This middleware sets a hypothetical user identifier on the request 243 | context and calls the next handler in the chain. 244 | 245 | ```go 246 | // HTTP middleware setting a value on the request context 247 | func MyMiddleware(next http.Handler) http.Handler { 248 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 249 | ctx := context.WithValue(r.Context(), "user", "123") 250 | next.ServeHTTP(w, r.WithContext(ctx)) 251 | }) 252 | } 253 | ``` 254 | 255 | 256 | ### Request handlers 257 | 258 | chi uses standard net/http request handlers. This little snippet is an example of a http.Handler 259 | func that reads a user identifier from the request context - hypothetically, identifying 260 | the user sending an authenticated request, validated+set by a previous middleware handler. 261 | 262 | ```go 263 | // HTTP handler accessing data from the request context. 264 | func MyRequestHandler(w http.ResponseWriter, r *http.Request) { 265 | user := r.Context().Value("user").(string) 266 | w.Write([]byte(fmt.Sprintf("hi %s", user))) 267 | } 268 | ``` 269 | 270 | 271 | ### URL parameters 272 | 273 | chi's router parses and stores URL parameters right onto the request context. Here is 274 | an example of how to access URL params in your net/http handlers. And of course, middlewares 275 | are able to access the same information. 276 | 277 | ```go 278 | // HTTP handler accessing the url routing parameters. 279 | func MyRequestHandler(w http.ResponseWriter, r *http.Request) { 280 | userID := chi.URLParam(r, "userID") // from a route like /users/:userID 281 | 282 | ctx := r.Context() 283 | key := ctx.Value("key").(string) 284 | 285 | w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key))) 286 | } 287 | ``` 288 | 289 | 290 | ## Middlewares 291 | 292 | chi comes equipped with an optional `middleware` package, providing a suite of standard 293 | `net/http` middlewares. Please note, any middleware in the ecosystem that is also compatible 294 | with `net/http` can be used with chi's mux. 295 | 296 | -------------------------------------------------------------------------------------------------- 297 | | Middleware | Description | 298 | |:-------------|:--------------------------------------------------------------------------------- 299 | | RequestID | Injects a request ID into the context of each request. | 300 | | RealIP | Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP. | 301 | | Logger | Logs the start and end of each request with the elapsed processing time. | 302 | | Recoverer | Gracefully absorb panics and prints the stack trace. | 303 | | NoCache | Sets response headers to prevent clients from caching. | 304 | | CloseNotify | Signals to the request context when a client has closed their connection. | 305 | | Timeout | Signals to the request context when the timeout deadline is reached. | 306 | | Throttle | Puts a ceiling on the number of concurrent requests. | 307 | | Compress | Gzip compression for clients that accept compressed responses. | 308 | | Profiler | Easily attach net/http/pprof to your routers. | 309 | | Slashes | Strip and redirect slashes on routing paths. | 310 | | WithValue | Short-hand middleware to set a key/value on the request context. | 311 | | Heartbeat | Monitoring endpoint to check the servers pulse. | 312 | -------------------------------------------------------------------------------------------------- 313 | 314 | Other cool net/http middlewares: 315 | 316 | * [jwtauth](https://github.com/goware/jwtauth) - JWT authenticator 317 | * [cors](https://github.com/goware/cors) - CORS middleware 318 | * [httpcoala](https://github.com/goware/httpcoala) - Request coalescer 319 | 320 | please [submit a PR](./CONTRIBUTING.md) if you'd like to include a link to a chi middleware 321 | 322 | 323 | ## context? 324 | 325 | `context` is a tiny pkg that provides simple interface to signal context across call stacks 326 | and goroutines. It was originally written by [Sameer Ajmani](https://github.com/Sajmani) 327 | and is available in stdlib since go1.7. 328 | 329 | Learn more at https://blog.golang.org/context 330 | 331 | and.. 332 | * Docs: https://golang.org/pkg/context 333 | * Source: https://github.com/golang/go/tree/master/src/context 334 | 335 | 336 | ## Benchmarks 337 | 338 | The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark 339 | 340 | Comparison with other routers (as of Aug 1/16): https://gist.github.com/pkieltyka/76a59d33492dd2732e691ad8c0b274a4 341 | 342 | ```shell 343 | BenchmarkChi_Param 5000000 251 ns/op 240 B/op 1 allocs/op 344 | BenchmarkChi_Param5 5000000 393 ns/op 240 B/op 1 allocs/op 345 | BenchmarkChi_Param20 1000000 1012 ns/op 240 B/op 1 allocs/op 346 | BenchmarkChi_ParamWrite 5000000 301 ns/op 240 B/op 1 allocs/op 347 | BenchmarkChi_GithubStatic 5000000 287 ns/op 240 B/op 1 allocs/op 348 | BenchmarkChi_GithubParam 3000000 442 ns/op 240 B/op 1 allocs/op 349 | BenchmarkChi_GithubAll 20000 90855 ns/op 48723 B/op 203 allocs/op 350 | BenchmarkChi_GPlusStatic 5000000 250 ns/op 240 B/op 1 allocs/op 351 | BenchmarkChi_GPlusParam 5000000 280 ns/op 240 B/op 1 allocs/op 352 | BenchmarkChi_GPlus2Params 5000000 337 ns/op 240 B/op 1 allocs/op 353 | BenchmarkChi_GPlusAll 300000 4128 ns/op 3120 B/op 13 allocs/op 354 | BenchmarkChi_ParseStatic 5000000 250 ns/op 240 B/op 1 allocs/op 355 | BenchmarkChi_ParseParam 5000000 275 ns/op 240 B/op 1 allocs/op 356 | BenchmarkChi_Parse2Params 5000000 305 ns/op 240 B/op 1 allocs/op 357 | BenchmarkChi_ParseAll 200000 7671 ns/op 6240 B/op 26 allocs/op 358 | BenchmarkChi_StaticAll 30000 55497 ns/op 37682 B/op 157 allocs/op 359 | ``` 360 | 361 | NOTE: the allocs in the benchmark above are from the calls to http.Request's 362 | `WithContext(context.Context)` method that clones the http.Request, sets the `Context()` 363 | on the duplicated (alloc'd) request and returns it the new request object. This is just 364 | how setting context on a request in Go 1.7 works. 365 | 366 | 367 | ## Credits 368 | 369 | * Carl Jackson for https://github.com/zenazn/goji 370 | * Parts of chi's thinking comes from goji, and chi's middleware package 371 | sources from goji. 372 | * Armon Dadgar for https://github.com/armon/go-radix 373 | * Contributions: [@VojtechVitek](https://github.com/VojtechVitek) 374 | 375 | We'll be more than happy to see [your contributions](./CONTRIBUTING.md)! 376 | 377 | 378 | ## Beyond REST 379 | 380 | chi is just a http router that lets you decompose request handling into many smaller layers. 381 | Many companies including Pressly.com (of course) use chi to write REST services for their public 382 | APIs. But, REST is just a convention for managing state via HTTP, and there's a lot of other pieces 383 | required to write a complete client-server system or network of microservices. 384 | 385 | Looking ahead beyond REST, I also recommend some newer works in the field coming from 386 | [gRPC](https://github.com/grpc/grpc-go), [NATS](https://nats.io), [go-kit](https://github.com/go-kit/kit) 387 | and even [graphql](https://github.com/graphql-go/graphql). They're all pretty cool with their 388 | own unique approaches and benefits. Specifically, I'd look at gRPC since it makes client-server 389 | communication feel like a single program on a single computer, no need to hand-write a client library 390 | and the request/response payloads are typed contracts. NATS is pretty amazing too as a super 391 | fast and lightweight pub-sub transport that can speak protobufs, with nice service discovery - 392 | an excellent combination with gRPC. 393 | 394 | 395 | ## License 396 | 397 | Copyright (c) 2015-present [Peter Kieltyka](https://github.com/pkieltyka) 398 | 399 | Licensed under [MIT License](./LICENSE) 400 | 401 | [GoDoc]: https://godoc.org/github.com/pressly/chi 402 | [GoDoc Widget]: https://godoc.org/github.com/pressly/chi?status.svg 403 | [Travis]: https://travis-ci.org/pressly/chi 404 | [Travis Widget]: https://travis-ci.org/pressly/chi.svg?branch=master 405 | --------------------------------------------------------------------------------