// WrapForMW takes a giving interface and asserts it into a DriveMiddleware or // wraps it if needed, returning the middleware. func WrapForMW(wm interface{}) DriveMiddleware { var localWM DriveMiddleware switch wm.(type) { case func(context.Context, error, interface{}) (interface{}, error): localWM = WrapMiddleware(wm.(func(context.Context, error, interface{}) (interface{}, error))) case []func(context.Context, error, interface{}) (interface{}, error): fm := wm.([]fractals.Handler) localWM = WrapMiddleware(fm...) case func(interface{}) fractals.Handler: localWM = WrapMiddleware(wm.(func(interface{}) fractals.Handler)(fractals.IdentityHandler())) case func(context.Context, *Request) error: elx := wm.(func(context.Context, *Request) error) localWM = func(ctx context.Context, rw *Request) (*Request, error) { if err := elx(ctx, rw); err != nil { return nil, err } return rw, nil } case func(ctx context.Context, rw *Request) (*Request, error): localWM = wm.(func(ctx context.Context, rw *Request) (*Request, error)) default: mws := fractals.MustWrap(wm) localWM = WrapMiddleware(mws) } return localWM }
// MimeWriterFor writes the mimetype for the provided file depending on the // extension of the file. func MimeWriterFor(file string) fractals.Handler { return fractals.MustWrap(func(rw *Request) *Request { ctn := mimes.GetByExtensionName(filepath.Ext(file)) rw.Res.Header().Add("Content-Type", ctn) return rw }) }
// Headers returns a fractals.Handler which hads the provided values into the // response headers. func Headers(h map[string]string) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, wm *Request) { for key, value := range h { wm.Res.Header().Set(key, value) } }) }
// WalkDir walks the giving path if indeed is a directory, else passing down // an error down the provided pipeline. It extends the provided os.FileInfo // with a structure that implements the ExtendedFileInfo interface. It sends the // individual fileInfo instead of the slice of FileInfos. func WalkDir(path string) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, _ interface{}) ([]ExtendedFileInfo, error) { file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE, 0700) if err != nil { return nil, err } defer file.Close() fdirs, err := file.Readdir(-1) if err != nil { return nil, err } var dirs []ExtendedFileInfo for _, dir := range fdirs { dirInfo := NewExtendedFileInfo(dir, path) // If this is a sysmbol link, then continue we won't read through it. if _, err := os.Readlink(dirInfo.Dir()); err == nil { continue } dirs = append(dirs, dirInfo) } return dirs, nil }) }
// ResolvePathIn returns an ExtendedFileInfo for paths recieved if they match // a specific root directory once resolved using the root directory. func ResolvePathIn(rootDir string) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, path string) (ExtendedFileInfo, error) { absRoot, err := filepath.Abs(rootDir) if err != nil { return nil, err } rootPath := filepath.Clean(absRoot) finalPath := filepath.Clean(filepath.Join(rootDir, path)) if !strings.Contains(finalPath, rootPath) { return nil, fmt.Errorf("Path is outside of root {Root: %q, Path: %q, Wanted: %q}", rootDir, path, finalPath) } file, err := os.Open(finalPath) if err != nil { return nil, err } stat, err := file.Stat() if err != nil { return nil, err } return NewExtendedFileInfo(stat, filepath.Base(finalPath)), nil }) }
// CORS setup a generic CORS hader within the response for recieved request response. func CORS() fractals.Handler { return fractals.MustWrap(func(ctx context.Context, wm *Request) { wm.Res.Header().Set("Access-Control-Allow-Origin", "*") wm.Res.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") wm.Res.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") wm.Res.Header().Set("Access-Control-Max-Age", "86400") }) }
// Close expects to receive a closer in its pipeline and closest the closer. func Close() fractals.Handler { return fractals.MustWrap(func(ctx context.Context, w io.Closer) error { if err := w.Close(); err != nil { return err } return nil }) }
// RemoveAll deletes the giving path and its subpaths if its a directory // and passes the path down // the pipeline. func RemoveAll(path string) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, _ interface{}) error { if err := os.RemoveAll(path); err != nil { return err } return nil }) }
// ReadReader reads the data pulled from the received reader from the // pipeline. func ReadReader() fractals.Handler { return fractals.MustWrap(func(ctx context.Context, r io.Reader) ([]byte, error) { var buf bytes.Buffer _, err := io.Copy(&buf, r) if err != nil && err != io.EOF { return nil, err } return buf.Bytes(), nil }) }
// OpenFile creates the giving file within the provided directly and // writes the any recieved data into the file. It sends the file Handler, // down the piepline. func OpenFile(path string) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, _ interface{}) (*os.File, error) { file, err := os.Open(path) if err != nil { return nil, err } return file, nil }) }
// JSONEncoder encodes the data it recieves into JSON and returns the values. func JSONEncoder() fractals.Handler { return fractals.MustWrap(func(ctx context.Context, data interface{}) ([]byte, error) { var d bytes.Buffer if err := json.NewEncoder(&d).Encode(data); err != nil { return nil, err } return d.Bytes(), nil }) }
// UnwrapStats takes the provided ExtendedFileInfo and unwraps them into their // full path, allows you to retrieve the strings path. func UnwrapStats() fractals.Handler { return fractals.MustWrap(func(ctx context.Context, info []ExtendedFileInfo) []string { var dirs []string for _, dir := range info { dirs = append(dirs, dir.Path()) } return dirs }) }
// LogWith returns a Handler which logs to the provided Writer details of the // http request. func LogWith(w io.Writer) fractals.Handler { return fractals.MustWrap(func(rw *Request) *Request { now := time.Now().UTC() content := rw.Req.Header.Get("Accept") if !rw.Res.StatusWritten() { fmt.Fprintf(w, "HTTP : %q : Content{%s} : Method{%s} : URI{%s}\n", now, content, rw.Req.Method, rw.Req.URL) } else { fmt.Fprintf(w, "HTTP : %q : Status{%d} : Content{%s} : Method{%s} : URI{%s}\n", now, rw.Res.Status(), rw.Res.Header().Get("Content-Type"), rw.Req.Method, rw.Req.URL) } return rw }) }
// Mkdir creates a directly returning the path down the pipeline. If the chain // flag is on, then mkdir when it's pipeline receives a non-empty string as // an argument, will join the string recieved with the path provided. // This allows chaining mkdir paths down the pipeline. func Mkdir(path string, chain bool) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, root string) error { if chain && root != "" { path = filepath.Join(root, path) } if err := os.MkdirAll(path, 0700); err != nil { return err } return nil }) }
// JSONDecoder decodes the data it recieves into an map type and returns the values. func JSONDecoder() fractals.Handler { return fractals.MustWrap(func(ctx context.Context, data []byte) (map[string]interface{}, error) { ms := make(map[string]interface{}) var b bytes.Buffer b.Write(data) if err := json.NewDecoder(&b).Decode(&ms); err != nil { return nil, err } return ms, nil }) }
// SkipStat takes a function to filter out the FileInfo that are running through // its pipeline. This allows you to define specific file paths you wish to treat. // If the filter function returns true, then any FileInfo/ExtendedFileInfo that // match its criteria are sent down its pipeline. func SkipStat(filter func(ExtendedFileInfo) bool) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, info []ExtendedFileInfo) []ExtendedFileInfo { var filtered []ExtendedFileInfo for _, dir := range info { if !filter(dir) { continue } filtered = append(filtered, dir) } return filtered }) }
// WriteWriter expects to recieve []bytes as input and writes the provided // bytes into the writer it recieves as argument. It returns error if the total // written does not match the size of the bytes. It passes the incoming data // down the pipeline. func WriteWriter(w io.Writer) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, data []byte) error { written, err := w.Write(data) if err != nil { return err } if written != len(data) { return errors.New("Data written is not matching provided data") } return nil }) }
// CreateFile creates the giving file within the provided directly and sends // out the file handler. func CreateFile(path string, useRoot bool) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, root string) (*os.File, error) { if useRoot && root != "" { path = filepath.Join(root, path) } file, err := os.Create(path) if err != nil { return nil, err } return file, nil }) }
// MkFile either creates or opens an existing file for appending. It passes // the file object for this files down its pipeline. If it gets a string from // the pipeline, it uses that string if not empty as its root path. func MkFile(path string, useRoot bool) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, root string) (*os.File, error) { if useRoot && root != "" { path = filepath.Join(root, path) } file, err := os.OpenFile(path, os.O_APPEND|os.O_RDWR, os.ModeAppend) if err != nil { return nil, err } return file, nil }) }
// ResolvePathStringIn returns the full valid path for paths recieved if they match // a specific root directory once resolved using the root directory. func ResolvePathStringIn(rootDir string) fractals.Handler { return fractals.MustWrap(func(ctx context.Context, path string) (string, error) { absRoot, err := filepath.Abs(rootDir) if err != nil { return "", err } rootPath := filepath.Clean(absRoot) finalPath := filepath.Clean(filepath.Join(rootDir, path)) if !strings.Contains(finalPath, rootPath) { return "", fmt.Errorf("Path is outside of root {Root: %q, Path: %q, Wanted: %q}", rootDir, path, finalPath) } return finalPath, nil }) }
// ReadFile adds a readFile operation whoes contents get passed to the next // event/Node/Task in the link. func ReadFile() fractals.Handler { return fractals.MustWrap(func(ctx context.Context, path string) ([]byte, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() var buf bytes.Buffer _, err = io.Copy(&buf, file) if err != nil && err != io.EOF { return nil, err } return buf.Bytes(), nil }) }
// ReplayReader reads the data pulled from the reader everytime, buffers it // and returns data everytime it gets called. func ReplayReader(r io.Reader) fractals.Handler { var buf bytes.Buffer var read bool return fractals.MustWrap(func(_ interface{}) interface{} { if read { return buf.Bytes() } _, err := io.Copy(&buf, r) if err != nil && err != io.EOF { return err } read = true return buf.Bytes() }) }
// JSONWrite encodes the data it recieves into JSON and returns the values. func JSONWrite(data interface{}) fractals.Handler { var bu bytes.Buffer var done bool return fractals.MustWrap(func(ctx context.Context, w io.Writer) error { if !done { if err := json.NewEncoder(&bu).Encode(data); err != nil { return err } done = true } if _, err := w.Write(bu.Bytes()); err != nil { return err } return nil }) }
// ReadDirPath reads the giving path if indeed is a directory, else passing down // an error down the provided pipeline. It extends the provided os.FileInfo // with a structure that implements the ExtendedFileInfo interface. It sends the // individual fileInfo instead of the slice of FileInfos. func ReadDirPath() fractals.Handler { return fractals.MustWrap(func(ctx context.Context, path string) ([]ExtendedFileInfo, error) { file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE, 0700) if err != nil { return nil, err } defer file.Close() dirs, err := file.Readdir(-1) if err != nil { return nil, err } var edirs []ExtendedFileInfo for _, dir := range dirs { edirs = append(edirs, NewExtendedFileInfo(dir, path)) } return edirs, nil }) }
// ResolvePath resolves a giving path or sets of paths into their absolute // form. func ResolvePath() fractals.Handler { return fractals.MustWrap(func(ctx context.Context, path interface{}) (interface{}, error) { switch path.(type) { case string: return filepath.Abs(path.(string)) case []string: var resolved []string for _, p := range path.([]string) { res, err := filepath.Abs(p) if err != nil { return nil, err } resolved = append(resolved, res) } return resolved, nil } return nil, errors.New("Invalid Type expected") }) }
// WrapForAction returns a action which is typed asserts or morph into a // fractal http handler. func WrapForAction(action interface{}) func(context.Context, *Request) error { switch action.(type) { case func(w http.ResponseWriter, r *http.Request, params map[string]interface{}): handler := action.(func(w http.ResponseWriter, r *http.Request, params map[string]string)) return func(ctx context.Context, rw *Request) error { handler(rw.Res, rw.Req, map[string]string{}) return nil } case func(context.Context, *Request) error: return action.(func(context.Context, *Request) error) case func(context.Context, error, interface{}) (interface{}, error): handler := action.(func(context.Context, error, interface{}) (interface{}, error)) return func(ctx context.Context, r *Request) error { if _, err := handler(ctx, nil, r); err != nil { return err } return nil } case fractals.Handler: handler := action.(fractals.Handler) return func(ctx context.Context, r *Request) error { if _, err := handler(ctx, nil, r); err != nil { return err } return nil } default: mw := fractals.MustWrap(action) return WrapRequestFractalHandler(mw) } }
// JoinPathName returns the path of the received *Request. func JoinPathName(file string) fractals.Handler { return fractals.MustWrap(func(rw *Request) string { return filepath.Join(rw.Req.URL.Path, file) }) }
// IdentityMiddlewareHandler returns the IdentityMiddleware returned value // as a fractals.Handler. func IdentityMiddlewareHandler() fractals.Handler { return fractals.MustWrap(IdentityMiddleware()) }
// StripPrefix returns the path which has a prefix if found stripped from its // string recieved. func StripPrefix(prefix string) fractals.Handler { return fractals.MustWrap(func(path string) string { return strings.TrimPrefix(path, prefix) }) }
// ReplayBytes resends the data provided everytime it is called. func ReplayBytes(b []byte) fractals.Handler { return fractals.MustWrap(func(ctx context.Context) []byte { return b }) }