// Serve creates a new Cookoo web server. // // Important details: // // - A URIPathResolver is used for resolving request names. // - The following datasources are added to the Context: // * url: A URLDatasource (Provides access to parts of the URL) // * path: A PathDatasource (Provides access to parts of a path. E.g. "/foo/bar") // * query: A QueryParameterDatasource (Provides access to URL query parameters.) // * post: A FormValuesDatasource (Provides access to form data or the body of a request.) // - The following context variables are set: // * http.Request: A pointer to the http.Request object // * http.ResponseWriter: The response writer. // * server.Address: The server's address and port (NOT ALWAYS PRESENT) // - The handler includes logic to redirect "not found" errors to a path named "@404" if present. // // Context Params: // // - server.Address: If this key exists in the context, it will be used to determine the host/port the // server runes on. EXPERIMENTAL. Default is ":8080". // // Example: // // package main // // import ( // //This is the path to Cookoo // "github.com/Masterminds/cookoo" // "github.com/Masterminds/cookoo/web" // "fmt" // ) // // func main() { // // Build a new Cookoo app. // registry, router, context := cookoo.Cookoo() // // // Fill the registry. // registry.Route("GET /", "The index").Does(web.Flush, "example"). // Using("content").WithDefault("Hello World") // // // Create a server // web.Serve(reg, router, cookoo.SyncContext(cxt)) // } // // Note that we synchronize the context before passing it into Serve(). This // is optional because each handler gets its own copy of the context already. // However, if commands pass the context to goroutines, the context ought to be // synchronized to avoid race conditions. // // Note that copies of the context are not synchronized with each other. // So by declaring the context synchronized here, you // are not therefore synchronizing across handlers. func Serve(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) { addr := cxt.Get("server.Address", ":8080").(string) handler := NewCookooHandler(reg, router, cxt) // MPB: I dont think there's any real point in having a multiplexer in // this particular case. The Cookoo handler is mux enough. // // Note that we can always use Cookoo with the built-in multiplexer. It // just doesn't make sense if Cookoo's the only handler on the app. //http.Handle("/", handler) server := &http.Server{Addr: addr} // Instead of mux, set a single default handler. // What we might be losing: // - Handling of non-conforming paths. server.Handler = handler go handleSignals(router, cxt, server) err := server.ListenAndServe() //err := http.ListenAndServe(addr, nil) if err != nil { cxt.Logf("error", "Caught error while serving: %s", err) if router.HasRoute("@crash") { router.HandleRequest("@crash", cxt, false) } } }
// ServeTLS does the same as Serve, but with SSL support. // // If `server.Address` is not found in the context, the default address is // `:4433`. // // Neither certFile nor keyFile are stored in the context. These values are // considered to be security sensitive. func ServeTLS(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context, certFile, keyFile string) { addr := cxt.Get("server.Address", ":4433").(string) server := &http.Server{Addr: addr} server.Handler = NewCookooHandler(reg, router, cxt) go handleSignals(router, cxt, server) err := server.ListenAndServeTLS(certFile, keyFile) if err != nil { cxt.Logf("error", "Caught error while serving: %s", err) if router.HasRoute("@crash") { router.HandleRequest("@crash", cxt, false) } } }
// shutdown runs an @shutdown route if it's found in the router. func shutdown(router *cookoo.Router, cxt cookoo.Context) { if router.HasRoute("@shutdown") { cxt.Logf("info", "Executing route @shutdown") router.HandleRequest("@shutdown", cxt, false) } }