package web import ( "context" "crypto/tls" "fmt" "go/weather/internal" "go/weather/internal/storage" "go/weather/pkg/headers" "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 whandler *WeatherHandler 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 { return context.Background() }, WriteTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second, }, router: mux.NewRouter(), wlogger: logger, version: version, } } //WithHandler register weather handler func (ws *WeatherServer) WithHandler(config *internal.WeatherConfig) *WeatherServer { storage := storage.NewS3Storage(ws.wlogger, &config.S3Storage) ws.whandler = NewWeatherHandler(ws.wlogger, storage, ws.router) // register each subrouter ws.whandler.RegisterBase() ws.whandler.RegisterApi(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.whandler.GetOrCreateSubRouterByName(apiRouterName, apiPrefix) api.HandleFunc("/log", func(rw http.ResponseWriter, r *http.Request) { rw.Header().Add(headers.ContentType, "application/json") ws.wlogger.Level.ServeHTTP(rw, r) }) 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("", "") } //handleError generic fatal error func (ws *WeatherServer) handleError(err error) { if err != nil { ws.wlogger.Fatal("Server error", zap.Error(err)) } }