weather/internal/web/server.go

125 lines
3.3 KiB
Go

package web
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"go/weather/pkg/logger"
"net"
"net/http"
"os"
"time"
"github.com/gorilla/mux"
"go.uber.org/zap"
)
const (
apiPrefix = "/api"
apiRouterName = "api"
)
//WeatherServer http server for weather application
type WeatherServer struct {
http.Server
router *mux.Router
wlogger *logger.WeatherLogger
subRouters map[string]*mux.Router
}
//New construct new instance
func New(logger *logger.WeatherLogger, addr ListenAddr) *WeatherServer {
return &WeatherServer{
Server: http.Server{
Addr: addr.String(),
BaseContext: func(l net.Listener) context.Context {
customContext := context.Background()
// here configure context properties
return customContext
},
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
},
router: mux.NewRouter(),
wlogger: logger,
subRouters: make(map[string]*mux.Router, 1),
}
}
//WithHandler register weather handler
func (ws *WeatherServer) WithHandler() *WeatherServer {
ws.router.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
rw.Write([]byte("coucou"))
})
api := ws.getOrCreateSubRouterByName(apiRouterName, apiPrefix)
api.HandleFunc("/health", func(rw http.ResponseWriter, r *http.Request) {
json.NewEncoder(rw).Encode(map[string]bool{"ok": true})
})
return ws
}
//WithHTTPLogging add http server request/response logging capability
func (ws *WeatherServer) WithHTTPLogging() *WeatherServer {
ws.Server.Handler = ws.wlogger.HTTPLogHandler(ws.router)
// ws.router.Use(ws.wlogger.HTTPLogHandler)
// ws.router.Use(apmgorilla.Middleware())
api := ws.getOrCreateSubRouterByName(apiRouterName, apiPrefix)
api.HandleFunc("/log", ws.wlogger.Level.ServeHTTP)
return ws
}
//WithErrorLogging add http server error logging capability
func (ws *WeatherServer) WithErrorLogging() *WeatherServer {
stdLogger, err := zap.NewStdLogAt(ws.wlogger.Logger, zap.ErrorLevel)
ws.handleError(err)
ws.Server.ErrorLog = stdLogger
return ws
}
//WithTLSConfigure add cert and key file to server, permit TLS capability
func (ws *WeatherServer) WithTLSConfigure() *WeatherServer {
currentPath, err := os.Getwd()
ws.handleError(err)
certPath := fmt.Sprintf("%s%ccerts%cout%cweather.pem", currentPath, os.PathSeparator, os.PathSeparator, os.PathSeparator)
keyPath := fmt.Sprintf("%s%ccerts%cout%cweather-key.pem", currentPath, os.PathSeparator, os.PathSeparator, os.PathSeparator)
cert, err := tls.LoadX509KeyPair(
certPath,
keyPath,
)
ws.handleError(err)
ws.Server.TLSConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
return ws
}
//Serve start http server
func (ws *WeatherServer) Serve() error {
// Server.Hanlder is nil when we don't use WithHTTPLogging
if ws.Server.Handler == nil {
ws.Server.Handler = ws.router
}
return ws.Server.ListenAndServeTLS("", "")
}
//getOrCreateSubRouterByName return a router by name or create if it doesn't exist yet
func (ws *WeatherServer) getOrCreateSubRouterByName(name, prefix string) *mux.Router {
if api := ws.subRouters[name]; api == nil {
ws.subRouters[name] = ws.router.PathPrefix(prefix).Subrouter()
}
return ws.subRouters[name]
}
//handleError generic fatal error
func (ws *WeatherServer) handleError(err error) {
if err != nil {
ws.wlogger.Fatal("Server error", zap.Error(err))
}
}