You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

170 lines
5.0 KiB

  1. // Ghostest is an interactive end-to-end Web site application to test
  2. // the ghost packages. It serves the following URLs, with the specified
  3. // features (handlers):
  4. //
  5. // / : panic;log;gzip;static; -> serve file index.html
  6. // /public/styles.css : panic;log;gzip;StripPrefix;FileServer; -> serve directory public/
  7. // /public/script.js : panic;log;gzip;StripPrefix;FileServer; -> serve directory public/
  8. // /public/logo.pn : panic;log;gzip;StripPrefix;FileServer; -> serve directory public/
  9. // /session : panic;log;gzip;session;context;Custom; -> serve dynamic Go template
  10. // /session/auth : panic;log;gzip;session;context;basicAuth;Custom; -> serve dynamic template
  11. // /panic : panic;log;gzip;Custom; -> panics
  12. // /context : panic;log;gzip;context;Custom1;Custom2; -> serve dynamic Amber template
  13. package main
  14. import (
  15. "log"
  16. "net/http"
  17. "time"
  18. "github.com/PuerkitoBio/ghost/handlers"
  19. "github.com/PuerkitoBio/ghost/templates"
  20. _ "github.com/PuerkitoBio/ghost/templates/amber"
  21. _ "github.com/PuerkitoBio/ghost/templates/gotpl"
  22. "github.com/bmizerany/pat"
  23. )
  24. const (
  25. sessionPageTitle = "Session Page"
  26. sessionPageAuthTitle = "Authenticated Session Page"
  27. sessionPageKey = "txt"
  28. contextPageKey = "time"
  29. sessionExpiration = 10 // Session expires after 10 seconds
  30. )
  31. var (
  32. // Create the common session store and secret
  33. memStore = handlers.NewMemoryStore(1)
  34. secret = "testimony of the ancients"
  35. )
  36. // The struct used to pass data to the session template.
  37. type sessionPageInfo struct {
  38. SessionID string
  39. Title string
  40. Text string
  41. }
  42. // Authenticate the Basic Auth credentials.
  43. func authenticate(u, p string) (interface{}, bool) {
  44. if u == "user" && p == "pwd" {
  45. return u + p, true
  46. }
  47. return nil, false
  48. }
  49. // Handle the session page requests.
  50. func sessionPageRenderer(w handlers.GhostWriter, r *http.Request) {
  51. var (
  52. txt interface{}
  53. data sessionPageInfo
  54. title string
  55. )
  56. ssn := w.Session()
  57. if r.Method == "GET" {
  58. txt = ssn.Data[sessionPageKey]
  59. } else {
  60. txt = r.FormValue(sessionPageKey)
  61. ssn.Data[sessionPageKey] = txt
  62. }
  63. if r.URL.Path == "/session/auth" {
  64. title = sessionPageAuthTitle
  65. } else {
  66. title = sessionPageTitle
  67. }
  68. if txt != nil {
  69. data = sessionPageInfo{ssn.ID(), title, txt.(string)}
  70. } else {
  71. data = sessionPageInfo{ssn.ID(), title, "[nil]"}
  72. }
  73. err := templates.Render("templates/session.tmpl", w, data)
  74. if err != nil {
  75. panic(err)
  76. }
  77. }
  78. // Prepare the context value for the chained handlers context page.
  79. func setContext(w handlers.GhostWriter, r *http.Request) {
  80. w.Context()[contextPageKey] = time.Now().String()
  81. }
  82. // Retrieve the context value and render the chained handlers context page.
  83. func renderContextPage(w handlers.GhostWriter, r *http.Request) {
  84. err := templates.Render("templates/amber/context.amber",
  85. w, &struct{ Val string }{w.Context()[contextPageKey].(string)})
  86. if err != nil {
  87. panic(err)
  88. }
  89. }
  90. // Prepare the web server and kick it off.
  91. func main() {
  92. // Blank the default logger's prefixes
  93. log.SetFlags(0)
  94. // Compile the dynamic templates (native Go templates and Amber
  95. // templates are both registered via the for-side-effects-only imports)
  96. err := templates.CompileDir("./templates/")
  97. if err != nil {
  98. panic(err)
  99. }
  100. // Set the simple routes for static files
  101. mux := pat.New()
  102. mux.Get("/", handlers.StaticFileHandler("./index.html"))
  103. mux.Get("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("./public/"))))
  104. // Set the more complex routes for session handling and dynamic page (same
  105. // handler is used for both GET and POST).
  106. ssnOpts := handlers.NewSessionOptions(memStore, secret)
  107. ssnOpts.CookieTemplate.MaxAge = sessionExpiration
  108. hSsn := handlers.SessionHandler(
  109. handlers.ContextHandlerFunc(
  110. handlers.GhostHandlerFunc(sessionPageRenderer),
  111. 1),
  112. ssnOpts)
  113. mux.Get("/session", hSsn)
  114. mux.Post("/session", hSsn)
  115. hAuthSsn := handlers.BasicAuthHandler(hSsn, authenticate, "")
  116. mux.Get("/session/auth", hAuthSsn)
  117. mux.Post("/session/auth", hAuthSsn)
  118. // Set the handler for the chained context route
  119. mux.Get("/context", handlers.ContextHandler(handlers.ChainHandlerFuncs(
  120. handlers.GhostHandlerFunc(setContext),
  121. handlers.GhostHandlerFunc(renderContextPage)),
  122. 1))
  123. // Set the panic route, which simply panics
  124. mux.Get("/panic", http.HandlerFunc(
  125. func(w http.ResponseWriter, r *http.Request) {
  126. panic("explicit panic")
  127. }))
  128. // Combine the top level handlers, that wrap around the muxer.
  129. // Panic is the outermost, so that any panic is caught and responded to with a code 500.
  130. // Log is next, so that every request is logged along with the URL, status code and response time.
  131. // GZIP is then applied, so that content is compressed.
  132. // Finally, the muxer finds the specific handler that applies to the route.
  133. h := handlers.FaviconHandler(
  134. handlers.PanicHandler(
  135. handlers.LogHandler(
  136. handlers.GZIPHandler(
  137. mux,
  138. nil),
  139. handlers.NewLogOptions(nil, handlers.Ltiny)),
  140. nil),
  141. "./public/favicon.ico",
  142. 48*time.Hour)
  143. // Assign the combined handler to the server.
  144. http.Handle("/", h)
  145. // Start it up.
  146. if err := http.ListenAndServe(":9000", nil); err != nil {
  147. panic(err)
  148. }
  149. }