iris/_examples/project/api/server.go

166 lines
4.1 KiB
Go
Raw Normal View History

package api
import (
"context"
"fmt"
"time"
"github.com/username/project/pkg/http/handlers"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/accesslog"
"github.com/kataras/golog"
)
// Server is a wrapper of the main iris application and our project's custom configuration fields.
type Server struct {
*iris.Application
config Configuration
// Here you can keep an instance of the database too.
// db *mydatabase_pkg.DB
closers []func() // See `AddCloser` method.
}
// NewServer initializes a new HTTP/2 server.
// Use its Run/Listen methods to start it based on network options.
func NewServer(c Configuration) *Server {
app := iris.New().SetName(c.ServerName)
app.Configure(iris.WithConfiguration(c.Iris), iris.WithLowercaseRouting)
srv := &Server{
Application: app,
config: c,
}
if err := srv.prepare(); err != nil {
srv.Logger().Fatal(err)
return nil
}
return srv
}
func (srv *Server) prepare() error {
// Here you can register the database instance
// and prepare any project-relative fields.
if srv.Logger().Level == golog.DebugLevel {
srv.registerDebugFeatures()
}
srv.registerMiddlewares()
srv.buildRouter()
return nil
}
// registers application-level middlewares.
func (srv *Server) registerMiddlewares() {
if srv.config.RequestLog != "" {
srv.registerAccessLogger()
}
srv.UseRouter(handlers.CORS(srv.config.AllowOrigin))
if srv.config.EnableCompression {
srv.Use(iris.Compression)
}
}
func (srv *Server) registerDebugFeatures() {}
func (srv *Server) registerAccessLogger() {
// Initialize a new request access log middleware,
// note that we use unbuffered data so we can have the results as fast as possible,
// this has its cost use it only on debug.
// Also, in the future see the iris example to
// enable log rotation (date eand filesize-based files).
ac := accesslog.FileUnbuffered(srv.config.RequestLog)
// The default configuration:
ac.Delim = '|'
ac.TimeFormat = "2006-01-02 15:04:05"
ac.Async = false
ac.IP = true
ac.BytesReceivedBody = true
ac.BytesSentBody = true
ac.BytesReceived = false
ac.BytesSent = false
ac.BodyMinify = false
ac.RequestBody = true
ac.ResponseBody = false
ac.KeepMultiLineError = true
ac.PanicLog = accesslog.LogHandler
// Default line format if formatter is missing:
// Time|Latency|Code|Method|Path|IP|Path Params Query Fields|Bytes Received|Bytes Sent|Request|Response|
//
// Set Custom Formatter:
ac.SetFormatter(&accesslog.JSON{
Indent: " ",
HumanTime: true,
})
// ac.SetFormatter(&accesslog.CSV{})
// ac.SetFormatter(&accesslog.Template{Text: "{{.Code}}"})
srv.UseRouter(ac.Handler)
}
// Start runs the server on the TCP network address "0.0.0.0:port" which
// handles HTTP/1.1 & 2 requests on incoming connections.
func (srv *Server) Start() error {
if srv.config.Domain != "" {
srv.config.Port = 80 // not required but let's force-modify it.
return srv.Application.Run(iris.AutoTLS(
":443",
srv.config.Domain,
"kataras2006@hotmail.com",
))
}
srv.ConfigureHost(func(su *iris.Supervisor) {
// Set timeouts. More than enough, normally we use 20-30 seconds.
su.Server.ReadTimeout = 5 * time.Minute
su.Server.WriteTimeout = 5 * time.Minute
su.Server.IdleTimeout = 10 * time.Minute
su.Server.ReadHeaderTimeout = 2 * time.Minute
})
addr := fmt.Sprintf("%s:%d", srv.config.Host, srv.config.Port)
return srv.Listen(addr)
}
// AddCloser adds one or more function that should be called on
// manual server shutdown or OS interrupt signals.
func (srv *Server) AddCloser(closers ...func()) {
for _, closer := range closers {
if closer == nil {
continue
}
// Terminate any opened connections on OS interrupt signals.
iris.RegisterOnInterrupt(closer)
}
srv.closers = append(srv.closers, closers...)
}
// Close gracefully terminates the HTTP server and calls the closers afterwards.
func (srv *Server) Close() error {
ctx, cancelCtx := context.WithTimeout(context.Background(), 5*time.Second)
err := srv.Shutdown(ctx)
cancelCtx()
for _, closer := range srv.closers {
if closer == nil {
continue
}
closer()
}
return err
}