mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-06-09 09:58:57 +00:00
225 lines
7.7 KiB
Java
225 lines
7.7 KiB
Java
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
package org.mozilla.goanna.background.fxa.oauth;
|
|
|
|
import java.io.IOException;
|
|
import java.security.GeneralSecurityException;
|
|
import java.util.Locale;
|
|
import java.util.concurrent.Executor;
|
|
|
|
import org.mozilla.goanna.background.fxa.FxAccountClientException;
|
|
import org.mozilla.goanna.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientMalformedResponseException;
|
|
import org.mozilla.goanna.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException;
|
|
import org.mozilla.goanna.fxa.FxAccountConstants;
|
|
import org.mozilla.goanna.sync.ExtendedJSONObject;
|
|
import org.mozilla.goanna.sync.Utils;
|
|
import org.mozilla.goanna.sync.net.AuthHeaderProvider;
|
|
import org.mozilla.goanna.sync.net.BaseResource;
|
|
import org.mozilla.goanna.sync.net.BaseResourceDelegate;
|
|
import org.mozilla.goanna.sync.net.Resource;
|
|
import org.mozilla.goanna.sync.net.SyncResponse;
|
|
import org.mozilla.goanna.sync.net.SyncStorageResponse;
|
|
|
|
import ch.boye.httpclientandroidlib.HttpEntity;
|
|
import ch.boye.httpclientandroidlib.HttpHeaders;
|
|
import ch.boye.httpclientandroidlib.HttpResponse;
|
|
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
|
import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
|
|
import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
|
|
|
|
public abstract class FxAccountAbstractClient {
|
|
protected static final String LOG_TAG = FxAccountAbstractClient.class.getSimpleName();
|
|
|
|
protected static final String ACCEPT_HEADER = "application/json;charset=utf-8";
|
|
protected static final String AUTHORIZATION_RESPONSE_TYPE = "token";
|
|
|
|
public static final String JSON_KEY_ERROR = "error";
|
|
public static final String JSON_KEY_MESSAGE = "message";
|
|
public static final String JSON_KEY_CODE = "code";
|
|
public static final String JSON_KEY_ERRNO = "errno";
|
|
|
|
protected static final String[] requiredErrorStringFields = { JSON_KEY_ERROR, JSON_KEY_MESSAGE };
|
|
protected static final String[] requiredErrorLongFields = { JSON_KEY_CODE, JSON_KEY_ERRNO };
|
|
|
|
/**
|
|
* The server's URI.
|
|
* <p>
|
|
* We assume throughout that this ends with a trailing slash (and guarantee as
|
|
* much in the constructor).
|
|
*/
|
|
protected final String serverURI;
|
|
|
|
protected final Executor executor;
|
|
|
|
public FxAccountAbstractClient(String serverURI, Executor executor) {
|
|
if (serverURI == null) {
|
|
throw new IllegalArgumentException("Must provide a server URI.");
|
|
}
|
|
if (executor == null) {
|
|
throw new IllegalArgumentException("Must provide a non-null executor.");
|
|
}
|
|
this.serverURI = serverURI.endsWith("/") ? serverURI : serverURI + "/";
|
|
if (!this.serverURI.endsWith("/")) {
|
|
throw new IllegalArgumentException("Constructed serverURI must end with a trailing slash: " + this.serverURI);
|
|
}
|
|
this.executor = executor;
|
|
}
|
|
|
|
/**
|
|
* Process a typed value extracted from a successful response (in an
|
|
* endpoint-dependent way).
|
|
*/
|
|
public interface RequestDelegate<T> {
|
|
public void handleError(Exception e);
|
|
public void handleFailure(FxAccountAbstractClientRemoteException e);
|
|
public void handleSuccess(T result);
|
|
}
|
|
|
|
/**
|
|
* Intepret a response from the auth server.
|
|
* <p>
|
|
* Throw an appropriate exception on errors; otherwise, return the response's
|
|
* status code.
|
|
*
|
|
* @return response's HTTP status code.
|
|
* @throws FxAccountClientException
|
|
*/
|
|
public static int validateResponse(HttpResponse response) throws FxAccountAbstractClientRemoteException {
|
|
final int status = response.getStatusLine().getStatusCode();
|
|
if (status == 200) {
|
|
return status;
|
|
}
|
|
int code;
|
|
int errno;
|
|
String error;
|
|
String message;
|
|
ExtendedJSONObject body;
|
|
try {
|
|
body = new SyncStorageResponse(response).jsonObjectBody();
|
|
body.throwIfFieldsMissingOrMisTyped(requiredErrorStringFields, String.class);
|
|
body.throwIfFieldsMissingOrMisTyped(requiredErrorLongFields, Long.class);
|
|
code = body.getLong(JSON_KEY_CODE).intValue();
|
|
errno = body.getLong(JSON_KEY_ERRNO).intValue();
|
|
error = body.getString(JSON_KEY_ERROR);
|
|
message = body.getString(JSON_KEY_MESSAGE);
|
|
} catch (Exception e) {
|
|
throw new FxAccountAbstractClientMalformedResponseException(response);
|
|
}
|
|
throw new FxAccountAbstractClientRemoteException(response, code, errno, error, message, body);
|
|
}
|
|
|
|
protected <T> void invokeHandleError(final RequestDelegate<T> delegate, final Exception e) {
|
|
executor.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
delegate.handleError(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected <T> void post(BaseResource resource, final ExtendedJSONObject requestBody, final RequestDelegate<T> delegate) {
|
|
try {
|
|
if (requestBody == null) {
|
|
resource.post((HttpEntity) null);
|
|
} else {
|
|
resource.post(requestBody);
|
|
}
|
|
} catch (Exception e) {
|
|
invokeHandleError(delegate, e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Translate resource callbacks into request callbacks invoked on the provided
|
|
* executor.
|
|
* <p>
|
|
* Override <code>handleSuccess</code> to parse the body of the resource
|
|
* request and call the request callback. <code>handleSuccess</code> is
|
|
* invoked via the executor, so you don't need to delegate further.
|
|
*/
|
|
protected abstract class ResourceDelegate<T> extends BaseResourceDelegate {
|
|
protected abstract void handleSuccess(final int status, HttpResponse response, final ExtendedJSONObject body);
|
|
|
|
protected final RequestDelegate<T> delegate;
|
|
|
|
/**
|
|
* Create a delegate for an un-authenticated resource.
|
|
*/
|
|
public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate) {
|
|
super(resource);
|
|
this.delegate = delegate;
|
|
}
|
|
|
|
@Override
|
|
public AuthHeaderProvider getAuthHeaderProvider() {
|
|
return super.getAuthHeaderProvider();
|
|
}
|
|
|
|
@Override
|
|
public String getUserAgent() {
|
|
return FxAccountConstants.USER_AGENT;
|
|
}
|
|
|
|
@Override
|
|
public void handleHttpResponse(HttpResponse response) {
|
|
try {
|
|
final int status = validateResponse(response);
|
|
invokeHandleSuccess(status, response);
|
|
} catch (FxAccountAbstractClientRemoteException e) {
|
|
invokeHandleFailure(e);
|
|
}
|
|
}
|
|
|
|
protected void invokeHandleFailure(final FxAccountAbstractClientRemoteException e) {
|
|
executor.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
delegate.handleFailure(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected void invokeHandleSuccess(final int status, final HttpResponse response) {
|
|
executor.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
ExtendedJSONObject body = new SyncResponse(response).jsonObjectBody();
|
|
ResourceDelegate.this.handleSuccess(status, response, body);
|
|
} catch (Exception e) {
|
|
delegate.handleError(e);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void handleHttpProtocolException(final ClientProtocolException e) {
|
|
invokeHandleError(delegate, e);
|
|
}
|
|
|
|
@Override
|
|
public void handleHttpIOException(IOException e) {
|
|
invokeHandleError(delegate, e);
|
|
}
|
|
|
|
@Override
|
|
public void handleTransportException(GeneralSecurityException e) {
|
|
invokeHandleError(delegate, e);
|
|
}
|
|
|
|
@Override
|
|
public void addHeaders(HttpRequestBase request, DefaultHttpClient client) {
|
|
super.addHeaders(request, client);
|
|
|
|
// The basics.
|
|
final Locale locale = Locale.getDefault();
|
|
request.addHeader(HttpHeaders.ACCEPT_LANGUAGE, Utils.getLanguageTag(locale));
|
|
request.addHeader(HttpHeaders.ACCEPT, ACCEPT_HEADER);
|
|
}
|
|
}
|
|
}
|