Bug 969227 - Handle X-Backoff headers. r=nalexander

This commit is contained in:
Richard Newman 2014-02-11 22:10:40 -08:00
Родитель 401af79efe
Коммит 6bc700bf8d
19 изменённых файлов: 109 добавлений и 14 удалений

Просмотреть файл

@ -265,6 +265,10 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
Logger.error(LOG_TAG, "Failed to get token.", e);
callback.handleError(null, e);
}
@Override
public void handleBackoff(int backoffSeconds) {
}
});
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 601 B

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 283 B

После

Ширина:  |  Высота:  |  Размер: 1.6 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 283 B

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.4 KiB

После

Ширина:  |  Высота:  |  Размер: 4.0 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 2.6 KiB

После

Ширина:  |  Высота:  |  Размер: 9.5 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.0 KiB

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 569 B

После

Ширина:  |  Высота:  |  Размер: 3.5 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 213 B

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 213 B

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 901 B

После

Ширина:  |  Высота:  |  Размер: 2.4 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.3 KiB

После

Ширина:  |  Высота:  |  Размер: 4.8 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 421 B

После

Ширина:  |  Высота:  |  Размер: 603 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 206 B

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 213 B

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Просмотреть файл

@ -49,6 +49,20 @@ public class SyncResponse {
return this.getStatusCode() == 200;
}
/**
* Fetch the content type of the HTTP response body.
*
* @return a <code>Header</code> instance, or <code>null</code> if there was
* no body or no valid Content-Type.
*/
public Header getContentType() {
HttpEntity entity = this.response.getEntity();
if (entity == null) {
return null;
}
return entity.getContentType();
}
private String body = null;
public String body() throws IllegalStateException, IOException {
if (body != null) {
@ -145,6 +159,14 @@ public class SyncResponse {
}
}
/**
* @return A number of seconds, or -1 if the 'X-Backoff' header was not
* present.
*/
public int backoffInSeconds() throws NumberFormatException {
return this.getIntegerHeader("x-backoff");
}
/**
* @return A number of seconds, or -1 if the 'X-Weave-Backoff' header was not
* present.
@ -154,15 +176,22 @@ public class SyncResponse {
}
/**
* @return A number of milliseconds, or -1 if neither the 'Retry-After' or
* 'X-Weave-Backoff' header was present.
* Extract a number of seconds, or -1 if none of the specified headers were present.
*
* @param includeRetryAfter
* if <code>true</code>, the Retry-After header is excluded. This is
* useful for processing non-error responses where a Retry-After
* header would be unexpected.
* @return the maximum of the three possible backoff headers, in seconds.
*/
public long totalBackoffInMilliseconds() {
public int totalBackoffInSeconds(boolean includeRetryAfter) {
int retryAfterInSeconds = -1;
if (includeRetryAfter) {
try {
retryAfterInSeconds = retryAfterInSeconds();
} catch (NumberFormatException e) {
}
}
int weaveBackoffInSeconds = -1;
try {
@ -170,7 +199,26 @@ public class SyncResponse {
} catch (NumberFormatException e) {
}
long totalBackoff = (long) Math.max(retryAfterInSeconds, weaveBackoffInSeconds);
int backoffInSeconds = -1;
try {
backoffInSeconds = backoffInSeconds();
} catch (NumberFormatException e) {
}
int totalBackoff = Math.max(retryAfterInSeconds, Math.max(backoffInSeconds, weaveBackoffInSeconds));
if (totalBackoff < 0) {
return -1;
} else {
return totalBackoff;
}
}
/**
* @return A number of milliseconds, or -1 if neither the 'Retry-After',
* 'X-Backoff', or 'X-Weave-Backoff' header were present.
*/
public long totalBackoffInMilliseconds() {
long totalBackoff = totalBackoffInSeconds(true);
if (totalBackoff < 0) {
return -1;
} else {

Просмотреть файл

@ -25,6 +25,7 @@ import org.mozilla.gecko.sync.ThreadPool;
* * Headers:
* * Retry-After
* * X-Weave-Backoff
* * X-Backoff
* * X-Weave-Records?
* * ...
* * Timeouts

Просмотреть файл

@ -29,6 +29,7 @@ import org.mozilla.gecko.tokenserver.TokenServerException.TokenServerMalformedRe
import org.mozilla.gecko.tokenserver.TokenServerException.TokenServerMalformedResponseException;
import org.mozilla.gecko.tokenserver.TokenServerException.TokenServerUnknownServiceException;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpHeaders;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
@ -93,6 +94,28 @@ public class TokenServerClient {
});
}
/**
* Notify the delegate that some kind of backoff header (X-Backoff,
* X-Weave-Backoff, Retry-After) was received and should be acted upon.
*
* This method is non-terminal, and will be followed by a separate
* <code>invoke*</code> call.
*
* @param delegate
* the delegate to inform.
* @param backoffSeconds
* the number of seconds for which the system should wait before
* making another token server request to this server.
*/
protected void notifyBackoff(final TokenServerClientDelegate delegate, final int backoffSeconds) {
executor.execute(new Runnable() {
@Override
public void run() {
delegate.handleBackoff(backoffSeconds);
}
});
}
protected void invokeHandleError(final TokenServerClientDelegate delegate, final Exception e) {
executor.execute(new Runnable() {
@Override
@ -102,17 +125,21 @@ public class TokenServerClient {
});
}
public TokenServerToken processResponse(HttpResponse response)
throws TokenServerException {
SyncResponse res = new SyncResponse(response);
public TokenServerToken processResponse(SyncResponse res) throws TokenServerException {
int statusCode = res.getStatusCode();
Logger.debug(LOG_TAG, "Got token response with status code " + statusCode + ".");
// Responses should *always* be JSON, even in the case of 4xx and 5xx
// errors. If we don't see JSON, the server is likely very unhappy.
String contentType = response.getEntity().getContentType().getValue();
if (!contentType.equals("application/json") && !contentType.startsWith("application/json;")) {
final Header contentType = res.getContentType();
if (contentType == null) {
throw new TokenServerMalformedResponseException(null, "Non-JSON response Content-Type.");
}
final String type = contentType.getValue();
if (!type.equals("application/json") &&
!type.startsWith("application/json;")) {
Logger.warn(LOG_TAG, "Got non-JSON response with Content-Type " +
contentType + ". Misconfigured server?");
throw new TokenServerMalformedResponseException(null, "Non-JSON response Content-Type.");
@ -230,10 +257,21 @@ public class TokenServerClient {
@Override
public void handleHttpResponse(HttpResponse response) {
// Skew.
SkewHandler skewHandler = SkewHandler.getSkewHandlerForResource(resource);
skewHandler.updateSkew(response, System.currentTimeMillis());
// Extract backoff regardless of whether this was an error response, and
// Retry-After for 503 responses. The error will be handled elsewhere.)
SyncResponse res = new SyncResponse(response);
final boolean includeRetryAfter = res.getStatusCode() == 503;
int backoffInSeconds = res.totalBackoffInSeconds(includeRetryAfter);
if (backoffInSeconds > -1) {
client.notifyBackoff(delegate, backoffInSeconds);
}
try {
TokenServerToken token = client.processResponse(response);
TokenServerToken token = client.processResponse(res);
client.invokeHandleSuccess(delegate, token);
} catch (TokenServerException e) {
client.invokeHandleFailure(delegate, e);

Просмотреть файл

@ -4,9 +4,13 @@
package org.mozilla.gecko.tokenserver;
public interface TokenServerClientDelegate {
void handleSuccess(TokenServerToken token);
void handleFailure(TokenServerException e);
void handleError(Exception e);
/**
* Might be called multiple times, in addition to the other terminating handler methods.
*/
void handleBackoff(int backoffSeconds);
}