mobile/android/base/sync/net/SyncResponse.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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.sync.net;
     7 import java.io.BufferedReader;
     8 import java.io.IOException;
     9 import java.io.InputStream;
    10 import java.io.InputStreamReader;
    11 import java.io.Reader;
    12 import java.util.Scanner;
    14 import org.json.simple.parser.ParseException;
    15 import org.mozilla.gecko.background.common.log.Logger;
    16 import org.mozilla.gecko.sync.ExtendedJSONObject;
    17 import org.mozilla.gecko.sync.NonObjectJSONException;
    18 import org.mozilla.gecko.sync.Utils;
    20 import ch.boye.httpclientandroidlib.Header;
    21 import ch.boye.httpclientandroidlib.HttpEntity;
    22 import ch.boye.httpclientandroidlib.HttpResponse;
    23 import ch.boye.httpclientandroidlib.impl.cookie.DateParseException;
    24 import ch.boye.httpclientandroidlib.impl.cookie.DateUtils;
    26 public class SyncResponse {
    27   private static final String HEADER_RETRY_AFTER = "retry-after";
    28   private static final String LOG_TAG = "SyncResponse";
    30   protected HttpResponse response;
    32   public SyncResponse() {
    33     super();
    34   }
    36   public SyncResponse(HttpResponse res) {
    37     response = res;
    38   }
    40   public HttpResponse httpResponse() {
    41     return this.response;
    42   }
    44   public int getStatusCode() {
    45     return this.response.getStatusLine().getStatusCode();
    46   }
    48   public boolean wasSuccessful() {
    49     return this.getStatusCode() == 200;
    50   }
    52   /**
    53    * Fetch the content type of the HTTP response body.
    54    *
    55    * @return a <code>Header</code> instance, or <code>null</code> if there was
    56    *         no body or no valid Content-Type.
    57    */
    58   public Header getContentType() {
    59     HttpEntity entity = this.response.getEntity();
    60     if (entity == null) {
    61       return null;
    62     }
    63     return entity.getContentType();
    64   }
    66   private String body = null;
    67   public String body() throws IllegalStateException, IOException {
    68     if (body != null) {
    69       return body;
    70     }
    71     InputStreamReader is = new InputStreamReader(this.response.getEntity().getContent());
    72     // Oh, Java, you are so evil.
    73     body = new Scanner(is).useDelimiter("\\A").next();
    74     return body;
    75   }
    77   /**
    78    * Return the body as a <b>non-null</b> <code>ExtendedJSONObject</code>.
    79    *
    80    * @return A non-null <code>ExtendedJSONObject</code>.
    81    *
    82    * @throws IllegalStateException
    83    * @throws IOException
    84    * @throws ParseException
    85    * @throws NonObjectJSONException
    86    */
    87   public ExtendedJSONObject jsonObjectBody() throws IllegalStateException,
    88                                             IOException, ParseException,
    89                                             NonObjectJSONException {
    90     if (body != null) {
    91       // Do it from the cached String.
    92       return ExtendedJSONObject.parseJSONObject(body);
    93     }
    95     HttpEntity entity = this.response.getEntity();
    96     if (entity == null) {
    97       throw new IOException("no entity");
    98     }
   100     InputStream content = entity.getContent();
   101     try {
   102       Reader in = new BufferedReader(new InputStreamReader(content, "UTF-8"));
   103       return ExtendedJSONObject.parseJSONObject(in);
   104     } finally {
   105       content.close();
   106     }
   107   }
   109   private boolean hasHeader(String h) {
   110     return this.response.containsHeader(h);
   111   }
   113   private static boolean missingHeader(String value) {
   114     return value == null ||
   115            value.trim().length() == 0;
   116   }
   118   private int getIntegerHeader(String h) throws NumberFormatException {
   119     if (this.hasHeader(h)) {
   120       Header header = this.response.getFirstHeader(h);
   121       String value  = header.getValue();
   122       if (missingHeader(value)) {
   123         Logger.warn(LOG_TAG, h + " header present but empty.");
   124         return -1;
   125       }
   126       return Integer.parseInt(value, 10);
   127     }
   128     return -1;
   129   }
   131   /**
   132    * @return A number of seconds, or -1 if the 'Retry-After' header was not present.
   133    */
   134   public int retryAfterInSeconds() throws NumberFormatException {
   135     if (!this.hasHeader(HEADER_RETRY_AFTER)) {
   136       return -1;
   137     }
   139     Header header = this.response.getFirstHeader(HEADER_RETRY_AFTER);
   140     String retryAfter = header.getValue();
   141     if (missingHeader(retryAfter)) {
   142       Logger.warn(LOG_TAG, "Retry-After header present but empty.");
   143       return -1;
   144     }
   146     try {
   147       return Integer.parseInt(retryAfter, 10);
   148     } catch (NumberFormatException e) {
   149       // Fall through to try date format.
   150     }
   152     try {
   153       final long then = DateUtils.parseDate(retryAfter).getTime();
   154       final long now  = System.currentTimeMillis();
   155       return (int)((then - now) / 1000);     // Convert milliseconds to seconds.
   156     } catch (DateParseException e) {
   157       Logger.warn(LOG_TAG, "Retry-After header neither integer nor date: " + retryAfter);
   158       return -1;
   159     }
   160   }
   162   /**
   163    * @return A number of seconds, or -1 if the 'X-Backoff' header was not
   164    *         present.
   165    */
   166   public int backoffInSeconds() throws NumberFormatException {
   167     return this.getIntegerHeader("x-backoff");
   168   }
   170   /**
   171    * @return A number of seconds, or -1 if the 'X-Weave-Backoff' header was not
   172    *         present.
   173    */
   174   public int weaveBackoffInSeconds() throws NumberFormatException {
   175     return this.getIntegerHeader("x-weave-backoff");
   176   }
   178   /**
   179    * Extract a number of seconds, or -1 if none of the specified headers were present.
   180    *
   181    * @param includeRetryAfter
   182    *          if <code>true</code>, the Retry-After header is excluded. This is
   183    *          useful for processing non-error responses where a Retry-After
   184    *          header would be unexpected.
   185    * @return the maximum of the three possible backoff headers, in seconds.
   186    */
   187   public int totalBackoffInSeconds(boolean includeRetryAfter) {
   188     int retryAfterInSeconds = -1;
   189     if (includeRetryAfter) {
   190       try {
   191         retryAfterInSeconds = retryAfterInSeconds();
   192       } catch (NumberFormatException e) {
   193       }
   194     }
   196     int weaveBackoffInSeconds = -1;
   197     try {
   198       weaveBackoffInSeconds = weaveBackoffInSeconds();
   199     } catch (NumberFormatException e) {
   200     }
   202     int backoffInSeconds = -1;
   203     try {
   204       backoffInSeconds = backoffInSeconds();
   205     } catch (NumberFormatException e) {
   206     }
   208     int totalBackoff = Math.max(retryAfterInSeconds, Math.max(backoffInSeconds, weaveBackoffInSeconds));
   209     if (totalBackoff < 0) {
   210       return -1;
   211     } else {
   212       return totalBackoff;
   213     }
   214   }
   216   /**
   217    * @return A number of milliseconds, or -1 if neither the 'Retry-After',
   218    *         'X-Backoff', or 'X-Weave-Backoff' header were present.
   219    */
   220   public long totalBackoffInMilliseconds() {
   221     long totalBackoff = totalBackoffInSeconds(true);
   222     if (totalBackoff < 0) {
   223       return -1;
   224     } else {
   225       return 1000 * totalBackoff;
   226     }
   227   }
   229   /**
   230    * The timestamp returned from a Sync server is a decimal number of seconds,
   231    * e.g., 1323393518.04.
   232    *
   233    * We want milliseconds since epoch.
   234    *
   235    * @return milliseconds since the epoch, as a long, or -1 if the header
   236    *         was missing or invalid.
   237    */
   238   public long normalizedWeaveTimestamp() {
   239     String h = "x-weave-timestamp";
   240     if (!this.hasHeader(h)) {
   241       return -1;
   242     }
   244     return Utils.decimalSecondsToMilliseconds(this.response.getFirstHeader(h).getValue());
   245   }
   247   public int weaveRecords() throws NumberFormatException {
   248     return this.getIntegerHeader("x-weave-records");
   249   }
   251   public int weaveQuotaRemaining() throws NumberFormatException {
   252     return this.getIntegerHeader("x-weave-quota-remaining");
   253   }
   255   public String weaveAlert() {
   256     if (this.hasHeader("x-weave-alert")) {
   257       return this.response.getFirstHeader("x-weave-alert").getValue();
   258     }
   259     return null;
   260   }
   261 }

mercurial