Add elementary support for reauth.

Preemptively attempting to reauthenticate before a token expires is
fraught with dangers.  Everything from buggy API implementations to
natural lack of determinism when an API races against a server can cause
opportunities to reauth to be missed.  The recommended solution is to
just detect when a 401 Unauthorized error happens, and attempt to handle
it with a new attempt at reauthentication.

This commit does not implement the full logic to do this just yet.
However, the groundwork seems to be in place, if my unit tests are any
indication.  Here's how I'd _like_ to use it:

Let's focus on ListServers.  We'd end up rewriting this function like
so:

// See the CloudServersProvider interface for details.
func (gcp *genericServersProvider) ListServers() ([]Server, error) {
  var ss []Server

  url := gcp.endpoint + "/servers"
  errOuter := gcp.context.WithReauth(func() error {  // NEW CODE
    errInner := perigee.Get(url, perigee.Options{
      CustomClient: gcp.context.httpClient,
      Results:      &struct{ Servers *[]Server }{&ss},
      MoreHeaders: map[string]string{
        "X-Auth-Token": gcp.access.AuthToken(),
      },
    })
    return errInner
  })                                                // NEW CODE
  return ss, errOuter
}

Note how small the change to the existing code is: two lines of code.

Context.WithReauth() works by invoking the supplied function,
optimistically hoping that it'd succeed (e.g., that error is nil).  If
so, we bee-line the results back to the ListServers implementation,
where it returns its results in ss and a nil value for errOuter.

If it does fail with an error other than a 401 result, ss is undefined,
and the error will propegate back out to errOuter, where again it'll be
returned to the caller.

If the error is a 401 error, however, we invoke the configured
reauthentication handler for the given context.  This code is
responsible for attempting the reauthentication process.  If an error
occurs in this handler, it too will propegate out to errOuter.

If everything succeeds up to this point, however, the function defined
above is called a second time.  Assuming it succeeds, it will overwrite
the desired result (ss in our case) with a valid value, and return a nil
error.  This will, as above, produce the desired outcome and all is
well.

If it fails a second time, however, you're on your own.  Even if the 2nd
failure is another 401 result, that error will propegate out to
errOuter, where it'll be returned to the caller.

Known problems with the code as it stands:

1) Procedure passed to WithReauth() is expected to return an
interface{}, being the desired, unmarshalled object from a web request
against an API.  But, I had forgotten that I could use a free variable
(as I do in the example above) and avoid having to return it explicitly.
I'll need to remove this result slot, saving code and complexity.

2) I *may* need to pass an AccessProvider as part of the WithReauth()
call, so that the reauth handler has something to work with.  Otherwise,
I'll need to define an interface for all xyzProvider entities to recover
the AccessProvider it uses.  I'm not entirely sure if this breaks
encapsulation or not.

Otherwise, the logic is 99.44% finished, and unit tests all pass with
this commit.  Acceptance tests aren't affected, as no production logic
has yet been changed to use this mechanism.
diff --git a/context.go b/context.go
index a946468..86f4ef5 100644
--- a/context.go
+++ b/context.go
@@ -12,6 +12,10 @@
 	AuthEndpoint string
 }
 
+// ReauthHandlerFunc functions are responsible for somehow performing the task of
+// reauthentication.
+type ReauthHandlerFunc func() error
+
 // Context structures encapsulate Gophercloud-global state in a manner which
 // facilitates easier unit testing.  As a user of this SDK, you'll never
 // have to use this structure, except when contributing new code to the SDK.
@@ -21,6 +25,17 @@
 
 	// httpClient refers to the current HTTP client interface to use.
 	httpClient *http.Client
+
+	// reauthHandler provides the functionality needed to re-authenticate
+	// if that feature is enabled.  Note: in order to allow for automatic
+	// re-authentication, the Context object will need to remember your
+	// username, password, and tenant ID as provided in the initial call
+	// to Authenticate().  If you do not desire this, you'll need to handle
+	// reauthentication yourself through other means.  Two methods exist:
+	// the first approach is to just handle errors yourself at the application
+	// layer, and the other is through a custom reauthentication handler
+	// set through the WithReauthHandler() method.
+	reauthHandler ReauthHandlerFunc
 }
 
 // TestContext yields a new Context instance, pre-initialized with a barren
@@ -92,3 +107,15 @@
 
 	return gcp, nil
 }
