From dda9114692c712c3139b156d968db66bff6ccabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Guillot?= Date: Thu, 8 Feb 2018 18:16:54 -0800 Subject: [PATCH] Improve error handling for HTTP client --- http/client.go | 39 ++++++++++++++++++++++++++++++++-- locale/translations.go | 10 ++++++--- locale/translations/fr_FR.json | 6 +++++- reader/feed/handler.go | 11 +++++++++- reader/subscription/finder.go | 3 +++ 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/http/client.go b/http/client.go index 09da43b4..401d6211 100644 --- a/http/client.go +++ b/http/client.go @@ -7,21 +7,36 @@ package http import ( "bytes" "crypto/tls" + "crypto/x509" "encoding/json" "fmt" "io" + "net" "net/http" "net/url" "strings" "time" + "github.com/miniflux/miniflux/errors" "github.com/miniflux/miniflux/logger" "github.com/miniflux/miniflux/timer" "github.com/miniflux/miniflux/version" ) -const requestTimeout = 300 -const maxBodySize = 1024 * 1024 * 15 +const ( + // 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 :) type Client struct { @@ -77,6 +92,26 @@ func (c *Client) executeRequest(request *http.Request) (*Response, error) { client := c.buildClient() resp, err := client.Do(request) 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 } diff --git a/locale/translations.go b/locale/translations.go index 9f43be75..7bf2039a 100644 --- a/locale/translations.go +++ b/locale/translations.go @@ -1,5 +1,5 @@ // 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 @@ -441,7 +441,11 @@ var translations = map[string]string{ "Miniflux API": "API de Miniflux", "API Endpoint": "Point de terminaison de l'API", "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{ "de_DE": "6528d76d5c78b45db4a137a092953155e81d47a64e91aa2411829e5d946e0e28", "en_US": "6fe95384260941e8a5a3c695a655a932e0a8a6a572c1e45cb2b1ae8baa01b897", - "fr_FR": "e1f5604630f7dfffe81718f3647d6d3689ec3c4d9a2c78ed7641704e2b913b74", + "fr_FR": "ae61f82ac14bc2c6c6a3c2d38cf1ad8309ac2eef19b0726b2969ac155ccddc14", } diff --git a/locale/translations/fr_FR.json b/locale/translations/fr_FR.json index 48e12bbd..ca2ec064 100644 --- a/locale/translations/fr_FR.json +++ b/locale/translations/fr_FR.json @@ -210,5 +210,9 @@ "Miniflux API": "API de Miniflux", "API Endpoint": "Point de terminaison de l'API", "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" } diff --git a/reader/feed/handler.go b/reader/feed/handler.go index cc712564..d6ac84ed 100644 --- a/reader/feed/handler.go +++ b/reader/feed/handler.go @@ -44,6 +44,9 @@ func (h *Handler) CreateFeed(userID, categoryID int64, url string, crawler bool) client := http.NewClient(url) response, err := client.Get() if err != nil { + if _, ok := err.(errors.LocalizedError); ok { + return nil, 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) response, err := client.Get() 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.ParsingErrorMsg = customErr.Error() h.store.UpdateFeed(originalFeed) diff --git a/reader/subscription/finder.go b/reader/subscription/finder.go index 8e361b7e..4e2879a1 100644 --- a/reader/subscription/finder.go +++ b/reader/subscription/finder.go @@ -33,6 +33,9 @@ func FindSubscriptions(websiteURL string) (Subscriptions, error) { client := http.NewClient(websiteURL) response, err := client.Get() if err != nil { + if _, ok := err.(errors.LocalizedError); ok { + return nil, err + } return nil, errors.NewLocalizedError(errConnectionFailure, err) }