// Flush sends content to output. // // If no writer is specified, this will attempt to write to whatever is in the // Context with the key "http.ResponseWriter". If no suitable writer is found, it will // not write to anything at all. // // Params: // - writer: A Writer of some sort. This will try to write to the HTTP response if no writer // is specified. // - content: The content to write as a body. If this is a byte[], it is sent unchanged. Otherwise. // we first try to convert to a string, then pass it into a writer. // - contentType: The content type header (e.g. text/html). Default is text/plain // - responseCode: Integer HTTP Response Code: Default is `http.StatusOK`. // - headers: a map[string]string of HTTP headers. The keys will be run through // http.CannonicalHeaderKey() // // Note that this is optimized for writing from strings or arrays, not Readers. For larger // objects, you may find it more efficient to use a different command. // // Context: // - If this finds `web.ContentEncoding`, it will set a content-encoding header. // // Returns // // - boolean true func Flush(cxt cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) { // Make sure we have a place to write this stuff. writer, ok := params.Has("writer") if writer == nil { writer, ok = cxt.Has("http.ResponseWriter") if !ok { return false, nil } } out := writer.(http.ResponseWriter) // Get the rest of the info. code := params.Get("responseCode", http.StatusOK).(int) header := out.Header() contentType := params.Get("contentType", "text/plain; charset=utf-8").(string) // Prepare the content. var content []byte rawContent, ok := params.Has("content") if !ok { // No content. Send nothing in the body. content = []byte("") } else if byteContent, ok := rawContent.([]byte); ok { // Got a byte[]; add it as is. content = byteContent } else { // Use the formatter to convert to a string, and then // cast it to bytes. content = []byte(fmt.Sprintf("%v", rawContent)) } // Add headers: header.Set(http.CanonicalHeaderKey("content-type"), contentType) te := cxt.Get(ContentEncoding, "").(string) if len(te) > 0 { header.Set(http.CanonicalHeaderKey("transfer-encoding"), te) } headerO, ok := params.Has("headers") if ok { headers := headerO.(map[string]string) for k, v := range headers { header.Add(http.CanonicalHeaderKey(k), v) } } // Send the headers. out.WriteHeader(code) //io.WriteString(out, content) out.Write(content) return true, nil }
// ServeFiles is a cookoo command to serve files from a set of filesystem directories. // // If no writer is specified, this will attempt to write to whatever is in the // Context with the key "http.ResponseWriter". If no suitable writer is found, it will // not write to anything at all. // // Example: // // registry.Route("GET /**", "Serve assets"). // Does(web.ServeFiles, "fileServer"). // Using("directory").WithDefault("static") // // Example 2: // // registry.Route("GET /foo/**", "Serve assets"). // Does(web.ServeFiles, "fileServer"). // Using("directory").WithDefault("static"). // Using("removePrefix").WithDefault("/foo") // // Params: // - directory: A directory to serve files from. // - removePrefix: A prefix to remove from the url before looking for it on the filesystem. // - writer: A Writer of some sort. This will try to write to the HTTP response if no writer // is specified. // - request: A request of some sort. This will try to use the HTTP request if no request // is specified. func ServeFiles(cxt cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) { writer, ok := params.Has("writer") if writer == nil { writer, ok = cxt.Has("http.ResponseWriter") if !ok { return nil, &cookoo.Reroute{"@404"} } } out := writer.(http.ResponseWriter) req, ok := params.Has("request") if req == nil { req, ok = cxt.Has("http.Request") if !ok { return nil, &cookoo.Reroute{"@404"} } } in := req.(*http.Request) directory := params.Get("directory", nil) if directory == nil { return nil, &cookoo.Reroute{"@404"} } prefix := params.Get("removePrefix", "").(string) urlPath := strings.TrimPrefix(in.URL.Path, prefix) staticFile := path.Join(directory.(string), urlPath) info, err := os.Stat(staticFile) if err != nil { return nil, &cookoo.Reroute{"@404"} } if info.IsDir() == false { http.ServeFile(out, in, staticFile) return true, nil } return nil, &cookoo.Reroute{"@404"} }
func (r *RequestResolver) Resolve(path string, cxt cookoo.Context) (string, error) { // Parse out any flags. Maybe flag specs are in context? flagsetO, ok := cxt.Has("globalFlags") if !ok { // No args to parse. Just return path. return path, nil } flagset := flagsetO.(*flag.FlagSet) flagset.Parse(strings.Split(path, " ")) addFlagsToContext(flagset, cxt) args := flagset.Args() // This is a failure condition... Need to fix Cookoo to support error return. if len(args) == 0 { return path, &cookoo.RouteError{"Could not resolve route " + path} } // Put the rest of the args to the context. cxt.Put("args", args[1:]) // Parse argv[0] as subcommand return args[0], nil }