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.
Victorique/vtqe/tools/http.go

332 lines
9.2 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 tools
import (
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"b612.me/starainrt"
"b612.me/starlog"
"github.com/spf13/cobra"
)
var port, ip, path string
var up bool
var basicAuth, certKey string
type TraceHandler struct {
h http.Handler
}
func init() {
httpcmd.Flags().StringVarP(&port, "port", "p", "80", "监听端口")
httpcmd.Flags().StringVarP(&ip, "ip", "i", "0.0.0.0", "监听ip")
httpcmd.Flags().StringVarP(&path, "folder", "f", "./", "本地文件地址")
httpcmd.Flags().BoolVarP(&up, "upload", "u", false, "是否开启文件上传")
httpcmd.Flags().StringVarP(&basicAuth, "auth", "a", "", "HTTP BASIC AUTH认证(用户名:密码)")
httpcmd.Flags().StringVarP(&certKey, "cert", "c", "", "TLS证书路径用:分割证书与密钥")
Maincmd.AddCommand(httpcmd)
}
// httpCmd represents the http command
var httpcmd = &cobra.Command{
Use: "http",
Short: "HTTP文件服务器",
Long: `HTTP文件服务器`,
Run: func(cmd *cobra.Command, args []string) {
var err error
http.HandleFunc("/", httplisten)
path, _ = filepath.Abs(path)
starlog.Infoln("Listening On Port:" + port)
if up {
starlog.Infoln("upload is openned,path is /vtqeupload1127")
http.HandleFunc("/vtqeupload1127", uploadfile)
}
if certKey == "" {
err = http.ListenAndServe(ip+":"+port, nil)
} else {
certs := strings.Split(certKey, ":")
if len(certs) != 2 {
starlog.Criticalln("证书不正确!")
return
}
err = http.ListenAndServeTLS(ip+":"+port, certs[0], certs[1], nil)
}
if err != nil {
starlog.Criticalln("Error:" + err.Error())
}
},
}
func 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, `<html><body><p>%v</p><h2><a href="./vtqeupload1127/web">Return To Web Page</a></h2></body></html>`, 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)
}
func httplisten(w http.ResponseWriter, r *http.Request) {
log := starlog.Std.NewFlag()
w.Header().Set("Server", "Vicorique")
w.Header().Set("Powered", "B612.ME")
write401 := func() {
w.Header().Set("WWW-Authenticate", ` Basic realm="Please Enter Passwd"`)
w.WriteHeader(401)
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>`))
}
if 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(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"]
log.Noticeln("Get " + p + " " + r.RemoteAddr)
fullpath, _ := filepath.Abs(path + p)
if p == "/" {
ReadFolder(w, r, fullpath, true)
return
}
if up {
if p == "/vtqeupload1127/web" {
w.Write([]byte(`<html><body><form id= "uploadForm" action= "../vtqeupload1127" method= "post" enctype ="multipart/form-data">
<h1 >B612 File Upload Page </h1>
<p >上传文件: <input type ="file" name="victorique" /></p>
<input type ="submit" value="上传"/>
</form>
<h2>Copyright@b612.me </h2></body></html>`))
return
}
}
if !starainrt.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("<h1>404 NOT FOUND</h1>"))
return
}
if starainrt.IsFolder(fullpath) {
ReadFolder(w, r, fullpath, false)
return
}
fpdst, err := os.Open(fullpath)
if err != nil {
log.Errorln(err)
w.WriteHeader(403)
w.Write([]byte("<h1>403 NO ACCESS</h1>"))
return
}
fpinfo, _ := os.Stat(fullpath)
name := filepath.Base(fullpath)
tmp := strings.Split(name, ".")
ext := ""
if len(tmp) >= 2 {
ext = strings.ToLower(tmp[len(tmp)-1])
}
switch ext {
case "jpeg", "jpg", "jpe":
w.Header().Set("Content-Type", "image/jpeg")
case "mpeg", "mpg", "mkv":
w.Header().Set("Content-Type", "video/mpeg")
case "mp4":
w.Header().Set("Content-Type", "video/mp4")
case "pdf":
w.Header().Set("Content-Type", "application/pdf")
default:
w.Header().Set("Content-Type", "application/download")
w.Header().Set("Content-Disposition", "attachment;filename="+name)
}
etag := new(starainrt.StarCrypto).MD5([]byte(fpinfo.ModTime().String()))
w.Header().Set("Content-Transfer-Encoding", "binary")
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 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("<h1>May Cannot Access!</h1>"))
}
w.Write([]byte("<html>\n<style>\np{margin: 2px auto}\n</style>\n<h1>B612 Http Server - " + Version + "</h1>"))
if up {
w.Write([]byte("<a href=/vtqeupload1127/web>Upload Web Page Is Openned!</a><br /><br />"))
}
w.Write([]byte("<hr /><pre>\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("<p><a href='%s'>%s</a> %s</p>\n", r.URL.Path+"/..", "..", "上层文件夹")))
}
for _, v := range dir {
if v.Name() != "." || v.Name() != ".." {
if !v.IsDir() {
w.Write([]byte(fmt.Sprintf("<p><a href='%s'>%s</a> %d %s</p>\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("<p><a href='%s'>%s</a> %s %s</p>\n", r.URL.Path+"/"+v.Name(), v.Name(), "文件夹", v.ModTime().Format("2006-01-02 15:04:05"))))
}
}
}
w.Write([]byte("</pre>\n</html>"))
return
}