gensupport: expand retryable errors (#529)

* gensupport: expand retryable errors

This commit improves retry handling by allowing retries
for the following:
* Wrapped errors that are retryable (Go 1.13+ only)
* Transient network errors

Following retry guidance from
https://cloud.google.com/storage/docs/exponential-backoff

Note that this conflicts with #528, but it's an easy conflict
for me to fix once one of them is merged.

Fixes googleapis#449
diff --git a/internal/gensupport/resumable.go b/internal/gensupport/resumable.go
index dd46b79..edc87ec 100644
--- a/internal/gensupport/resumable.go
+++ b/internal/gensupport/resumable.go
@@ -28,6 +28,8 @@
 	backoff       = func() Backoff {
 		return &gax.Backoff{Initial: 100 * time.Millisecond}
 	}
+	// isRetryable is a platform-specific hook, specified in retryable_linux.go
+	syscallRetryable func(error) bool = func(err error) bool { return false }
 )
 
 const (
@@ -226,7 +228,8 @@
 }
 
 // shouldRetry indicates whether an error is retryable for the purposes of this
-// package.
+// package, following guidance from
+// https://cloud.google.com/storage/docs/exponential-backoff .
 func shouldRetry(status int, err error) bool {
 	if 500 <= status && status <= 599 {
 		return true
@@ -237,8 +240,19 @@
 	if err == io.ErrUnexpectedEOF {
 		return true
 	}
+	// Transient network errors should be retried.
+	if syscallRetryable(err) {
+		return true
+	}
 	if err, ok := err.(interface{ Temporary() bool }); ok {
-		return err.Temporary()
+		if err.Temporary() {
+			return true
+		}
+	}
+	// If Go 1.13 error unwrapping is available, use this to examine wrapped
+	// errors.
+	if err, ok := err.(interface{ Unwrap() error }); ok {
+		return shouldRetry(status, err.Unwrap())
 	}
 	return false
 }
diff --git a/internal/gensupport/retryable_linux.go b/internal/gensupport/retryable_linux.go
new file mode 100644
index 0000000..fed998b
--- /dev/null
+++ b/internal/gensupport/retryable_linux.go
@@ -0,0 +1,15 @@
+// Copyright 2020 Google LLC.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+
+package gensupport
+
+import "syscall"
+
+func init() {
+	// Initialize syscallRetryable to return true on transient socket-level
+	// errors. These errors are specific to Linux.
+	syscallRetryable = func(err error) bool { return err == syscall.ECONNRESET || err == syscall.ECONNREFUSED }
+}