133 lines
3.5 KiB
Go
133 lines
3.5 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.elastic.co/apm"
|
|
"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
|
|
version string
|
|
}
|
|
|
|
//New construct new instance
|
|
func New(logger *logger.WeatherLogger, addr ListenAddr, version string) *WeatherServer {
|
|
return &WeatherServer{
|
|
Server: http.Server{
|
|
Addr: addr.String(),
|
|
BaseContext: func(l net.Listener) context.Context {
|
|
// here configure context properties
|
|
dt := apm.DefaultTracer
|
|
tx := dt.StartTransactionOptions("http", "request", apm.TransactionOptions{
|
|
Start: time.Now(),
|
|
})
|
|
defer tx.End()
|
|
return apm.ContextWithTransaction(context.Background(), tx)
|
|
},
|
|
WriteTimeout: 15 * time.Second,
|
|
ReadTimeout: 15 * time.Second,
|
|
},
|
|
router: mux.NewRouter(),
|
|
wlogger: logger,
|
|
subRouters: make(map[string]*mux.Router, 1),
|
|
version: version,
|
|
}
|
|
}
|
|
|
|
//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]interface{}{
|
|
"ok": true,
|
|
"version": ws.version,
|
|
})
|
|
})
|
|
|
|
return ws
|
|
}
|
|
|
|
//WithHTTPLogging add http server request/response logging capability
|
|
func (ws *WeatherServer) WithHTTPLogging() *WeatherServer {
|
|
ws.Server.Handler = ws.wlogger.HTTPLogHandler(ws.router)
|
|
|
|
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))
|
|
}
|
|
}
|