/* 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.gecko.sync.jpake.stage;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.util.Timer;
import java.util.TimerTask;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.jpake.JPakeClient;
import org.mozilla.gecko.sync.net.BaseResource;
import org.mozilla.gecko.sync.net.Resource;
import org.mozilla.gecko.sync.net.SyncResourceDelegate;
import org.mozilla.gecko.sync.setup.Constants;
import ch.boye.httpclientandroidlib.Header;
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;
import ch.boye.httpclientandroidlib.message.BasicHeader;
public class PutRequestStage extends JPakeStage {
private interface PutRequestStageDelegate {
public void handleSuccess(HttpResponse response);
public void handleFailure(String error);
public void handleError(Exception e);
};
@Override
public void execute(final JPakeClient jClient) {
Logger.debug(LOG_TAG, "Upload message.");
// Create delegate.
final PutRequestStageDelegate callbackDelegate = new PutRequestStageDelegate() {
@Override
public void handleSuccess(HttpResponse response) {
TimerTask runNextStage = new TimerTask() {
@Override
public void run() {
jClient.runNextStage();
}
};
Timer timer = new Timer();
Logger.debug(LOG_TAG, "Pause for 2 * pollInterval before continuing.");
// There's no point in returning early here since the next step will
// always be a GET, so let's pause for twice the poll interval.
timer.schedule(runNextStage, 2 * jClient.jpakePollInterval);
}
@Override
public void handleFailure(String error) {
Logger.error(LOG_TAG, "Got HTTP failure: " + error);
jClient.abort(error);
}
@Override
public void handleError(Exception e) {
Logger.error(LOG_TAG, "HTTP exception.", e);
jClient.abort(Constants.JPAKE_ERROR_NETWORK);
}
};
// Create PUT request.
Resource putRequest;
try {
putRequest = createPutRequest(callbackDelegate, jClient);
} catch (URISyntaxException e) {
Logger.error(LOG_TAG, "URISyntaxException", e);
jClient.abort(Constants.JPAKE_ERROR_CHANNEL);
return;
}
try {
putRequest.put(JPakeClient.jsonEntity(jClient.jOutgoing.object));
} catch (UnsupportedEncodingException e) {
Logger.error(LOG_TAG, "UnsupportedEncodingException", e);
jClient.abort(Constants.JPAKE_ERROR_INTERNAL);
return;
}
Logger.debug(LOG_TAG, "Outgoing message: " + jClient.jOutgoing.toJSONString());
}
private Resource createPutRequest(final PutRequestStageDelegate callbackDelegate, final JPakeClient jpakeClient) throws URISyntaxException {
BaseResource httpResource = new BaseResource(jpakeClient.channelUrl);
httpResource.delegate = new SyncResourceDelegate(httpResource) {
@Override
public void addHeaders(HttpRequestBase request, DefaultHttpClient client) {
request.setHeader(new BasicHeader("X-KeyExchange-Id", jpakeClient.clientId));
if (jpakeClient.theirEtag != null) {
request.setHeader(new BasicHeader("If-Match", jpakeClient.theirEtag));
} else {
request.setHeader(new BasicHeader("If-None-Match", "*"));
}
}
@Override
public void handleHttpResponse(HttpResponse response) {
try {
int statusCode = response.getStatusLine().getStatusCode();
switch (statusCode) {
case 200:
Header etagHeader = response.getFirstHeader("etag");
if (etagHeader == null) {
Logger.error(LOG_TAG, "Server did not supply ETag.");
callbackDelegate.handleFailure(Constants.JPAKE_ERROR_SERVER);
return;
}
jpakeClient.myEtag = etagHeader.getValue();
callbackDelegate.handleSuccess(response);
break;
default:
Logger.error(LOG_TAG, "Could not upload data. Server responded with HTTP " + statusCode);
callbackDelegate.handleFailure(Constants.JPAKE_ERROR_SERVER);
}
} finally {
BaseResource.consumeEntity(response);
}
}
@Override
public void handleHttpProtocolException(ClientProtocolException e) {
callbackDelegate.handleError(e);
}
@Override
public void handleHttpIOException(IOException e) {
callbackDelegate.handleError(e);
}
@Override
public void handleTransportException(GeneralSecurityException e) {
callbackDelegate.handleError(e);
}
@Override
public int connectionTimeout() {
return JPakeClient.REQUEST_TIMEOUT;
}
};
return httpResource;
}
}