You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
star/httpserver/server.go

850 lines
22 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package httpserver
import (
"b612.me/starcrypto"
"b612.me/starlog"
"b612.me/starnet"
"b612.me/staros"
"context"
_ "embed"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"html/template"
"io"
"io/ioutil"
"math"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
)
var version = "2.1.0.b11"
type HttpServerCfgs func(cfg *HttpServerCfg)
type HttpServerCfg struct {
basicAuthUser string
basicAuthPwd string
envPath string
uploadFolder string
logpath string
indexFile string
cert string
key string
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,
}
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 != "" {
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 == "" {
if err := server.ListenAndServe(); err != http.ErrServerClosed {
return err
}
return nil
}
if err := server.ListenAndServeTLS(h.cert, h.key); err != http.ErrServerClosed {
return err
}
return nil
}
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(`<html><title>B612 Http Server</title><body><h1 "style="text-align: center;">404 NOT FOUND</h1><hr ></body></html>`))
}
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(`
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>B612 HTTP SERVER</center>
</body>
</html>`))
}
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(`
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>B612 HTTP SERVER</center>
</body>
</html>`))
}
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 := `<html><head><meta charset="utf-8"><title>B612 Http Server</title></head><body><h1 "style="text-align: center;">Debug Mode</h1><hr >%s</body></html>`
resp := "<h2>Url</h2>"
resp += "<p>" + r.Method + " " + r.URL.Path + "</p>"
resp += "<p> query " + r.URL.RawQuery + "</p>"
resp += "<p> fragment " + r.URL.Fragment + "</p>"
resp += "<p> FullUrl " + r.URL.String() + "</p>"
resp += "<h2>Query</h2>"
for k, v := range r.URL.Query() {
resp += fmt.Sprintf("<p>%s:%s</p>", k, v)
}
resp += "<h2>Header</h2>"
for key, val := range r.Header {
for _, v := range val {
resp += fmt.Sprintf("<p>%s:%s</p>", key, v)
}
}
resp += "<h2>Cookie</h2>"
for _, c := range r.Cookies() {
resp += fmt.Sprintf("<p>%s:%s</p>", c.Name, c.Value)
}
resp += "<h2>RemoteAddr</h2>"
resp += "<p>" + r.RemoteAddr + "</p>"
resp += "<h2>Proto</h2>"
resp += "<p>" + r.Proto + "</p>"
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/"+version)
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)
}
}
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("%s %s From %s %s %.2fs %v\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds(), err)
} else {
log.Infof("%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("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("%s %s From %s %s %.2fs %v\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds(), err)
return
}
log.Infof("%s %s From %s %s %.2fs\n", r.Method, path, r.RemoteAddr, ua, time.Since(now).Seconds())
default:
log.Errorf("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("<h1>Cannot Access!</h1>"))
}
if r.Method != "GET" {
return nil
}
var upload string
if h.uploadFolder != "" {
upload = `<a href=/b612?upload=true>Upload Web Page Is Openned!</a>`
}
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(`<html><title>B612 Http Server</title><body><h1 "style="text-align: center;">502 SERVER ERROR</h1><hr ></body></html>`))
return err
}
jData, err := json.Marshal(fdatas)
if err != nil {
log.Errorf("Json Marshal Failed:%v\n", err)
w.WriteHeader(502)
w.Write([]byte(`<html><title>B612 Http Server</title><body><h1 "style="text-align: center;">502 SERVER ERROR</h1><hr ></body></html>`))
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": version,
"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(`<html><title>B612 Http Server</title><body><h1 "style="text-align: center;">502 SERVER ERROR</h1><hr ></body></html>`))
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) 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")
}
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(`<html><title>B612 Http Server</title><body><h1 "style="text-align: center;">502 SERVER ERROR</h1><hr ></body></html>`))
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("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("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(`<html><title>B612 Http Server</title><body><h1 "style="text-align: center;">502 SERVER ERROR</h1><hr ></body></html>`))
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("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("Transfer File %s to Remote Failed:%v\n", fullpath, err)
return err
}
return nil
}
log.Debugf("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("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("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, `<html><body><p>%v</p><h2><a href="/b612?upload=true">Return To Web Page</a></h2></body></html>`, handler.Header)
}