├── .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 |
52 |
GET /battery-status
53 | Get the status of the device battery.
54 |
55 |
56 |
57 |
GET /camera-info
58 | Get information about device camera(s).
59 |
60 |
61 |
62 |
GET /camera-photo?c={cameraID}
63 | Take a photo and view it in JPEG format.
64 |
65 |
66 |
67 |
GET /contact-list
68 | List all contacts.
69 |
70 |
71 |
72 |
GET /infrared-frequencies
73 | Query the infrared transmitter's supported carrier frequencies.
74 |
75 |
76 |
77 |
GET /location?p={provider}
78 | Get the device location.
79 |
80 |
81 |
82 |
GET /notification?c={content}&i={id}&t={title}&u={url}
83 | Display a system notification.
84 |
85 |
86 |
87 |
GET /sms-inbox?d={true}&l={limit}&n={true}&o={offset}
88 | List received SMS messages.
89 |
90 |
91 |
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 |
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 |
103 |
GET /telephony-deviceinfo
104 | Get information about the telephony device.
105 |
106 |
107 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------