|
- // Ghostest is an interactive end-to-end Web site application to test
- // the ghost packages. It serves the following URLs, with the specified
- // features (handlers):
- //
- // / : panic;log;gzip;static; -> serve file index.html
- // /public/styles.css : panic;log;gzip;StripPrefix;FileServer; -> serve directory public/
- // /public/script.js : panic;log;gzip;StripPrefix;FileServer; -> serve directory public/
- // /public/logo.pn : panic;log;gzip;StripPrefix;FileServer; -> serve directory public/
- // /session : panic;log;gzip;session;context;Custom; -> serve dynamic Go template
- // /session/auth : panic;log;gzip;session;context;basicAuth;Custom; -> serve dynamic template
- // /panic : panic;log;gzip;Custom; -> panics
- // /context : panic;log;gzip;context;Custom1;Custom2; -> serve dynamic Amber template
- package main
-
- import (
- "log"
- "net/http"
- "time"
-
- "github.com/PuerkitoBio/ghost/handlers"
- "github.com/PuerkitoBio/ghost/templates"
- _ "github.com/PuerkitoBio/ghost/templates/amber"
- _ "github.com/PuerkitoBio/ghost/templates/gotpl"
- "github.com/bmizerany/pat"
- )
-
- const (
- sessionPageTitle = "Session Page"
- sessionPageAuthTitle = "Authenticated Session Page"
- sessionPageKey = "txt"
- contextPageKey = "time"
- sessionExpiration = 10 // Session expires after 10 seconds
- )
-
- var (
- // Create the common session store and secret
- memStore = handlers.NewMemoryStore(1)
- secret = "testimony of the ancients"
- )
-
- // The struct used to pass data to the session template.
- type sessionPageInfo struct {
- SessionID string
- Title string
- Text string
- }
-
- // Authenticate the Basic Auth credentials.
- func authenticate(u, p string) (interface{}, bool) {
- if u == "user" && p == "pwd" {
- return u + p, true
- }
- return nil, false
- }
-
- // Handle the session page requests.
- func sessionPageRenderer(w handlers.GhostWriter, r *http.Request) {
- var (
- txt interface{}
- data sessionPageInfo
- title string
- )
-
- ssn := w.Session()
- if r.Method == "GET" {
- txt = ssn.Data[sessionPageKey]
- } else {
- txt = r.FormValue(sessionPageKey)
- ssn.Data[sessionPageKey] = txt
- }
- if r.URL.Path == "/session/auth" {
- title = sessionPageAuthTitle
- } else {
- title = sessionPageTitle
- }
- if txt != nil {
- data = sessionPageInfo{ssn.ID(), title, txt.(string)}
- } else {
- data = sessionPageInfo{ssn.ID(), title, "[nil]"}
- }
- err := templates.Render("templates/session.tmpl", w, data)
- if err != nil {
- panic(err)
- }
- }
-
- // Prepare the context value for the chained handlers context page.
- func setContext(w handlers.GhostWriter, r *http.Request) {
- w.Context()[contextPageKey] = time.Now().String()
- }
-
- // Retrieve the context value and render the chained handlers context page.
- func renderContextPage(w handlers.GhostWriter, r *http.Request) {
- err := templates.Render("templates/amber/context.amber",
- w, &struct{ Val string }{w.Context()[contextPageKey].(string)})
- if err != nil {
- panic(err)
- }
- }
-
- // Prepare the web server and kick it off.
- func main() {
- // Blank the default logger's prefixes
- log.SetFlags(0)
-
- // Compile the dynamic templates (native Go templates and Amber
- // templates are both registered via the for-side-effects-only imports)
- err := templates.CompileDir("./templates/")
- if err != nil {
- panic(err)
- }
-
- // Set the simple routes for static files
- mux := pat.New()
- mux.Get("/", handlers.StaticFileHandler("./index.html"))
- mux.Get("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("./public/"))))
-
- // Set the more complex routes for session handling and dynamic page (same
- // handler is used for both GET and POST).
- ssnOpts := handlers.NewSessionOptions(memStore, secret)
- ssnOpts.CookieTemplate.MaxAge = sessionExpiration
- hSsn := handlers.SessionHandler(
- handlers.ContextHandlerFunc(
- handlers.GhostHandlerFunc(sessionPageRenderer),
- 1),
- ssnOpts)
- mux.Get("/session", hSsn)
- mux.Post("/session", hSsn)
-
- hAuthSsn := handlers.BasicAuthHandler(hSsn, authenticate, "")
- mux.Get("/session/auth", hAuthSsn)
- mux.Post("/session/auth", hAuthSsn)
-
- // Set the handler for the chained context route
- mux.Get("/context", handlers.ContextHandler(handlers.ChainHandlerFuncs(
- handlers.GhostHandlerFunc(setContext),
- handlers.GhostHandlerFunc(renderContextPage)),
- 1))
-
- // Set the panic route, which simply panics
- mux.Get("/panic", http.HandlerFunc(
- func(w http.ResponseWriter, r *http.Request) {
- panic("explicit panic")
- }))
-
- // Combine the top level handlers, that wrap around the muxer.
- // Panic is the outermost, so that any panic is caught and responded to with a code 500.
- // Log is next, so that every request is logged along with the URL, status code and response time.
- // GZIP is then applied, so that content is compressed.
- // Finally, the muxer finds the specific handler that applies to the route.
- h := handlers.FaviconHandler(
- handlers.PanicHandler(
- handlers.LogHandler(
- handlers.GZIPHandler(
- mux,
- nil),
- handlers.NewLogOptions(nil, handlers.Ltiny)),
- nil),
- "./public/favicon.ico",
- 48*time.Hour)
-
- // Assign the combined handler to the server.
- http.Handle("/", h)
-
- // Start it up.
- if err := http.ListenAndServe(":9000", nil); err != nil {
- panic(err)
- }
- }
|