package com.example.client;
import android.util.Base64;
import com.example.client.request.Request;
import com.example.client.response.Response;
import org.codehaus.jackson.JsonParseException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.zip.GZIPInputStream;
public class APICall
{
private Request mRequest = null;
private APICallTask mAPICallTask = null;
private Exception mException = null;
private ResponseStatus mResponseStatus = new ResponseStatus();
private HttpURLConnection mConnection = null;
//private HttpsURLConnection mConnection = null; // for SSL
private OutputStream mRequestStream = null;
private InputStream mResponseStream = null;
public APICall(Request request)
{
mRequest = request;
}
public APICall(Request request, APICallTask task)
{
mRequest = request;
mAPICallTask = task;
}
public Request getRequest()
{
return mRequest;
}
public Exception getException()
{
return mException;
}
public ResponseStatus getResponseStatus()
{
return mResponseStatus;
}
public void kill()
{
disconnect();
}
public Response<?> execute()
{
try
{
// disables Keep-Alive for all connections
if(mAPICallTask != null && mAPICallTask.isCancelled()) return null;
System.setProperty("http.keepAlive", "false");
// new connection
byte[] requestData = mRequest.getContent();
URL url = new URL(mRequest.getAddress());
mConnection = (HttpURLConnection) url.openConnection();
//mConnection = (HttpsURLConnection) url.openConnection(); // for SSL
// ssl connection properties
//SelfSignedSSLUtility.setupSSLConnection(mConnection, url); // for SSL using self signed certificate
//CertificateAuthoritySSLUtility.setupSSLConnection(mConnection, url); // for SSL using certificate authority
// connect
setupConnection(requestData);
mConnection.connect();
// send request
if(mAPICallTask != null && mAPICallTask.isCancelled()) return null;
sendRequest(requestData);
// receive response
if(mAPICallTask != null && mAPICallTask.isCancelled()) return null;
mResponseStream = receiveResponse();
// response info
//Logcat.d("connection.getURL() = " + mConnection.getURL());
//Logcat.d("connection.getContentType() = " + mConnection.getContentType());
//Logcat.d("connection.getContentEncoding() = " + mConnection.getContentEncoding());
//Logcat.d("connection.getResponseCode() = " + mConnection.getResponseCode());
//Logcat.d("connection.getResponseMessage() = " + mConnection.getResponseMessage());
// parse response
if(mAPICallTask != null && mAPICallTask.isCancelled()) return null;
Response<?> response = parseResponse();
if(mAPICallTask != null && mAPICallTask.isCancelled()) return null;
return response;
}
catch(UnknownHostException e)
{
mException = e;
e.printStackTrace();
return null;
}
catch(FileNotFoundException e)
{
mException = e;
e.printStackTrace();
return null;
}
catch(SocketException e)
{
mException = e;
e.printStackTrace();
return null;
}
catch(SocketTimeoutException e)
{
mException = e;
e.printStackTrace();
return null;
}
catch(JsonParseException e)
{
mException = e;
e.printStackTrace();
return null;
}
catch(IOException e)
{
mException = e;
e.printStackTrace();
return null;
}
catch(Exception e)
{
mException = e;
e.printStackTrace();
return null;
}
finally
{
disconnect();
}
}
private void disconnect()
{
try
{
if(mRequestStream != null) mRequestStream.close();
}
catch(IOException e) {}
try
{
if(mResponseStream != null) mResponseStream.close();
}
catch(IOException e) {}
try
{
// set status
if(mConnection != null)
{
mResponseStatus.setStatusCode(mConnection.getResponseCode());
mResponseStatus.setStatusMessage(mConnection.getResponseMessage());
mConnection.disconnect();
}
}
catch(Throwable e) {}
mRequestStream = null;
mResponseStream = null;
mConnection = null;
}
private void setupConnection(byte[] requestData) throws ProtocolException
{
if(mRequest.getRequestMethod() != null)
{
mConnection.setRequestMethod(mRequest.getRequestMethod()); // GET, POST, OPTIONS, HEAD, PUT, DELETE, TRACE
}
if(mRequest.getBasicAuthUsername() != null && mRequest.getBasicAuthPassword() != null)
{
mConnection.setRequestProperty("Authorization", getBasicAuthToken(mRequest.getBasicAuthUsername(), mRequest.getBasicAuthPassword()));
}
mConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
//mConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + Request.BOUNDARY); // for multipart
mConnection.setRequestProperty("Accept", "application/json");
mConnection.setRequestProperty("Accept-Encoding", "gzip");
mConnection.setRequestProperty("Accept-Charset", "UTF-8");
//mConnection.setRequestProperty("Content-Length", requestData == null ? "0" : String.valueOf(requestData.length));
//if(requestData!=null) mConnection.setChunkedStreamingMode(0);
if(requestData != null) mConnection.setFixedLengthStreamingMode(requestData.length);
mConnection.setConnectTimeout(30000);
mConnection.setReadTimeout(30000);
if(requestData != null)
{
// this call automatically sets request method to POST on Android 4
// if you don't want your app to POST, you must not call setDoOutput
// http://webdiary.com/2011/12/14/ics-get-post/
mConnection.setDoOutput(true);
}
mConnection.setDoInput(true);
mConnection.setUseCaches(false);
}
private void sendRequest(byte[] requestData) throws IOException
{
if(requestData != null)
{
mRequestStream = new BufferedOutputStream(mConnection.getOutputStream());
mRequestStream.write(requestData);
mRequestStream.flush();
}
}
private InputStream receiveResponse() throws IOException
{
InputStream responseStream;
String encoding = mConnection.getHeaderField("Content-Encoding");
boolean gzipped = encoding != null && encoding.toLowerCase().contains("gzip");
try
{
InputStream inputStream = mConnection.getInputStream();
if(gzipped) responseStream = new BufferedInputStream(new GZIPInputStream(inputStream));
else responseStream = new BufferedInputStream(inputStream);
}
catch(FileNotFoundException e)
{
// error stream
InputStream errorStream = mConnection.getErrorStream();
if(gzipped) responseStream = new BufferedInputStream(new GZIPInputStream(errorStream));
else responseStream = new BufferedInputStream(errorStream);
}
return responseStream;
}
private Response<?> parseResponse() throws IOException
{
Response<?> response = mRequest.parseResponse(mResponseStream);
if(response == null) throw new RuntimeException("Parser returned null response");
return response;
}
private String getBasicAuthToken(String username, String password)
{
// Base64.NO_WRAP because of Android <4 problem
String base64 = Base64.encodeToString((username + ":" + password).getBytes(), Base64.NO_WRAP);
return "Basic " + base64;
}
}