go项目脚手架
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
3.9 KiB

package runtime
import (
"fmt"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net"
"net/http"
"sorbet/internal/util"
"sorbet/pkg/env"
"sorbet/pkg/rsp"
"time"
)
var (
// maxHeaderBytes is used by the http server to limit the size of request headers.
// This may need to be increased if accepting cookies from the public.
maxHeaderBytes = 1 << 20
// readTimeout is used by the http server to set a maximum duration before
// timing out read of the request. The default timeout is 10 seconds.
readTimeout = 10 * time.Second
// writeTimeout is used by the http server to set a maximum duration before
// timing out write of the response. The default timeout is 10 seconds.
writeTimeout = 10 * time.Second
// idleTimeout is used by the http server to set a maximum duration for
// keep-alive connections.
idleTimeout = 120 * time.Second
)
func newEchoFramework() (*echo.Echo, error) {
e := echo.New()
e.HideBanner = true
e.HidePort = true
e.HTTPErrorHandler = rsp.HTTPErrorHandler
e.Debug = !env.IsEnv("prod")
e.Logger = util.NewEchoLogger()
// 配置错误捕获
e.Use(Recover)
// 配置跨域
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: env.List("CORS_ALLOW_ORIGINS", []string{"*"}),
AllowMethods: env.List("CORS_ALLOW_METHODS", []string{"GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"}),
AllowCredentials: env.Bool("CORS_ALLOW_CREDENTIALS", false),
AllowHeaders: env.List("CORS_ALLOW_HEADERS", []string{"X-TICKET"}),
ExposeHeaders: env.List("CORS_EXPOSE_HEADERS"),
MaxAge: env.Int("CORS_MAX_AGE", 0),
}))
// 配置日志
e.Use(Logger)
// 配置静态服务
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
Root: env.String("STATIC_ROOT", "web"),
Index: env.String("STATIC_INDEX", "index.html"),
HTML5: env.Bool("STATIC_HTML5", false),
Browse: env.Bool("STATIC_DIRECTORY_BROWSE", false),
}))
for _, servlet := range servlets {
group := e.Group("")
for _, routable := range servlet.Routes() {
routable.InitRoutes(group)
}
}
routes := e.Routes()
e.GET("/_routes", func(c echo.Context) error {
return c.JSON(http.StatusOK, routes)
})
return e, nil
}
func createServer() (*http.Server, net.Listener, error) {
e, err := newEchoFramework()
if err != nil {
return nil, nil, err
}
l, err := createTCPListener()
if err != nil {
return nil, nil, err
}
s := &http.Server{
Handler: e,
MaxHeaderBytes: env.Int("SERVER_MAX_HEADER_BYTES", maxHeaderBytes),
ReadTimeout: env.Duration("SERVER_READ_TIMEOUT", readTimeout),
WriteTimeout: env.Duration("SERVER_WRITE_TIMEOUT", writeTimeout),
IdleTimeout: env.Duration("SERVER_IDLE_TIMEOUT", idleTimeout),
}
return s, l, nil
}
func createTCPListener() (net.Listener, error) {
l, err := net.Listen(
env.String("SERVER_NETWORK", "tcp"),
fmt.Sprintf("%s:%d",
env.String("SERVER_ADDRESS", "0.0.0.0"),
env.Int("SERVER_PORT", 1324),
),
)
if err == nil {
l = net.Listener(TCPKeepAliveListener{
TCPListener: l.(*net.TCPListener),
})
}
return l, err
}
// TCPKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
//
// This is here because it is not exposed in the stdlib and
// we'd prefer to have a hold of the http.Server's net.Listener so we can close it
// on shutdown.
//
// Taken from here: https://golang.org/src/net/http/server.go?s=63121:63175#L2120
type TCPKeepAliveListener struct {
*net.TCPListener
}
// Accept accepts the next incoming call and returns the new
// connection. KeepAlivePeriod is set properly.
func (ln TCPKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
err = tc.SetKeepAlive(true)
if err != nil {
return
}
err = tc.SetKeepAlivePeriod(3 * time.Minute)
if err != nil {
return
}
return tc, nil
}