From 86ab5846ce9bbb18ede40b8ceb4e72c6cabea669 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Mon, 15 Apr 2024 10:20:33 -0300 Subject: [PATCH] feat: retry on 5xx (#37) --- main.go | 15 +++++++++++++++ main_test.go | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 3bbe5dc..a59b423 100644 --- a/main.go +++ b/main.go @@ -61,8 +61,14 @@ func NewProduct(product string) (Product, error) { }, nil } +const maxRetries = 5 + // CheckWithContext verifies a license key against a product in Gumroad. func (gp Product) VerifyWithContext(ctx context.Context, key string) error { + return gp.doVerify(ctx, key, 1) +} + +func (gp Product) doVerify(ctx context.Context, key string, try int) error { // early return if license key is empty if key == "" { return errors.New("license: license key cannot be empty") @@ -86,6 +92,15 @@ func (gp Product) VerifyWithContext(ctx context.Context, key string) error { } defer resp.Body.Close() + // something on server side, should probably retry... + if resp.StatusCode >= 500 { + if try == maxRetries { + return fmt.Errorf("license: likely gumroad issue: %s", string(bts)) + } + time.Sleep(time.Duration(try*500) * time.Millisecond) + return gp.doVerify(ctx, key, try+1) + } + var gumroad GumroadResponse if err := json.Unmarshal(bts, &gumroad); err != nil { return fmt.Errorf("license: failed check license: %w", err) diff --git a/main_test.go b/main_test.go index c113451..7c63502 100644 --- a/main_test.go +++ b/main_test.go @@ -3,6 +3,7 @@ package gumroad import ( "encoding/json" "fmt" + "io" "io/ioutil" "log" "net/http" @@ -14,6 +15,8 @@ import ( "time" ) +const license = "DEADBEEF-CAFE1234-5678DEAD-BEEFCAFE" + func TestIntegrationInvalidLicense(t *testing.T) { t.Parallel() expected := "license: invalid license: That license does not exist for the provided product." @@ -71,6 +74,47 @@ func TestErrors(t *testing.T) { } } +func Test5xx(t *testing.T) { + t.Parallel() + + calls := 0 + + // server will stand in for GumRoad, and assume that any license it sees is invalid + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + calls++ + t.Log("try", calls) + if calls == 3 { + bts, _ := json.Marshal(GumroadResponse{ + Success: true, + Purchase: Purchase{ + SaleTimestamp: time.Now(), + Email: "foo@example.com", + }, + }) + _, _ = w.Write(bts) + return + } + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(`some error`)) + })) + t.Cleanup(server.Close) + + p, err := NewProduct("product") + if err != nil { + t.Errorf("unexpected error %v", err) + } + p.API = server.URL + p.Client = server.Client() + + err = p.Verify(license) + if err != nil { + t.Fatal("expected no error") + } + if calls != 3 { + t.Errorf("should have called the api 3 times, but called %d", calls) + } +} + func TestMITM(t *testing.T) { t.Parallel() license := "DEADBEEF-CAFE1234-5678DEAD-BEEFCAFE" @@ -136,7 +180,7 @@ func TestMITM(t *testing.T) { })) t.Cleanup(mitm.Close) // Throw away the log message from http.Server.go complaining about the invalid TLS cert - mitm.Config.ErrorLog = log.New(ioutil.Discard, "", 0) + mitm.Config.ErrorLog = log.New(io.Discard, "", 0) p.API = mitm.URL // Set the client back to the default, which doesn't trust the test certificate used by mitm