Merge pull request #1555 from jclouds/issue-1492-1.6.x

fix issue #1492: invalidate dynect session on ip mismatch (1.6.x)
diff --git a/providers/dynect/src/main/java/org/jclouds/dynect/v3/filters/SessionManager.java b/providers/dynect/src/main/java/org/jclouds/dynect/v3/filters/SessionManager.java
index 07174e5..7cfdb02 100644
--- a/providers/dynect/src/main/java/org/jclouds/dynect/v3/filters/SessionManager.java
+++ b/providers/dynect/src/main/java/org/jclouds/dynect/v3/filters/SessionManager.java
@@ -101,12 +101,15 @@
       return builder.build();
    }
 
+   private static final String IP_MISMATCH = "IP address does not match current session";
+
    @Override
    public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
       boolean retry = false; // default
       try {
-         if (response.getStatusCode() == 401) {
-            closeClientButKeepContentStream(response);
+         byte[] data = closeClientButKeepContentStream(response);
+         String message = data != null ? new String(data) : null;
+         if (response.getStatusCode() == 401 || (message != null && message.indexOf(IP_MISMATCH) != -1)) {
             logger.debug("invalidating session");
             sessionCache.invalidateAll();
             retry = super.shouldRetryRequest(command, response);
diff --git a/providers/dynect/src/test/java/org/jclouds/dynect/v3/filters/SessionManagerTest.java b/providers/dynect/src/test/java/org/jclouds/dynect/v3/filters/SessionManagerTest.java
index 66f25ae..c80401e 100644
--- a/providers/dynect/src/test/java/org/jclouds/dynect/v3/filters/SessionManagerTest.java
+++ b/providers/dynect/src/test/java/org/jclouds/dynect/v3/filters/SessionManagerTest.java
@@ -18,6 +18,8 @@
  */
 package org.jclouds.dynect.v3.filters;
 
+import static com.google.common.io.Resources.getResource;
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
 import static javax.ws.rs.core.Response.Status.FORBIDDEN;
 import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
 import static org.easymock.EasyMock.createMock;
@@ -30,6 +32,8 @@
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
+import java.io.IOException;
+
 import org.jclouds.domain.Credentials;
 import org.jclouds.dynect.v3.domain.Session;
 import org.jclouds.dynect.v3.domain.SessionCredentials;
@@ -97,6 +101,34 @@
 
    @SuppressWarnings("unchecked")
    @Test
+   public void testIPMismatchShouldInvalidateSessionAndRetry() throws IOException {
+      HttpCommand command = createMock(HttpCommand.class);
+      Supplier<Credentials> creds = createMock(Supplier.class);
+      LoadingCache<Credentials, Session> sessionCache = createMock(LoadingCache.class);
+      SessionApi sessionApi = createMock(SessionApi.class);
+
+      sessionCache.invalidateAll();
+      expectLastCall();
+      expect(command.incrementFailureCount()).andReturn(1);
+      expect(command.isReplayable()).andReturn(true);
+      expect(command.getFailureCount()).andReturn(1).atLeastOnce();
+
+      replay(creds, sessionCache, sessionApi, command);
+
+      HttpResponse response = HttpResponse.builder()
+                                          .statusCode(BAD_REQUEST.getStatusCode())
+                                          .payload(getResource("ip_mismatch.json").openStream())
+                                          .build();
+
+      SessionManager retry = new SessionManager(creds, sessionCache, sessionApi);
+
+      assertTrue(retry.shouldRetryRequest(command, response));
+
+      verify(creds, sessionCache, sessionApi, command);
+   }
+
+   @SuppressWarnings("unchecked")
+   @Test
    public void testForbiddenShouldNotInvalidateSessionOrRetry() {
       HttpCommand command = createMock(HttpCommand.class);
       Supplier<Credentials> creds = createMock(Supplier.class);
diff --git a/providers/dynect/src/test/resources/ip_mismatch.json b/providers/dynect/src/test/resources/ip_mismatch.json
new file mode 100644
index 0000000..1fdcb6b
--- /dev/null
+++ b/providers/dynect/src/test/resources/ip_mismatch.json
@@ -0,0 +1,17 @@
+{
+    "status": "failure",
+    "data": {},
+    "job_id": 305900967,
+    "msgs": [{
+            "INFO": "login: IP address does not match current session",
+            "SOURCE": "BLL",
+            "ERR_CD": "INVALID_DATA",
+            "LVL": "ERROR"
+        }, {
+            "INFO": "login: There was a problem with your credentials",
+            "SOURCE": "BLL",
+            "ERR_CD": null,
+            "LVL": "INFO"
+        }
+    ]
+}