package httpserver import ( "b612.me/apps/b612/utils" "b612.me/apps/b612/version" "b612.me/starcrypto" "b612.me/starlog" "b612.me/starnet" "b612.me/staros" "context" "crypto/tls" "crypto/x509" _ "embed" "encoding/base64" "encoding/json" "errors" "fmt" "html/template" "io" "io/ioutil" "math" "net" "net/http" "os" "path/filepath" "strconv" "strings" "time" ) var ver = version.Version type HttpServerCfgs func(cfg *HttpServerCfg) type HttpServerCfg struct { basicAuthUser string basicAuthPwd string envPath string uploadFolder string logpath string indexFile string cert string key string allowHttpWithHttps bool autoGenCert bool addr string port string page404 string page403 string page401 string protectAuthPage []string disableMIME bool ctx context.Context hooks []ServerHook httpDebug bool noListPath []string listPwd map[string]string listSameForFile bool // speed limit means xx bytes/s speedlimit uint64 background string mobildBackground string } type ServerHook struct { MatchType []string `json:"match_type"` Url string `json:"url"` Timeout int `json:"timeout"` MaxHookLength int `json:"max_hook_length"` } type HttpServer struct { HttpServerCfg } //go:embed bootstrap.css var bootStrap []byte //go:embed jquery.js var jquery []byte //go:embed upload.html var uploadPage []byte //go:embed template.html var templateHtml []byte func WithHooks(hooks []ServerHook) HttpServerCfgs { return func(cfg *HttpServerCfg) { for k, v := range hooks { if v.MaxHookLength == 0 { hooks[k].MaxHookLength = 1024 * 1024 } } cfg.hooks = hooks } } func WithTLSCert(cert, key string) HttpServerCfgs { return func(cfg *HttpServerCfg) { cfg.key = key cfg.cert = cert } } func WithUploadFolder(path string) HttpServerCfgs { return func(cfg *HttpServerCfg) { cfg.uploadFolder = path } } func NewHttpServer(addr, port, path string, opts ...HttpServerCfgs) *HttpServer { var server = HttpServer{ HttpServerCfg: HttpServerCfg{ addr: addr, port: port, envPath: path, }, } for _, opt := range opts { opt(&server.HttpServerCfg) } return &server } func (h *HttpServer) Run(ctx context.Context) error { h.ctx = ctx server := http.Server{ Addr: h.addr + ":" + h.port, Handler: h, ConnContext: func(ctx context.Context, c net.Conn) context.Context { switch conn := c.(type) { case *tls.Conn: return context.WithValue(ctx, "istls", true) case *starnet.Conn: return context.WithValue(ctx, "istls", conn) } return context.WithValue(ctx, "istls", false) }, } go func() { select { case <-h.ctx.Done(): ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() server.Shutdown(ctx) } }() if h.logpath != "" && starlog.GetWriter() == nil { starlog.SetLogFile(h.logpath, starlog.Std, true) } netcards, err := net.Interfaces() if err == nil { for _, v := range netcards { if strings.Contains(v.Flags.String(), "up") { addrs, err := v.Addrs() if err == nil { for _, ip := range addrs { starlog.Cyan("Name:%s\tIP:%s\n", v.Name, ip) } } } } } h.envPath, err = filepath.Abs(h.envPath) if err != nil { starlog.Errorln("Failed to get abs path of", h.envPath) return err } uconn, err := net.Dial("udp", "106.55.44.79:80") if err == nil { schema := "http://" if h.cert != "" || h.autoGenCert { schema = "https://" } starlog.Infof("Visit: %s%s:%s\n", schema, uconn.LocalAddr().(*net.UDPAddr).IP.String(), h.port) uconn.Close() } starlog.Infoln("Listening on " + h.addr + ":" + h.port) if h.cert == "" && !h.autoGenCert { if err := server.ListenAndServe(); err != http.ErrServerClosed { return err } return nil } if !h.allowHttpWithHttps && !h.autoGenCert { if err := server.ListenAndServeTLS(h.cert, h.key); err != http.ErrServerClosed { return err } return nil } var lis net.Listener if !h.autoGenCert { lis, err = starnet.ListenTLS("tcp", h.addr+":"+h.port, h.cert, h.key, h.allowHttpWithHttps) if err != nil { return err } } else { lis, err = starnet.ListenTLSWithConfig("tcp", h.addr+":"+h.port, &tls.Config{}, autoGenCert, h.allowHttpWithHttps) if err != nil { return err } } defer lis.Close() if err := server.Serve(lis); err != http.ErrServerClosed { return err } return nil } var certCache = make(map[string]tls.Certificate) var toolCa *x509.Certificate var toolCaKey any func autoGenCert(hostname string) *tls.Config { if cert, ok := certCache[hostname]; ok { return &tls.Config{Certificates: []tls.Certificate{cert}} } if toolCa == nil { toolCa, toolCaKey = utils.ToolCert("") } cert, err := utils.GenerateTlsCert(utils.GenerateCertParams{ Country: "CN", Organization: "B612 HTTP SERVER", OrganizationUnit: "cert@b612.me", CommonName: hostname, Dns: []string{hostname}, KeyUsage: int(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign), ExtendedKeyUsage: []int{ int(x509.ExtKeyUsageServerAuth), int(x509.ExtKeyUsageClientAuth), }, IsCA: false, StartDate: time.Now().Add(-24 * time.Hour), EndDate: time.Now().AddDate(1, 0, 0), Type: "RSA", Bits: 2048, CA: toolCa, CAPriv: toolCaKey, }) if err != nil { return nil } certCache[hostname] = cert return &tls.Config{Certificates: []tls.Certificate{cert}} } func (h *HttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.Listen(w, r) } func (h *HttpServer) Page404(w http.ResponseWriter) { w.WriteHeader(404) if h.page404 != "" { data, err := os.ReadFile(h.page404) if err == nil { w.Write(data) return } } w.Write([]byte(`B612 Http Server

404 NOT FOUND


`)) } func (h *HttpServer) Page403(w http.ResponseWriter) { w.WriteHeader(403) if h.page403 != "" { data, err := os.ReadFile(h.page403) if err == nil { w.Write(data) return } } w.Write([]byte(` 403 Forbidden

403 Forbidden


B612 HTTP SERVER
`)) } func (h *HttpServer) Page401(w http.ResponseWriter) { w.WriteHeader(401) if h.page401 != "" { data, err := os.ReadFile(h.page401) if err == nil { w.Write(data) return } } w.Write([]byte(` 401 Authorization Required

401 Authorization Required


B612 HTTP SERVER
`)) } func (h *HttpServer) GiveBasicAuth(w http.ResponseWriter) { w.Header().Set("WWW-Authenticate", ` Basic realm="Please Enter Passwd"`) h.Page401(w) } func (h *HttpServer) BasicAuth(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request) bool { if h.basicAuthPwd != "" { if len(h.protectAuthPage) != 0 { for _, v := range h.protectAuthPage { if !(strings.Index(r.URL.Path, v) == 0 || strings.Contains(r.URL.RawQuery, v)) { return true } else { break } } } authHeader := strings.TrimSpace(r.Header.Get("Authorization")) if len(authHeader) == 0 { log.Noticeln("No Authed! Get Path is", r.URL.Path, r.RemoteAddr) h.GiveBasicAuth(w) return false } else { userAuth := base64.StdEncoding.EncodeToString([]byte(h.basicAuthUser + ":" + h.basicAuthPwd)) authStr := strings.Split(authHeader, " ") if strings.TrimSpace(authStr[1]) != userAuth || strings.ToLower(strings.TrimSpace(authStr[0])) != "basic" { log.Noticeln("Auth Failed! Get Path is", r.URL.Path, r.RemoteAddr, "pwd enter is", authHeader) h.GiveBasicAuth(w) return false } log.Infof("Path %s Authoried by %s:%s\n", r.URL.Path, r.RemoteAddr, authHeader) } } return true } func (h *HttpServer) SetUpload(w http.ResponseWriter, r *http.Request, path string) bool { if h.uploadFolder != "" { if r.URL.Query().Get("bootstrap") == "true" { w.Header().Set("Content-Type", "text/css") w.Write(bootStrap) return true } if r.URL.Query().Get("jquery") == "true" { w.Header().Set("Content-Type", "application/javascript") w.Write(jquery) return true } if len(r.URL.Query()["upload"]) != 0 { w.Header().Set("Content-Type", "text/html") w.Write(uploadPage) return true } } return false } func (h *HttpServer) debugMode(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(200) html := `B612 Http Server

Debug Mode


%s` resp := "

Url

" resp += "

" + r.Method + " " + r.URL.Path + "

" resp += "

query: " + r.URL.RawQuery + "

" resp += "

fragment: " + r.URL.Fragment + "

" resp += "

FullUrl: " + r.URL.String() + "

" resp += "

Query

" for k, v := range r.URL.Query() { resp += fmt.Sprintf("

%s:%s

", k, v) } resp += "

Header

" for key, val := range r.Header { for _, v := range val { resp += fmt.Sprintf("

%s:%s

", key, v) } } resp += "

Cookie

" for _, c := range r.Cookies() { resp += fmt.Sprintf("

%s:%s

", c.Name, c.Value) } resp += "

RemoteAddr

" resp += "

" + r.RemoteAddr + "

" resp += "

Proto

" resp += "

" + r.Proto + "

" w.Write([]byte(fmt.Sprintf(html, resp))) } func (h *HttpServer) Listen(w http.ResponseWriter, r *http.Request) { log := starlog.Std.NewFlag() log.SetShowFuncName(false) log.SetShowOriginFile(false) w.Header().Set("X-Powered-By", "B612.ME") w.Header().Set("Server", "B612/"+ver) if !h.BasicAuth(log, w, r) { return } path := r.URL.Path ua := r.Header.Get("User-Agent") if h.httpDebug { log.Infof("debug mode:%s %s From %s %s\n", r.Method, path, r.RemoteAddr, ua) h.debugMode(w, r) return } if h.uploadFolder != "" && path == "/recv" && len(r.URL.Query()["upload"]) != 0 { h.uploadFile(w, r) return } fullpath := filepath.Clean(filepath.Join(h.envPath, path)) { //security check if fullpath != h.envPath && !strings.HasPrefix(fullpath, h.envPath) { log.Warningf("Invalid Path %s IP:%s Fullpath:%s\n", path, r.RemoteAddr, fullpath) h.Page403(w) return } } if h.indexFile != "" && staros.IsFolder(fullpath) { if staros.Exists(filepath.Join(fullpath, h.indexFile)) { fullpath = filepath.Join(fullpath, h.indexFile) path = filepath.Join(path, h.indexFile) } } isTls := h.isTlsStr(r) now := time.Now() if h.SetUpload(w, r, path) { return } switch r.Method { case "OPTIONS", "HEAD": err := h.BuildHeader(w, r, fullpath) if err != nil { log.Warningf(isTls+"%s %s From %s %s %.2fs %v\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds(), err) } else { log.Infof(isTls+"%s %s From %s %s %.2fs \n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds()) } case "GET": err := h.BuildHeader(w, r, fullpath) if err != nil { log.Warningf(isTls+"GET Header Build Failed Path:%s IP:%s Err:%v\n", path, r.RemoteAddr, err) } err = h.ResponseGet(log, w, r, fullpath) if err != nil { log.Warningf(isTls+"%s %s From %s %s %.2fs %v\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds(), err) return } log.Infof(isTls+"%s %s From %s %s %.2fs\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds()) default: log.Errorf(isTls+"Invalid %s %s From %s %s %.2fs\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds()) return } } func (h *HttpServer) CalcRange(r *http.Request) (int64, int64) { var rangeStart, rangeEnd int64 = -1, -1 ranges := r.Header.Get("Range") if ranges == "" { return rangeStart, rangeEnd } if !strings.Contains(ranges, "bytes=") { return rangeStart, rangeEnd } ranges = strings.TrimPrefix(ranges, "bytes=") data := strings.Split(ranges, "-") if len(data) == 0 { return rangeStart, rangeEnd } rangeStart, _ = strconv.ParseInt(data[0], 10, 64) if len(data) > 1 { rangeEnd, _ = strconv.ParseInt(data[1], 10, 64) } if rangeEnd == 0 { rangeEnd = -1 } return rangeStart, rangeEnd } func (h *HttpServer) BuildHeader(w http.ResponseWriter, r *http.Request, fullpath string) error { if r.Method == "OPTIONS" { w.Header().Set("Allow", "OPTIONS,GET,HEAD") w.Header().Set("Content-Length", "0") } w.Header().Set("Date", strings.ReplaceAll(time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"), "UTC", "GMT")) if staros.IsFolder(fullpath) { return nil } mime := h.MIME(fullpath) if h.disableMIME || mime == "" { w.Header().Set("Content-Type", "application/download") w.Header().Set("Content-Disposition", "attachment;filename="+filepath.Base(fullpath)) w.Header().Set("Content-Transfer-Encoding", "binary") } else { w.Header().Set("Content-Type", mime) } if staros.Exists(fullpath) { finfo, err := os.Stat(fullpath) if err != nil { w.WriteHeader(502) w.Write([]byte("Failed to Read " + fullpath + ",reason is " + err.Error())) return err } w.Header().Set("Accept-Ranges", "bytes") w.Header().Set("ETag", starcrypto.Md5Str([]byte(finfo.ModTime().String()))) w.Header().Set("Last-Modified", strings.ReplaceAll(finfo.ModTime().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"), "UTC", "GMT")) if r.Method != "OPTIONS" { if _, ok := h.willHook(fullpath); ok { return nil } start, end := h.CalcRange(r) if start != -1 { if end == -1 { w.Header().Set("Content-Length", strconv.FormatInt(finfo.Size()-start, 10)) w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(finfo.Size()-1, 10)+"/"+strconv.FormatInt(finfo.Size(), 10)) //w.Header().Set("Content-Length", strconv.FormatInt(fpinfo.Size()-rangeStart, 10)) } else { w.Header().Set("Content-Length", strconv.FormatInt(end-start+1, 10)) w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(end, 10)+"/"+strconv.FormatInt(finfo.Size(), 10)) //w.Header().Set("Content-Length", strconv.FormatInt(1+rangeEnd-rangeStart, 10)) } } else { w.Header().Set("Content-Length", strconv.FormatInt(finfo.Size(), 10)) } } } return nil } func (h *HttpServer) willHook(fullpath string) (ServerHook, bool) { finfo, err := os.Stat(fullpath) if err != nil { return ServerHook{}, false } if finfo.Size() < 1024*1024*10 && len(h.hooks) > 0 { ext := h.GetExt(fullpath) for _, hk := range h.hooks { for _, e := range hk.MatchType { if e == ext { return hk, true } } } } return ServerHook{}, false } func (h *HttpServer) ResponseGet(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error { if staros.IsFolder(fullpath) { if len(h.listPwd) != 0 { for k, v := range h.listPwd { if strings.HasPrefix(r.URL.Path, k) { if r.URL.Query().Get("list") == v { return h.getFolder(log, w, r, fullpath) } } } } if len(h.noListPath) != 0 { for _, v := range h.noListPath { if strings.HasPrefix(r.URL.Path, v) { h.Page403(w) return nil } } } return h.getFolder(log, w, r, fullpath) } if !h.listSameForFile { return h.getFile(log, w, r, fullpath) } if len(h.listPwd) != 0 { for k, v := range h.listPwd { if strings.HasPrefix(r.URL.Path, k) { if r.URL.Query().Get("list") == v { return h.getFile(log, w, r, fullpath) } } } } if len(h.noListPath) != 0 { for _, v := range h.noListPath { if strings.HasPrefix(r.URL.Path, v) { h.Page403(w) return nil } } } return h.getFile(log, w, r, fullpath) } type FileData struct { Attr string `json:"attr"` Name string `json:"name"` Modified string `json:"modified"` Size int64 `json:"size"` Type string `json:"type"` } func (h *HttpServer) getFolder(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error { dir, err := ioutil.ReadDir(fullpath) if err != nil { log.Errorf("Read Folder %s failed:%v\n", fullpath, err) w.WriteHeader(403) if r.Method == "HEAD" { return err } w.Write([]byte("

Cannot Access!

")) } if r.Method != "GET" { return nil } var upload string if h.uploadFolder != "" { upload = `Upload Web Page Is Openned!` } attr := r.URL.Query().Get("list") if attr != "" { attr = "/?list=" + attr } var fdatas = make([]FileData, 0, len(dir)+1) if r.URL.Path != "/" { p := r.URL.Path if p[len(p)-1:] != "/" { p += "/" } fdatas = append(fdatas, FileData{ Attr: p + ".." + attr, Name: "..", Modified: "-", Size: -1, Type: "上层文件夹", }) } if r.URL.Path == "/" { r.URL.Path = "" } else if r.URL.Path[len(r.URL.Path)-1:] == "/" { r.URL.Path = r.URL.Path[0 : len(r.URL.Path)-1] } for _, v := range dir { if v.Name() != "." || v.Name() != ".." { if !v.IsDir() { fdatas = append(fdatas, FileData{ Name: v.Name(), Attr: r.URL.Path + "/" + v.Name(), Modified: v.ModTime().Format("2006-01-02 15:04:05"), Size: v.Size(), Type: h.FileType(v.Name()), }) } else { fdatas = append(fdatas, FileData{ Name: v.Name() + "/", Attr: r.URL.Path + "/" + v.Name() + attr, Modified: v.ModTime().Format("2006-01-02 15:04:05"), Size: -1, Type: "文件夹", }) } } } tmpt, err := template.New("index").Parse(string(templateHtml)) if err != nil { log.Errorf("Parse Template Error:%v\n", err) w.WriteHeader(502) w.Write([]byte(`B612 Http Server

502 SERVER ERROR


`)) return err } jData, err := json.Marshal(fdatas) if err != nil { log.Errorf("Json Marshal Failed:%v\n", err) w.WriteHeader(502) w.Write([]byte(`B612 Http Server

502 SERVER ERROR


`)) return err } if r.URL.Path == "" { r.URL.Path = "/" } var bk, mbk string if h.background != "" { bk = `background: url('` + h.background + `') no-repeat center center fixed;` } if h.mobildBackground != "" { mbk = `background: url('` + h.mobildBackground + `') no-repeat center center fixed;` } if h.mobildBackground == "" && h.background != "" { mbk = bk } err = tmpt.Execute(w, map[string]interface{}{ "IdxTitle": r.URL.Path, "Version": ver, "Idx": "Index of " + r.URL.Path, "Upload": template.HTML(upload), "Data": template.JS(jData), "Photo": template.CSS(bk), "MobilePhoto": template.CSS(mbk), }) if err != nil { log.Errorf("Template Execute Failed:%v\n", err) w.WriteHeader(502) w.Write([]byte(`B612 Http Server

502 SERVER ERROR


`)) return err } return nil } func (h *HttpServer) getSleepTime() time.Duration { if h.speedlimit == 0 { return 0 } return time.Nanosecond * time.Duration(16384*1000*1000*1000/h.speedlimit) / 2 } func (h *HttpServer) isTls(r *http.Request) bool { if r.Context().Value("istls") != nil { if v, ok := r.Context().Value("istls").(bool); ok { if v { return true } } if v, ok := r.Context().Value("istls").(*starnet.Conn); ok { return v.IsTLS() } } return false } func (h *HttpServer) isTlsStr(r *http.Request) string { if h.isTls(r) { return "TLS " } return "PLN " } func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error { if !staros.Exists(fullpath) { h.Page404(w) return errors.New("File Not Found! 404 ERROR") } isTls := h.isTlsStr(r) var lastCount int64 var lastDate time.Time = time.Now() var currentCount int64 speedControl := func(count int) { if h.speedlimit == 0 { return } currentCount += int64(count) for { if time.Since(lastDate) < time.Second { if uint64(currentCount-lastCount) > h.speedlimit { time.Sleep(h.getSleepTime()) } else { break } } else { lastDate = time.Now() lastCount = currentCount break } } } //starlog.Debugln(r.Header) startRange, endRange := h.CalcRange(r) fp, err := os.Open(fullpath) if err != nil { log.Errorf("Failed to open file %s,reason:%v\n", r.URL.Path, err) w.WriteHeader(502) w.Write([]byte(`B612 Http Server

502 SERVER ERROR


`)) return err } defer fp.Close() var transferData int defer func() { if transferData != 0 { var tani string tani = fmt.Sprintf("%v Byte", transferData) if f64 := float64(transferData) / 1024; f64 > 1 { tani = fmt.Sprintf("%v KiB", f64) if f64 = float64(f64) / 1024; f64 > 1 { tani = fmt.Sprintf("%v MiB", f64) if f64 = float64(f64) / 1024; f64 > 1 { tani = fmt.Sprintf("%v GiB", f64) } } } log.Infof(isTls+"Tranfered File %s %d bytes (%s) to remote %v\n", r.URL.Path, transferData, tani, r.RemoteAddr) } }() if startRange == -1 { hook, needCurl := h.willHook(fullpath) if !needCurl { w.WriteHeader(200) for { buf := make([]byte, 16384) n, err := fp.Read(buf) if n != 0 { speedControl(n) ns, err := w.Write(buf[:n]) transferData += ns if err != nil { log.Errorf(isTls+"Transfer File %s to Remote Failed:%v\n", fullpath, err) return err } } if err != nil { if err == io.EOF { break } log.Errorf("Read File %s Failed:%v\n", fullpath, err) return err } } return nil } data, err := os.ReadFile(fullpath) if err != nil { w.WriteHeader(502) w.Write([]byte(`B612 Http Server

502 SERVER ERROR


`)) log.Errorf("Read File %s Failed:%v\n", fullpath, err) return err } b64 := base64.StdEncoding.EncodeToString(data) req, err := starnet.Curl(starnet.NewSimpleRequest(hook.Url, "POST", starnet.WithBytes(starnet.BuildPostForm(map[string]string{ "data": b64, "ip": r.RemoteAddr, })), starnet.WithTimeout(time.Duration(hook.Timeout)*time.Millisecond))) if err != nil { w.Header().Set("Content-Length", strconv.Itoa(len(data))) w.WriteHeader(200) ns, err := w.Write(data) transferData += ns if err != nil { log.Errorf(isTls+"Transfer File %s to Remote Failed:%v\n", fullpath, err) return err } return nil } recvData := req.Body().Bytes() w.WriteHeader(200) w.Header().Set("Content-Length", strconv.Itoa(len(recvData))) ns, err := w.Write(recvData) transferData += ns if err != nil { log.Errorf(isTls+"Transfer File %s to Remote Failed:%v\n", fullpath, err) return err } return nil } log.Debugf(isTls+"206 transfer mode for %v %v start %v end %v\n", r.URL.Path, r.RemoteAddr, startRange, endRange) w.WriteHeader(206) fp.Seek(int64(startRange), 0) count := startRange for { buf := make([]byte, 16384) n, err := fp.Read(buf) if err != nil { if err == io.EOF { break } log.Errorf(isTls+"Read File %s Failed:%v\n", r.URL.Path, err) return err } speedControl(n) if endRange == -1 { ns, err := w.Write(buf[:n]) transferData += ns if err != nil { log.Errorf(isTls+"Transfer File %s to Remote Failed:%v\n", r.URL.Path, err) return err } } else { if count > endRange { break } writeNum := n if count+int64(n) > endRange { writeNum = int(endRange - count + 1) } ns, err := w.Write(buf[0:writeNum]) if err != nil { log.Errorln("Transfer Error:", err) return err } transferData += ns count += int64(n) } } return nil } func (h *HttpServer) trimSize(size int64) string { var tani string tani = fmt.Sprintf("%v Byte", size) if f64 := float64(size) / 1024; f64 > 1 { tani = fmt.Sprintf("%.3f KiB", math.Trunc(f64*1e3+0.5)*1e-3) if f64 = float64(f64) / 1024; f64 > 1 { tani = fmt.Sprintf("%.3f MiB", math.Trunc(f64*1e3+0.5)*1e-3) if f64 = float64(f64) / 1024; f64 > 1 { tani = fmt.Sprintf("%.3f GiB", math.Trunc(f64*1e3+0.5)*1e-3) } } } return tani } func (h *HttpServer) uploadFile(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(200) w.Write([]byte("USE POST METHOD!")) return } r.ParseMultipartForm(10485760) file, handler, err := r.FormFile("victorique") if err != nil { starlog.Errorf("Parse File From Form Failed:%v\n", err) w.WriteHeader(502) w.Write([]byte(err.Error())) return } defer file.Close() starlog.Noticef("Uploading %s From %s\n", handler.Filename, r.RemoteAddr) os.MkdirAll(filepath.Join(h.envPath, h.uploadFolder), 0755) f, err := os.OpenFile(filepath.Join(h.uploadFolder, handler.Filename), os.O_WRONLY|os.O_CREATE, 0755) if err != nil { starlog.Errorf("Open Local File %s Form %v Failed:%v\n", handler.Filename, r.RemoteAddr, err) return } defer f.Close() _, err = io.Copy(f, file) if err != nil { starlog.Errorf("Write File %s Form %v Failed:%v\n", handler.Filename, r.RemoteAddr, err) return } starlog.Infof("Write File %s Form %v Finished\n", handler.Filename, r.RemoteAddr) fmt.Fprintf(w, `

%v

Return To Web Page

`, handler.Header) }