• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2022 Huawei Device Co., Ltd.
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package main
15
16//遇到报错请在当前目录下执行这个命令: go mod download golang.org/x/text
17import (
18	"bufio"
19	"bytes"
20	"crypto"
21	"crypto/rand"
22	"crypto/rsa"
23	"crypto/sha512"
24	"crypto/tls"
25	"crypto/x509"
26	"crypto/x509/pkix"
27	"encoding/base64"
28	"encoding/json"
29	"encoding/pem"
30	"flag"
31	"fmt"
32	"io"
33	"io/fs"
34	"log"
35	"math/big"
36	"mime"
37	"net"
38	"net/http"
39	"net/http/cookiejar"
40	"os"
41	"os/exec"
42	"path"
43	"path/filepath"
44	"regexp"
45	"runtime"
46	"strconv"
47	"strings"
48	"sync"
49	"time"
50)
51
52const HttpPort = 9000
53
54var exPath string
55var serveInfo string
56var aiInfo string
57var msgPublishData MsgPublishData
58var hdcPublicKey string
59var hdcPrivateKey *rsa.PrivateKey
60
61// CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
62// CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go
63func cors(fs http.Handler, version string) http.HandlerFunc {
64	return func(w http.ResponseWriter, r *http.Request) {
65		// return if you do not want the FileServer handle a specific request
66		r.Header.Add("Cross-Origin-Opener-Policy", "same-origin")
67		r.Header.Add("Cross-Origin-Embedder-Policy", "require-corp")
68		w.Header().Add("Cross-Origin-Opener-Policy", "same-origin")
69		w.Header().Add("Cross-Origin-Embedder-Policy", "require-corp")
70		w.Header().Set("Access-Control-Allow-Origin", "*")
71		w.Header().Set("Access-Control-Allow-Credentials", "true")
72		w.Header().Set("Access-Control-Allow-Headers", "x-requested-with, authorization, blade-auth") //*
73		w.Header().Set("Access-Control-Allow-Methods", "*")                                           //*
74		w.Header().Set("Access-Control-Max-Age", "3600")
75		w.Header().Set("data-version", version)
76		w.Header().Set("Cache-Control", "no-cache")
77		w.Header().Set("Pragma", "no-cache")
78		fs.ServeHTTP(w, r)
79	}
80}
81
82func exist(path string) bool {
83	_, err := os.Stat(path)
84	if err != nil {
85		if os.IsExist(err) {
86			return true
87		}
88		return false
89	}
90	return true
91}
92
93func genSSL() {
94	if exist("cert/keyFile.key") || exist("cert/certFile.pem") {
95		fmt.Println("keyFile.key exists")
96		return
97	}
98	max := new(big.Int).Lsh(big.NewInt(1), 128)
99	serialNumber, _ := rand.Int(rand.Reader, max)
100	subject := pkix.Name{
101		Organization:       []string{"www.smartperf.com"},
102		OrganizationalUnit: []string{"ITs"},
103		CommonName:         "www.smartperf.com",
104	}
105	certificate509 := x509.Certificate{
106		SerialNumber: serialNumber,
107		Subject:      subject,
108		NotBefore:    time.Now(),
109		NotAfter:     time.Now().AddDate(10, 0, 0),
110		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
111		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
112		IPAddresses:  []net.IP{net.ParseIP("127.0.0.1")},
113	}
114	chekDir("cert")
115	pk, _ := rsa.GenerateKey(rand.Reader, 1024)
116	derBytes, _ := x509.CreateCertificate(rand.Reader, &certificate509, &certificate509, &pk.PublicKey, pk)
117	certOut, _ := os.Create("cert/certFile.pem")
118	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
119	certOut.Close()
120	keyOut, _ := os.Create("cert/keyFile.key")
121	pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)})
122	keyOut.Close()
123}
124
125func genRsa() {
126	// generate hdc private key
127	privateKey, err := rsa.GenerateKey(rand.Reader, 3072)
128	if err != nil {
129		fmt.Println("Generate hdc rsa private key failed")
130		return
131	}
132	hdcPrivateKey = privateKey
133
134	// generate hdc public key
135	publicKey := &privateKey.PublicKey
136	pkixPublicKey, err := x509.MarshalPKIXPublicKey(publicKey)
137	if err != nil {
138		fmt.Println(err)
139		return
140	}
141	publicKeyBlock := &pem.Block{
142		Type: "PUBLIC KEY",
143
144		Bytes: pkixPublicKey,
145	}
146	hdcPublicKey = string(pem.EncodeToMemory(publicKeyBlock))
147}
148
149func main() {
150	port := HttpPort
151	isOpen := 1
152	flag.IntVar(&port, "p", HttpPort, "The port number used")
153	flag.IntVar(&isOpen, "o", 1, "Whether to immediately open the website in your browser; 1 is true; 0 is false")
154	flag.Parse()
155	if isOpen < 0 || isOpen > 1 {
156		fmt.Println("Error: -o must be 0 or 1")
157		return
158	}
159	checkPort(port)
160	genSSL()
161	genRsa()
162	exPath = getCurrentAbPath()
163	fmt.Println(exPath)
164	go func() {
165		version := ""
166		readVersion, versionErr := os.ReadFile(exPath + "/version.txt")
167		if versionErr != nil {
168			version = ""
169		} else {
170			version = string(readVersion)
171		}
172		readReqServerConfig()
173		mux := http.NewServeMux()
174		mime.TypeByExtension(".js")
175		mime.AddExtensionType(".js", "application/javascript")
176		log.Println(mime.TypeByExtension(".js"))
177		mux.HandleFunc("/application/logger", consoleHandler)
178		mux.Handle("/application/upload/", http.StripPrefix("/application/upload/", http.FileServer(http.Dir(filepath.FromSlash(exPath+"/upload")))))
179		mux.HandleFunc("/application/download-file", downloadHandler)
180		mux.HandleFunc("/application/serverInfo", serverInfo)
181		mux.HandleFunc("/application/getAiInfo", getAiInfo)
182		mux.HandleFunc("/application/hdcPublicKey", getHdcPublicKey)
183		mux.HandleFunc("/application/encryptHdcMsg", encryptHdcMsg)
184		mux.HandleFunc("/application/signatureHdcMsg", signatureHdcMsg)
185		mux.HandleFunc("/application/messagePublish", getMsgPublish)
186		fs := http.FileServer(http.Dir(exPath + "/"))
187		mux.Handle("/application/", http.StripPrefix("/application/", cors(fs, version)))
188		go func() {
189			ser := &http.Server{
190				Addr:    fmt.Sprintf(":%d", port),
191				Handler: mux,
192			}
193			log.Println(fmt.Sprintf("HTTPS[%d]服务启动", port))
194			err := ser.ListenAndServeTLS("cert/certFile.pem", "cert/keyFile.key")
195			CheckErr(err)
196		}()
197		go func() {
198			ser := &http.Server{
199				Addr:    fmt.Sprintf(":%d", port+1),
200				Handler: mux,
201			}
202			log.Println(fmt.Sprintf("HTTP[%d]服务启动", port))
203			err := ser.ListenAndServe()
204			CheckErr(err)
205		}()
206		if isOpen == 1 {
207			open(fmt.Sprintf("https://localhost:%d/application", port))
208		}
209	}()
210	select {}
211}
212
213func getPidByPort(portNumber int) int {
214	resPid := -1
215	var out bytes.Buffer
216	cmdRes := exec.Command("cmd", "/c", fmt.Sprintf("netstat -ano -p tcp | findstr %d", portNumber))
217	cmdRes.Stdout = &out
218	cmdRes.Run()
219	cmdResStr := out.String()
220	findStr := regexp.MustCompile(`\s\d+\s`).FindAllString(cmdResStr, -1)
221	if len(findStr) > 0 {
222		pid, err := strconv.Atoi(strings.TrimSpace(findStr[0]))
223		if err != nil {
224			resPid = -1
225		} else {
226			resPid = pid
227		}
228	}
229	return resPid
230}
231
232type LoggerReq struct {
233	FileName string `json:"fileName"`
234	FileSize string `json:"fileSize"`
235}
236
237func consoleHandler(w http.ResponseWriter, r *http.Request) {
238	chekDir(exPath + "/logger")
239	var now = time.Now()
240	var fileName = fmt.Sprintf("%d-%d-%d", now.Year(), now.Month(), now.Day())
241	dst, err := os.OpenFile(exPath+"/logger/"+fileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND|os.O_SYNC, 0666)
242	CheckErr(err)
243	contentType := r.Header["Content-Type"]
244	if len(contentType) > 0 {
245		contentTypeName := contentType[0]
246		if strings.HasPrefix(contentTypeName, "application/json") {
247			decoder := json.NewDecoder(r.Body)
248			var req LoggerReq
249			decoder.Decode(&req)
250			dst.WriteString(fmt.Sprintf("%s %s (%s M)\n", now.Format("2006-01-02 15:04:05"), req.FileName, req.FileSize))
251			fmt.Fprintf(w, fmt.Sprintf("日志写入成功%s", exPath))
252		}
253	}
254}
255
256func serverInfo(w http.ResponseWriter, r *http.Request) {
257	w.Header().Set("Access-Control-Allow-Origin", "*")
258	w.Header().Set("request_info", serveInfo)
259	w.WriteHeader(200)
260}
261
262func getAiInfo(w http.ResponseWriter, r *http.Request) {
263	w.Header().Set("Access-Control-Allow-Origin", "*")
264	w.Header().Set("ai_info", aiInfo)
265	w.WriteHeader(200)
266}
267
268func getHdcPublicKey(w http.ResponseWriter, r *http.Request) {
269	w.Header().Set("Access-Control-Allow-Origin", "*")
270	w.Header().Set("Content-Type", "text/json")
271	resp(&w)(true, 0, "success", map[string]interface{}{
272		"publicKey": hdcPublicKey,
273	})
274}
275
276func encryptHdcMsg(w http.ResponseWriter, r *http.Request) {
277	w.Header().Set("Access-Control-Allow-Origin", "*")
278	w.Header().Set("Content-Type", "text/json")
279	hdcMsg := r.URL.Query().Get("message")
280	if len(hdcMsg) == 0 {
281		resp(&w)(false, -1, "Invalid message", nil)
282		return
283	}
284	signatures, err := rsa.SignPKCS1v15(nil, hdcPrivateKey, crypto.Hash(0), []byte(hdcMsg))
285	if err != nil {
286		resp(&w)(false, -1, "sign failed", nil)
287	} else {
288		resp(&w)(true, 0, "success", map[string]interface{}{
289			"signatures": base64.StdEncoding.EncodeToString(signatures),
290		})
291	}
292}
293
294func signatureHdcMsg(w http.ResponseWriter, r *http.Request) {
295	w.Header().Set("Access-Control-Allow-Origin", "*")
296	w.Header().Set("Content-Type", "text/json")
297	hdcMsg := r.URL.Query().Get("message")
298	if len(hdcMsg) == 0 {
299		resp(&w)(false, -1, "Invalid message", nil)
300		return
301	}
302	hashed := sha512.Sum512([]byte(hdcMsg))
303	signatures, err := rsa.SignPKCS1v15(nil, hdcPrivateKey, crypto.SHA512, hashed[:])
304	if err != nil {
305		resp(&w)(false, -1, "sign failed", nil)
306	} else {
307		resp(&w)(true, 0, "success", map[string]interface{}{
308			"signatures": base64.StdEncoding.EncodeToString(signatures),
309		})
310	}
311}
312
313func parseMsgPublishFile() {
314	defer func() {
315		if r := recover(); r != nil {
316			fmt.Printf("parseMsgPublishFile happen panic, content is %+v\n", r)
317		}
318	}()
319	msgPublishData.Mux.Lock()
320	defer msgPublishData.Mux.Unlock()
321	exist, err := PathExists(msgPublishData.FilePath)
322	if err != nil || !exist {
323		return
324	}
325	buf, err := os.ReadFile(msgPublishData.FilePath)
326	if err != nil {
327		fmt.Println("read fail", err)
328		return
329	}
330	msgPublishData.Msg = string(buf)
331}
332
333func getMsgPublish(w http.ResponseWriter, r *http.Request) {
334	w.Header().Set("Access-Control-Allow-Origin", "*")
335	w.Header().Set("Content-Type", "text/json")
336	msgPublishData.Mux.RLock()
337	data := msgPublishData.Msg
338	msgPublishData.Mux.RUnlock()
339	if len(data) == 0 {
340		resp(&w)(false, -1, "msg failed", nil)
341	} else {
342		resp(&w)(true, 0, "success", map[string]interface{}{
343			"data": data,
344		})
345	}
346}
347
348type ServerConfig struct {
349	ServeInfo      string `json:"ServeInfo"`
350	MsgPublishFile string `json:"MsgPublishFile"`
351	AiInfo         string
352}
353
354type MsgPublishData struct {
355	FilePath string
356	Msg      string
357	Mux      sync.RWMutex
358}
359
360func loopUpdateMsgPublishData() {
361	loopTime := 5 * time.Minute
362	timer := time.NewTimer(5 * time.Second)
363	for {
364		select {
365		case <-timer.C:
366			parseMsgPublishFile()
367		}
368		timer.Reset(loopTime)
369	}
370}
371
372func readReqServerConfig() {
373	serverConfigBuffer, err := os.ReadFile(exPath + "/server-config.json")
374	if err != nil {
375		return
376	}
377	var sc ServerConfig
378	err = json.Unmarshal(serverConfigBuffer, &sc)
379	if err != nil {
380		return
381	}
382	serveInfo = sc.ServeInfo
383	aiInfo = sc.AiInfo
384	msgPublishData.Mux.Lock()
385	msgPublishData.FilePath = sc.MsgPublishFile
386	msgPublishData.Mux.Unlock()
387	go loopUpdateMsgPublishData()
388}
389
390func mapToJson(m map[string]interface{}) (string, error) {
391	marshal, err := json.Marshal(m)
392	if err != nil {
393		return "", err
394	}
395	var str = string(marshal)
396	return str, nil
397}
398func jsonToMap(str string) (map[string]interface{}, error) {
399	var m = make(map[string]interface{})
400	err := json.Unmarshal([]byte(str), &m)
401	if err != nil {
402		return nil, err
403	}
404	return m, nil
405}
406
407// MkDir 创建目录
408func MkDir(path string) {
409	dir := path[0:strings.LastIndex(path, string(os.PathSeparator))] //从文件路径获取目录
410	if _, err := os.Stat(dir); err != nil {                          //如果目录不存在,创建目录
411		os.MkdirAll(dir, os.ModePerm)
412	}
413}
414
415func resp(w *http.ResponseWriter) func(bool, int, string, map[string]interface{}) {
416	return func(success bool, code int, msg string, obj map[string]interface{}) {
417		toJson, err := mapToJson(map[string]interface{}{
418			"success": success,
419			"code":    code,
420			"msg":     msg,
421			"data":    obj,
422		})
423		if err != nil {
424			errRes, _ := mapToJson(map[string]interface{}{
425				"success": false,
426				"code":    -1,
427				"msg":     err.Error(),
428			})
429			fmt.Fprintf(*w, errRes)
430		} else {
431			fmt.Fprintf(*w, toJson)
432		}
433	}
434}
435
436func get(url string) (*http.Response, error) {
437	jar, _ := cookiejar.New(nil)
438	c := &http.Client{
439		Transport:     &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}},
440		CheckRedirect: nil,
441		Jar:           jar,
442		Timeout:       time.Duration(3600) * time.Second,
443	}
444	return c.Get(url)
445}
446
447func clearOverdueFile() {
448	MkDir(filepath.FromSlash(fmt.Sprintf("./upload/")))
449	now := time.Now()
450	loc, err := time.LoadLocation("Asia/Shanghai")
451	if err != nil {
452		return
453	}
454	var checkDue = func(fileName string) bool {
455		f := getSuffixByUrl(fileName)
456		parseTime, err := time.ParseInLocation("20060102150405000", f.fileName, loc)
457		if err != nil {
458			return false
459		}
460		sub := now.Sub(parseTime)
461		if sub.Minutes() > 60 { //bigger than 60 min flag due
462			return true
463		}
464		return false
465	}
466	slash := filepath.FromSlash(fmt.Sprintf("./upload/"))
467	filepath.WalkDir(slash, func(path string, d fs.DirEntry, err error) error {
468		if checkDue(d.Name()) {
469			fmt.Println(now, "delete->", path, d.Name(), err)
470			os.Remove(path)
471		}
472		return nil
473	})
474}
475func getSuffixByUrl(u string) struct {
476	fileName string
477	suffix   string
478} {
479	lastIndex := strings.LastIndex(u, "/")
480	var f string
481	if lastIndex != -1 {
482		f = u[lastIndex:]
483	} else {
484		f = u
485	}
486	index := strings.LastIndex(f, ".")
487	if index != -1 {
488		return struct {
489			fileName string
490			suffix   string
491		}{
492			f[0:index],
493			f[index:],
494		}
495	} else {
496		return struct {
497			fileName string
498			suffix   string
499		}{
500			f,
501			"",
502		}
503	}
504}
505
506func downloadHandler(w http.ResponseWriter, r *http.Request) {
507	w.Header().Set("content-type", "text/json")
508	clearOverdueFile()
509	contentType := r.Header["Content-Type"]
510	if len(contentType) > 0 {
511		contentTypeName := contentType[0]
512		if strings.HasPrefix(contentTypeName, "application/x-www-form-urlencoded") {
513			url := r.PostFormValue("url")
514			res, err := get(url)
515			if err != nil {
516				resp(&w)(false, -1, err.Error(), nil)
517				return
518			}
519			pth := filepath.FromSlash(fmt.Sprintf("/upload/%s%s", time.Now().Format("20060102150405000"), getSuffixByUrl(url).suffix))
520			MkDir("." + pth)
521			create, err := os.Create("." + pth)
522			if err != nil {
523				resp(&w)(false, -1, err.Error(), nil)
524				return
525			}
526			written, err := io.Copy(create, res.Body)
527			if err != nil {
528				resp(&w)(false, -1, err.Error(), nil)
529				return
530			}
531			fmt.Println(url, written)
532			resp(&w)(true, 0, "success", map[string]interface{}{
533				"url":  pth,
534				"size": written,
535			})
536			return
537		}
538	}
539	resp(&w)(false, -1, "请求方式错误", nil)
540}
541
542func SplitLines(s string) []string {
543	var lines []string
544	sc := bufio.NewScanner(strings.NewReader(s))
545	for sc.Scan() {
546		lines = append(lines, sc.Text())
547	}
548	return lines
549}
550
551func readFileFirstLine(path string) string {
552	file, err := os.Open(path)
553	if err != nil {
554		return ""
555	}
556	defer file.Close()
557
558	readFile := bufio.NewReader(file)
559	line, readErr := readFile.ReadString('\n')
560	if readErr != nil || io.EOF == err {
561		return ""
562	}
563	return line
564}
565
566func PathExists(path string) (bool, error) {
567	_, err := os.Stat(path)
568	if err == nil {
569		return true, nil
570	}
571	if os.IsNotExist(err) {
572		return false, nil
573	}
574	return false, err
575}
576
577func chekDir(path string) {
578	_, err := os.Stat(path)
579	if err != nil {
580		err := os.Mkdir(path, os.ModePerm)
581		if err != nil {
582			fmt.Printf("mkdir failed![%v]\n", err)
583		} else {
584			fmt.Printf("mkdir success!\n")
585		}
586	}
587}
588func CheckErr(err error) {
589	if err != nil {
590		log.Panicln(err)
591	}
592}
593
594func open(url string) error {
595	if isWindows() {
596		return openUrlWindows(url)
597	} else if isDarwin() {
598		return openUrlDarwin(url)
599	} else {
600		return openUrlOther(url)
601	}
602}
603
604func openUrlWindows(url string) error {
605	cmd := "cmd"
606	args := []string{"/c", "start", url}
607	return exec.Command(cmd, args...).Start()
608}
609func openUrlDarwin(url string) error {
610	var cmd = "open"
611	var args = []string{url}
612	return exec.Command(cmd, args...).Start()
613}
614func openUrlOther(url string) error {
615	var cmd = "xdg-open"
616	var args = []string{url}
617	return exec.Command(cmd, args...).Start()
618}
619
620func isWindows() bool {
621	return runtime.GOOS == "windows"
622}
623func isDarwin() bool {
624	return runtime.GOOS == "darwin"
625}
626
627func getCurrentAbPath() string {
628	dir := getExecutePath()
629	tmpDir, _ := filepath.EvalSymlinks(os.TempDir())
630	if strings.Contains(dir, tmpDir) {
631		return getCallerPath()
632	}
633	return dir
634}
635
636func getCallerPath() string {
637	var pth string
638	_, fName, _, ok := runtime.Caller(0)
639	if ok {
640		pth = path.Dir(fName)
641	}
642	return pth
643}
644func getExecutePath() string {
645	pth, err := os.Executable()
646	if err != nil {
647		log.Fatal(err)
648	}
649	res, _ := filepath.EvalSymlinks(filepath.Dir(pth))
650	return res
651}
652
653func checkPort(port int) {
654	if isWindows() {
655		pid := getPidByPort(port)
656		if pid != -1 {
657			res := exec.Command("cmd", "/c", fmt.Sprintf("taskkill /F /PID %d /T", pid))
658			res.Run()
659		}
660	}
661}
662