Skip to content
Snippets Groups Projects
Commit dda91146 authored by Frédéric Guillot's avatar Frédéric Guillot
Browse files

Improve error handling for HTTP client

parent 16c2dc4a
No related branches found
No related tags found
No related merge requests found
...@@ -7,21 +7,36 @@ package http ...@@ -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) { ...@@ -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
} }
......
// 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{ ...@@ -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{ ...@@ -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",
} }
...@@ -210,5 +210,9 @@ ...@@ -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"
} }
...@@ -44,6 +44,9 @@ func (h *Handler) CreateFeed(userID, categoryID int64, url string, crawler bool) ...@@ -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 { ...@@ -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)
......
...@@ -33,6 +33,9 @@ func FindSubscriptions(websiteURL string) (Subscriptions, error) { ...@@ -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)
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment