Commit c17d640a authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Retry HTTP requests on timeouts or HTTP 50x status codes

parent 42b9140f
......@@ -3,13 +3,14 @@
package test
import (
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/go-errors/errors"
"github.com/privacybydesign/irmago/internal/fs"
"github.com/stretchr/testify/require"
)
......@@ -26,8 +27,10 @@ func checkError(t *testing.T, err error) {
}
var schemeServer *http.Server
var badServer *http.Server
var badServerCount int
func StartSchemeManagerServer() {
func StartSchemeManagerHttpServer() {
path := findTestdataFolder(nil)
schemeServer = &http.Server{Addr: ":48681", Handler: http.FileServer(http.Dir(path))}
go func() {
......@@ -36,10 +39,34 @@ func StartSchemeManagerServer() {
time.Sleep(100 * time.Millisecond) // Give server time to start
}
func StopSchemeManagerServer() {
func StopSchemeManagerHttpServer() {
schemeServer.Close()
}
// StartBadHttpServer starts an HTTP server that times out and returns 500 on the first few times.
func StartBadHttpServer(count int, timeout time.Duration, success string) {
badServer = &http.Server{Addr: ":48682", Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if badServerCount >= count {
fmt.Fprintln(w, success)
return
}
badServerCount++
if badServerCount == 1 {
time.Sleep(timeout)
}
w.WriteHeader(500)
})}
go func() {
badServer.ListenAndServe()
}()
time.Sleep(100 * time.Millisecond) // Give server time to start
}
func StopBadHttpServer() {
badServer.Close()
}
// findTestdataFolder finds the "testdata" folder which is in . or ..
// depending on which package is calling us.
func findTestdataFolder(t *testing.T) string {
......
......@@ -16,14 +16,14 @@ import (
func TestMain(m *testing.M) {
// Create HTTP server for scheme managers
test.StartSchemeManagerServer()
test.StartSchemeManagerHttpServer()
test.ClearTestStorage(nil)
test.CreateTestStorage(nil)
retCode := m.Run()
test.ClearTestStorage(nil)
test.StopSchemeManagerServer()
test.StopSchemeManagerHttpServer()
os.Exit(retCode)
}
......
......@@ -69,8 +69,20 @@ func TestParseInvalidIrmaConfiguration(t *testing.T) {
require.Equal(t, false, conf.SchemeManagers[smerr.Manager].Valid)
}
func TestRetryHTTPRequest(t *testing.T) {
test.StartBadHttpServer(3, 1*time.Second, "42")
transport := NewHTTPTransport("http://localhost:48682")
transport.client.HTTPClient.Timeout = 500 * time.Millisecond
bts, err := transport.GetBytes("")
require.NoError(t, err)
require.Equal(t, "42\n", string(bts))
test.StopBadHttpServer()
}
func TestInvalidIrmaConfigurationRestoreFromRemote(t *testing.T) {
test.StartSchemeManagerServer()
test.StartSchemeManagerHttpServer()
require.NoError(t, fs.EnsureDirectoryExists("testdata/storage/test"))
conf, err := NewConfiguration("testdata/storage/test/irma_configuration", "testdata/irma_configuration_invalid")
......@@ -82,7 +94,7 @@ func TestInvalidIrmaConfigurationRestoreFromRemote(t *testing.T) {
require.Contains(t, conf.SchemeManagers, NewSchemeManagerIdentifier("irma-demo"))
require.Contains(t, conf.CredentialTypes, NewCredentialTypeIdentifier("irma-demo.RU.studentCard"))
test.StopSchemeManagerServer()
test.StopSchemeManagerHttpServer()
test.ClearTestStorage(t)
}
......
......@@ -14,6 +14,8 @@ import (
"time"
"github.com/go-errors/errors"
"github.com/hashicorp/go-retryablehttp"
"github.com/privacybydesign/irmago/internal/disable_sigpipe"
"github.com/privacybydesign/irmago/internal/fs"
)
......@@ -21,7 +23,7 @@ import (
// HTTPTransport sends and receives JSON messages to a HTTP server.
type HTTPTransport struct {
Server string
client *http.Client
client *retryablehttp.Client
headers map[string]string
}
......@@ -48,13 +50,20 @@ func NewHTTPTransport(serverURL string) *HTTPTransport {
return c, nil
}
client := retryablehttp.NewClient()
client.RetryMax = 3
client.RetryWaitMin = 100 * time.Millisecond
client.RetryWaitMax = 500 * time.Millisecond
client.Logger = nil
client.HTTPClient = &http.Client{
Timeout: time.Second * 5,
Transport: &innerTransport,
}
return &HTTPTransport{
Server: url,
headers: map[string]string{},
client: &http.Client{
Timeout: time.Second * 15,
Transport: &innerTransport,
},
client: client,
}
}
......@@ -66,7 +75,8 @@ func (transport *HTTPTransport) SetHeader(name, val string) {
func (transport *HTTPTransport) request(
url string, method string, reader io.Reader, isstr bool,
) (response *http.Response, err error) {
req, err := http.NewRequest(method, transport.Server+url, reader)
var req retryablehttp.Request
req.Request, err = http.NewRequest(method, transport.Server+url, reader)
if err != nil {
return nil, &SessionError{ErrorType: ErrorTransport, Err: err}
}
......@@ -83,7 +93,7 @@ func (transport *HTTPTransport) request(
req.Header.Set(name, val)
}
res, err := transport.client.Do(req)
res, err := transport.client.Do(&req)
if err != nil {
return nil, &SessionError{ErrorType: ErrorTransport, Err: err}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment