{"action":"created","comment":{"url":"https://api.github.com/repos/zulip/zulip-android/pulls/comments/67940693","id":67940693,"diff_hunk":"@@ -1,141 +1,115 @@\n package com.zulip.android.networking;\n \n-import java.io.ByteArrayOutputStream;\n-import java.io.IOException;\n-import java.util.ArrayList;\n-import java.util.List;\n-\n-import org.apache.http.HttpResponse;\n-import org.apache.http.HttpStatus;\n-import org.apache.http.NameValuePair;\n-import org.apache.http.StatusLine;\n-import org.apache.http.client.HttpResponseException;\n-import org.apache.http.client.entity.UrlEncodedFormEntity;\n-import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;\n-import org.apache.http.client.methods.HttpGet;\n-import org.apache.http.client.methods.HttpPost;\n-import org.apache.http.client.methods.HttpPut;\n-import org.apache.http.client.methods.HttpRequestBase;\n-import org.apache.http.client.utils.URLEncodedUtils;\n-import org.apache.http.message.BasicNameValuePair;\n-import org.apache.http.params.HttpConnectionParams;\n-\n-import android.net.http.AndroidHttpClient;\n import android.os.AsyncTask;\n import android.util.Base64;\n import android.util.Log;\n \n+import com.zulip.android.R;\n import com.zulip.android.ZulipApp;\n+import com.zulip.android.util.ZLog;\n+\n+import java.io.IOException;\n+import java.io.UnsupportedEncodingException;\n+import java.net.URLEncoder;\n+import java.util.HashMap;\n+import java.util.Map;\n+import java.util.concurrent.TimeUnit;\n+\n+import okhttp3.FormBody;\n+import okhttp3.OkHttpClient;\n+import okhttp3.Request;\n+import okhttp3.Response;\n \n /**\n  * Simplified HTTP request API\n  */\n public class HTTPRequest {\n     ZulipApp app;\n-    List<NameValuePair> nameValuePairs;\n-    HttpRequestBase request;\n-    HttpResponse response;\n     volatile boolean aborting = false;\n+    HashMap<String, String> properties;\n+    OkHttpClient okHttpClient;\n+    Response response = null;\n+    Request request;\n \n     public HTTPRequest(ZulipApp app) {\n-        nameValuePairs = new ArrayList<NameValuePair>();\n+        properties = new HashMap<>();\n         this.app = app;\n+        okHttpClient = new OkHttpClient.Builder().readTimeout(60, TimeUnit.SECONDS).build();\n     }\n \n+\n     public void setProperty(String key, String value) {\n-        this.nameValuePairs.add(new BasicNameValuePair(key, value));\n+        properties.put(key, value);\n     }\n \n     public void clearProperties() {\n-        this.nameValuePairs.clear();\n+        properties.clear();\n     }\n \n     public void abort() {\n         aborting = true;\n-        if (request != null) {\n+        if (response != null) {\n             (new AsyncTask<Void, Void, Void>() {\n                 @Override\n                 protected Void doInBackground(Void... voids) {\n-                    request.abort();\n+                    response.body().close();\n                     return null;\n                 }\n             }).execute();\n         }\n     }\n \n-    // Java doesn't support a request body on DELETE requests, but RFC2616 does\n-    // not prohibit it.\n-    class HttpDeleteWithReq extends HttpPost {\n-        HttpDeleteWithReq(String url) {\n-            super(url);\n+    public Response execute(String method, String path) throws IOException {\n+        if (\"\".equals(method)) throw new IOException(app.getString(R.string.method_null));\n+        Request.Builder requestBuilder = new Request.Builder();\n+        String url = app.getServerURI() + path;\n+        requestBuilder.addHeader(\"client\", \"Android\");\n+\n+        switch (method){\n+            case \"GET\":\n+                requestBuilder.url(generateURL(url, properties)).get();\n+                break;\n+            case \"DELETE\":\n+                requestBuilder.url(generateURL(url, properties)).delete();\n+                break;\n+            case \"POST\":\n+                FormBody.Builder formBody = new FormBody.Builder();\n+                for (Map.Entry<String, String> map : properties.entrySet()) {\n+                    formBody.addEncoded(map.getKey(), map.getValue());\n+                }\n+                requestBuilder.url(url).post(formBody.build());\n+                break;\n+            case \"PUT\":\n+                FormBody.Builder builder = new FormBody.Builder();\n+                for (Map.Entry<String, String> map : properties.entrySet()) {\n+                    builder.addEncoded(map.getKey(), map.getValue());\n+                }\n+                requestBuilder.url(url).put(builder.build());\n+                break;\n+            default:\n+                throw new IOException(app.getString(R.string.method_error));\n         }\n \n-        @Override\n-        public String getMethod() {\n-            return \"DELETE\";\n+        if (this.app.getApiKey() != null) {\n+            String authstr = this.app.getEmail() + \":\" + this.app.getApiKey();\n+            requestBuilder.addHeader(\"Authorization\", \"Basic \" + Base64.encodeToString(authstr.getBytes(), Base64.NO_WRAP));\n         }\n+        request = requestBuilder.build();\n+        response = okHttpClient.newCall(request).execute();\n+        return response;\n     }\n \n-    public String execute(String method, String path) throws IOException {\n-        AndroidHttpClient httpclient = AndroidHttpClient.newInstance(app\n-                .getUserAgent());\n-\n-        String responseString = null;\n-        try {\n-            String url = app.getServerURI() + path;\n-            nameValuePairs.add(new BasicNameValuePair(\"client\", \"Android\"));\n-\n-            if (method.equals(\"POST\")) {\n-                request = new HttpPost(url);\n-            } else if (method.equals(\"PUT\")) {\n-                request = new HttpPut(url);\n-            } else if (method.equals(\"DELETE\")) {\n-                request = new HttpDeleteWithReq(url);\n-            } else if (method.equals(\"GET\")) {\n-                request = new HttpGet(url + \"?\"\n-                        + URLEncodedUtils.format(nameValuePairs, \"utf-8\"));\n+    private String generateURL(String url, HashMap<String, String> properties) {\n+        if (properties.isEmpty()) return url;\n+        String encodedUrl = url + \"?\";\n+        for (Map.Entry<String, String> map : properties.entrySet()) {\n+            try {\n+                encodedUrl += URLEncoder.encode(map.getKey(), (\"utf-8\")) + \"=\" + URLEncoder.encode(map.getValue(), \"utf-8\") + \"&\";\n+            } catch (UnsupportedEncodingException e) {\n+                ZLog.logException(e);\n             }\n-\n-            if (!method.equals(\"GET\")) {\n-                ((HttpEntityEnclosingRequestBase) request)\n-                        .setEntity(new UrlEncodedFormEntity(nameValuePairs,\n-                                \"UTF-8\"));\n-            }\n-\n-            Log.i(\"HTTP.request\", request.getMethod() + \" \" + request.getURI());\n-\n-            if (this.app.getApiKey() != null) {\n-                String authstr = this.app.getEmail() + \":\" + this.app.getApiKey();\n-                request.setHeader(\n-                        \"Authorization\",\n-                        \"Basic \"+ Base64.encodeToString(authstr.getBytes(), Base64.NO_WRAP));\n-            }\n-            // timeout after 60 seconds in ms\n-            HttpConnectionParams.setSoTimeout(request.getParams(), 60 * 1000);\n-\n-            response = httpclient.execute(request);\n-\n-            StatusLine statusLine = response.getStatusLine();\n-\n-            ByteArrayOutputStream out = new ByteArrayOutputStream();\n-            response.getEntity().writeTo(out);\n-            out.close();\n-            responseString = out.toString();\n-            if (statusLine.getStatusCode() != HttpStatus.SC_OK) {\n-                Log.e(\"HTTP\", statusLine.getReasonPhrase());\n-                Log.e(\"HTTP\", responseString);\n-                throw new HttpResponseException(statusLine.getStatusCode(),\n-                        responseString);\n-            }\n-        } finally {\n-            httpclient.close();\n         }\n-\n-        if (responseString != null) {\n-            Log.i(\"HTTP.response\", responseString);\n-        } else {\n-            Log.i(\"HTTP.response\", \"<empty>\");\n-        }\n-        return responseString;\n+        return encodedUrl.substring(0, encodedUrl.length() - 1);","path":"app/src/main/java/com/zulip/android/networking/HTTPRequest.java","position":208,"original_position":208,"commit_id":"6079d9f71e54fdd8b44b2fccb8856c2d721fae17","original_commit_id":"6079d9f71e54fdd8b44b2fccb8856c2d721fae17","user":{"login":"niftynei","id":1097769,"avatar_url":"https://avatars.githubusercontent.com/u/1097769?v=3","gravatar_id":"","url":"https://api.github.com/users/niftynei","html_url":"https://github.com/niftynei","followers_url":"https://api.github.com/users/niftynei/followers","following_url":"https://api.github.com/users/niftynei/following{/other_user}","gists_url":"https://api.github.com/users/niftynei/gists{/gist_id}","starred_url":"https://api.github.com/users/niftynei/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/niftynei/subscriptions","organizations_url":"https://api.github.com/users/niftynei/orgs","repos_url":"https://api.github.com/users/niftynei/repos","events_url":"https://api.github.com/users/niftynei/events{/privacy}","received_events_url":"https://api.github.com/users/niftynei/received_events","type":"User","site_admin":false},"body":"there's probably a safer way to removing the last character only if it's an `&`.","created_at":"2016-06-21T20:00:45Z","updated_at":"2016-06-21T20:00:45Z","html_url":"https://github.com/zulip/zulip-android/pull/69#discussion_r67940693","pull_request_url":"https://api.github.com/repos/zulip/zulip-android/pulls/69","_links":{"self":{"href":"https://api.github.com/repos/zulip/zulip-android/pulls/comments/67940693"},"html":{"href":"https://github.com/zulip/zulip-android/pull/69#discussion_r67940693"},"pull_request":{"href":"https://api.github.com/repos/zulip/zulip-android/pulls/69"}}},"pull_request":{"url":"https://api.github.com/repos/zulip/zulip-android/pulls/69","id":74371399,"html_url":"https://github.com/zulip/zulip-android/pull/69","diff_url":"https://github.com/zulip/zulip-android/pull/69.diff","patch_url":"https://github.com/zulip/zulip-android/pull/69.patch","issue_url":"https://api.github.com/repos/zulip/zulip-android/issues/69","number":69,"state":"open","locked":false,"title":"Replaced Old Apache HTTP Libraries with OkHttpClient","user":{"login":"kunall17","id":12700799,"avatar_url":"https://avatars.githubusercontent.com/u/12700799?v=3","gravatar_id":"","url":"https://api.github.com/users/kunall17","html_url":"https://github.com/kunall17","followers_url":"https://api.github.com/users/kunall17/followers","following_url":"https://api.github.com/users/kunall17/following{/other_user}","gists_url":"https://api.github.com/users/kunall17/gists{/gist_id}","starred_url":"https://api.github.com/users/kunall17/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kunall17/subscriptions","organizations_url":"https://api.github.com/users/kunall17/orgs","repos_url":"https://api.github.com/users/kunall17/repos","events_url":"https://api.github.com/users/kunall17/events{/privacy}","received_events_url":"https://api.github.com/users/kunall17/received_events","type":"User","site_admin":false},"body":"Espresso Test's are already included in #45 and #48!","created_at":"2016-06-19T19:09:23Z","updated_at":"2016-06-21T20:00:45Z","closed_at":null,"merged_at":null,"merge_commit_sha":"044d30f48408f337c58bab5a8c51342f9a13600c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/zulip/zulip-android/pulls/69/commits","review_comments_url":"https://api.github.com/repos/zulip/zulip-android/pulls/69/comments","review_comment_url":"https://api.github.com/repos/zulip/zulip-android/pulls/comments{/number}","comments_url":"https://api.github.com/repos/zulip/zulip-android/issues/69/comments","statuses_url":"https://api.github.com/repos/zulip/zulip-android/statuses/6079d9f71e54fdd8b44b2fccb8856c2d721fae17","head":{"label":"kunall17:patch-5","ref":"patch-5","sha":"6079d9f71e54fdd8b44b2fccb8856c2d721fae17","user":{"login":"kunall17","id":12700799,"avatar_url":"https://avatars.githubusercontent.com/u/12700799?v=3","gravatar_id":"","url":"https://api.github.com/users/kunall17","html_url":"https://github.com/kunall17","followers_url":"https://api.github.com/users/kunall17/followers","following_url":"https://api.github.com/users/kunall17/following{/other_user}","gists_url":"https://api.github.com/users/kunall17/gists{/gist_id}","starred_url":"https://api.github.com/users/kunall17/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kunall17/subscriptions","organizations_url":"https://api.github.com/users/kunall17/orgs","repos_url":"https://api.github.com/users/kunall17/repos","events_url":"https://api.github.com/users/kunall17/events{/privacy}","received_events_url":"https://api.github.com/users/kunall17/received_events","type":"User","site_admin":false},"repo":{"id":52900233,"name":"zulip-android","full_name":"kunall17/zulip-android","owner":{"login":"kunall17","id":12700799,"avatar_url":"https://avatars.githubusercontent.com/u/12700799?v=3","gravatar_id":"","url":"https://api.github.com/users/kunall17","html_url":"https://github.com/kunall17","followers_url":"https://api.github.com/users/kunall17/followers","following_url":"https://api.github.com/users/kunall17/following{/other_user}","gists_url":"https://api.github.com/users/kunall17/gists{/gist_id}","starred_url":"https://api.github.com/users/kunall17/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kunall17/subscriptions","organizations_url":"https://api.github.com/users/kunall17/orgs","repos_url":"https://api.github.com/users/kunall17/repos","events_url":"https://api.github.com/users/kunall17/events{/privacy}","received_events_url":"https://api.github.com/users/kunall17/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/kunall17/zulip-android","description":"Zulip Android app","fork":true,"url":"https://api.github.com/repos/kunall17/zulip-android","forks_url":"https://api.github.com/repos/kunall17/zulip-android/forks","keys_url":"https://api.github.com/repos/kunall17/zulip-android/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kunall17/zulip-android/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kunall17/zulip-android/teams","hooks_url":"https://api.github.com/repos/kunall17/zulip-android/hooks","issue_events_url":"https://api.github.com/repos/kunall17/zulip-android/issues/events{/number}","events_url":"https://api.github.com/repos/kunall17/zulip-android/events","assignees_url":"https://api.github.com/repos/kunall17/zulip-android/assignees{/user}","branches_url":"https://api.github.com/repos/kunall17/zulip-android/branches{/branch}","tags_url":"https://api.github.com/repos/kunall17/zulip-android/tags","blobs_url":"https://api.github.com/repos/kunall17/zulip-android/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kunall17/zulip-android/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kunall17/zulip-android/git/refs{/sha}","trees_url":"https://api.github.com/repos/kunall17/zulip-android/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kunall17/zulip-android/statuses/{sha}","languages_url":"https://api.github.com/repos/kunall17/zulip-android/languages","stargazers_url":"https://api.github.com/repos/kunall17/zulip-android/stargazers","contributors_url":"https://api.github.com/repos/kunall17/zulip-android/contributors","subscribers_url":"https://api.github.com/repos/kunall17/zulip-android/subscribers","subscription_url":"https://api.github.com/repos/kunall17/zulip-android/subscription","commits_url":"https://api.github.com/repos/kunall17/zulip-android/commits{/sha}","git_commits_url":"https://api.github.com/repos/kunall17/zulip-android/git/commits{/sha}","comments_url":"https://api.github.com/repos/kunall17/zulip-android/comments{/number}","issue_comment_url":"https://api.github.com/repos/kunall17/zulip-android/issues/comments{/number}","contents_url":"https://api.github.com/repos/kunall17/zulip-android/contents/{+path}","compare_url":"https://api.github.com/repos/kunall17/zulip-android/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kunall17/zulip-android/merges","archive_url":"https://api.github.com/repos/kunall17/zulip-android/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kunall17/zulip-android/downloads","issues_url":"https://api.github.com/repos/kunall17/zulip-android/issues{/number}","pulls_url":"https://api.github.com/repos/kunall17/zulip-android/pulls{/number}","milestones_url":"https://api.github.com/repos/kunall17/zulip-android/milestones{/number}","notifications_url":"https://api.github.com/repos/kunall17/zulip-android/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kunall17/zulip-android/labels{/name}","releases_url":"https://api.github.com/repos/kunall17/zulip-android/releases{/id}","deployments_url":"https://api.github.com/repos/kunall17/zulip-android/deployments","created_at":"2016-03-01T18:35:15Z","updated_at":"2016-03-01T18:35:17Z","pushed_at":"2016-06-20T21:29:07Z","git_url":"git://github.com/kunall17/zulip-android.git","ssh_url":"git@github.com:kunall17/zulip-android.git","clone_url":"https://github.com/kunall17/zulip-android.git","svn_url":"https://github.com/kunall17/zulip-android","homepage":null,"size":6257,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"zulip:master","ref":"master","sha":"4cfc4d3a78ad40b950640bd427d9e7632bd83a09","user":{"login":"zulip","id":4921959,"avatar_url":"https://avatars.githubusercontent.com/u/4921959?v=3","gravatar_id":"","url":"https://api.github.com/users/zulip","html_url":"https://github.com/zulip","followers_url":"https://api.github.com/users/zulip/followers","following_url":"https://api.github.com/users/zulip/following{/other_user}","gists_url":"https://api.github.com/users/zulip/gists{/gist_id}","starred_url":"https://api.github.com/users/zulip/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zulip/subscriptions","organizations_url":"https://api.github.com/users/zulip/orgs","repos_url":"https://api.github.com/users/zulip/repos","events_url":"https://api.github.com/users/zulip/events{/privacy}","received_events_url":"https://api.github.com/users/zulip/received_events","type":"Organization","site_admin":false},"repo":{"id":43160849,"name":"zulip-android","full_name":"zulip/zulip-android","owner":{"login":"zulip","id":4921959,"avatar_url":"https://avatars.githubusercontent.com/u/4921959?v=3","gravatar_id":"","url":"https://api.github.com/users/zulip","html_url":"https://github.com/zulip","followers_url":"https://api.github.com/users/zulip/followers","following_url":"https://api.github.com/users/zulip/following{/other_user}","gists_url":"https://api.github.com/users/zulip/gists{/gist_id}","starred_url":"https://api.github.com/users/zulip/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zulip/subscriptions","organizations_url":"https://api.github.com/users/zulip/orgs","repos_url":"https://api.github.com/users/zulip/repos","events_url":"https://api.github.com/users/zulip/events{/privacy}","received_events_url":"https://api.github.com/users/zulip/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/zulip/zulip-android","description":"Zulip Android app","fork":false,"url":"https://api.github.com/repos/zulip/zulip-android","forks_url":"https://api.github.com/repos/zulip/zulip-android/forks","keys_url":"https://api.github.com/repos/zulip/zulip-android/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zulip/zulip-android/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zulip/zulip-android/teams","hooks_url":"https://api.github.com/repos/zulip/zulip-android/hooks","issue_events_url":"https://api.github.com/repos/zulip/zulip-android/issues/events{/number}","events_url":"https://api.github.com/repos/zulip/zulip-android/events","assignees_url":"https://api.github.com/repos/zulip/zulip-android/assignees{/user}","branches_url":"https://api.github.com/repos/zulip/zulip-android/branches{/branch}","tags_url":"https://api.github.com/repos/zulip/zulip-android/tags","blobs_url":"https://api.github.com/repos/zulip/zulip-android/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zulip/zulip-android/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zulip/zulip-android/git/refs{/sha}","trees_url":"https://api.github.com/repos/zulip/zulip-android/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zulip/zulip-android/statuses/{sha}","languages_url":"https://api.github.com/repos/zulip/zulip-android/languages","stargazers_url":"https://api.github.com/repos/zulip/zulip-android/stargazers","contributors_url":"https://api.github.com/repos/zulip/zulip-android/contributors","subscribers_url":"https://api.github.com/repos/zulip/zulip-android/subscribers","subscription_url":"https://api.github.com/repos/zulip/zulip-android/subscription","commits_url":"https://api.github.com/repos/zulip/zulip-android/commits{/sha}","git_commits_url":"https://api.github.com/repos/zulip/zulip-android/git/commits{/sha}","comments_url":"https://api.github.com/repos/zulip/zulip-android/comments{/number}","issue_comment_url":"https://api.github.com/repos/zulip/zulip-android/issues/comments{/number}","contents_url":"https://api.github.com/repos/zulip/zulip-android/contents/{+path}","compare_url":"https://api.github.com/repos/zulip/zulip-android/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zulip/zulip-android/merges","archive_url":"https://api.github.com/repos/zulip/zulip-android/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zulip/zulip-android/downloads","issues_url":"https://api.github.com/repos/zulip/zulip-android/issues{/number}","pulls_url":"https://api.github.com/repos/zulip/zulip-android/pulls{/number}","milestones_url":"https://api.github.com/repos/zulip/zulip-android/milestones{/number}","notifications_url":"https://api.github.com/repos/zulip/zulip-android/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zulip/zulip-android/labels{/name}","releases_url":"https://api.github.com/repos/zulip/zulip-android/releases{/id}","deployments_url":"https://api.github.com/repos/zulip/zulip-android/deployments","created_at":"2015-09-25T16:39:37Z","updated_at":"2016-06-19T13:12:30Z","pushed_at":"2016-06-21T19:40:07Z","git_url":"git://github.com/zulip/zulip-android.git","ssh_url":"git@github.com:zulip/zulip-android.git","clone_url":"https://github.com/zulip/zulip-android.git","svn_url":"https://github.com/zulip/zulip-android","homepage":null,"size":19206,"stargazers_count":270,"watchers_count":270,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":128,"mirror_url":null,"open_issues_count":24,"forks":128,"open_issues":24,"watchers":270,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/zulip/zulip-android/pulls/69"},"html":{"href":"https://github.com/zulip/zulip-android/pull/69"},"issue":{"href":"https://api.github.com/repos/zulip/zulip-android/issues/69"},"comments":{"href":"https://api.github.com/repos/zulip/zulip-android/issues/69/comments"},"review_comments":{"href":"https://api.github.com/repos/zulip/zulip-android/pulls/69/comments"},"review_comment":{"href":"https://api.github.com/repos/zulip/zulip-android/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/zulip/zulip-android/pulls/69/commits"},"statuses":{"href":"https://api.github.com/repos/zulip/zulip-android/statuses/6079d9f71e54fdd8b44b2fccb8856c2d721fae17"}}}}
{"actor":{"display_login":"niftynei"}}