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
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
|
|
}
|
|
|