Improve error handling for HTTP client

This commit is contained in:
Frédéric Guillot 2018-02-08 18:16:54 -08:00
parent 16c2dc4a8c
commit dda9114692
5 changed files with 62 additions and 7 deletions

View file

@ -7,21 +7,36 @@ package http
import ( import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"crypto/x509"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"time" "time"
"github.com/miniflux/miniflux/errors"
"github.com/miniflux/miniflux/logger" "github.com/miniflux/miniflux/logger"
"github.com/miniflux/miniflux/timer" "github.com/miniflux/miniflux/timer"
"github.com/miniflux/miniflux/version" "github.com/miniflux/miniflux/version"
) )
const requestTimeout = 300 const (
const maxBodySize = 1024 * 1024 * 15 // 20 seconds max.
requestTimeout = 20
// 15MB max.
maxBodySize = 1024 * 1024 * 15
)
var (
errInvalidCertificate = "Invalid SSL certificate (original error: %q)"
errTemporaryNetworkOperation = "This website is temporarily unreachable (original error: %q)"
errPermanentNetworkOperation = "This website is permanently unreachable (original error: %q)"
errRequestTimeout = "Website unreachable, the request timed out after %d seconds"
)
// Client is a HTTP Client :) // Client is a HTTP Client :)
type Client struct { type Client struct {
@ -77,6 +92,26 @@ func (c *Client) executeRequest(request *http.Request) (*Response, error) {
client := c.buildClient() client := c.buildClient()
resp, err := client.Do(request) resp, err := client.Do(request)
if err != nil { if err != nil {
if uerr, ok := err.(*url.Error); ok {
switch uerr.Err.(type) {
case x509.CertificateInvalidError, x509.HostnameError:
err = errors.NewLocalizedError(errInvalidCertificate, uerr.Err)
case *net.OpError:
if uerr.Err.(*net.OpError).Temporary() {
err = errors.NewLocalizedError(errTemporaryNetworkOperation, uerr.Err)
} else {
err = errors.NewLocalizedError(errPermanentNetworkOperation, uerr.Err)
}
case net.Error:
nerr := uerr.Err.(net.Error)
if nerr.Timeout() {
err = errors.NewLocalizedError(errRequestTimeout, requestTimeout)
} else if nerr.Temporary() {
err = errors.NewLocalizedError(errTemporaryNetworkOperation, nerr)
}
}
}
return nil, err return nil, err
} }

View file

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// 2018-02-08 08:24:29.272801 +0100 CET m=+0.026701596 // 2018-02-08 18:11:55.663532104 -0800 PST m=+0.009491930
package locale package locale
@ -441,7 +441,11 @@ var translations = map[string]string{
"Miniflux API": "API de Miniflux", "Miniflux API": "API de Miniflux",
"API Endpoint": "Point de terminaison de l'API", "API Endpoint": "Point de terminaison de l'API",
"Your account password": "Le mot de passe de votre compte", "Your account password": "Le mot de passe de votre compte",
"This web page is empty": "Cette page web est vide" "This web page is empty": "Cette page web est vide",
"Invalid SSL certificate (original error: %q)": "Certificat SSL invalide (erreur originale : %q)",
"This website is temporarily unreachable (original error: %q)": "Ce site web est temporairement injoignable (erreur originale : %q)",
"This website is permanently unreachable (original error: %q)": "Ce site web n'est pas joignable de façon permanente (erreur originale : %q)",
"Website unreachable, the request timed out after %d seconds": "Site web injoignable, la requête à échouée après %d secondes"
} }
`, `,
} }
@ -449,5 +453,5 @@ var translations = map[string]string{
var translationsChecksums = map[string]string{ var translationsChecksums = map[string]string{
"de_DE": "6528d76d5c78b45db4a137a092953155e81d47a64e91aa2411829e5d946e0e28", "de_DE": "6528d76d5c78b45db4a137a092953155e81d47a64e91aa2411829e5d946e0e28",
"en_US": "6fe95384260941e8a5a3c695a655a932e0a8a6a572c1e45cb2b1ae8baa01b897", "en_US": "6fe95384260941e8a5a3c695a655a932e0a8a6a572c1e45cb2b1ae8baa01b897",
"fr_FR": "e1f5604630f7dfffe81718f3647d6d3689ec3c4d9a2c78ed7641704e2b913b74", "fr_FR": "ae61f82ac14bc2c6c6a3c2d38cf1ad8309ac2eef19b0726b2969ac155ccddc14",
} }

View file

@ -210,5 +210,9 @@
"Miniflux API": "API de Miniflux", "Miniflux API": "API de Miniflux",
"API Endpoint": "Point de terminaison de l'API", "API Endpoint": "Point de terminaison de l'API",
"Your account password": "Le mot de passe de votre compte", "Your account password": "Le mot de passe de votre compte",
"This web page is empty": "Cette page web est vide" "This web page is empty": "Cette page web est vide",
"Invalid SSL certificate (original error: %q)": "Certificat SSL invalide (erreur originale : %q)",
"This website is temporarily unreachable (original error: %q)": "Ce site web est temporairement injoignable (erreur originale : %q)",
"This website is permanently unreachable (original error: %q)": "Ce site web n'est pas joignable de façon permanente (erreur originale : %q)",
"Website unreachable, the request timed out after %d seconds": "Site web injoignable, la requête à échouée après %d secondes"
} }

View file

@ -44,6 +44,9 @@ func (h *Handler) CreateFeed(userID, categoryID int64, url string, crawler bool)
client := http.NewClient(url) client := http.NewClient(url)
response, err := client.Get() response, err := client.Get()
if err != nil { if err != nil {
if _, ok := err.(errors.LocalizedError); ok {
return nil, err
}
return nil, errors.NewLocalizedError(errRequestFailed, err) return nil, errors.NewLocalizedError(errRequestFailed, err)
} }
@ -120,7 +123,13 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
client := http.NewClientWithCacheHeaders(originalFeed.FeedURL, originalFeed.EtagHeader, originalFeed.LastModifiedHeader) client := http.NewClientWithCacheHeaders(originalFeed.FeedURL, originalFeed.EtagHeader, originalFeed.LastModifiedHeader)
response, err := client.Get() response, err := client.Get()
if err != nil { if err != nil {
customErr := errors.NewLocalizedError(errRequestFailed, err) var customErr errors.LocalizedError
if lerr, ok := err.(errors.LocalizedError); ok {
customErr = lerr
} else {
customErr = errors.NewLocalizedError(errRequestFailed, err)
}
originalFeed.ParsingErrorCount++ originalFeed.ParsingErrorCount++
originalFeed.ParsingErrorMsg = customErr.Error() originalFeed.ParsingErrorMsg = customErr.Error()
h.store.UpdateFeed(originalFeed) h.store.UpdateFeed(originalFeed)

View file

@ -33,6 +33,9 @@ func FindSubscriptions(websiteURL string) (Subscriptions, error) {
client := http.NewClient(websiteURL) client := http.NewClient(websiteURL)
response, err := client.Get() response, err := client.Get()
if err != nil { if err != nil {
if _, ok := err.(errors.LocalizedError); ok {
return nil, err
}
return nil, errors.NewLocalizedError(errConnectionFailure, err) return nil, errors.NewLocalizedError(errConnectionFailure, err)
} }