mobile/android/base/background/announcements/AnnouncementsFetchResourceDelegate.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.background.announcements;
michael@0 6
michael@0 7 import java.io.IOException;
michael@0 8 import java.security.GeneralSecurityException;
michael@0 9 import java.util.ArrayList;
michael@0 10 import java.util.Date;
michael@0 11 import java.util.List;
michael@0 12
michael@0 13 import org.json.simple.JSONArray;
michael@0 14 import org.json.simple.JSONObject;
michael@0 15 import org.mozilla.gecko.background.common.log.Logger;
michael@0 16 import org.mozilla.gecko.sync.ExtendedJSONObject;
michael@0 17 import org.mozilla.gecko.sync.NonArrayJSONException;
michael@0 18 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
michael@0 19 import org.mozilla.gecko.sync.net.BaseResource;
michael@0 20 import org.mozilla.gecko.sync.net.BaseResourceDelegate;
michael@0 21 import org.mozilla.gecko.sync.net.Resource;
michael@0 22 import org.mozilla.gecko.sync.net.SyncResponse;
michael@0 23
michael@0 24 import ch.boye.httpclientandroidlib.Header;
michael@0 25 import ch.boye.httpclientandroidlib.HttpResponse;
michael@0 26 import ch.boye.httpclientandroidlib.client.ClientProtocolException;
michael@0 27 import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
michael@0 28 import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
michael@0 29 import ch.boye.httpclientandroidlib.impl.cookie.DateUtils;
michael@0 30 import ch.boye.httpclientandroidlib.protocol.HTTP;
michael@0 31
michael@0 32 /**
michael@0 33 * Converts HTTP resource callbacks into AnnouncementsFetchDelegate callbacks.
michael@0 34 */
michael@0 35 public class AnnouncementsFetchResourceDelegate extends BaseResourceDelegate {
michael@0 36 private static final String ACCEPT_HEADER = "application/json;charset=utf-8";
michael@0 37
michael@0 38 private static final String LOG_TAG = "AnnounceFetchRD";
michael@0 39
michael@0 40 protected final long startTime;
michael@0 41 protected AnnouncementsFetchDelegate delegate;
michael@0 42
michael@0 43 public AnnouncementsFetchResourceDelegate(Resource resource, AnnouncementsFetchDelegate delegate) {
michael@0 44 super(resource);
michael@0 45 this.startTime = System.currentTimeMillis();
michael@0 46 this.delegate = delegate;
michael@0 47 }
michael@0 48
michael@0 49 @Override
michael@0 50 public String getUserAgent() {
michael@0 51 return delegate.getUserAgent();
michael@0 52 }
michael@0 53
michael@0 54 @Override
michael@0 55 public void addHeaders(HttpRequestBase request, DefaultHttpClient client) {
michael@0 56 super.addHeaders(request, client);
michael@0 57
michael@0 58 // The basics.
michael@0 59 request.addHeader("Accept-Language", delegate.getLocale().toString());
michael@0 60 request.addHeader("Accept", ACCEPT_HEADER);
michael@0 61
michael@0 62 // We never want to keep connections alive.
michael@0 63 request.addHeader("Connection", "close");
michael@0 64
michael@0 65 // Set If-Modified-Since to avoid re-fetching content.
michael@0 66 final String ifModifiedSince = delegate.getLastDate();
michael@0 67 if (ifModifiedSince != null) {
michael@0 68 Logger.info(LOG_TAG, "If-Modified-Since: " + ifModifiedSince);
michael@0 69 request.addHeader("If-Modified-Since", ifModifiedSince);
michael@0 70 }
michael@0 71
michael@0 72 // Just in case.
michael@0 73 request.removeHeaders("Cookie");
michael@0 74 }
michael@0 75
michael@0 76 private List<Announcement> parseBody(ExtendedJSONObject body) throws NonArrayJSONException {
michael@0 77 List<Announcement> out = new ArrayList<Announcement>(1);
michael@0 78 JSONArray snippets = body.getArray("announcements");
michael@0 79 if (snippets == null) {
michael@0 80 Logger.warn(LOG_TAG, "Missing announcements body. Returning empty.");
michael@0 81 return out;
michael@0 82 }
michael@0 83
michael@0 84 for (Object s : snippets) {
michael@0 85 try {
michael@0 86 out.add(Announcement.parseAnnouncement(new ExtendedJSONObject((JSONObject) s)));
michael@0 87 } catch (Exception e) {
michael@0 88 Logger.warn(LOG_TAG, "Malformed announcement or display failed. Skipping.", e);
michael@0 89 }
michael@0 90 }
michael@0 91 return out;
michael@0 92 }
michael@0 93
michael@0 94 @Override
michael@0 95 public void handleHttpResponse(HttpResponse response) {
michael@0 96 final Header dateHeader = response.getFirstHeader(HTTP.DATE_HEADER);
michael@0 97 String date = null;
michael@0 98 if (dateHeader != null) {
michael@0 99 // Note that we are deliberately not validating the server time here.
michael@0 100 // We pass it directly back to the server; we don't care about the
michael@0 101 // contents, and if we reject a value we essentially re-initialize
michael@0 102 // the client, which will cause stale announcements to be re-fetched.
michael@0 103 date = dateHeader.getValue();
michael@0 104 }
michael@0 105 if (date == null) {
michael@0 106 // Use local clock, because skipping is better than re-fetching.
michael@0 107 date = DateUtils.formatDate(new Date());
michael@0 108 Logger.warn(LOG_TAG, "No fetch date; using local time " + date);
michael@0 109 }
michael@0 110
michael@0 111 final SyncResponse r = new SyncResponse(response); // For convenience.
michael@0 112 try {
michael@0 113 final int statusCode = r.getStatusCode();
michael@0 114 Logger.debug(LOG_TAG, "Got announcements response: " + statusCode);
michael@0 115
michael@0 116 if (statusCode == 204 || statusCode == 304) {
michael@0 117 BaseResource.consumeEntity(response);
michael@0 118 delegate.onNoNewAnnouncements(startTime, date);
michael@0 119 return;
michael@0 120 }
michael@0 121
michael@0 122 if (statusCode == 200) {
michael@0 123 final List<Announcement> snippets;
michael@0 124 try {
michael@0 125 snippets = parseBody(r.jsonObjectBody());
michael@0 126 } catch (Exception e) {
michael@0 127 delegate.onRemoteError(e);
michael@0 128 return;
michael@0 129 }
michael@0 130 delegate.onNewAnnouncements(snippets, startTime, date);
michael@0 131 return;
michael@0 132 }
michael@0 133
michael@0 134 if (statusCode == 400 || statusCode == 405) {
michael@0 135 // We did something wrong.
michael@0 136 Logger.warn(LOG_TAG, "We did something wrong. Oh dear.");
michael@0 137 // Fall through.
michael@0 138 }
michael@0 139
michael@0 140 if (statusCode == 503 || statusCode == 500) {
michael@0 141 Logger.warn(LOG_TAG, "Server issue: " + r.body());
michael@0 142 delegate.onBackoff(r.retryAfterInSeconds());
michael@0 143 return;
michael@0 144 }
michael@0 145
michael@0 146 // Otherwise, clean up.
michael@0 147 delegate.onRemoteFailure(statusCode);
michael@0 148
michael@0 149 } catch (Exception e) {
michael@0 150 Logger.warn(LOG_TAG, "Failed to extract body.", e);
michael@0 151 delegate.onRemoteError(e);
michael@0 152 }
michael@0 153 }
michael@0 154
michael@0 155 @Override
michael@0 156 public void handleHttpProtocolException(ClientProtocolException e) {
michael@0 157 Logger.warn(LOG_TAG, "Protocol exception.", e);
michael@0 158 delegate.onLocalError(e);
michael@0 159 }
michael@0 160
michael@0 161 @Override
michael@0 162 public void handleHttpIOException(IOException e) {
michael@0 163 Logger.warn(LOG_TAG, "IO exception.", e);
michael@0 164 delegate.onLocalError(e);
michael@0 165 }
michael@0 166
michael@0 167 @Override
michael@0 168 public void handleTransportException(GeneralSecurityException e) {
michael@0 169 Logger.warn(LOG_TAG, "Transport exception.", e);
michael@0 170 // Class this as a remote error, because it's probably something odd
michael@0 171 // with SSL negotiation.
michael@0 172 delegate.onRemoteError(e);
michael@0 173 }
michael@0 174
michael@0 175 /**
michael@0 176 * Be very thorough in case the superclass implementation changes.
michael@0 177 * We never want this to be an authenticated request.
michael@0 178 */
michael@0 179 @Override
michael@0 180 public AuthHeaderProvider getAuthHeaderProvider() {
michael@0 181 return null;
michael@0 182 }
michael@0 183 }

mercurial