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

901 lines
24 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"
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
)
var version = "2.1.0"
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
}
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
var htmlTitle string = `<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>B612 Http Server %s</title>
<style>
* {
box-sizing: border-box;
}
body {
background-color: #f5f5f5;
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
max-width: 960px;
margin: 0 auto;
padding: 24px;
}
h1 {
text-align: center;
margin-bottom: 24px;
}
table {
width: 100%%;
border-collapse: collapse;
margin-top: 24px;
}
th,
td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
position: relative;
}
th[data-sort]:before {
content: "▼";
display: inline-block;
height: 20px;
width: 20px;
margin-right: 10px;
vertical-align: middle;
position: absolute;
right: 0;
top: 50%%;
transform: translateY(-50%%);
opacity: 0.3;
transition: all 0.2s ease-in-out;
}
th[data-sort].asc:before {
content: "▲";
opacity: 1;
}
th:hover:before {
opacity: 1;
}
.filename {
color: #007bff;
text-decoration: underline;
}
.filetype {
text-transform: uppercase;
}
@media screen and (max-width: 600px) {
table {
font-size: 14px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>B612 Http Server - %s</h1>
<hr /><pre><h2> %s </h2></pre>%s
<table>
<thead>
<tr>
<th data-sort="name" class="asc">Name</th>
<th data-sort="modified">Modified</th>
<th data-sort="size">Size</th>
<th data-sort="type">Type</th>
</tr>
</thead>
<tbody>`
var htmlTail = ` </tbody>
</table>
<hr />
<pre>
<h2 style="text-align: center;">B612.Me © Apache 2.0 License</h2>
</pre>
</div>
<script>
function sortTable(th, n) {
const table = document.querySelector('table');
const rows = table.rows;
let switching = true;
let shouldSwitch = false;
let direction = 'asc';
let switchcount = 0;
while (switching) {
switching = false;
let i;
for (i = 1; i < rows.length - 1; i++) {
shouldSwitch = false;
const x = rows[i].getElementsByTagName("td")[n];
const y = rows[i + 1].getElementsByTagName("td")[n];
let xValue, yValue;
if (n === 2) { // size sorting
if (x.innerText==="-") {
xValue=-1;
}else{
xValue = parseInt(x.innerText.split(' ')[0]);
}
if (y.innerText==="-") {
yValue=-1;
}else{
yValue = parseInt(y.innerText.split(' ')[0]);
}
} else {
xValue = x.innerText.toLowerCase();
yValue = y.innerText.toLowerCase();
}
if (direction === 'asc') {
if (xValue > yValue) {
shouldSwitch = true;
break;
}
} else if (direction === 'desc') {
if (xValue < yValue) {
shouldSwitch = true;
break;
}
}
}
if (shouldSwitch) {
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
switchcount++;
} else {
if (switchcount === 0 && direction === 'asc') {
direction = 'desc';
switching = true;
}
}
}
// update sort class
const ths = table.getElementsByTagName('th');
for (let i = 0; i < ths.length; i++) {
const currentTh = ths[i];
if (currentTh !== th) {
currentTh.classList.remove('asc');
} else {
currentTh.classList.toggle('asc');
}
}
// hide arrow on non-sorting columns
const sortableThs = table.querySelectorAll('thead th[data-sort]');
for (let i = 0; i < sortableThs.length; i++) {
const sortableTh = sortableThs[i];
if (sortableTh !== th) {
sortableTh.classList.remove('asc');
}
}
}
// add sorting event listener to thead
const ths = document.querySelectorAll('table th[data-sort]');
for (let i = 0; i < ths.length; i++) {
const th = ths[i];
th.addEventListener('click', () => {
const sortType = th.getAttribute('data-sort');
let columnIndex;
switch (sortType) {
case 'name':
columnIndex = 0;
break;
case 'modified':
columnIndex = 1;
break;
case 'size':
columnIndex = 2;
break;
case 'type':
columnIndex = 3;
break;
}
sortTable(th, columnIndex);
});
}
</script>
</body>
</html>
`
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.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><title>B612 Http Server</title><body><h1 "style="text-align: center;">403 Forbidden</h1><hr ></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
rangeStart, rangeEnd = -1, -1
for k, v := range r.Header {
if strings.ToLower(k) == "range" {
if strings.Contains(v[0], "bytes=") {
v[0] = strings.Replace(v[0], "bytes=", "", -1)
} else {
continue
}
data := strings.Split(v[0], "-")
if len(data) == 0 {
break
}
rangeStart, _ = strconv.ParseInt(data[0], 10, 64)
if len(data) > 1 {
rangeEnd, _ = strconv.ParseInt(data[1], 10, 64)
}
//w.WriteHeader(206) //206 支持断点续传
break
}
}
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
}
w.Header().Set("Content-Length", strconv.FormatInt(finfo.Size(), 10))
start, end := h.CalcRange(r)
if start != -1 {
if end == -1 {
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-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))
}
}
}
}
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) {
return h.getFolder(log, w, r, fullpath)
}
return h.getFile(log, w, r, fullpath)
}
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>`
}
w.Write([]byte(fmt.Sprintf(htmlTitle, r.URL.Path, version, "Index of "+r.URL.Path, upload)))
if r.URL.Path != "/" {
p := r.URL.Path
if p[len(p)-1:] != "/" {
p += "/"
}
w.Write([]byte(fmt.Sprintf(`<tr><td><a class="filename" href="%s">%s</a></td><td>%s</td><td>%s</td><td class="filetype">%s</td></tr>`,
p+"..", "../", "-", "-", "上层文件夹")))
}
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() {
w.Write([]byte(fmt.Sprintf(`<tr><td><a class="filename" href="%s">%s</a></td><td>%s</td><td>%s</td><td class="filetype">%s</td></tr>`,
r.URL.Path+"/"+v.Name(), v.Name(), v.ModTime().Format("2006-01-02 15:04:05"), fmt.Sprintf("%d (%s)", v.Size(), h.trimSize(v.Size())), h.FileType(v.Name()))))
} else {
w.Write([]byte(fmt.Sprintf(`<tr><td><a class="filename" href="%s">%s</a></td><td>%s</td><td>%s</td><td class="filetype">%s</td></tr>`,
r.URL.Path+"/"+v.Name(), v.Name()+"/", v.ModTime().Format("2006-01-02 15:04:05"), "-", "文件夹")))
}
}
}
w.Write([]byte(htmlTail))
return nil
}
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")
}
//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, 1048576)
n, err := fp.Read(buf)
if n != 0 {
ns, err := w.Write(buf[0: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.Errorln("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.NewRequests(hook.Url, starnet.BuildPostForm(map[string]string{
"data": b64,
"ip": r.RemoteAddr,
}),
"POST",
starnet.WithTimeout(time.Duration(hook.Timeout)*time.Millisecond)))
if err != nil || len(req.RecvData) == 0 {
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
}
w.WriteHeader(200)
w.Header().Set("Content-Length", strconv.Itoa(len(req.RecvData)))
ns, err := w.Write(req.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, 1048576)
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
}
if endRange == -1 {
ns, err := w.Write(buf[0: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])
transferData += ns
if err != nil {
log.Errorln("Transfer Error:", err)
return err
}
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)
}