| language: go | |||||
| install: | |||||
| - go get github.com/smartystreets/goconvey | |||||
| go: | |||||
| - 1.4 | |||||
| - tip |
| /* ---------------------------------------------------------------------- | |||||
| * ______ ___ __ | |||||
| * / ____/___ / | ____ __ ___ __/ /_ ___ ________ | |||||
| * / / __/ __ \/ /| | / __ \/ / / / | /| / / __ \/ _ \/ ___/ _ \ | |||||
| * / /_/ / /_/ / ___ |/ / / / /_/ /| |/ |/ / / / / __/ / / __/ | |||||
| * \____/\____/_/ |_/_/ /_/\__. / |__/|__/_/ /_/\___/_/ \___/ | |||||
| * /____/ | |||||
| * | |||||
| * (C) Copyright 2015 GoAnywhere (http://goanywhere.io). | |||||
| * ---------------------------------------------------------------------- | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| * ----------------------------------------------------------------------*/ | |||||
| package internal | |||||
| const Root = "rex.root" |
| /* ---------------------------------------------------------------------- | |||||
| * ______ ___ __ | |||||
| * / ____/___ / | ____ __ ___ __/ /_ ___ ________ | |||||
| * / / __/ __ \/ /| | / __ \/ / / / | /| / / __ \/ _ \/ ___/ _ \ | |||||
| * / /_/ / /_/ / ___ |/ / / / /_/ /| |/ |/ / / / / __/ / / __/ | |||||
| * \____/\____/_/ |_/_/ /_/\__. / |__/|__/_/ /_/\___/_/ \___/ | |||||
| * /____/ | |||||
| * | |||||
| * (C) Copyright 2015 GoAnywhere (http://goanywhere.io). | |||||
| * ---------------------------------------------------------------------- | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| * ----------------------------------------------------------------------*/ | |||||
| package internal | |||||
| import ( | |||||
| "net/http" | |||||
| "github.com/Sirupsen/logrus" | |||||
| ) | |||||
| type Module struct { | |||||
| cache http.Handler | |||||
| stack []func(http.Handler) http.Handler | |||||
| } | |||||
| func (self *Module) build() http.Handler { | |||||
| if self.cache == nil { | |||||
| next := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {})) | |||||
| // Activate modules in FIFO order. | |||||
| for index := len(self.stack) - 1; index >= 0; index-- { | |||||
| next = self.stack[index](next) | |||||
| } | |||||
| self.cache = next | |||||
| } | |||||
| return self.cache | |||||
| } | |||||
| // Use add the middleware module into the stack chain. | |||||
| // Supported middleware modules: | |||||
| // - http.Handler | |||||
| // - func(http.Handler) http.Handler | |||||
| func (self *Module) Use(mod interface{}) { | |||||
| switch H := mod.(type) { | |||||
| case http.Handler: | |||||
| handler := func(next http.Handler) http.Handler { | |||||
| return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | |||||
| H.ServeHTTP(w, req) | |||||
| next.ServeHTTP(w, req) | |||||
| }) | |||||
| } | |||||
| self.stack = append(self.stack, handler) | |||||
| case func(http.Handler) http.Handler: | |||||
| self.stack = append(self.stack, H) | |||||
| default: | |||||
| logrus.Fatal("Unsupported middleware module passed in.") | |||||
| } | |||||
| } | |||||
| // Implements the net/http Handler interface and calls the middleware stack. | |||||
| func (self *Module) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||||
| self.build().ServeHTTP(w, r) | |||||
| } |
| import "net/http" | import "net/http" | ||||
| type response interface { | |||||
| SetStatus(code int) | |||||
| SetContentType(contentType string) | |||||
| type module struct { | |||||
| stack []func(http.Handler) http.Handler | |||||
| } | } | ||||
| func Render(w http.ResponseWriter) { | |||||
| // build sets up the whole middleware modules in a FIFO chain. | |||||
| func (self *module) build() http.Handler { | |||||
| var next http.Handler = http.DefaultServeMux | |||||
| // Activate modules in FIFO order. | |||||
| for index := len(self.stack) - 1; index >= 0; index-- { | |||||
| next = self.stack[index](next) | |||||
| } | |||||
| return next | |||||
| } | |||||
| // Use add the middleware module into the stack chain. | |||||
| func (self *module) Use(modules ...func(http.Handler) http.Handler) { | |||||
| self.stack = append(self.stack, modules...) | |||||
| } | |||||
| // Implements the net/http Handler interface and calls the middleware stack. | |||||
| func (self *module) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||||
| self.build().ServeHTTP(w, r) | |||||
| } | } |
| "path/filepath" | "path/filepath" | ||||
| "runtime" | "runtime" | ||||
| "github.com/goanywhere/rex/internal" | |||||
| "github.com/goanywhere/rex/modules" | "github.com/goanywhere/rex/modules" | ||||
| "github.com/goanywhere/x/env" | "github.com/goanywhere/x/env" | ||||
| "github.com/goanywhere/x/fs" | "github.com/goanywhere/x/fs" | ||||
| // FileServer registers a handler to serve HTTP requests | // FileServer registers a handler to serve HTTP requests | ||||
| // with the contents of the file system rooted at root. | // with the contents of the file system rooted at root. | ||||
| /* | |||||
| func FileServer(prefix, dir string) { | func FileServer(prefix, dir string) { | ||||
| DefaultMux.FileServer(prefix, dir) | DefaultMux.FileServer(prefix, dir) | ||||
| } | } | ||||
| */ | |||||
| // Use appends middleware module into the serving list, modules will be served in FIFO order. | // Use appends middleware module into the serving list, modules will be served in FIFO order. | ||||
| func Use(module func(http.Handler) http.Handler) { | func Use(module func(http.Handler) http.Handler) { | ||||
| } | } | ||||
| func init() { | func init() { | ||||
| // ---------------------------------------- | |||||
| // Project Root | |||||
| // ---------------------------------------- | |||||
| // setup project root | |||||
| var root = fs.Getcd(2) | var root = fs.Getcd(2) | ||||
| env.Set(internal.Root, root) | |||||
| env.Set("rex.root", fs.Getcd(2)) | |||||
| env.Load(filepath.Join(root, ".env")) | env.Load(filepath.Join(root, ".env")) | ||||
| // cmd arguments | // cmd arguments |
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "net/http" | "net/http" | ||||
| "path/filepath" | |||||
| "reflect" | "reflect" | ||||
| "runtime" | "runtime" | ||||
| "time" | "time" | ||||
| "github.com/goanywhere/rex/internal" | |||||
| "github.com/gorilla/mux" | "github.com/gorilla/mux" | ||||
| ) | ) | ||||
| type Router struct { | type Router struct { | ||||
| mod *internal.Module | |||||
| module *module | |||||
| mux *mux.Router | mux *mux.Router | ||||
| ready bool | ready bool | ||||
| subrouters []*Router | subrouters []*Router | ||||
| func New() *Router { | func New() *Router { | ||||
| return &Router{ | return &Router{ | ||||
| mod: new(internal.Module), | |||||
| mux: mux.NewRouter().StrictSlash(true), | |||||
| module: new(module), | |||||
| mux: mux.NewRouter().StrictSlash(true), | |||||
| } | } | ||||
| } | } | ||||
| if !self.ready { | if !self.ready { | ||||
| self.ready = true | self.ready = true | ||||
| // * activate router's middleware modules. | // * activate router's middleware modules. | ||||
| self.mod.Use(self.mux) | |||||
| self.module.Use(func(http.Handler) http.Handler { | |||||
| return self.mux | |||||
| }) | |||||
| // * activate subrouters's middleware modules. | // * activate subrouters's middleware modules. | ||||
| for index := 0; index < len(self.subrouters); index++ { | for index := 0; index < len(self.subrouters); index++ { | ||||
| sr := self.subrouters[index] | sr := self.subrouters[index] | ||||
| sr.mod.Use(sr.mux) | |||||
| sr.module.Use(func(http.Handler) http.Handler { | |||||
| return sr.mux | |||||
| }) | |||||
| } | } | ||||
| } | } | ||||
| return self.mod | |||||
| return self.module | |||||
| } | } | ||||
| // register adds the http.Handler/http.HandleFunc into Gorilla mux. | // register adds the http.Handler/http.HandleFunc into Gorilla mux. | ||||
| // Group creates a new application group under the given path prefix. | // Group creates a new application group under the given path prefix. | ||||
| func (self *Router) Group(prefix string) *Router { | func (self *Router) Group(prefix string) *Router { | ||||
| var mod = new(internal.Module) | |||||
| self.mux.PathPrefix(prefix).Handler(mod) | |||||
| var module = new(module) | |||||
| self.mux.PathPrefix(prefix).Handler(module) | |||||
| var mux = self.mux.PathPrefix(prefix).Subrouter() | var mux = self.mux.PathPrefix(prefix).Subrouter() | ||||
| router := &Router{mod: mod, mux: mux} | |||||
| router := &Router{module: module, mux: mux} | |||||
| self.subrouters = append(self.subrouters, router) | self.subrouters = append(self.subrouters, router) | ||||
| return router | return router | ||||
| } | } | ||||
| // FileServer registers a handler to serve HTTP requests | |||||
| // with the contents of the file system rooted at root. | |||||
| func (self *Router) FileServer(prefix, dir string) { | |||||
| if abs, err := filepath.Abs(dir); err == nil { | |||||
| Infof("FS: %s", abs) | |||||
| fs := http.StripPrefix(prefix, http.FileServer(http.Dir(abs))) | |||||
| self.mux.PathPrefix(prefix).Handler(fs) | |||||
| } else { | |||||
| Fatalf("Failed to setup file server: %v", err) | |||||
| } | |||||
| } | |||||
| // Name returns route name for the given request, if any. | // Name returns route name for the given request, if any. | ||||
| func (self *Router) Name(r *http.Request) (name string) { | func (self *Router) Name(r *http.Request) (name string) { | ||||
| var match mux.RouteMatch | var match mux.RouteMatch | ||||
| return name | return name | ||||
| } | } | ||||
| func (self *Router) Use(module interface{}) { | |||||
| self.mod.Use(module) | |||||
| // Use add the middleware module into the stack chain. | |||||
| func (self *Router) Use(module func(http.Handler) http.Handler) { | |||||
| self.module.Use(module) | |||||
| } | } | ||||
| // ServeHTTP dispatches the request to the handler whose | // ServeHTTP dispatches the request to the handler whose |