func serveContent(ctx context.Context, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) (string, int)   {
	ctx.SetLastModified(modtime)
	done, rangeReq := checkPreconditions(ctx, modtime)
	if done {
		return "", http.StatusNotModified
	}
	code := http.StatusOK
	ctype, err := detectOrWriteContentType(ctx, name, content)
	if err != nil {
		return "while seeking", http.StatusInternalServerError
	}
	size, err := sizeFunc()
	if err != nil {
		return err.Error(), http.StatusInternalServerError
	}
	sendSize := size
	var sendContent io.Reader = content
	if size >= 0 {
		ranges, err := parseRange(rangeReq, size)
		if err != nil {
			if err == errNoOverlap {
				ctx.Header("Content-Range", fmt.Sprintf("bytes */%d", size))
			}
			return err.Error(), http.StatusRequestedRangeNotSatisfiable
		}
		if sumRangesSize(ranges) > size {
			ranges = nil
		}
		switch {
		case len(ranges) == 1:
			ra := ranges[0]
			if _, err := content.Seek(ra.start, io.SeekStart); err != nil {
				return err.Error(), http.StatusRequestedRangeNotSatisfiable
			}
			sendSize = ra.length
			code = http.StatusPartialContent
			ctx.Header("Content-Range", ra.contentRange(size))
		case len(ranges) > 1:
			sendSize = rangesMIMESize(ranges, ctype, size)
			code = http.StatusPartialContent
			pr, pw := io.Pipe()
			mw := multipart.NewWriter(pw)
			ctx.ContentType("multipart/byteranges; boundary=" + mw.Boundary())
			sendContent = pr
			defer pr.Close()  
			go func() {
				for _, ra := range ranges {
					part, err := mw.CreatePart(ra.mimeHeader(ctype, size))
					if err != nil {
						pw.CloseWithError(err)
						return
					}
					if _, err := content.Seek(ra.start, io.SeekStart); err != nil {
						pw.CloseWithError(err)
						return
					}
					if _, err := io.CopyN(part, content, ra.length); err != nil {
						pw.CloseWithError(err)
						return
					}
				}
				mw.Close()
				pw.Close()
			}()
		}
		ctx.Header("Accept-Ranges", "bytes")
		if ctx.ResponseWriter().Header().Get(context.ContentEncodingHeaderKey) == "" {
			ctx.Header(context.ContentLengthHeaderKey, strconv.FormatInt(sendSize, 10))
		}
	}
	ctx.StatusCode(code)
	if ctx.Method() != http.MethodHead {
		io.CopyN(ctx.ResponseWriter(), sendContent, sendSize)
	}
	return "", code
}
if name is empty, filename is unknown. (used for mime type, before sniffing)
if modtime.IsZero(), modtime is unknown.
content must be seeked to the beginning of the file.
The sizeFunc is called at most once. Its error, if any, is sent in the HTTP response.
package router
import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/kataras/iris/context"
)
func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string, assetsGziped bool) context.Handler {
	if len(vdir) > 0 {
		if vdir[0] == '.' {
			vdir = vdir[1:]
		}
		if vdir[0] == '/' || vdir[0] == os.PathSeparator {  
			vdir = vdir[1:]
		}
		if trailingSlashIdx := len(vdir) - 1; vdir[trailingSlashIdx] == '/' {
			vdir = vdir[0:trailingSlashIdx]
		}
	}
	allNames := namesFn()
	var names []string
	for _, path := range allNames {
		if !strings.HasPrefix(path, vdir) {
			continue
		}
		names = append(names, path)
	}
	h := func(ctx context.Context) {
		reqPath := strings.TrimPrefix(ctx.Request().URL.Path, "/"+vdir)
		for _, path := range names {
			if path == "/index.html" && reqPath == "/" {
				reqPath = "/index.html"
			}
			if path != vdir+reqPath {
				continue
			}
			cType := TypeByFilename(path)
			buf, err := assetFn(path)  
			if assetsGziped {
				context.AddGzipHeaders(ctx.ResponseWriter())
			}
			if err != nil {
				continue
			}
			ctx.ContentType(cType)
			if _, err := ctx.Write(buf); err != nil {
				ctx.StatusCode(http.StatusInternalServerError)
				ctx.StopExecution()
			}
			return
		}
		ctx.NotFound()
	}
	return h
}
func StaticHandler(systemPath string, showList bool, gzip bool) context.Handler {
	return NewStaticHandlerBuilder(systemPath).
		Gzip(gzip).
		Listing(showList).
		Build()
}
type StaticHandlerBuilder interface {
	Gzip(enable bool) StaticHandlerBuilder
	Listing(listDirectoriesOnOff bool) StaticHandlerBuilder
	Build() context.Handler
}
type fsHandler struct {
	directory       http.Dir
	listDirectories bool
	gzip            bool
	filesystem http.FileSystem
	once       sync.Once
	handler    context.Handler
	begin      context.Handlers
}
func toWebPath(systemPath string) string {
	webpath := strings.Replace(systemPath, "\\", "/", -1)
	webpath = strings.Replace(webpath, "//", "/", -1)
	webpath = strings.Replace(webpath, ".", "", -1)
	return webpath
}
func Abs(path string) string {
	absPath, err := filepath.Abs(path)
	if err != nil {
		return path
	}
	return absPath
}
func NewStaticHandlerBuilder(dir string) StaticHandlerBuilder {
	return &fsHandler{
		directory: http.Dir(Abs(dir)),
		listDirectories: false,
	}
}
func (w *fsHandler) Gzip(enable bool) StaticHandlerBuilder {
	w.gzip = enable
	return w
}
func (w *fsHandler) Listing(listDirectoriesOnOff bool) StaticHandlerBuilder {
	w.listDirectories = listDirectoriesOnOff
	return w
}
type (
	noListFile struct {
		http.File
	}
)
func (n noListFile) Readdir(count int) ([]os.FileInfo, error) {
	return nil, nil
}
func (w *fsHandler) Open(name string) (http.File, error) {
	info, err := w.filesystem.Open(name)
	if err != nil {
		return nil, err
	}
	if !w.listDirectories {
		return noListFile{info}, nil
	}
	return info, nil
}
func (w *fsHandler) Build() context.Handler {
	w.once.Do(func() {
		w.filesystem = w.directory
		fileserver := func(ctx context.Context) {
			upath := ctx.Request().URL.Path
			if !strings.HasPrefix(upath, "/") {
				upath = "/" + upath
				ctx.Request().URL.Path = upath
			}
			gzipEnabled := w.gzip
			if !gzipEnabled {
				_, gzipEnabled = ctx.ResponseWriter().(*context.GzipResponseWriter)
			}
			_, prevStatusCode := serveFile(ctx,
				w.filesystem,
				path.Clean(upath),
				false,
				w.listDirectories,
				gzipEnabled)
			if context.StatusCodeNotSuccessful(prevStatusCode) {  
				if writer, ok := ctx.ResponseWriter().(*context.GzipResponseWriter); ok && writer != nil {
					writer.ResetBody()
					writer.Disable()
				}
				ctx.StatusCode(prevStatusCode)
				return
			}
			ctx.Next()
		}
		w.handler = fileserver
	})
	return w.handler
}
func StripPrefix(prefix string, h context.Handler) context.Handler {
	if prefix == "" {
		return h
	}
	fixedPrefix := prefix
	if dotWSlashIdx := strings.Index(fixedPrefix, SubdomainPrefix); dotWSlashIdx > 0 {
		fixedPrefix = fixedPrefix[dotWSlashIdx+1:]
	}
	fixedPrefix = toWebPath(fixedPrefix)
	return func(ctx context.Context) {
		if p := strings.TrimPrefix(ctx.Request().URL.Path, fixedPrefix); len(p) < len(ctx.Request().URL.Path) {
			ctx.Request().URL.Path = p
			h(ctx)
		} else {
			ctx.NotFound()
		}
	}
}
var htmlReplacer = strings.NewReplacer(
	"&", "&amp;",
	"<", "&lt;",
	">", "&gt;",
	`"`, "&#34;",
	// "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
	"'", "&#39;",
)
func dirList(ctx context.Context, f http.File) (string, int) {
	dirs, err := f.Readdir(-1)
	if err != nil {
		// TODO: log err.Error() to the Server.ErrorLog, once it's possible
		return "Error reading directory", http.StatusInternalServerError
	}
	sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
	ctx.ContentType("text/html")
	fmt.Fprintf(ctx.ResponseWriter(), "<pre>\n")
	for _, d := range dirs {
		name := d.Name()
		if d.IsDir() {
			name += "/"
		}
		url := url.URL{Path: name}
		fmt.Fprintf(ctx.ResponseWriter(), "<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name))
	}
	fmt.Fprintf(ctx.ResponseWriter(), "</pre>\n")
	return "", http.StatusOK
}
var errSeeker = errors.New("seeker can't seek")
var errNoOverlap = errors.New("invalid range: failed to overlap")
const sniffLen = 512
func detectOrWriteContentType(ctx context.Context, name string, content io.ReadSeeker) (string, error) {
	ctypes, haveType := ctx.ResponseWriter().Header()["Content-Type"]
	var ctype string
	if !haveType {
		ctype = TypeByExtension(filepath.Ext(name))
		if ctype == "" {
			var buf [sniffLen]byte
			n, _ := io.ReadFull(content, buf[:])
			ctype = http.DetectContentType(buf[:n])
			_, err := content.Seek(0, io.SeekStart)  
			if err != nil {
				return "", err
			}
		}
		ctx.ContentType(ctype)
	} else if len(ctypes) > 0 {
		ctype = ctypes[0]
	}
	return ctype, nil
}