package httpserver
import (
"encoding/base64"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"b612.me/starcrypto"
"b612.me/starlog"
"b612.me/staros"
)
type VtqeHttpServer struct {
basicAuth string
path string
upload bool
version string
indexFile string
stopMime bool
}
func RunHttpServer(listenIp, port, auth, folderPath, certKey, version, indexFile string, doUpload, stopMime bool) error {
var err error
var vtqe VtqeHttpServer = VtqeHttpServer{auth, folderPath, doUpload, version, indexFile, stopMime}
http.HandleFunc("/", vtqe.httpListen)
starlog.Noticeln("Listening On " + listenIp + ":" + port)
if doUpload {
starlog.Noticeln("upload is openned,path is /vtqeupload1127")
http.HandleFunc("/vtqeupload1127", vtqe.uploadFile)
}
if certKey == "" {
err = http.ListenAndServe(listenIp+":"+port, nil)
} else {
certs := strings.Split(certKey, ":")
if len(certs) != 2 {
starlog.Criticalln("证书不正确!")
return errors.New("ZSBZQ")
}
err = http.ListenAndServeTLS(listenIp+":"+port, certs[0], certs[1], nil)
}
if err != nil {
starlog.Criticalln("Error:" + err.Error())
return err
}
return nil
}
func (v VtqeHttpServer) httpListen(w http.ResponseWriter, r *http.Request) {
log := starlog.Std.NewFlag()
w.Header().Set("Server", "Victorique")
w.Header().Set("Powered", "B612.ME")
write401 := func() {
w.Header().Set("WWW-Authenticate", ` Basic realm="Please Enter Passwd"`)
w.WriteHeader(401)
w.Write([]byte(`
401 Authorization Required
401 Authorization Required
B612 HTTP SERVER
`))
}
if v.basicAuth != "" {
authHeader := strings.TrimSpace(r.Header.Get("Authorization"))
if len(authHeader) == 0 {
log.Noticeln("No Authed! Get Path is", r.URL.Path, r.RemoteAddr)
write401()
return
} else {
userAuth := base64.StdEncoding.EncodeToString([]byte(v.basicAuth))
authStr := strings.Split(authHeader, " ")
if strings.TrimSpace(authStr[1]) != userAuth {
log.Noticeln("Auth Failed! Get Path is", r.URL.Path, r.RemoteAddr, "pwd enter is", authHeader)
write401()
return
}
}
}
p := r.URL.Path
cmd := r.URL.Query()["cmd"]
fullpath, _ := filepath.Abs(v.path + p)
if p == "/" && v.indexFile != "" {
tmppath, _ := filepath.Abs(v.path + "/" + v.indexFile)
if staros.Exists(tmppath) {
fullpath = tmppath
p = "/" + v.indexFile
}
}
log.Noticeln("Get " + p + " " + r.RemoteAddr)
if p == "/" {
v.readFolder(w, r, fullpath, true)
return
}
if v.upload {
if p == "/vtqeupload1127/web" {
w.Write([]byte(`
Copyright@b612.me
`))
return
}
}
if !staros.Exists(fullpath) {
w.WriteHeader(404)
if len(cmd) != 0 {
if cmd[0] == "header" {
for k, v := range r.Header {
for _, v2 := range v {
fmt.Fprintf(w, "%s:%s\n", k, v2)
}
}
}
}
w.Write([]byte("404 NOT FOUND
"))
return
}
if staros.IsFolder(fullpath) {
v.readFolder(w, r, fullpath, false)
return
}
fpdst, err := os.Open(fullpath)
if err != nil {
log.Errorln(err)
w.WriteHeader(403)
w.Write([]byte("403 NO ACCESS
"))
return
}
fpinfo, _ := os.Stat(fullpath)
name := filepath.Base(fullpath)
ext := filepath.Ext(name)
mime := GetMIME(ext)
if mime == "" || v.stopMime {
w.Header().Set("Content-Type", "application/download")
w.Header().Set("Content-Disposition", "attachment;filename="+name)
w.Header().Set("Content-Transfer-Encoding", "binary")
} else {
w.Header().Set("Content-Type", mime)
}
etag := starcrypto.Md5Str([]byte(fpinfo.ModTime().String()))
w.Header().Set("Accept-Ranges", "bytes")
w.Header().Set("ETag", etag)
w.Header().Set("Last-Modified", strings.ReplaceAll(fpinfo.ModTime().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"), "UTC", "GMT"))
isRange := false
var rangeStart, rangeEnd int64
rangeEnd = -1
for k, v := range r.Header {
if strings.ToLower(k) == "range" {
if strings.Index(v[0], "bytes=") < 0 {
break
}
v[0] = strings.Replace(v[0], "bytes=", "", -1)
data := strings.Split(v[0], "-")
if len(data) == 0 {
break
}
rangeStart, _ = strconv.ParseInt(data[0], 10, 64)
if len(data) == 2 {
rangeEnd, _ = strconv.ParseInt(data[1], 10, 64)
}
//w.WriteHeader(206) //206 支持断点续传
isRange = true
break
}
}
defer fpdst.Close()
var transferData int
if !isRange {
w.Header().Set("Content-Length", strconv.FormatInt(fpinfo.Size(), 10))
w.WriteHeader(200)
for {
buf := make([]byte, 1048576)
n, err := fpdst.Read(buf)
if n != 0 {
ns, err := w.Write(buf[0:n])
transferData += ns
if err != nil {
starlog.Errorln("Transfer Error:", err)
}
}
if err != nil {
if err == io.EOF {
break
}
break
}
}
} else {
w.Header().Set("Content-Length", strconv.FormatInt(fpinfo.Size(), 10))
if rangeEnd == -1 {
w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(rangeStart, 10)+"-"+strconv.FormatInt(fpinfo.Size(), 10)+"/"+strconv.FormatInt(fpinfo.Size(), 10))
//w.Header().Set("Content-Length", strconv.FormatInt(fpinfo.Size()-rangeStart, 10))
} else {
w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(rangeStart, 10)+"-"+strconv.FormatInt(rangeEnd, 10)+"/"+strconv.FormatInt(fpinfo.Size(), 10))
//w.Header().Set("Content-Length", strconv.FormatInt(1+rangeEnd-rangeStart, 10))
}
w.WriteHeader(206)
fpdst.Seek(int64(rangeStart), 0)
count := rangeStart
for {
buf := make([]byte, 1048576)
n, err := fpdst.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return
}
if rangeEnd == -1 {
ns, err := w.Write(buf[0:n])
transferData += ns
if err != nil {
starlog.Errorln("Transfer Error:", err)
}
} else {
if count > rangeEnd {
break
}
if count+int64(n) > rangeEnd {
w.Write(buf[0 : rangeEnd-count+1])
break
} else {
ns, err := w.Write(buf[0:n])
transferData += ns
if err != nil {
starlog.Errorln("Transfer Error:", err)
}
count += int64(n)
}
}
}
}
var tani string
tani = fmt.Sprintf("%v Byte", transferData)
if f64 := float64(transferData) / 1024; f64 > 1 {
tani = fmt.Sprintf("%v KB", f64)
if f64 = float64(f64) / 1024; f64 > 1 {
tani = fmt.Sprintf("%v MB", f64)
if f64 = float64(f64) / 1024; f64 > 1 {
tani = fmt.Sprintf("%v GB", f64)
}
}
}
log.Infoln(fpinfo.Name(), "客户端下载已结束,共传输大小:"+tani)
}
func (v VtqeHttpServer) readFolder(w http.ResponseWriter, r *http.Request, fullpath string, isroot bool) {
dir, err := ioutil.ReadDir(fullpath)
if err != nil {
starlog.Errorln(err)
w.WriteHeader(403)
w.Write([]byte("May Cannot Access!
"))
}
w.Write([]byte("\n\nB612 Http Server - " + v.version + "
"))
if v.upload {
w.Write([]byte("Upload Web Page Is Openned!
"))
}
w.Write([]byte("
\n"))
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]
}
if !isroot {
w.Write([]byte(fmt.Sprintf("%s %s
\n", r.URL.Path+"/..", "..", "上层文件夹")))
}
for _, v := range dir {
if v.Name() != "." || v.Name() != ".." {
if !v.IsDir() {
w.Write([]byte(fmt.Sprintf("%s %d %s
\n", r.URL.Path+"/"+v.Name(), v.Name(), int(v.Size()), v.ModTime().Format("2006-01-02 15:04:05"))))
} else {
w.Write([]byte(fmt.Sprintf("%s %s %s
\n", r.URL.Path+"/"+v.Name(), v.Name(), "文件夹", v.ModTime().Format("2006-01-02 15:04:05"))))
}
}
}
w.Write([]byte("
\n"))
return
}
func (v VtqeHttpServer) uploadFile(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.Write([]byte("USE POST METHOD!"))
return
}
r.ParseMultipartForm(10485760)
file, handler, err := r.FormFile("victorique")
if err != nil {
starlog.Errorln(err)
w.WriteHeader(502)
w.Write([]byte(err.Error()))
return
}
defer file.Close()
starlog.Noticef("Upload %s From %s\n", handler.Filename, r.RemoteAddr)
fmt.Fprintf(w, `%v
`, handler.Header)
os.Mkdir("./vtqeupload1127", 0755)
f, err := os.OpenFile("./vtqeupload1127/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
starlog.Errorln(err)
return
}
defer f.Close()
io.Copy(f, file)
}