package services import ( "context" "encoding/json" "fmt" "io" "mailsrv/mail" "mailsrv/runtime" "net/http" "time" "github.com/rs/zerolog/log" ) type HTTPServer interface { Serve() Done() <-chan struct{} } type Server struct { ctx context.Context fnCancel context.CancelFunc port string queue *runtime.Queue chDone chan struct{} } func NewServer(ctx context.Context, port string, queue *runtime.Queue) Server { ctxChild, fnCancel := context.WithCancel(ctx) return Server{ ctx: ctxChild, fnCancel: fnCancel, port: port, queue: queue, chDone: make(chan struct{}), } } func (s Server) Done() <-chan struct{} { return s.chDone } func (s *Server) Serve() { mux := http.NewServeMux() mux.HandleFunc("/mail", s.handler) log.Info().Str("port", s.port).Msg("http server is listening...") server := &http.Server{ Addr: fmt.Sprintf(":%s", s.port), Handler: mux, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } go func() { <-s.ctx.Done() if err := server.Shutdown(s.ctx); err != nil { log.Err(err).Msg("bad server shutdown") } s.chDone <- struct{}{} }() go func() { if err := server.ListenAndServe(); err != nil { log.Err(err).Msg("http server stops listening") s.fnCancel() } }() } func (s *Server) handler(w http.ResponseWriter, r *http.Request) { content, err := io.ReadAll(r.Body) if err != nil { log.Err(err).Msg("unable to read request body") w.WriteHeader(http.StatusInternalServerError) return } var email mail.Email if err := json.Unmarshal(content, &email); err != nil { log.Err(err).Msg("unable to deserialized request body into mail") w.WriteHeader(http.StatusInternalServerError) return } if err := email.Validate(); err != nil { log.Err(err).Msg("email validation failed") w.WriteHeader(http.StatusBadRequest) return } s.queue.Add(email) w.WriteHeader(http.StatusOK) }