Skip to content

Commit b401067

Browse files
committed
update
1 parent 07e3907 commit b401067

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

http.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,19 @@ package com
1616

1717
import (
1818
"bytes"
19+
"context"
1920
"encoding/json"
21+
"errors"
2022
"fmt"
2123
"io"
2224
"io/ioutil"
25+
"log"
2326
"net"
2427
"net/http"
28+
"net/url"
2529
"os"
2630
"path/filepath"
31+
"strings"
2732
"time"
2833
)
2934

@@ -296,3 +301,95 @@ func HTTPClientWithTimeout(timeout time.Duration, options ...HTTPClientOptions)
296301
}
297302
return client
298303
}
304+
305+
// IsNetworkOrHostDown - if there was a network error or if the host is down.
306+
// expectTimeouts indicates that *context* timeouts are expected and does not
307+
// indicate a downed host. Other timeouts still returns down.
308+
func IsNetworkOrHostDown(err error, expectTimeouts bool) bool {
309+
if err == nil {
310+
return false
311+
}
312+
313+
if errors.Is(err, context.Canceled) {
314+
return false
315+
}
316+
317+
if expectTimeouts && errors.Is(err, context.DeadlineExceeded) {
318+
return false
319+
}
320+
321+
if errors.Is(err, context.DeadlineExceeded) {
322+
return true
323+
}
324+
325+
// We need to figure if the error either a timeout
326+
// or a non-temporary error.
327+
urlErr := &url.Error{}
328+
if errors.As(err, &urlErr) {
329+
switch urlErr.Err.(type) {
330+
case *net.DNSError, *net.OpError, net.UnknownNetworkError:
331+
return true
332+
}
333+
}
334+
var e net.Error
335+
if errors.As(err, &e) {
336+
if e.Timeout() {
337+
return true
338+
}
339+
}
340+
341+
// Fallback to other mechanisms.
342+
switch {
343+
case strings.Contains(err.Error(), "Connection closed by foreign host"):
344+
return true
345+
case strings.Contains(err.Error(), "TLS handshake timeout"):
346+
// If error is - tlsHandshakeTimeoutError.
347+
return true
348+
case strings.Contains(err.Error(), "i/o timeout"):
349+
// If error is - tcp timeoutError.
350+
return true
351+
case strings.Contains(err.Error(), "connection timed out"):
352+
// If err is a net.Dial timeout.
353+
return true
354+
case strings.Contains(err.Error(), "connection refused"):
355+
// If err is connection refused
356+
return true
357+
358+
case strings.Contains(strings.ToLower(err.Error()), "503 service unavailable"):
359+
// Denial errors
360+
return true
361+
}
362+
return false
363+
}
364+
365+
func HTTPCanRetry(code int) bool {
366+
return code < 200 || (code > 299 && code < http.StatusInternalServerError)
367+
}
368+
369+
func ParseHTTPRetryAfter(res http.ResponseWriter) time.Duration {
370+
r := res.Header().Get(`Retry-After`)
371+
return ParseRetryAfter(r)
372+
}
373+
374+
func ParseRetryAfter(r string) time.Duration {
375+
if len(r) == 0 {
376+
return 0
377+
}
378+
if StrIsNumeric(r) {
379+
i := Int64(r)
380+
if i <= 0 {
381+
return 0
382+
}
383+
return time.Duration(i) * time.Second
384+
}
385+
t, err := time.Parse(time.RFC1123, r)
386+
if err != nil {
387+
log.Printf(`failed to ParseRetryAfter(%q): %v`, r, err)
388+
return 0
389+
}
390+
//fmt.Printf("%+v", t.String())
391+
if t.Before(time.Now()) {
392+
return 0
393+
}
394+
return time.Until(t)
395+
}

http_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestParseRetryAfter(t *testing.T) {
12+
now := time.Now().UTC()
13+
tim := now.Format(`15:04:05`)
14+
year := now.AddDate(1, 0, 0).Year()
15+
retryAfter := fmt.Sprintf(`Wed, 21 Oct %d %s GMT`, year, tim)
16+
d := ParseRetryAfter(retryAfter)
17+
assert.Equal(t, fmt.Sprintf(`%d-10-21T%sZ`, year, now.Add(-time.Second).Format(`15:04:05`)), now.Add(d).UTC().Format(time.RFC3339))
18+
retryAfter = `300`
19+
d = ParseRetryAfter(retryAfter)
20+
assert.Equal(t, float64(5), d.Minutes())
21+
}

0 commit comments

Comments
 (0)