// New returns a new App. Any errors will result in a panic, Since // this function should be called only during app initialization. func New(opts Options) *App { if opts.Name == "" { panic(errors.New("reusable app name can't be empty")) } a := &App{ App: *app.New(), opts: &opts, name: opts.Name, } var k interface{} = reusableAppKey if opts.DataKey != nil { k = opts.DataKey } a.Set(k, a) _, file, _, ok := runtime.Caller(1) if ok { a.dir = filepath.Dir(file) } assetsFS, assetsRel := a.mustOpenVFS("assets", opts.AssetsDir, opts.AssetsData) if assetsFS != nil { assetsPrefix := path.Clean("/" + filepath.ToSlash(assetsRel) + "/") manager := assets.New(assetsFS, assetsPrefix) a.SetAssetsManager(manager) a.Handle("^"+assetsPrefix, app.HandlerFromHTTPFunc(manager.Handler())) } templatesFS, _ := a.mustOpenVFS("tmpl", opts.TemplatesDir, opts.TemplatesData) if templatesFS != nil { a.SetTemplatesFS(templatesFS) } return a }
func init() { template.AddFuncs(template.FuncMap{ "#config": func(name string, def string) template.HTML { var buf bytes.Buffer buf.WriteString("<h3 class=\"config\">") buf.WriteString(html.Escape(name)) if def != "" { buf.WriteString(" <span class=\"label label-success\">optional</span>") fmt.Fprintf(&buf, " <span class=\"default\">default: %s</span>", html.Escape(def)) } else { buf.WriteString(" <span class=\"label label-danger\">required</span>") } buf.WriteString("</h3>") return template.HTML(buf.String()) }}) config.MustParse() App = app.New() App.SetTrustXHeaders(true) // Redirect all other possible hosts to governator.io redir := app.RedirectHandler("http://governator.io${0}", true) App.Handle("(.*)", redir, app.HostHandler("governator-io.appspot.com")) App.Handle("(.*)", redir, app.HostHandler("www.governator.io")) App.HandleAssets("/assets/", pathutil.Relative("assets")) App.Handle("^/$", app.TemplateHandler("main.html", nil)) App.Handle("^/install\\.sh$", fileHandler("contrib/install.sh")) App.Handle("^/get/releases/linux/x86_64/latest/governator$", fileHandler("governator")) App.Handle("^/contrib/(.*)", func(ctx *app.Context) { path := filepath.Join(data, "contrib", filepath.FromSlash(ctx.IndexValue(0))) serveFile(ctx, path) }) }
func TestXHeaders(t *testing.T) { a := app.New() a.Handle("/", func(ctx *app.Context) { fmt.Fprintf(ctx, "%s\n%s", ctx.RemoteAddress(), ctx.URL().String()) }) tt := tester.New(t, a) tt.Get("/", nil).AddHeader("X-Real-IP", "8.8.8.8").AddHeader("X-Scheme", "https").Expect("\nhttp://localhost/") a.SetTrustXHeaders(true) tt.Get("/", nil).AddHeader("X-Real-IP", "8.8.8.8").AddHeader("X-Scheme", "https").Expect("8.8.8.8\nhttps://localhost/") }
func TestAppendSlash(t *testing.T) { a := app.New() a.Handle("/foo/", func(ctx *app.Context) { ctx.WriteString("Hello world") }) tt := tester.New(t, a) tt.Get("/foo", nil).Expect(301).ExpectHeader("Location", "/foo/") a.SetAppendSlash(false) tt.Get("/foo", nil).Expect(404) }
func TestParameters(t *testing.T) { a := app.New() a.Handle("/parse-string-param/(?P<string>\\w+)?$", func(ctx *app.Context) { str := "default" ctx.ParseParamValue("string", &str) ctx.WriteString(str) }) a.Handle("/parse-int-param/(?P<number>\\d+)?$", func(ctx *app.Context) { val := -1 ctx.ParseParamValue("number", &val) ctx.WriteString(strconv.Itoa(val)) }) a.Handle("/parse-int-form-value/$", func(ctx *app.Context) { val := -1 ctx.ParseFormValue("v", &val) ctx.WriteString(strconv.Itoa(val)) }) a.Handle("/must-parse-int-form-value/$", func(ctx *app.Context) { val := -1 ctx.MustParseFormValue("v", &val) ctx.WriteString(strconv.Itoa(val)) }) a.Handle("/parse-index-value/(\\d+)?$", func(ctx *app.Context) { val := -1 ctx.ParseIndexValue(0, &val) ctx.WriteString(strconv.Itoa(val)) }) tester := tester.New(t, a) tester.Get("/parse-string-param/", nil).Expect(200).Expect("default") tester.Get("/parse-string-param/foo", nil).Expect(200).Expect("foo") tester.Get("/parse-int-param/", nil).Expect(200).Expect("-1") // -10 does not match the handler tester.Get("/parse-int-param/-10", nil).Expect(404) tester.Get("/parse-int-param/42", nil).Expect(200).Expect("42") tester.Get("/parse-int-form-value/", nil).Expect(200).Expect("-1") tester.Get("/parse-int-form-value/", map[string]interface{}{"v": 9000}).Expect(200).Expect("9000") tester.Get("/parse-int-form-value/", map[string]interface{}{"v": "not-a-number"}).Expect(200).Expect("-1") tester.Get("/must-parse-int-form-value/", nil).Expect(400) tester.Get("/must-parse-int-form-value/", map[string]interface{}{"v": 9000}).Expect(200).Expect("9000") tester.Get("/must-parse-int-form-value/", map[string]interface{}{"v": "not-a-number"}).Expect(400) tester.Get("/parse-index-value/", nil).Expect(200).Expect("-1") tester.Get("/parse-index-value/42", nil).Expect(200).Expect("42") }
func TestGoWait(t *testing.T) { a := app.New() a.Handle("/(no)?wait", func(ctx *app.Context) { value := 42 ctx.Go(func(bg *app.Context) { time.Sleep(time.Second) value++ panic("handled") }) if ctx.IndexValue(0) != "no" { ctx.Wait() } fmt.Fprintf(ctx, "%d", value) }) tt := tester.New(t, a) tt.Get("/wait", nil).Expect("43") tt.Get("/nowait", nil).Expect("42") }
func init() { testApp = app.New() testApp.Config().Secret = stringutil.Random(32) testApp.Handle("^/hello$", func(ctx *app.Context) { ctx.Header().Add("X-Hello", "World") ctx.Header().Add("X-Number", "42") ctx.WriteString("hello world") }) testApp.Handle("^/empty$", func(ctx *app.Context) {}) testApp.Handle("^/echo$", func(ctx *app.Context) { if ctx.R.Method == "POST" { data, err := ioutil.ReadAll(ctx.R.Body) if err != nil { panic(err) } ctx.Write(data) } }) testApp.Handle("^/echo-form$", func(ctx *app.Context) { if err := ctx.R.ParseForm(); err != nil { panic(err) } var values url.Values if ctx.R.Method == "POST" || ctx.R.Method == "PUT" { values = ctx.R.PostForm } else { values = ctx.R.Form } keys := generic.Keys(values).([]string) sort.Strings(keys) for _, k := range keys { fmt.Fprintf(ctx, "%s=%s\n", k, values.Get(k)) } }) testApp.Handle("^/invalid-write-header$", func(ctx *app.Context) { ctx.WriteHeader(0) }) testApp.Handle("^/multiple-write-header$", func(ctx *app.Context) { ctx.WriteHeader(200) ctx.WriteHeader(300) }) }