bump go modules, fix StartTLS email notifications

In #1359, we discovered that StartTLS was not working\
due to the wrong host passed. This bumps the library for the fix.

Also, after a switch to go-pkgz/notify MailGun email sending
broke due to the difference in the destination email parsing,
the fix is also applied after this commit.
This commit is contained in:
Dmitry Verkhoturov
2022-05-20 16:00:30 -05:00
committed by Umputun
parent 9049d7a616
commit 3b5f44da46
23 changed files with 347 additions and 146 deletions
+1
View File
@@ -32,6 +32,7 @@ func (l *limiterWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
default:
httpError := tollbooth.LimitByRequest(l.lmt, w, r)
if httpError != nil {
l.lmt.ExecOnLimitReached(w, r)
w.Header().Add("Content-Type", l.lmt.GetMessageContentType())
w.WriteHeader(httpError.StatusCode)
w.Write([]byte(httpError.Message))
+1 -1
View File
@@ -51,7 +51,7 @@ func parseHeaderList(headerList string) []string {
} else {
h = append(h, b)
}
} else if b == '-' || (b >= '0' && b <= '9') {
} else if b == '-' || b == '_' || b == '.' || (b >= '0' && b <= '9') {
h = append(h, b)
}
+1 -1
View File
@@ -190,7 +190,7 @@ func (em *Sender) client() (c *smtp.Client, err error) {
return nil, fmt.Errorf("timeout connecting to %s: %w", srvAddress, err)
}
c, err = smtp.NewClient(conn, srvAddress)
c, err = smtp.NewClient(conn, em.host)
if err != nil {
return nil, fmt.Errorf("failed to dial: %w", err)
}
+10 -1
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"net/mail"
"net/url"
"strings"
"time"
"github.com/go-pkgz/email"
@@ -99,6 +100,9 @@ func (e *Email) String() string {
if e.TLS {
str += " with TLS"
}
if e.StartTLS {
str += " with StartTLS"
}
return str
}
@@ -120,7 +124,12 @@ func (e *Email) parseDestination(destination string) (email.Params, error) {
}
destinations := []string{}
for _, addr := range addresses {
destinations = append(destinations, addr.String())
stringAddr := addr.String()
// in case of mailgun, correct RFC5322 address with <> yield 501 error, so we need to remove brackets
if strings.HasPrefix(stringAddr, "<") && strings.HasSuffix(stringAddr, ">") {
stringAddr = stringAddr[1 : len(stringAddr)-1]
}
destinations = append(destinations, stringAddr)
}
return email.Params{
+1
View File
@@ -449,6 +449,7 @@ func (t *Telegram) Request(ctx context.Context, method string, b []byte, data in
}
client := http.Client{Timeout: t.Timeout}
defer client.CloseIdleConnections()
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
+6
View File
@@ -119,6 +119,12 @@ Maybe middleware will allow you to change the flow of the middleware stack execu
value of maybeFn(request). This is useful for example if you'd like to skip a middleware handler if
a request does not satisfy the maybeFn logic.
### Benchmarks middleware
Benchmarks middleware allows to measure the time of request handling, number of request per second and report aggregated metrics. This middleware keeps track of the request in the memory and keep up to 900 points (15 minutes, data-point per second).
In order to retrieve the data user should call `Stats(d duration)` method. duration is the time window for which the benchmark data should be returned. It can be any duration from 1s to 15m.
## Helpers
- `rest.Wrap` - converts a list of middlewares to nested handlers calls (in reverse order)
+146
View File
@@ -0,0 +1,146 @@
package rest
import (
"container/list"
"net/http"
"sync"
"time"
)
var maxTimeRange = time.Duration(15) * time.Minute
// Benchmarks is a basic benchmarking middleware collecting and reporting performance metrics
// It keeps track of the requests speeds and counts in 1s benchData buckets ,limiting the number of buckets
// to maxTimeRange. User can request the benchmark for any time duration. This is intended to be used
// for retrieving the benchmark data for the last minute, 5 minutes and up to maxTimeRange.
type Benchmarks struct {
st time.Time
data *list.List
lock sync.RWMutex
nowFn func() time.Time // for testing only
}
type benchData struct {
// 1s aggregates
requests int
respTime time.Duration
minRespTime time.Duration
maxRespTime time.Duration
ts time.Time
}
// BenchmarkStats holds the stats for a given interval
type BenchmarkStats struct {
Requests int `json:"total_requests"`
RequestsSec float64 `json:"total_requests_sec"`
AverageRespTime float64 `json:"average_resp_time"`
MinRespTime float64 `json:"min_resp_time"`
MaxRespTime float64 `json:"max_resp_time"`
}
// NewBenchmarks creates a new benchmark middleware
func NewBenchmarks() *Benchmarks {
res := &Benchmarks{
st: time.Now(),
data: list.New(),
nowFn: time.Now,
}
return res
}
// Handler calculates 1/5/10m request per second and allows to access those values
func (b *Benchmarks) Handler(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
st := b.nowFn()
defer func() {
b.update(time.Since(st))
}()
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func (b *Benchmarks) update(reqDuration time.Duration) {
now := b.nowFn().Truncate(time.Second)
b.lock.Lock()
defer b.lock.Unlock()
// keep maxTimeRange in the list, drop the rest
for e := b.data.Front(); e != nil; e = e.Next() {
if b.data.Front().Value.(benchData).ts.After(b.nowFn().Add(-maxTimeRange)) {
break
}
b.data.Remove(b.data.Front())
}
last := b.data.Back()
if last == nil || last.Value.(benchData).ts.Before(now) {
b.data.PushBack(benchData{requests: 1, respTime: reqDuration, ts: now,
minRespTime: reqDuration, maxRespTime: reqDuration})
return
}
bd := last.Value.(benchData)
bd.requests++
bd.respTime += reqDuration
if bd.minRespTime == 0 || reqDuration < bd.minRespTime {
bd.minRespTime = reqDuration
}
if bd.maxRespTime == 0 || reqDuration > bd.maxRespTime {
bd.maxRespTime = reqDuration
}
last.Value = bd
}
// Stats returns the current benchmark stats for the given duration
func (b *Benchmarks) Stats(interval time.Duration) BenchmarkStats {
if interval < time.Second { // minimum interval is 1s due to the bucket size
return BenchmarkStats{}
}
b.lock.RLock()
defer b.lock.RUnlock()
var (
requests int
respTime time.Duration
)
stInterval, fnInterval := time.Time{}, time.Time{}
var minRespTime, maxRespTime time.Duration
for e := b.data.Back(); e != nil; e = e.Prev() { // reverse order
bd := e.Value.(benchData)
if bd.ts.Before(b.nowFn().Add(-interval)) {
break
}
if minRespTime == 0 || bd.minRespTime < minRespTime {
minRespTime = bd.minRespTime
}
if maxRespTime == 0 || bd.maxRespTime > maxRespTime {
maxRespTime = bd.maxRespTime
}
requests += bd.requests
respTime += bd.respTime
if fnInterval.IsZero() {
fnInterval = bd.ts.Add(time.Second)
}
stInterval = bd.ts
}
if requests == 0 {
return BenchmarkStats{}
}
return BenchmarkStats{
Requests: requests,
RequestsSec: float64(requests) / (fnInterval.Sub(stInterval).Seconds()),
AverageRespTime: respTime.Seconds() / float64(requests),
MinRespTime: minRespTime.Seconds(),
MaxRespTime: maxRespTime.Seconds(),
}
}