162 lines
3.9 KiB
Go
162 lines
3.9 KiB
Go
package server
|
||
|
||
import (
|
||
"fmt"
|
||
"io/ioutil"
|
||
"net/http"
|
||
"os"
|
||
"os/signal"
|
||
"strconv"
|
||
"strings"
|
||
"syscall"
|
||
"time"
|
||
"videoweb/app/appg"
|
||
"videoweb/app/router"
|
||
|
||
// "videoweb/app/service/configser"
|
||
// "videoweb/app/service/translationser"
|
||
"videoweb/pub/common/log"
|
||
"videoweb/pub/common/srvversion"
|
||
|
||
// "videoweb/pub/storage/proxy"
|
||
// "videoweb/web/service"
|
||
|
||
"gopkg.in/yaml.v2"
|
||
)
|
||
|
||
func init() {
|
||
time.Local, _ = time.LoadLocation("Asia/Shanghai")
|
||
}
|
||
func Run(args []string) {
|
||
showVersion()
|
||
if err := initConfig(); err != nil {
|
||
log.Error("init config failed", log.E(err))
|
||
os.Exit(1)
|
||
}
|
||
|
||
initHttpServer()
|
||
initDHServer()
|
||
//tgbot.SendToProgram("启动", "app启动完毕")
|
||
shutdownHandler()
|
||
}
|
||
|
||
func initConfig() error {
|
||
|
||
buf, err := ioutil.ReadFile("conf/app.yaml")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if err = yaml.Unmarshal(buf, &appg.Conf); err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func showVersion() {
|
||
log.Info(fmt.Sprintf("sevice version:%s", srvversion.SERVICE_VERSION))
|
||
log.Info(fmt.Sprintf("git version:%s", srvversion.GIT_VERSION))
|
||
log.Info(fmt.Sprintf("build date:%s", srvversion.BUILD_DATE))
|
||
log.Info(fmt.Sprintf("build ip:%s", srvversion.BUILD_IP))
|
||
}
|
||
|
||
// headWriter 拦截 WriteHeader 和 Write,丢弃 body 并记录长度,
|
||
// 在 flush 时补设 Content-Length 后再发送真正的状态码。
|
||
type headWriter struct {
|
||
http.ResponseWriter
|
||
contentLength int
|
||
statusCode int
|
||
wroteHeader bool
|
||
}
|
||
|
||
func (w *headWriter) WriteHeader(code int) {
|
||
if !w.wroteHeader {
|
||
w.statusCode = code
|
||
w.wroteHeader = true
|
||
}
|
||
}
|
||
|
||
func (w *headWriter) Write(b []byte) (int, error) {
|
||
if !w.wroteHeader {
|
||
w.statusCode = http.StatusOK
|
||
w.wroteHeader = true
|
||
}
|
||
w.contentLength += len(b)
|
||
return len(b), nil
|
||
}
|
||
|
||
// Flush 实现 http.Flusher,HEAD 响应无 body,做 no-op
|
||
func (w *headWriter) Flush() {}
|
||
|
||
// Unwrap 让 http.NewResponseController 能发现底层 writer 的其他接口
|
||
func (w *headWriter) Unwrap() http.ResponseWriter { return w.ResponseWriter }
|
||
|
||
// flush 将缓存的 Content-Length 和状态码写入底层 ResponseWriter
|
||
func (w *headWriter) flush() {
|
||
if !w.wroteHeader {
|
||
w.statusCode = http.StatusOK
|
||
w.wroteHeader = true
|
||
}
|
||
if w.contentLength > 0 && w.Header().Get("Content-Length") == "" {
|
||
w.Header().Set("Content-Length", strconv.Itoa(w.contentLength))
|
||
}
|
||
w.ResponseWriter.WriteHeader(w.statusCode)
|
||
}
|
||
|
||
// headToGet 将 HEAD 改写为 GET 让 Gin 路由匹配,同时用 headWriter 丢弃 body。
|
||
// 通过 X-Origin-Head 内部 header 让 templatecache / filecache 等中间件识别出原始 HEAD 请求,
|
||
// 从而避免一次性 URL 污染缓存造成 DoS 放大(进入前先 Del 防客户端伪造)。
|
||
func headToGet(h http.Handler) http.Handler {
|
||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
r.Header.Del("X-Origin-Head")
|
||
if r.Method == http.MethodHead {
|
||
r.Method = http.MethodGet
|
||
r.Header.Set("X-Origin-Head", "1")
|
||
hw := &headWriter{ResponseWriter: w}
|
||
h.ServeHTTP(hw, r)
|
||
hw.flush()
|
||
return
|
||
}
|
||
h.ServeHTTP(w, r)
|
||
})
|
||
}
|
||
|
||
func initHttpServer() {
|
||
addr := fmt.Sprintf(":%s", appg.Conf.ListenAddr)
|
||
srv := &http.Server{
|
||
Addr: addr,
|
||
Handler: headToGet(router.Init()),
|
||
}
|
||
go func() {
|
||
// service connections
|
||
log.Info("服务已启动", log.Any("地址", addr))
|
||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||
log.Fatal("listen:", log.E(err))
|
||
}
|
||
}()
|
||
}
|
||
|
||
func initDHServer() {
|
||
if strings.TrimSpace(appg.Conf.DHListenAddr) == "" {
|
||
return
|
||
}
|
||
addr := fmt.Sprintf(":%s", appg.Conf.DHListenAddr)
|
||
srv := &http.Server{
|
||
Addr: addr,
|
||
Handler: headToGet(router.DHInit()),
|
||
}
|
||
go func() {
|
||
log.Info("导航服务已启动", log.Any("地址", addr))
|
||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||
log.Fatal("listen:", log.E(err))
|
||
}
|
||
}()
|
||
}
|
||
|
||
func shutdownHandler() {
|
||
quit := make(chan os.Signal, 1)
|
||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, os.Kill)
|
||
<-quit
|
||
log.Warn("Shutdown... server")
|
||
}
|