Beispiel #1
0
// 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)
		}
	}
}
Beispiel #2
0
func setupHandler(c *cli.Context, route string, cxt cookoo.Context, router *cookoo.Router) {
	cxt.Put("q", c.GlobalBool("quiet"))
	cxt.Put("yaml", c.GlobalString("yaml"))
	cxt.Put("cliArgs", c.Args())
	if err := router.HandleRequest(route, cxt, false); err != nil {
		fmt.Printf("Oops! %s\n", err)
		os.Exit(1)
	}
}
Beispiel #3
0
// Create a new Cookoo HTTP handler.
//
// This will create an HTTP hanlder, but will not automatically attach it to a server. Implementors
// can take the handler and attach it to an existing HTTP server wiht http.HandleFunc() or
// http.ListenAndServe().
//
// For simple web servers, using this package's Serve() function may be the easier route.
//
// 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)
func NewCookooHandler(reg *cookoo.Registry, router *cookoo.Router, cxt cookoo.Context) *CookooHandler {
	handler := new(CookooHandler)
	handler.Registry = reg
	handler.Router = router
	handler.BaseContext = cxt

	// Use the URI oriented request resolver in this package.
	resolver := new(URIPathResolver)
	resolver.Init(reg)
	router.SetRequestResolver(resolver)

	return handler
}
Beispiel #4
0
// 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)
		}
	}
}
Beispiel #5
0
// 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)
	}
}