+
+// WithReauthHandler configures the context to handle reauthentication attempts using the supplied
+// funtion.  By default, reauthentication happens by invoking Authenticate(), which is unlikely to be
+// useful in a unit test.
+//
+// Do not confuse this function with WithReauth()!  Although they work together to support reauthentication,
+// WithReauth() actually contains the decision-making logic to determine when to perform a reauth,
+// while WithReauthHandler() is used to configure what a reauth actually entails.
+func (c *Context) WithReauthHandler(f ReauthHandlerFunc) *Context {
+	c.reauthHandler = f
+	return c
+}
diff --git a/reauth.go b/reauth.go
new file mode 100644
index 0000000..98fba55
--- /dev/null
+++ b/reauth.go
@@ -0,0 +1,23 @@
+package gophercloud
+
+import (
+	"github.com/racker/perigee"
+)
+
+// WithReauth wraps a Perigee request fragment with logic to perform re-authentication
+// if it's deemed necessary.
+//
+// Do not confuse this function with WithReauth()!  Although they work together to support reauthentication,
+// WithReauth() actually contains the decision-making logic to determine when to perform a reauth,
+// while WithReauthHandler() is used to configure what a reauth actually entails.
+func (c *Context) WithReauth(f func() (interface{}, error)) (interface{}, error) {
+	result, err := f()
+	cause, ok := err.(*perigee.UnexpectedResponseCodeError)
+	if ok && cause.Actual == 401 {
+		err = c.reauthHandler()
+		if err == nil {
+			result, err = f()
+		}
+	}
+	return result, err
+}
\ No newline at end of file
diff --git a/reauth_test.go b/reauth_test.go
new file mode 100644
index 0000000..e04968a
--- /dev/null
+++ b/reauth_test.go
@@ -0,0 +1,109 @@
+package gophercloud
+
+import (
+	"testing"
+	"github.com/racker/perigee"
+)
+
+// This reauth-handler does nothing, and returns no error.
+func doNothing() error {
+	return nil
+}
+
+func TestOtherErrorsPropegate(t *testing.T) {
+	calls := 0
+	c := TestContext().WithReauthHandler(doNothing)
+
+	myObj, err := c.WithReauth(func() (interface{}, error) {
+		calls++
+		return nil, &perigee.UnexpectedResponseCodeError{
+			Expected: []int{204},
+			Actual: 404,
+		}
+	})
+
+	if myObj != nil {
+		t.Errorf("Returned nil myObj; got %#v", myObj)
+		return
+	}
+	if err == nil {
+		t.Error("Expected MyError to be returned; got nil instead.")
+		return
+	}
+	if _, ok := err.(*perigee.UnexpectedResponseCodeError); !ok {
+		t.Error("Expected UnexpectedResponseCodeError; got %#v", err)
+		return
+	}
+	if calls != 1 {
+		t.Errorf("Expected the body to be invoked once; found %d calls instead", calls)
+		return
+	}
+}
+
+func Test401ErrorCausesBodyInvokation2ndTime(t *testing.T) {
+	calls := 0
+	c := TestContext().WithReauthHandler(doNothing)
+
+	myObj, err := c.WithReauth(func() (interface{}, error) {
+		calls++
+		return nil, &perigee.UnexpectedResponseCodeError{
+			Expected: []int{204},
+			Actual: 401,
+		}
+	})
+
+	if myObj != nil {
+		t.Errorf("Returned nil myObj; got %#v", myObj)
+		return
+	}
+	if err == nil {
+		t.Error("Expected MyError to be returned; got nil instead.")
+		return
+	}
+	if calls != 2 {
+		t.Errorf("Expected the body to be invoked once; found %d calls instead", calls)
+		return
+	}
+}
+
+func TestReauthAttemptShouldHappen(t *testing.T) {
+	calls := 0
+	c := TestContext().WithReauthHandler(func() error {
+		calls++
+		return nil
+	})
+	c.WithReauth(func() (interface{}, error) {
+		return nil, &perigee.UnexpectedResponseCodeError{
+			Expected: []int{204},
+			Actual: 401,
+		}
+	})
+
+	if calls != 1 {
+		t.Errorf("Expected Reauthenticator to be called once; found %d instead", calls)
+		return
+	}
+}
+
+type MyError struct {}
+func (*MyError) Error() string {
+	return "MyError instance"
+}
+
+func TestReauthErrorShouldPropegate(t *testing.T) {
+	c := TestContext().WithReauthHandler(func() error {
+		return &MyError{}
+	})
+
+	_, err := c.WithReauth(func() (interface{}, error) {
+		return nil, &perigee.UnexpectedResponseCodeError{
+			Expected: []int{204},
+			Actual: 401,
+		}
+	})
+
+	if _, ok := err.(*MyError); !ok {
+		t.Errorf("Expected a MyError; got %#v", err)
+		return
+	}
+}