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