func TestWrapPanic(t *testing.T) { var buf bytes.Buffer reqlog.SetLogger(stdlog.New(&buf, "", 0)) mux := goji.NewMux() mux.HandleFunc(pat.New("/safe"), func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) }) mux.HandleFunc(pat.New("/panic"), func(w http.ResponseWriter, r *http.Request) { panic("hi there!") }) mux.UseC(respond.WrapPanicC) req, err := http.NewRequest("FOO", "/safe", nil) if err != nil { t.Fatal(err) } rr := httptest.NewRecorder() mux.ServeHTTP(rr, req) if err := apptest.AssertStatus(rr, http.StatusNoContent); err != nil { t.Error(err) } buf.Reset() rr = httptest.NewRecorder() req.URL.Path = "/panic" mux.ServeHTTP(rr, req) if err := apptest.AssertStatus(rr, http.StatusInternalServerError); err != nil { t.Error(err) } t.Log(rr.Body.String()) uerr, err := usererrors.UnmarshalJSON(rr.Body.Bytes()) if err != nil { t.Fatal(err) } _, ok := uerr.(usererrors.InternalFailure) if !ok { t.Errorf("expected an InternalFailure but got %#v", uerr) } t.Log(buf.String()) }
// Add implements mux support for a given resource which is effectively handled as: // pat.New("/(prefix/)resource.Plu*) func (a *API) Add(resource *Resource) { // track our associated resources, will enable auto-generation docs later a.Resources[resource.Type] = resource // Because of how prefix matches work: // https://godoc.org/github.com/goji/goji/pat#hdr-Prefix_Matches // We need two separate routes, // /(prefix/)resources matcher := path.Join(a.prefix, resource.Type) a.Mux.HandleC(pat.New(matcher), resource) // And: // /(prefix/)resources/* idMatcher := path.Join(a.prefix, resource.Type, "*") a.Mux.HandleC(pat.New(idMatcher), resource) }
func New(cfg *config.Config, r brew.Reader) http.Handler { mux := goji.NewMux() app := Web{ r: r, t: template.Must(template.New("card").Parse(tmpl)), } // Setup middleware mux.UseC(api.Recover) mux.UseC(api.Tracing) mux.HandleFuncC(pat.Get("/mtg/cards/:id"), app.HandleCard) mux.Handle(pat.New("/*"), http.FileServer(http.Dir("./web/static/"))) return mux }
func API() *goji.Mux { mux := goji.SubMux() // We pass the routes as relative to the point where the API router // will be mounted. The super-router will strip any prefix off for us. mux.HandleFuncC(pat.Get("/people"), api.ListPeople) mux.HandleFuncC(pat.Post("/people"), api.CreatePerson) mux.HandleFuncC(pat.Get("/people/:person"), api.GetPerson) mux.HandleFuncC(pat.Delete("/people/:person"), api.DeletePerson) // Add default 'not found' route that responds with JSON mux.HandleFunc(pat.New("/*"), func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(404) fmt.Fprint(w, `{"error":"not found"}`) }) return mux }
func TestMetrics(t *testing.T) { backend := &stubBackend{} metrics.SetBackend(backend) inner := goji.NewMux() inner.HandleFunc(pat.Get("/:baz"), func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(1) }) inner.UseC(metrics.WrapSubmuxC) outer := goji.NewMux() outer.HandleFunc(pat.Get("/foo"), func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(2) }) outer.HandleC(pat.New("/bar/*"), inner) outer.UseC(metrics.WrapC) cases := []struct { path string expect map[string]int }{ {"/foo", map[string]int{"foo.request": 1, "foo.response.2": 1}}, {"/bar/baz", map[string]int{"bar.:baz.request": 1, "bar.:baz.response.1": 1}}, {"/bar", map[string]int{}}, } for _, testcase := range cases { backend.Reset() req, err := http.NewRequest("GET", testcase.path, nil) if err != nil { t.Fatal(err) } rr := httptest.NewRecorder() outer.ServeHTTPC(context.Background(), rr, req) if have, want := backend.data, testcase.expect; !reflect.DeepEqual(have, want) { t.Errorf("%s: Expected %#v but got %#v", testcase.path, want, have) } } }
// New creates an App for the given configuration. func New(config *Configuration) (*App, error) { var app App db, err := buildDB(config.DBDSN, config.DBMaxIdle, config.DBMaxOpen) if err != nil { defer app.Close() return nil, err } app.closers = append(app.closers, db) ipQuota := throttled.RateQuota{throttled.PerMin(config.IPPerMinute), config.IPRateBurst} ipLimiter, err := buildLimiter(ipQuota) authCtrl, err := auth.New(config.UserSecret) if err != nil { defer app.Close() return nil, err } sayCtrl, err := say.New(db) if err != nil { defer app.Close() return nil, err } app.closers = append(app.closers, sayCtrl) // TODO: Proper not found handler privMux := goji.NewMux() privMux.UseC(metrics.WrapSubmuxC) privMux.UseC(authCtrl.WrapC) privMux.HandleFuncC(Routes.GetAnimals, sayCtrl.GetAnimals) privMux.HandleFuncC(Routes.ListMoods, sayCtrl.ListMoods) privMux.HandleFuncC(Routes.SetMood, sayCtrl.SetMood) privMux.HandleFuncC(Routes.GetMood, sayCtrl.GetMood) privMux.HandleFuncC(Routes.DeleteMood, sayCtrl.DeleteMood) privMux.HandleFuncC(Routes.ListConversations, sayCtrl.ListConversations) privMux.HandleFuncC(Routes.CreateConversation, sayCtrl.CreateConversation) privMux.HandleFuncC(Routes.GetConversation, sayCtrl.GetConversation) privMux.HandleFuncC(Routes.DeleteConversation, sayCtrl.DeleteConversation) privMux.HandleFuncC(Routes.CreateLine, sayCtrl.CreateLine) privMux.HandleFuncC(Routes.GetLine, sayCtrl.GetLine) privMux.HandleFuncC(Routes.DeleteLine, sayCtrl.DeleteLine) mainMux := goji.NewMux() mainMux.HandleFuncC(Routes.CreateUser, authCtrl.CreateUser) mainMux.HandleFuncC(Routes.GetUser, authCtrl.GetUser) mainMux.HandleC(pat.New("/*"), privMux) mainMux.UseC(reqlog.WrapC) mainMux.UseC(respond.WrapPanicC) mainMux.UseC(metrics.WrapC) mainMux.Use(ipLimiter.RateLimit) app.srv = mainMux return &app, nil }
func main() { log.Printf("initializing project_name=%q version=%q revision=%q", conf.ProjectName, conf.Version, conf.Revision) // Connect to the database. db, err := database.Connect(conf.C.DbType, conf.C.DbConn) if err != nil { log.Printf("could not connect to database err=%q db_type=%q db_conn=%q", err, conf.C.DbType, conf.C.DbConn) return } // Create datastore. ds := database.NewDatastore(db) // Create API router and add middleware. apiMux := router.API() apiMux.Use(middleware.Options) apiMux.Use(middleware.JSON) // Create web router. webMux := router.Web() // Create root mux and add common middleware. rootMux := goji.NewMux() rootMux.UseC(middleware.RequestID) rootMux.UseC(middleware.Logger) rootMux.UseC(middleware.Recoverer) rootMux.Use(middleware.SetHeaders) // Serve all static assets from the root. serveAssetAt := func(asset, path string) { info, _ := static.AssetInfo(asset) modTime := info.ModTime() data := static.MustAsset(asset) webMux.Handle(pat.Get(path), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Printf("debug: serving asset: %s", asset) http.ServeContent(w, r, asset, modTime, bytes.NewReader(data)) })) } for _, asset := range static.AssetNames() { log.Printf("debug: adding route for asset: %s", asset) serveAssetAt(asset, "/static/"+asset) } // Special case a bunch of assets that should be served from the root. for _, asset := range []string{ "clientaccesspolicy.xml", "crossdomain.xml", "favicon.ico", "humans.txt", "robots.txt", } { // Note: only serve if we have this asset. if _, err := static.Asset(asset); err == nil { log.Printf("debug: adding special route for asset: %s", asset) serveAssetAt(asset, "/"+asset) } } // Serve the index page if we have one. for _, asset := range []string{"index.html", "index.htm"} { // Note: only serve if we have this asset, and only serve the first // option. if _, err := static.Asset(asset); err == nil { log.Printf("debug: adding index route for asset: %s", asset) serveAssetAt(asset, "/") break } } // Mount the API/Web muxes last (since order matters). rootMux.HandleC(pat.New("/api/*"), apiMux) rootMux.HandleC(pat.New("/*"), webMux) // Create a top-level wrapper that implements ServeHTTP, so we can inject // the root (Background) context and any other contexts that we wish to // inject. outer := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := context.Background() ctx = datastore.NewContext(ctx, ds) rootMux.ServeHTTPC(ctx, w, r) }) // Start serving log.Printf("starting server on: %s", conf.C.HostString()) graceful.Run(conf.C.HostString(), 10*time.Second, outer) log.Printf("server finished") }
func (m *Mux) Handle(pattern string, submux *Mux) { handleMap(m, submux, pattern) m.webmux.Handle(pat.New(pattern), submux.Mux()) }