package net.hockeyapp.android.utils; import android.content.Context; import android.net.Uri; import android.os.Build; import android.text.TextUtils; import net.hockeyapp.android.Constants; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * <h3>Description</h3> * * Builder class for HttpURLConnection. * **/ public class HttpURLConnectionBuilder { private static final int DEFAULT_TIMEOUT = 2 * 60 * 1000; public static final String DEFAULT_CHARSET = "UTF-8"; public static final long FORM_FIELD_LIMIT = 4 * 1024 * 1024; private final String mUrlString; private String mRequestMethod; private String mRequestBody; private SimpleMultipartEntity mMultipartEntity; private int mTimeout = DEFAULT_TIMEOUT; private final Map<String, String> mHeaders; public HttpURLConnectionBuilder(String urlString) { mUrlString = urlString; mHeaders = new HashMap<String, String>(); mHeaders.put("User-Agent", Constants.SDK_USER_AGENT); } public HttpURLConnectionBuilder setRequestMethod(String requestMethod) { mRequestMethod = requestMethod; return this; } public HttpURLConnectionBuilder setRequestBody(String requestBody) { mRequestBody = requestBody; return this; } public HttpURLConnectionBuilder writeFormFields(Map<String, String> fields) { for (String key: fields.keySet()) { String value = fields.get(key); if (value.length() > FORM_FIELD_LIMIT) { throw new IllegalArgumentException("Form field " + key + " size too large: " + value.length() + " - max allowed: " + FORM_FIELD_LIMIT); } } try { String formString = getFormString(fields, DEFAULT_CHARSET); setHeader("Content-Type", "application/x-www-form-urlencoded"); setRequestBody(formString); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } return this; } public HttpURLConnectionBuilder writeMultipartData(Map<String, String> fields, Context context, List<Uri> attachmentUris) { try { mMultipartEntity = new SimpleMultipartEntity(); mMultipartEntity.writeFirstBoundaryIfNeeds(); for (String key : fields.keySet()) { mMultipartEntity.addPart(key, fields.get(key)); } for (int i = 0; i < attachmentUris.size(); i++) { Uri attachmentUri = attachmentUris.get(i); boolean lastFile = (i == attachmentUris.size() - 1); InputStream input = context.getContentResolver().openInputStream(attachmentUri); String filename = attachmentUri.getLastPathSegment(); mMultipartEntity.addPart("attachment" + i, filename, input, lastFile); } mMultipartEntity.writeLastBoundaryIfNeeds(); setHeader("Content-Type", "multipart/form-data; boundary=" + mMultipartEntity.getBoundary()); } catch (IOException e) { throw new RuntimeException(e); } return this; } public HttpURLConnectionBuilder setTimeout(int timeout) { if (timeout < 0) { throw new IllegalArgumentException("Timeout has to be positive."); } mTimeout = timeout; return this; } public HttpURLConnectionBuilder setHeader(String name, String value) { mHeaders.put(name, value); return this; } public HttpURLConnectionBuilder setBasicAuthorization(String username, String password) { String authString = "Basic " + net.hockeyapp.android.utils.Base64.encodeToString( (username + ":" + password).getBytes(), android.util.Base64.NO_WRAP); setHeader("Authorization", authString); return this; } public HttpURLConnection build() throws IOException { HttpURLConnection connection; URL url = new URL(mUrlString); connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(mTimeout); connection.setReadTimeout(mTimeout); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD) { connection.setRequestProperty("Connection", "close"); } if (!TextUtils.isEmpty(mRequestMethod)) { connection.setRequestMethod(mRequestMethod); if (!TextUtils.isEmpty(mRequestBody) || mRequestMethod.equalsIgnoreCase("POST") || mRequestMethod.equalsIgnoreCase("PUT")) { connection.setDoOutput(true); } } for (String name : mHeaders.keySet()) { connection.setRequestProperty(name, mHeaders.get(name)); } if (!TextUtils.isEmpty(mRequestBody)) { OutputStream outputStream = connection.getOutputStream(); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, DEFAULT_CHARSET)); writer.write(mRequestBody); writer.flush(); writer.close(); } if (mMultipartEntity != null) { connection.setRequestProperty("Content-Length", String.valueOf(mMultipartEntity.getContentLength())); BufferedOutputStream outputStream = new BufferedOutputStream(connection.getOutputStream()); outputStream.write(mMultipartEntity.getOutputStream().toByteArray()); outputStream.flush(); outputStream.close(); } return connection; } private static String getFormString(Map<String, String> params, String charset) throws UnsupportedEncodingException { List<String> protoList = new ArrayList<String>(); for (String key : params.keySet()) { String value = params.get(key); key = URLEncoder.encode(key, charset); value = URLEncoder.encode(value, charset); protoList.add(key + "=" + value); } return TextUtils.join("&", protoList); } }