Home | History | Annotate | Download | only in picasa
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.cooliris.picasa;
     18 
     19 import java.io.ByteArrayOutputStream;
     20 import java.io.IOException;
     21 import java.io.InputStream;
     22 import java.util.zip.GZIPInputStream;
     23 import java.util.zip.GZIPOutputStream;
     24 
     25 import org.apache.http.Header;
     26 import org.apache.http.HttpEntity;
     27 import org.apache.http.HttpResponse;
     28 import org.apache.http.client.methods.HttpGet;
     29 import org.apache.http.client.methods.HttpPost;
     30 import org.apache.http.client.methods.HttpUriRequest;
     31 import org.apache.http.client.params.HttpClientParams;
     32 import org.apache.http.conn.scheme.PlainSocketFactory;
     33 import org.apache.http.conn.scheme.Scheme;
     34 import org.apache.http.conn.scheme.SchemeRegistry;
     35 import org.apache.http.entity.ByteArrayEntity;
     36 import org.apache.http.entity.InputStreamEntity;
     37 import org.apache.http.impl.client.DefaultHttpClient;
     38 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
     39 import org.apache.http.params.BasicHttpParams;
     40 import org.apache.http.params.HttpConnectionParams;
     41 import org.apache.http.params.HttpParams;
     42 import org.apache.http.params.HttpProtocolParams;
     43 
     44 import android.text.TextUtils;
     45 import android.util.Log;
     46 
     47 public final class GDataClient {
     48     private static final String TAG = "GDataClient";
     49     private static final String USER_AGENT = "Cooliris-GData/1.0; gzip";
     50     private static final String X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override";
     51     private static final String IF_MATCH = "If-Match";
     52     private static final int CONNECTION_TIMEOUT = 20000; // ms.
     53     private static final int MIN_GZIP_SIZE = 512;
     54     public static final HttpParams HTTP_PARAMS;
     55     public static final ThreadSafeClientConnManager HTTP_CONNECTION_MANAGER;
     56 
     57     private final DefaultHttpClient mHttpClient = new DefaultHttpClient(HTTP_CONNECTION_MANAGER, HTTP_PARAMS);
     58     private String mAuthToken;
     59 
     60     static {
     61         // Prepare HTTP parameters.
     62         HttpParams params = new BasicHttpParams();
     63         HttpConnectionParams.setStaleCheckingEnabled(params, false);
     64         HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
     65         HttpConnectionParams.setSoTimeout(params, CONNECTION_TIMEOUT);
     66         HttpClientParams.setRedirecting(params, true);
     67         HttpProtocolParams.setUserAgent(params, USER_AGENT);
     68         HTTP_PARAMS = params;
     69 
     70         // Register HTTP protocol.
     71         SchemeRegistry schemeRegistry = new SchemeRegistry();
     72         schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
     73 
     74         // Create the connection manager.
     75         HTTP_CONNECTION_MANAGER = new ThreadSafeClientConnManager(params, schemeRegistry);
     76     }
     77 
     78     public static final class Operation {
     79         public String inOutEtag;
     80         public int outStatus;
     81         public InputStream outBody;
     82     }
     83 
     84     public void setAuthToken(String authToken) {
     85         mAuthToken = authToken;
     86     }
     87 
     88     public void get(String feedUrl, Operation operation) throws IOException {
     89         callMethod(new HttpGet(feedUrl), operation);
     90     }
     91 
     92     public void post(String feedUrl, byte[] data, String contentType, Operation operation) throws IOException {
     93         ByteArrayEntity entity = getCompressedEntity(data);
     94         entity.setContentType(contentType);
     95         HttpPost post = new HttpPost(feedUrl);
     96         post.setEntity(entity);
     97         callMethod(post, operation);
     98     }
     99 
    100     public void put(String feedUrl, byte[] data, String contentType, Operation operation) throws IOException {
    101         ByteArrayEntity entity = getCompressedEntity(data);
    102         entity.setContentType(contentType);
    103         HttpPost post = new HttpPost(feedUrl);
    104         post.setHeader(X_HTTP_METHOD_OVERRIDE, "PUT");
    105         post.setEntity(entity);
    106         callMethod(post, operation);
    107     }
    108 
    109     public void putStream(String feedUrl, InputStream stream, String contentType, Operation operation) throws IOException {
    110         InputStreamEntity entity = new InputStreamEntity(stream, -1);
    111         entity.setContentType(contentType);
    112         HttpPost post = new HttpPost(feedUrl);
    113         post.setHeader(X_HTTP_METHOD_OVERRIDE, "PUT");
    114         post.setEntity(entity);
    115         callMethod(post, operation);
    116     }
    117 
    118     public void delete(String feedUrl, Operation operation) throws IOException {
    119         HttpPost post = new HttpPost(feedUrl);
    120         String etag = operation.inOutEtag;
    121         post.setHeader(X_HTTP_METHOD_OVERRIDE, "DELETE");
    122         post.setHeader(IF_MATCH, etag != null ? etag : "*");
    123         callMethod(post, operation);
    124     }
    125 
    126     private void callMethod(HttpUriRequest request, Operation operation) throws IOException {
    127         // Specify GData protocol version 2.0.
    128         request.addHeader("GData-Version", "2");
    129 
    130         // Indicate support for gzip-compressed responses.
    131         request.addHeader("Accept-Encoding", "gzip");
    132 
    133         // Specify authorization token if provided.
    134         String authToken = mAuthToken;
    135         if (!TextUtils.isEmpty(authToken)) {
    136             request.addHeader("Authorization", "GoogleLogin auth=" + authToken);
    137         }
    138 
    139         // Specify the ETag of a prior response, if available.
    140         String etag = operation.inOutEtag;
    141         if (etag != null) {
    142             request.addHeader("If-None-Match", etag);
    143         }
    144 
    145         // Execute the HTTP request.
    146         HttpResponse httpResponse = null;
    147         try {
    148             httpResponse = mHttpClient.execute(request);
    149         } catch (IOException e) {
    150             Log.w(TAG, "Request failed: " + request.getURI());
    151             throw e;
    152         }
    153 
    154         // Get the status code and response body.
    155         int status = httpResponse.getStatusLine().getStatusCode();
    156         InputStream stream = null;
    157         HttpEntity entity = httpResponse.getEntity();
    158         if (entity != null) {
    159             // Wrap the entity input stream in a GZIP decoder if necessary.
    160             stream = entity.getContent();
    161             if (stream != null) {
    162                 Header header = entity.getContentEncoding();
    163                 if (header != null) {
    164                     if (header.getValue().contains("gzip")) {
    165                         stream = new GZIPInputStream(stream);
    166                     }
    167                 }
    168             }
    169         }
    170 
    171         // Return the stream if successful.
    172         Header etagHeader = httpResponse.getFirstHeader("ETag");
    173         operation.outStatus = status;
    174         operation.inOutEtag = etagHeader != null ? etagHeader.getValue() : null;
    175         operation.outBody = stream;
    176     }
    177 
    178     private ByteArrayEntity getCompressedEntity(byte[] data) throws IOException {
    179         ByteArrayEntity entity;
    180         if (data.length >= MIN_GZIP_SIZE) {
    181             ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(data.length / 2);
    182             GZIPOutputStream gzipOutput = new GZIPOutputStream(byteOutput);
    183             gzipOutput.write(data);
    184             gzipOutput.close();
    185             entity = new ByteArrayEntity(byteOutput.toByteArray());
    186         } else {
    187             entity = new ByteArrayEntity(data);
    188         }
    189         return entity;
    190     }
    191 
    192     public static String inputStreamToString(InputStream stream) {
    193         if (stream != null) {
    194             try {
    195                 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    196                 byte[] buffer = new byte[4096];
    197                 int bytesRead;
    198                 while ((bytesRead = stream.read(buffer)) != -1) {
    199                     bytes.write(buffer, 0, bytesRead);
    200                 }
    201                 return new String(bytes.toString());
    202             } catch (IOException e) {
    203                 // Fall through and return null.
    204             }
    205         }
    206         return null;
    207     }
    208 }
    209