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.

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

mercurial