From 748e20bf79bcda6b37a17e50d695d48002180b3f Mon Sep 17 00:00:00 2001 From: Emmanuel T Odeke Date: Wed, 20 Mar 2024 10:05:34 -0700 Subject: [PATCH] fix: properly close HTTP responses after sending each email This change extracts per-email sending logic into a helper function for which proper resource closing can be performed. Previously it invoke defer res.Body.Close in a for-loop but Go's specifications and operations dictate that the defer will only be invoked when the surrounding function is returning or panicking: this means then that if very many emails are being sent, that risks potential resource exhaustion because so many connections won't be closed yet nor can they be garbage collected. Fixes #13 --- notify_client.go | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/notify_client.go b/notify_client.go index a30a413..00cf956 100644 --- a/notify_client.go +++ b/notify_client.go @@ -92,28 +92,35 @@ func sendEmail(client *NotifyClient, email *NotifyEmail) error { req.Header.Set("Content-Type", contentType) req.Header.Set("Authorization", fmt.Sprintf("ApiKey-v1 %s", client.ApiKey)) - resp, err := client.Client.Do(req) - - if err != nil { - log.Error().Msgf("Error sending email: %s", err) + if err := doSendEmail(client.Client, req); err != nil { return err } + } + + return nil +} - defer resp.Body.Close() +func doSendEmail(client *http.Client, req *http.Request) error { + resp, err := client.Do(req) + if err != nil { + log.Error().Msgf("Error sending email: %s", err) + return err + } + defer resp.Body.Close() - if resp.StatusCode != 201 { - log.Error().Msgf("Unexpected status code: %d", resp.StatusCode) - respbody, err := io.ReadAll(resp.Body) + if resp.StatusCode == 201 { + return nil + } - if err != nil { - log.Error().Msgf("Error reading response body: %s", err) - return err - } - log.Error().Msgf("Response: %s", respbody) + // A non-201 status code so craft the error. + log.Error().Msgf("Unexpected status code: %d", resp.StatusCode) + respbody, err := io.ReadAll(resp.Body) - return fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } + if err != nil { + log.Error().Msgf("Error reading response body: %s", err) + return err } + log.Error().Msgf("Response: %s", respbody) - return nil + return fmt.Errorf("unexpected status code: %d", resp.StatusCode) }