diff --git a/Tophe-Ion/README.md b/Tophe-Ion/README.md deleted file mode 100644 index 53bc8cc..0000000 --- a/Tophe-Ion/README.md +++ /dev/null @@ -1,66 +0,0 @@ -#TOPHE Ion module - -A module for TopheClient that uses [Ion][1] as the HTTP engine. You can also construct an `HttpEngineIon` yourself and use -the `requestBuilder` field to do additional work on the Ion request, like using the query to load a picture. - -##Sample Code - -###Setup the Ion engine - -Make Tophe use [Ion][1] by default for all HTTP requests. The Ion engine doesn't support `HttpStream` output so the default -engine will be used in that case. - -```java -IonClient.setup(context); -``` - -###Get the Ion request to retrieve a Bitmap - -```java -BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("https://ton.twitter.com/dmpics/somepic51215423.jpg") - .setResponseHandler(new BaseResponseHandler(XferTransformResponseInputStream.INSTANCE)) - .setErrorParser(BodyToServerException.INSTANCE) - .build(); - -IonHttpEngineFactory.getInstance(context); -HttpEngineIon ionRequest = (HttpEngineIon) - new HttpEngine.Builder() - .setTypedRequest(request) - .build(); - -ionRequest.requestBuilder.intoImageView(imageView); -``` - -## Download - -Download [the latest JAR][2] or grab via Maven [![Maven Central](https://maven-badges.herokuapp.com/maven-central/co.tophe/tophe-ion/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/co.tophe/tophe-ion) -```xml - - co.tophe - tophe-ion - 1.0.0 - -``` -or Gradle: -```groovy -compile 'co.tophe:tophe-ion:1.0.0' -``` - -## License - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -[1]: https://github.com/koush/ion -[2]: https://search.maven.org/remote_content?g=co.tophe&a=tophe-ion&v=LATEST - diff --git a/Tophe-Ion/build.gradle b/Tophe-Ion/build.gradle deleted file mode 100644 index 297884d..0000000 --- a/Tophe-Ion/build.gradle +++ /dev/null @@ -1,119 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) - buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION - - defaultConfig { - minSdkVersion Math.max(9, Integer.parseInt(ANDROID_BUILD_MIN_SDK_VERSION)) - targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) - versionCode Integer.parseInt(project.VERSION_CODE) - versionName project.VERSION_NAME - } - - libraryVariants.all { variant -> - def name = variant.buildType.name - if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) { - return; // Skip debug builds. - } - def task = project.tasks.create "jar${name.capitalize()}", Jar - task.dependsOn variant.javaCompile - task.from variant.javaCompile.destinationDir - artifacts.add('archives', task); - - configurations.archives.artifacts.with { archives -> - def aarArtifact - archives.each { - if (it.file =~ 'aar') { - aarArtifact = it - } - } - //println "AAR to delete: ${aarArtifact}" - remove(aarArtifact) - } - } -} - -dependencies { - def artifactSupportV4 = 'com.android.support:support-v4:21.0.3' - def artifactIon = 'com.koushikdutta.ion:ion:2.0.7' - def artifactTophe = 'co.tophe:tophe:1.0.1' - - def androidAsyncProject = ':AndroidAsync:AndroidAsync' - def ionProject = ':Ion:Ion' - def supportv4Project = ':support-v4' - def localTophe = ':Tophe' - - def useLocalAndroidAsync = false - def useLocalIon = false - def useLocalV4 = false - def useLocalTophe = false - - allprojects { - rootProject.allprojects.project.each { - if (it.path == ionProject) { - useLocalIon = true - } - else if (it.path == androidAsyncProject) { - useLocalAndroidAsync = true - } - else if (it.path == supportv4Project) { - useLocalV4 = true - } - else if (it.path == localTophe) { - useLocalTophe = true - } - } - } - - if (useLocalV4) { - println 'Using local support-v4 library in ' + project.path - compile project(supportv4Project) - } else { - compile artifactSupportV4 - } - - if (useLocalIon) { - println 'Using local Ion library in ' + project.path - compile project(ionProject) - } else { - compile (artifactIon) { - if (useLocalV4) { - println 'Exclude support-v4 dependency for ' + artifactIon + ' in ' + project.path - exclude group: 'com.android.support', module: 'support-v4' - } - if (useLocalAndroidAsync) { - println 'Exclude androidasync dependency for ' + artifactIon + ' in ' + project.path - exclude group: 'com.koushikdutta.async', module:'androidasync' - } - } - } - - if (useLocalAndroidAsync) { - println 'Using local AndroidAsync library in ' + project.path - compile project(androidAsyncProject) - } - - if (useLocalTophe) { - println 'Using local Tophe library in ' + project.path - compile project(localTophe) - } else { - compile artifactTophe - } - - androidTestCompile 'com.squareup.okio:okio:1.0.1' -} - -apply from: 'https://raw.githubusercontent.com/mcxiaoke/gradle-mvn-push/master/gradle-mvn-push.gradle' - -afterEvaluate { project -> - uploadArchives { - repositories { - mavenDeployer { - pom*.whenConfigured { pom -> - pom.dependencies.removeAll { dep -> dep.artifactId == 'support-v4' } - } - } - } - } -} \ No newline at end of file diff --git a/Tophe-Ion/gradle.properties b/Tophe-Ion/gradle.properties deleted file mode 100644 index 20bbcce..0000000 --- a/Tophe-Ion/gradle.properties +++ /dev/null @@ -1,22 +0,0 @@ -POM_NAME=TOPHE Ion -POM_DESCRIPTION=A Fully Typed HTTP library for Android using the Ion engine -GROUP=co.tophe -POM_URL=https://github.com/levelup/Android-HttpClient -POM_SCM_URL=https://github.com/levelup/Android-HttpClient -POM_SCM_CONNECTION=scm:git@github.com:levelup/Android-HttpClient.git -POM_SCM_DEV_CONNECTION=scm:git@github.com:levelup/Android-HttpClient.git -POM_LICENCE_NAME=The Apache Software License, Version 2.0 -POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt -POM_LICENCE_DIST=repo -POM_DEVELOPER_ID=robux4 -POM_DEVELOPER_NAME=Steve Lhomme - -POM_ARTIFACT_ID=tophe-ion -POM_PACKAGING=jar -VERSION_NAME=1.0.2 -VERSION_CODE=10002 - -ANDROID_BUILD_TARGET_SDK_VERSION=21 -ANDROID_BUILD_SDK_VERSION=21 -ANDROID_BUILD_TOOLS_VERSION=21.1.2 -ANDROID_BUILD_MIN_SDK_VERSION=9 diff --git a/Tophe-Ion/src/androidTest/AndroidManifest.xml b/Tophe-Ion/src/androidTest/AndroidManifest.xml deleted file mode 100644 index ec8d13e..0000000 --- a/Tophe-Ion/src/androidTest/AndroidManifest.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/Tophe-Ion/src/androidTest/java/co/tophe/ion/BodyToJSONObjectTest.java b/Tophe-Ion/src/androidTest/java/co/tophe/ion/BodyToJSONObjectTest.java deleted file mode 100644 index ef70b15..0000000 --- a/Tophe-Ion/src/androidTest/java/co/tophe/ion/BodyToJSONObjectTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package co.tophe.ion; - -import org.json.JSONObject; - -import android.content.Context; -import android.test.AndroidTestCase; - -import co.tophe.BaseHttpRequest; -import co.tophe.TopheClient; -import co.tophe.HttpDataParserException; -import co.tophe.ServerException; -import co.tophe.parser.BodyToJSONObject; - -public class BodyToJSONObjectTest extends AndroidTestCase { - - @Override - public void setContext(Context context) { - super.setContext(context); - IonClient.setup(context); - } - - public void testBogusData() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://android.com/"). - setResponseHandler(BodyToJSONObject.RESPONSE_HANDLER). - build(); - - try { - TopheClient.parseRequest(request); - } catch (HttpDataParserException e) { - assertNotNull(e.getMessage()); - assertTrue(e.getCause().getMessage().startsWith("Bad JSON data")); - assertNotNull(e.getCause().getSourceData()); - } - } -} \ No newline at end of file diff --git a/Tophe-Ion/src/androidTest/java/co/tophe/ion/IonClientTest.java b/Tophe-Ion/src/androidTest/java/co/tophe/ion/IonClientTest.java deleted file mode 100644 index 763e099..0000000 --- a/Tophe-Ion/src/androidTest/java/co/tophe/ion/IonClientTest.java +++ /dev/null @@ -1,517 +0,0 @@ -package co.tophe.ion; - -import java.io.ByteArrayInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.json.JSONObject; - -import android.content.Context; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; - -import co.tophe.BaseHttpRequest; -import co.tophe.TopheClient; -import co.tophe.HttpConfig; -import co.tophe.HttpMimeException; -import co.tophe.HttpRequest; -import co.tophe.HttpRequestInfo; -import co.tophe.HttpStream; -import co.tophe.HttpTimeoutException; -import co.tophe.ServerException; -import co.tophe.body.HttpBodyJSON; -import co.tophe.body.HttpBodyMultiPart; -import co.tophe.body.HttpBodyParameters; -import co.tophe.body.HttpBodyUrlEncoded; -import co.tophe.engine.HttpEngineUrlConnection; -import co.tophe.parser.BodyToHttpStream; -import co.tophe.parser.BodyToJSONObject; -import co.tophe.parser.BodyToString; - -import okio.BufferedSource; -import okio.Okio; - -public class IonClientTest extends AndroidTestCase { - - private static final Class ENGINE_CLASS = HttpEngineIon.class; - private static final Class STREAM_ENGINE_CLASS = HttpEngineUrlConnection.class; - - @Override - public void setContext(Context context) { - super.setContext(context); - IonClient.setup(context); - } - - public void testNoSSLv3() throws Exception{ - BaseHttpRequest request = new BaseHttpRequest.Builder() - //.setUrl("https://userstream.twitter.com/1.1/user.json") - .setUrl("https://github.com/robUx4") - .setResponseHandler(BodyToString.RESPONSE_HANDLER) - .build(); - try { - TopheClient.parseRequest(request); - } catch (ServerException e) { - assertEquals(401, e.getStatusCode()); - } - } - - @MediumTest - public void testUploadInputStream() throws Exception { - final String fileFieldName = "media"; - final String uploadData = "Uploaded Streamé Data"; - HttpBodyMultiPart body = new HttpBodyMultiPart(1); - body.addStream("media", new ByteArrayInputStream(uploadData.getBytes()), uploadData.getBytes().length, "text/plain"); - - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://httpbin.org/post?test=stream"). - setBody(body). - setResponseHandler(BodyToJSONObject.RESPONSE_HANDLER). - build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - - JSONObject result = TopheClient.parseRequest(request); - assertNotNull(result); - assertFalse(result.isNull("files")); - JSONObject files = result.optJSONObject("files"); - assertFalse(files.isNull(fileFieldName)); - assertEquals(uploadData, files.optString(fileFieldName)); - } - - @MediumTest - public void testUploadFile() throws Exception { - final String fileFieldName = "media"; - final String uploadData = "Uploaded File Data"; - File tempFile = new File(getContext().getCacheDir(), "upload.txt"); - FileOutputStream outStream = new FileOutputStream(tempFile); - outStream.write(uploadData.getBytes()); - outStream.close(); - - try { - HttpBodyMultiPart body = new HttpBodyMultiPart(1); - body.addFile(fileFieldName, tempFile, "text/plain"); - - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://httpbin.org/post?test=file"). - setBody(body). - setResponseHandler(BodyToJSONObject.RESPONSE_HANDLER). - build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - - JSONObject result = TopheClient.parseRequest(request); - assertNotNull(result); - assertFalse(result.isNull("files")); - JSONObject files = result.optJSONObject("files"); - assertFalse(files.isNull(fileFieldName)); - assertEquals(uploadData, files.optString(fileFieldName)); - } finally { - tempFile.delete(); - } - } - - public void testUploadMultipartText() throws Exception { - final String fieldName1 = "field1"; - final String uploadData1 = "First Field Data"; - final String fieldName2 = "field2"; - final String uploadData2 = "Second Field Data"; - - HttpBodyMultiPart body = new HttpBodyMultiPart(2); - body.add(fieldName1, uploadData1); - body.add(fieldName2, uploadData2); - - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://httpbin.org/post?test=multitext"). - setBody(body). - setResponseHandler(BodyToJSONObject.RESPONSE_HANDLER). - build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - - JSONObject result = TopheClient.parseRequest(request); - assertNotNull(result); - assertFalse(result.isNull("form")); - JSONObject form = result.optJSONObject("form"); - assertFalse(form.isNull(fieldName1)); - assertEquals(uploadData1, form.optString(fieldName1)); - assertFalse(form.isNull(fieldName2)); - assertEquals(uploadData2, form.optString(fieldName2)); - } - - public void testUploadUrlEncoded() throws Exception { - final String fieldName = "fieldName"; - final String uploadData = "Uploaded Post Data URL encoded"; - - HttpBodyParameters body = new HttpBodyUrlEncoded(); - body.add(fieldName, uploadData); - - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://httpbin.org/post?test=urlencoded"). - setBody(body). - setResponseHandler(BodyToJSONObject.RESPONSE_HANDLER). - build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - - JSONObject result = TopheClient.parseRequest(request); - assertNotNull(result); - assertFalse(result.isNull("form")); - JSONObject form = result.optJSONObject("form"); - assertFalse(form.isNull(fieldName)); - assertEquals(uploadData, form.optString(fieldName)); - } - - public void testUploadJson() throws Exception { - final String fieldName1 = "name"; - final String uploadData1 = "Steve Lhomme"; - final String fieldName2 = "screenName"; - final String uploadData2 = "robUx4"; - - JSONObject object = new JSONObject(); - object.put(fieldName1, uploadData1); - object.put(fieldName2, uploadData2); - - HttpBodyJSON body = new HttpBodyJSON(object); - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://httpbin.org/post?test=jsonBody"). - setBody(body). - setResponseHandler(BodyToJSONObject.RESPONSE_HANDLER). - build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - - JSONObject result = TopheClient.parseRequest(request); - assertNotNull(result); - assertFalse(result.isNull("json")); - JSONObject json = result.optJSONObject("json"); - assertFalse(json.isNull(fieldName1)); - assertEquals(uploadData1, json.optString(fieldName1)); - assertFalse(json.isNull(fieldName2)); - assertEquals(uploadData2, json.optString(fieldName2)); - } - - public void testTimeout() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://httpbin.org/delay/10"). - setResponseHandler(BodyToJSONObject.RESPONSE_HANDLER). - build(); - request.setHttpConfig(new HttpConfig() { - @Override - public int getReadTimeout(HttpRequestInfo request) { - return 3000; // 3s - } - }); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - - try { - JSONObject result = TopheClient.parseRequest(request); - fail("we should have timed out after 3s"); - } catch (HttpTimeoutException e) { - // ok - } - } - - private void testError(int errorCode) throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://httpbin.org/status/" + errorCode). - setResponseHandler(BodyToString.RESPONSE_HANDLER). - build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - - try { - String result = TopheClient.parseRequest(request); - fail("we should have an HTTP error " + errorCode); - } catch (ServerException e) { - assertEquals(errorCode, e.getStatusCode()); - } - } - - private void testStreamingError(int errorCode) throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/status/" + errorCode) - .setResponseHandler(BodyToHttpStream.RESPONSE_HANDLER) - .build(); - - try { - HttpStream result = TopheClient.parseRequest(request); - fail("we should have an HTTP error " + errorCode + ", not a stream"); - } catch (ServerException e) { - assertEquals(errorCode, e.getStatusCode()); - } - } - - public void testError401() throws Exception { - testError(401); - } - - public void testError500() throws Exception { - testError(500); - } - - public void testStreamingError401() throws Exception { - testStreamingError(401); - } - - @MediumTest - public void testString() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/ip") - .setResponseHandler(BodyToString.RESPONSE_HANDLER) - .build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - - String result = TopheClient.parseRequest(request); - assertNotNull(result); - assertTrue(result.contains("\"origin\"")); - } - - @MediumTest - public void testMime() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/html") - .setResponseHandler(BodyToString.RESPONSE_HANDLER) - .build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - request.setHeader(HttpRequest.HEADER_ACCEPT, "application/json"); - - try { - String result = TopheClient.parseRequest(request); - fail("we should not be here"); - } catch (HttpMimeException e) { - // ok - } - } - - @MediumTest - public void testStreaming() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/drip?numbytes=5&duration=5") - .setResponseHandler(BodyToHttpStream.RESPONSE_HANDLER) - .build(); - // TODO assertEquals(STREAM_ENGINE_CLASS, request.getHttpEngine().getClass()); - - HttpStream stream = TopheClient.parseRequest(request); - try { - InputStream streamIn = stream.getInputStream(); - byte[] buffer = new byte[1]; - int read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - assertEquals(-1, streamIn.read(buffer)); - } finally { - stream.disconnect(); - } - } - - @MediumTest - public void testStreamingCompressed() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/drip?numbytes=5&duration=5") - .setResponseHandler(BodyToHttpStream.RESPONSE_HANDLER) - .build(); - request.setHeader(HttpRequest.HEADER_ACCEPT_ENCODING, "gzip,deflate"); - // TODO assertEquals(STREAM_ENGINE_CLASS, request.getHttpEngine().getClass()); - - HttpStream stream = TopheClient.parseRequest(request); - try { - InputStream streamIn = stream.getInputStream(); - byte[] buffer = new byte[1]; - int read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - assertEquals(-1, streamIn.read(buffer)); - } finally { - stream.disconnect(); - } - } - - @MediumTest - public void testStreamingLine() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/stream/2") - .setResponseHandler(BodyToHttpStream.RESPONSE_HANDLER) - .build(); - // TODO assertEquals(STREAM_ENGINE_CLASS, request.getHttpEngine().getClass()); - - HttpStream stream = TopheClient.parseRequest(request); - try { - BufferedSource lineReader = Okio.buffer(Okio.source(stream.getInputStream())); - String line = lineReader.readUtf8LineStrict(); - assertNotNull(line); - assertTrue(line.contains("\"headers\":")); - - line = lineReader.readUtf8LineStrict(); - assertNotNull(line); - assertTrue(line.contains("\"headers\":")); - - try { - line = lineReader.readUtf8LineStrict(); - fail("we should throw after the 2 lines are received"); - } catch (EOFException ok) { - // all good - } - } finally { - stream.disconnect(); - } - } - - @MediumTest - public void testStreamingTimeout() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/drip?numbytes=5&duration=2&delay=8") - .setResponseHandler(BodyToHttpStream.RESPONSE_HANDLER) - .build(); - request.setHttpConfig(new HttpConfig() { - @Override - public int getReadTimeout(HttpRequestInfo request) { - return 5000; // 5s - } - }); - // TODO assertEquals(STREAM_ENGINE_CLASS, request.getHttpEngine().getClass()); - - try { - HttpStream stream = TopheClient.parseRequest(request); - try { - BufferedSource lineReader = Okio.buffer(Okio.source(stream.getInputStream())); - String line = lineReader.readUtf8(); - assertNotNull(line); - assertTrue(line.startsWith("*****")); - fail("we should not read 1 item"); - } finally { - stream.disconnect(); - } - fail("we should have been in timeout"); - } catch (HttpTimeoutException e) { - // ok - } - } - - @MediumTest - public void testStreamingDisconnect() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/drip?numbytes=5&duration=2&delay=2") - .setResponseHandler(BodyToHttpStream.RESPONSE_HANDLER) - .build(); - // TODO assertEquals(STREAM_ENGINE_CLASS, request.getHttpEngine().getClass()); - - HttpStream stream = TopheClient.parseRequest(request); - InputStream streamIn = stream.getInputStream(); - byte[] buffer = new byte[1]; - int read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - stream.disconnect(); - - try { - stream.getInputStream().read(new byte[1]); - fail("we shouldn't be able to read after disconnection"); - } catch (EOFException ok) { - // all good - } catch (IOException ok) { - assertTrue(ok.getMessage().contains("closed")); - } - } - - @MediumTest - public void testStreamingDisconnectAsync() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/drip?numbytes=5&duration=2&delay=2") - .setResponseHandler(BodyToHttpStream.RESPONSE_HANDLER) - .build(); - // TODO assertEquals(STREAM_ENGINE_CLASS, request.getHttpEngine().getClass()); - - final HttpStream stream = TopheClient.parseRequest(request); - InputStream streamIn = stream.getInputStream(); - byte[] buffer = new byte[1]; - int read = streamIn.read(buffer); - if (read == -1) throw new EOFException("could not read more"); - assertEquals('*', buffer[0]); - - final CountDownLatch latch = new CountDownLatch(1); - new Thread() { - @Override - public void run() { - stream.disconnect(); - latch.countDown(); - } - }.start(); - latch.await(40, TimeUnit.SECONDS); - - try { - stream.getInputStream().read(new byte[1]); - fail("we shouldn't be able to read after disconnection"); - } catch (EOFException ok) { - // all good - } catch (IOException ok) { - assertTrue(ok.getMessage().contains("closed")); - } - } - - public void testNullContext() throws Exception { - try { - TopheClient.setup(null); - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/drip?numbytes=5&duration=200&delay=2") - .setResponseHandler(BodyToString.RESPONSE_HANDLER) - .build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - //when not using Ion, we don't need a Context fail("A query with no context is invalid"); - } catch (NullPointerException e) { - // all good - } - } - - public void testNullContextStreaming() throws Exception { - TopheClient.setup(null); - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/drip?numbytes=5&duration=200&delay=2") - .setResponseHandler(BodyToString.RESPONSE_HANDLER) - .build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - } - - public void testSetupContext() throws Exception { - TopheClient.setup(getContext()); - BaseHttpRequest request = new BaseHttpRequest.Builder() - .setUrl("http://httpbin.org/drip?numbytes=5&duration=200&delay=2") - .setResponseHandler(BodyToString.RESPONSE_HANDLER) - .build(); - // TODO assertEquals(ENGINE_CLASS, request.getHttpEngine().getClass()); - TopheClient.setup(null); - } -} diff --git a/Tophe-Ion/src/androidTest/java/co/tophe/ion/async/AsyncClientTest.java b/Tophe-Ion/src/androidTest/java/co/tophe/ion/async/AsyncClientTest.java deleted file mode 100644 index 5f56d65..0000000 --- a/Tophe-Ion/src/androidTest/java/co/tophe/ion/async/AsyncClientTest.java +++ /dev/null @@ -1,163 +0,0 @@ -package co.tophe.ion.async; - -import java.util.concurrent.CancellationException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -import android.content.Context; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; - -import co.tophe.HttpException; -import co.tophe.HttpRequestGet; -import co.tophe.async.AsyncTopheClient; -import co.tophe.async.AsyncTask; -import co.tophe.async.BaseAsyncCallback; -import co.tophe.ion.IonClient; -import co.tophe.parser.BodyToString; - -public class AsyncClientTest extends AndroidTestCase { - - private static final String BASIC_URL = "http://www.levelupstudio.com/"; - private static final String BASIC_URL_TAG = "test1"; - private static final String SLOW_URL = "http://httpbin.org/delay/10"; - private static final String BASIC_URL_HTTPS = "https://www.google.com/"; - private static final String SLOW_URL_HTTPS = "https://httpbin.org/delay/10"; - - // TODO test with streaming connection (chunked over HTTPS with sometimes no data sent for 1 minute) - // TODO test with streaming connection with SPDY - // TODO test with long POST - - private static final HttpRequestGet BASIC_REQUEST = new HttpRequestGet(BASIC_URL, BodyToString.INSTANCE); - - @Override - public void setContext(Context context) { - super.setContext(context); - IonClient.setup(context); - } - - public void testAsyncSimpleQuery() { - new AsyncTask.Builder() - .setTypedRequest(BASIC_REQUEST) - .setTaskTag(BASIC_URL_TAG) - .execute(); - } - - private static class TestAsyncCallback extends BaseAsyncCallback { - @Override - public void onAsyncFailed(Throwable t) { - if (t instanceof HttpException && ((HttpException) t).isTemporaryFailure()) { - // shit happens - } else { - fail(t.getMessage()); - } - } - } - - private static class TestLongAsyncCallback extends TestAsyncCallback { - @Override - public void onAsyncResult(String result) { - fail("We're not supposed to have received this"); - } - } - - @MediumTest - public void testAsyncSimpleQueryResult() { - final CountDownLatch latch = new CountDownLatch(1); - - new AsyncTask.Builder() - .setTypedRequest(BASIC_REQUEST) - .setTaskTag(BASIC_URL_TAG) - .setHttpAsyncCallback(new TestAsyncCallback() { - @Override - public void onAsyncResult(String result) { - // we received the result successfully - latch.countDown(); - } - }) - .execute(); - - try { - latch.await(); - } catch (InterruptedException e) { - fail("unreasonably slow"); - } - } - - - public void testCancelShort() { - Future downloadTask = AsyncTopheClient.postRequest(BASIC_REQUEST, new TestLongAsyncCallback()); - - downloadTask.cancel(true); - - try { - downloadTask.get(); - } catch(CancellationException e) { - // fine - } catch (InterruptedException e) { - // fine - } catch (ExecutionException e) { - fail("the task did not exit correctly "+e); - } - } - - public void testCancelShortHttps() { - HttpRequestGet request = new HttpRequestGet(BASIC_URL_HTTPS, BodyToString.INSTANCE); - Future downloadTask = AsyncTopheClient.postRequest(request, new TestLongAsyncCallback()); - - downloadTask.cancel(true); - - try { - downloadTask.get(); - } catch(CancellationException e) { - // fine - } catch (InterruptedException e) { - // fine - } catch (ExecutionException e) { - fail("the task did not exit correctly "+e); - } - } - - public void testCancelLong() { - HttpRequestGet request = new HttpRequestGet(SLOW_URL, BodyToString.INSTANCE); - Future downloadTask = AsyncTopheClient.postRequest(request, new TestLongAsyncCallback()); - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - } - - downloadTask.cancel(true); - - try { - downloadTask.get(); - } catch(CancellationException e) { - // fine - } catch (InterruptedException e) { - // fine - } catch (ExecutionException e) { - fail("the task did not exit correctly "+e); - } - } - - public void testCancelLongHttps() { - HttpRequestGet request = new HttpRequestGet(SLOW_URL_HTTPS, BodyToString.INSTANCE); - Future downloadTask = AsyncTopheClient.postRequest(request, new TestLongAsyncCallback()); - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - } - - downloadTask.cancel(true); - - try { - downloadTask.get(); - } catch(CancellationException e) { - // fine - } catch (InterruptedException e) { - // fine - } catch (ExecutionException e) { - fail("the task did not exit correctly "+e); - } - } -} diff --git a/Tophe-Ion/src/androidTest/java/co/tophe/ion/gson/AsyncGsonParse.java b/Tophe-Ion/src/androidTest/java/co/tophe/ion/gson/AsyncGsonParse.java deleted file mode 100644 index 1b35b43..0000000 --- a/Tophe-Ion/src/androidTest/java/co/tophe/ion/gson/AsyncGsonParse.java +++ /dev/null @@ -1,132 +0,0 @@ -package co.tophe.ion.gson; - -import android.content.Context; -import android.test.AndroidTestCase; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import co.tophe.TopheClient; -import co.tophe.HttpRequestGet; -import co.tophe.gson.BodyViaGson; -import co.tophe.gson.ReadOnlyTypeAdapter; -import co.tophe.ion.IonClient; - -import java.io.IOException; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; - -public class AsyncGsonParse extends AndroidTestCase { - - private static final String JSON_URL = "http://social.appxoid.com/json/get_apps_by_pages2"; - private static final String JSON_DATE_FORMAT = "yyyy-mm-dd'T'HH:mm:ss"; - - public static class AppXoidInfo { - @SerializedName("available_fr") boolean availableInFrance; - @SerializedName("rating") String rating; - @SerializedName("app_updated") Date dateUpdated; - @SerializedName("creator") String creator; - } - - public static class AppXoid { - @SerializedName("pk") int apkNum; - @SerializedName("model") String model; - @SerializedName("fields") AppXoidInfo data; - } - - @Override - public void setContext(Context context) { - super.setContext(context); - IonClient.setup(context); - } - - public void testGsonParser() throws Exception { - Gson gson = new GsonBuilder().setDateFormat(JSON_DATE_FORMAT).create(); - - BodyViaGson> parser = BodyViaGson.asList(gson, AppXoid.class); - HttpRequestGet> request = new HttpRequestGet>(JSON_URL, parser); - List items = TopheClient.parseRequest(request); - assertNotNull(items); - } - - public static class AppXoidReader { - @SerializedName("pk") int apkNum; - @SerializedName("model") String model; - @SerializedName("fields") AppXoidInfo data; - } - - public static class AppXoidInfoReader { - public final boolean availableInFrance; - public final String rating; - public final Date dateUpdated; - public final String creator; - - private AppXoidInfoReader(Builder builder) { - this.availableInFrance = builder.availableInFrance; - this.rating = builder.rating; - this.dateUpdated = builder.dateUpdated; - this.creator = builder.creator; - } - - public static final class Builder { - public boolean availableInFrance; - public String rating; - public Date dateUpdated; - public String creator; - - public AppXoidInfoReader build() { - return new AppXoidInfoReader(this); - } - } - } - - public void testGsonDeserialize() throws Exception { - TypeAdapter infoAdapter = new ReadOnlyTypeAdapter() { - private final SimpleDateFormat format = new SimpleDateFormat(JSON_DATE_FORMAT); - @Override - public AppXoidInfoReader read(JsonReader reader) throws IOException { - // the first token is the start object - JsonToken token = reader.peek(); - AppXoidInfoReader.Builder builder = new AppXoidInfoReader.Builder(); - if (token.equals(JsonToken.BEGIN_OBJECT)) { - reader.beginObject(); - while (!reader.peek().equals(JsonToken.END_OBJECT)) { - if (reader.peek().equals(JsonToken.NAME)) { - String name = reader.nextName(); - if ("available_fr".equals(name)) - builder.availableInFrance = reader.nextBoolean(); - else if ("rating".equals(name)) - builder.rating = reader.nextString(); - else if ("app_updated".equals(name)) - try { - builder.dateUpdated = format.parse(reader.nextString()); - } catch (ParseException e) { - } - else if ("creator".equals(name)) - builder.creator = reader.nextString(); - else - reader.skipValue(); - } - } - reader.endObject(); - } - return builder.build(); - } - }; - - Gson gson = new GsonBuilder() - .registerTypeAdapter(AppXoidInfoReader.class, infoAdapter) - .setDateFormat(JSON_DATE_FORMAT) - .create(); - - BodyViaGson> parser = BodyViaGson.asList(gson, AppXoidReader.class); - HttpRequestGet> request = new HttpRequestGet>(JSON_URL, parser); - List items = TopheClient.parseRequest(request); - assertNotNull(items); - } -} diff --git a/Tophe-Ion/src/androidTest/java/co/tophe/ion/gson/BodyViaGsonTest.java b/Tophe-Ion/src/androidTest/java/co/tophe/ion/gson/BodyViaGsonTest.java deleted file mode 100644 index aafdf89..0000000 --- a/Tophe-Ion/src/androidTest/java/co/tophe/ion/gson/BodyViaGsonTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package co.tophe.ion.gson; - -import android.content.Context; -import android.test.AndroidTestCase; - -import com.google.gson.annotations.SerializedName; -import co.tophe.BaseHttpRequest; -import co.tophe.BaseResponseHandler; -import co.tophe.TopheClient; -import co.tophe.HttpDataParserException; -import co.tophe.ImmutableHttpRequest; -import co.tophe.ResponseHandler; -import co.tophe.ServerException; -import co.tophe.gson.BodyViaGson; -import co.tophe.ion.IonClient; -import co.tophe.parser.BodyToString; -import co.tophe.parser.BodyTransformChain; -import co.tophe.parser.XferTransform; - -public class BodyViaGsonTest extends AndroidTestCase { - - @Override - public void setContext(Context context) { - super.setContext(context); - IonClient.setup(context); - } - - private static class HttpbinData { - @SerializedName("url") String url; - } - - public void testGsonData() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://httpbin.org/get"). - setResponseHandler(new BaseResponseHandler(new BodyViaGson(HttpbinData.class))). - build(); - - HttpbinData data = TopheClient.parseRequest(request); - assertNotNull(data); - assertEquals(request.getUri().toString(), data.url); - } - - public static class FacebookErrorData { - private static class ErrorInfo { - String message; - String type; - int code; - } - - ErrorInfo error; - } - - private static class FacebookException extends ServerException { - protected FacebookException(ImmutableHttpRequest request, Object parsedError) { - super(request, parsedError); - } - } - - private static final XferTransform exceptionParser = new XferTransform() { - @Override - public FacebookException transformData(FacebookErrorData facebookErrorData, ImmutableHttpRequest request) { - return new FacebookException(request, facebookErrorData); - } - }; - - public void testGsonErrorData() throws Exception { - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://graph.facebook.com/test"). - setResponseHandler( - new ResponseHandler(BodyToString.INSTANCE, - BodyTransformChain.createBuilder(new BodyViaGson(FacebookErrorData.class)) - .addDataTransform(exceptionParser) - .build() - ) - ). - build(); - - try { - String data = TopheClient.parseRequest(request); - fail("We should never have received data:"+data); - } catch (FacebookException e) { - Object serverError = e.getServerError(); - assertNotNull(serverError); - assertEquals(FacebookErrorData.class, serverError.getClass()); - FacebookErrorData errorData = (FacebookErrorData) serverError; - assertNotNull(errorData.error); - assertEquals(803, errorData.error.code); - } - } - - public void testGsonErrorDebugData() throws Exception { - BodyViaGson testParser = new BodyViaGson(FacebookErrorData.class); - testParser.enableDebugData(true); - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://graph.facebook.com/test"). - setResponseHandler( - new ResponseHandler(BodyToString.INSTANCE, - BodyTransformChain.createBuilder(new BodyViaGson(FacebookErrorData.class)) - .addDataTransform(exceptionParser) - .build() - ) - ). - build(); - - try { - String data = TopheClient.parseRequest(request); - fail("We should never have received data:"+data); - } catch (FacebookException e) { - Object serverError = e.getServerError(); - assertNotNull(serverError); - assertEquals(FacebookErrorData.class, serverError.getClass()); - FacebookErrorData errorData = (FacebookErrorData) serverError; - assertNotNull(errorData.error); - assertEquals(803, errorData.error.code); - } - } - - public void testSetDebugData() throws Exception { - BodyViaGson testParser = new BodyViaGson(Void.class); - testParser.enableDebugData(true); - BaseHttpRequest request = new BaseHttpRequest.Builder(). - setUrl("http://httpbin.org/ip"). - setResponseHandler(new BaseResponseHandler(testParser)). - build(); - - try { - TopheClient.parseRequest(request); - } catch (HttpDataParserException e) { - assertEquals("Bad data for GSON", e.getCause().getMessage()); - assertNotNull(e.getCause().getSourceData()); - } - } -} \ No newline at end of file diff --git a/Tophe-Ion/src/androidTest/java/co/tophe/ion/testParser.java b/Tophe-Ion/src/androidTest/java/co/tophe/ion/testParser.java deleted file mode 100644 index b76aebf..0000000 --- a/Tophe-Ion/src/androidTest/java/co/tophe/ion/testParser.java +++ /dev/null @@ -1,58 +0,0 @@ -package co.tophe.ion; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.test.AndroidTestCase; -import android.util.JsonReader; - -import co.tophe.TopheClient; -import co.tophe.HttpException; -import co.tophe.HttpRequestGet; -import co.tophe.ImmutableHttpRequest; -import co.tophe.ServerException; -import co.tophe.parser.BodyTransformChain; -import co.tophe.parser.ParserException; -import co.tophe.parser.XferTransform; - -public class testParser extends AndroidTestCase { - - @Override - public void setContext(Context context) { - super.setContext(context); - IonClient.setup(context); - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public void testCustomParser() throws ServerException { - HttpRequestGet apiGet = new HttpRequestGet("http://social.appxoid.com/json/get_apps_by_pages2", - new BodyTransformChain(new XferTransform() { - @Override - public Void transformData(InputStream inputStream, ImmutableHttpRequest request) throws IOException, ParserException { - // Process your InputStream - JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")); - try { - return readMessagesArray(reader); - } finally { - reader.close(); - } - } - }) - ); - - try { - Void parsed = TopheClient.parseRequest(apiGet); - } catch (HttpException e) { - // shit happens - } - } - - private Void readMessagesArray(JsonReader reader) { - return null; - - } -} diff --git a/Tophe-Ion/src/androidTest/libraries/okio-1.0.0-javadoc.jar b/Tophe-Ion/src/androidTest/libraries/okio-1.0.0-javadoc.jar deleted file mode 100644 index 6656661..0000000 Binary files a/Tophe-Ion/src/androidTest/libraries/okio-1.0.0-javadoc.jar and /dev/null differ diff --git a/Tophe-Ion/src/androidTest/libraries/okio-1.0.0-sources.jar b/Tophe-Ion/src/androidTest/libraries/okio-1.0.0-sources.jar deleted file mode 100644 index 9d2766a..0000000 Binary files a/Tophe-Ion/src/androidTest/libraries/okio-1.0.0-sources.jar and /dev/null differ diff --git a/Tophe-Ion/src/androidTest/libs/okio-1.0.0.jar b/Tophe-Ion/src/androidTest/libs/okio-1.0.0.jar deleted file mode 100644 index b41d025..0000000 Binary files a/Tophe-Ion/src/androidTest/libs/okio-1.0.0.jar and /dev/null differ diff --git a/Tophe-Ion/src/androidTest/libs/okio-1.0.0.jar.properties b/Tophe-Ion/src/androidTest/libs/okio-1.0.0.jar.properties deleted file mode 100644 index 0134e86..0000000 --- a/Tophe-Ion/src/androidTest/libs/okio-1.0.0.jar.properties +++ /dev/null @@ -1,2 +0,0 @@ -doc=../libraries/okio-1.0.0-javadoc.jar -src=../libraries/okio-1.0.0-sources.jar diff --git a/Tophe-Ion/src/androidTest/project.properties b/Tophe-Ion/src/androidTest/project.properties deleted file mode 100644 index 78ddcfc..0000000 --- a/Tophe-Ion/src/androidTest/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-19 -android.library.reference.1=../main diff --git a/Tophe-Ion/src/androidTest/res/.empty b/Tophe-Ion/src/androidTest/res/.empty deleted file mode 100644 index e69de29..0000000 diff --git a/Tophe-Ion/src/main/AndroidManifest.xml b/Tophe-Ion/src/main/AndroidManifest.xml deleted file mode 100644 index f68e8b2..0000000 --- a/Tophe-Ion/src/main/AndroidManifest.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/HttpEngineIon.java b/Tophe-Ion/src/main/java/co/tophe/ion/HttpEngineIon.java deleted file mode 100644 index f7e0b2c..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/HttpEngineIon.java +++ /dev/null @@ -1,301 +0,0 @@ -package co.tophe.ion; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Map.Entry; -import java.util.concurrent.ExecutionException; - -import org.apache.http.protocol.HTTP; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; - -import com.koushikdutta.async.ByteBufferList; -import com.koushikdutta.async.DataEmitter; -import com.koushikdutta.async.DataSink; -import com.koushikdutta.async.callback.CompletedCallback; -import com.koushikdutta.async.future.Future; -import com.koushikdutta.async.future.TransformFuture; -import com.koushikdutta.async.http.ConnectionClosedException; -import com.koushikdutta.async.http.filter.PrematureDataEndException; -import com.koushikdutta.async.parser.AsyncParser; -import com.koushikdutta.async.parser.ByteBufferListParser; -import com.koushikdutta.async.parser.JSONArrayParser; -import com.koushikdutta.async.parser.JSONObjectParser; -import com.koushikdutta.async.parser.StringParser; -import com.koushikdutta.async.stream.ByteBufferListInputStream; -import com.koushikdutta.ion.*; -import com.koushikdutta.ion.builder.Builders; -import com.koushikdutta.ion.builder.LoadBuilder; -import com.koushikdutta.ion.future.ResponseFuture; - -import co.tophe.AbstractHttpEngine; -import co.tophe.HttpConfig; -import co.tophe.HttpException; -import co.tophe.HttpResponse; -import co.tophe.HttpTimeoutException; -import co.tophe.ServerException; -import co.tophe.UploadProgressListener; -import co.tophe.body.HttpBodyJSON; -import co.tophe.body.HttpBodyMultiPart; -import co.tophe.body.HttpBodyParameters; -import co.tophe.body.HttpBodyString; -import co.tophe.body.HttpBodyUrlEncoded; -import co.tophe.ion.internal.HttpResponseIon; -import co.tophe.ion.internal.IonBody; -import co.tophe.ion.internal.IonHttpBodyJSON; -import co.tophe.ion.internal.IonHttpBodyMultiPart; -import co.tophe.ion.internal.IonHttpBodyString; -import co.tophe.ion.internal.IonHttpBodyUrlEncoded; -import co.tophe.log.LogManager; -import co.tophe.parser.ParserException; -import co.tophe.parser.Utils; -import co.tophe.parser.XferTransform; -import co.tophe.parser.XferTransformChain; -import co.tophe.parser.XferTransformInputStreamString; -import co.tophe.parser.XferTransformResponseInputStream; -import co.tophe.parser.XferTransformStringJSONArray; -import co.tophe.parser.XferTransformStringJSONObject; - -/** - * An {@link co.tophe.HttpEngine} that uses Ion to process the data. - * - * @param type of the data read from the HTTP response - * @param type of the exception raised when there's a server generated error. - */ -public class HttpEngineIon extends AbstractHttpEngine> { - public final Builders.Any.B requestBuilder; - private static final String ENGINE_SIGNATURE = "Ion-"+ com.koushikdutta.ion.BuildConfig.VERSION_CODE+"+AndroidAsync-"+ com.koushikdutta.async.BuildConfig.VERSION_CODE; - - protected HttpEngineIon(Builder builder, Ion ion) { - super(builder); - - final LoadBuilder ionLoadBuilder = ion.build(ion.getContext()); - this.requestBuilder = ionLoadBuilder.load(request.getHttpMethod(), null==request.getUri() ? null : request.getUri().toString()); - - final HttpBodyParameters sourceBody = request.getBodyParameters(); - final IonBody ionBody; - if (sourceBody instanceof HttpBodyMultiPart) - ionBody = new IonHttpBodyMultiPart((HttpBodyMultiPart) sourceBody); - else if (sourceBody instanceof HttpBodyJSON) - ionBody = new IonHttpBodyJSON((HttpBodyJSON) sourceBody); - else if (sourceBody instanceof HttpBodyUrlEncoded) - ionBody = new IonHttpBodyUrlEncoded((HttpBodyUrlEncoded) sourceBody); - else if (sourceBody instanceof HttpBodyString) - ionBody = new IonHttpBodyString((HttpBodyString) sourceBody); - else if (sourceBody != null) - throw new IllegalStateException("Unknown body type "+sourceBody); - else - ionBody = null; - - if (null != ionBody) { - ionBody.setOutputData(requestBuilder); - - final UploadProgressListener progressListener = request.getProgressListener(); - if (null != progressListener) { - requestBuilder.progress(new ProgressCallback() { - @Override - public void onProgress(long downloaded, long total) { - progressListener.onParamUploadProgress(request, null, (int) ((100 * downloaded) / total)); - } - }); - } - } - } - - @Override - protected String getEngineSignature() { - return ENGINE_SIGNATURE; - } - - @Override - protected void setContentLength(long contentLength) { - if (0L == contentLength) - super.setContentLength(contentLength); - } - - @Override - public void setHeadersAndConfig() { - if (request.getBodyParameters() instanceof HttpBodyMultiPart) { - setHeader(HTTP.CONTENT_TYPE, null); - } - - for (Entry entry : requestHeaders.entrySet()) { - requestBuilder.setHeader(entry.getKey(), entry.getValue()); - } - - if (null != responseHandler.followsRedirect()) { - requestBuilder.followRedirect(responseHandler.followsRedirect()); - } - - HttpConfig httpConfig = request.getHttpConfig(); - if (null != httpConfig) { - int readTimeout = httpConfig.getReadTimeout(request); - if (readTimeout >= 0) - requestBuilder.setTimeout(readTimeout); - } - } - - @Override - protected HttpResponseIon queryResponse() throws SE, HttpException { - XferTransform errorParser = responseHandler.errorParser; - XferTransform commonTransforms = Utils.getCommonXferTransform(responseHandler.contentParser, errorParser, true); - AsyncParser parser = getXferTransformParser(commonTransforms); - ResponseFuture req = requestBuilder.as(parser); - Future> withResponse = req.withResponse(); - try { - Response response = withResponse.get(); - HttpResponseIon ionResponse = new HttpResponseIon(response, commonTransforms); - setRequestResponse(ionResponse); - - Exception e = response.getException(); - if (null != e) { - throw exceptionToHttpException(e).build(); - } - - if (isHttpError(ionResponse)) { - Object data = response.getResult(); - XferTransform transformToResult = Utils.skipCommonTransforms(errorParser, commonTransforms); - SE errorData; - if (null == transformToResult) - errorData = (SE) data; - else - errorData = (SE) transformToResult.transformData(data, this); - throw errorData; - } - - return ionResponse; - - } catch (InterruptedException e) { - throw exceptionToHttpException(e).build(); - - } catch (ExecutionException e) { - throw exceptionToHttpException(e).build(); - - } catch (ParserException e) { - throw exceptionToHttpException(e).build(); - - } catch (IOException e) { - throw exceptionToHttpException(e).build(); - - } - } - - @Override - protected T responseToResult(HttpResponseIon response) throws ParserException, IOException { - Object data = response.getResult(); - XferTransform transformToResult = Utils.skipCommonTransforms(responseHandler.contentParser, response.getCommonTransform()); - if (null == transformToResult) - return (T) data; - - return (T) transformToResult.transformData(data, this); - } - - @Override - protected HttpException.AbstractBuilder exceptionToHttpException(Exception e) throws HttpException { - if (e instanceof IllegalArgumentException && e.getMessage().contains("bytesConsumed is negative")) { - // TODO only check for a Play Services error when the Exception stack trace comes from "com.google.android.gms.org.conscrypt" - Context context = IonHttpEngineFactory.getInstance(null).getDefaultIon().getContext(); - PackageManager pm = context.getPackageManager(); - String playServicesVersion = ""; - try { - PackageInfo pI = pm.getPackageInfo("com.google.android.gms", 0); - if (pI != null) { - playServicesVersion = String.valueOf(pI.versionCode) + '-' + pI.versionName; - } - } catch (PackageManager.NameNotFoundException ignored) { - } - LogManager.getLogger().e("Issue #99698 detected on PS:"+playServicesVersion, e); - } - - if (e instanceof ConnectionClosedException && e.getCause() instanceof Exception) { - return exceptionToHttpException((Exception) e.getCause()); - } - - if (e instanceof PrematureDataEndException) { - LogManager.getLogger().d("timeout for "+request); - HttpTimeoutException.Builder builder = new HttpTimeoutException.Builder(request, httpResponse); - builder.setErrorMessage("Timeout error " + e.getMessage()); - builder.setCause(e); - return builder; - } - - return super.exceptionToHttpException(e); - } - - private static final AsyncParser INPUT_STREAM_ASYNC_PARSER = new AsyncParser() { - @Override - public Future parse(DataEmitter emitter) { - return new ByteBufferListParser().parse(emitter) - .then(new TransformFuture() { - @Override - protected void transform(ByteBufferList result) throws Exception { - setComplete(new ByteBufferListInputStream(result)); - } - }); - } - - @Override - public void write(DataSink sink, InputStream value, CompletedCallback completed) { - throw new AssertionError("not implemented"); - } - }; - private static final AsyncParser STRING_ASYNC_PARSER = new StringParser(); - private static final AsyncParser JSON_OBJECT_ASYNC_PARSER = new JSONObjectParser(); - private static final AsyncParser JSON_ARRAY_ASYNC_PARSER = new JSONArrayParser(); - - private

AsyncParser

getXferTransformParser(XferTransform transform) { - if (transform == XferTransformResponseInputStream.INSTANCE) { - return (AsyncParser

) INPUT_STREAM_ASYNC_PARSER; - } - - if (transform instanceof XferTransformChain) { - final XferTransformChain chain = (XferTransformChain) transform; - if (chain.transforms.length != 0) { - if (chain.transforms[0] == XferTransformResponseInputStream.INSTANCE) { - if (chain.transforms.length == 1) { - return (AsyncParser

) INPUT_STREAM_ASYNC_PARSER; - } - - if (chain.transforms[1] == XferTransformInputStreamString.INSTANCE) { - if (chain.transforms.length == 2) { - return (AsyncParser

) STRING_ASYNC_PARSER; - } - - if (chain.transforms[2] == XferTransformStringJSONObject.INSTANCE) { - if (chain.transforms.length == 3) { - return (AsyncParser

) JSON_OBJECT_ASYNC_PARSER; - } - } - - if (chain.transforms[2] == XferTransformStringJSONArray.INSTANCE) { - if (chain.transforms.length == 3) { - return (AsyncParser

) JSON_ARRAY_ASYNC_PARSER; - } - } - } - - return new AsyncParser

() { - @Override - public Future

parse(DataEmitter emitter) { - Future inputStreamFuture = INPUT_STREAM_ASYNC_PARSER.parse(emitter); - return inputStreamFuture.then(new TransformFuture() { - @Override - protected void transform(InputStream result) throws Exception { - setComplete((P) chain.skipFirstTransform().transformData(result, HttpEngineIon.this)); - } - }); - } - - @Override - public void write(DataSink sink, P value, CompletedCallback completed) { - } - }; - } - } - } - - throw new IllegalStateException(); - } -} diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/IonClient.java b/Tophe-Ion/src/main/java/co/tophe/ion/IonClient.java deleted file mode 100644 index e8c39c1..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/IonClient.java +++ /dev/null @@ -1,137 +0,0 @@ -package co.tophe.ion; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.support.annotation.NonNull; - -import com.koushikdutta.async.http.AsyncHttpClientMiddleware; -import com.koushikdutta.async.http.AsyncSSLEngineConfigurator; -import com.koushikdutta.ion.Ion; - -import javax.net.ssl.SSLEngine; - -import co.tophe.TopheClient; -import co.tophe.engine.HttpEngineFactoryFallback; -import co.tophe.log.LogManager; - -/** - * Setup class for the TOPHE client to use the Ion engine for all HTTP processing. - * You must call {@link #setup(android.content.Context)} before using TOPHE with Ion. - *

Live {@link co.tophe.HttpStream HttpStream} is currently not supported by the Ion engine.

- * - * @author Created by Steve Lhomme on 15/07/2014. - * @see #setupIon(com.koushikdutta.ion.Ion) - */ -public final class IonClient { - - public static final int PLAY_SERVICES_BOGUS_CONSCRYPT = 5089034; // see https://github.com/koush/AndroidAsync/issues/210 - private static Boolean useConscrypt; - static Boolean forbidSSL; - private static Integer conscryptVersion; - - private IonClient() { - } - - /** - * Base setup function that sets Ion as the default {@link co.tophe.HttpEngine} - * - * @param context - */ - public static void setup(@NonNull Context context) { - IonHttpEngineFactory factory = IonHttpEngineFactory.getInstance(context); - TopheClient.setHttpEngineFactory(new HttpEngineFactoryFallback(factory, TopheClient.getHttpEngineFactory())); - - //factory.getDefaultIon().configure().setLogging("TOPHE", Log.VERBOSE); - - TopheClient.setup(context); - } - - /** - * Setup the specified Ion engine to work well with TOPHE. - *

It will disable SSLv3 and use Google's Conscrypt SSL stack from the Play Services if available.

- * @param ion - */ - public static void setupIon(Ion ion) { - if (null == conscryptVersion) { - conscryptVersion = 0; - PackageManager pm = ion.getContext().getPackageManager(); - try { - PackageInfo pI = pm.getPackageInfo("com.google.android.gms", 0); - if (pI != null) { - conscryptVersion = pI.versionCode; - } - } catch (PackageManager.NameNotFoundException ignored) { - try { - Class conscryptClass = ion.getClass().getClassLoader().loadClass("com.android.org.conscrypt.OpenSSLEngineImpl"); - if (conscryptClass != null) { - conscryptVersion = PLAY_SERVICES_BOGUS_CONSCRYPT /*CONSCRYPT_LACKS_SNI*/ + 1; // assume everything is fine - } - } catch (ClassNotFoundException ignored2) { - } - } - - - useConscrypt = conscryptVersion > PLAY_SERVICES_BOGUS_CONSCRYPT; - // dual parallel connection to Feedly results in data never received https://github.com/koush/ion/issues/428 - forbidSSL = false; //useConscrypt && conscryptVersion >= BOGUS_CONSCRYPT_DUAL_FEEDLY; - } - - ion.getConscryptMiddleware().enable(useConscrypt); - - if (useConscrypt) { - ion.getConscryptMiddleware().initialize(); - } - - ion.getHttpClient().getSSLSocketMiddleware().addEngineConfigurator(new AsyncSSLEngineConfigurator() { - @Override - public void configureEngine(SSLEngine engine, AsyncHttpClientMiddleware.GetSocketData data, String host, int port) { - if (false /*conscryptVersion > CONSCRYPT_LACKS_SNI || !useConscrypt*/) { - try { - Field sslParameters = engine.getClass().getDeclaredField("sslParameters"); - Field useSni = sslParameters.getType().getDeclaredField("useSni"); - Field peerHost = engine.getClass().getSuperclass().getDeclaredField("peerHost"); - Field peerPort = engine.getClass().getSuperclass().getDeclaredField("peerPort"); - - peerHost.setAccessible(true); - peerPort.setAccessible(true); - sslParameters.setAccessible(true); - useSni.setAccessible(true); - - Object sslp = sslParameters.get(engine); - - peerHost.set(engine, host); - peerPort.set(engine, port); - useSni.set(sslp, true); - } catch (Exception e) { - if (engine.getClass().getCanonicalName().contains(".conscrypt.")) { - if (forbidSSL /*&& conscryptVersion <= CONSCRYPT_LACKS_SNI*/) // we know that Conscrypt version - LogManager.getLogger().v("Failed to set the flags in " + engine + " conscryptVersion=" + conscryptVersion); - else - LogManager.getLogger().w("Failed to set the flags in " + engine + " conscryptVersion=" + conscryptVersion, e); - } else if (useConscrypt) { - LogManager.getLogger().e("Failed to set the flags in " + engine + " conscryptVersion=" + conscryptVersion, e); - } else { - LogManager.getLogger().i("Failed to set the flags in " + engine + " conscryptVersion=" + conscryptVersion, e); - } - } - } - - // disable SSLv3 except if it's alone - String[] protocols = engine.getEnabledProtocols(); - if (protocols != null && protocols.length > 1) { - List enabledProtocols = new ArrayList(Arrays.asList(protocols)); - if (enabledProtocols.remove("SSLv3")) { - protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]); - engine.setEnabledProtocols(protocols); - } - } - } - }); - } -} diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/IonHttpEngineFactory.java b/Tophe-Ion/src/main/java/co/tophe/ion/IonHttpEngineFactory.java deleted file mode 100644 index 0ac4192..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/IonHttpEngineFactory.java +++ /dev/null @@ -1,110 +0,0 @@ -package co.tophe.ion; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.koushikdutta.ion.Ion; - -import co.tophe.HttpEngine; -import co.tophe.HttpEngineFactory; -import co.tophe.HttpResponse; -import co.tophe.ResponseHandler; -import co.tophe.ServerException; -import co.tophe.parser.Utils; -import co.tophe.parser.XferTransform; -import co.tophe.parser.XferTransformChain; -import co.tophe.parser.XferTransformInputStreamHttpStream; - -/** - * An {@link co.tophe.HttpEngineFactory} to create {@link co.tophe.ion.HttpEngineIon} objects for the submitted requests or {@code null} - * for unsupported requests (like with a live {@link co.tophe.HttpStream HttpStream} output). - * - * @author Created by Steve Lhomme on 15/07/2014. - * @see #getInstance(android.content.Context) - * @see IonClient#setupIon(com.koushikdutta.ion.Ion) - */ -public class IonHttpEngineFactory implements HttpEngineFactory { - - private static IonHttpEngineFactory INSTANCE; - //public static final int BOGUS_CONSCRYPT_DUAL_FEEDLY = 6587000; // see https://github.com/koush/ion/issues/443 - //public static final int CONSCRYPT_LACKS_SNI = 6599038; // 6587030 to 6599038 don't have it see https://github.com/koush/ion/issues/428 - - private final Ion ion; - - public static IonHttpEngineFactory getInstance(Context context) { - if (null == INSTANCE) { - INSTANCE = new IonHttpEngineFactory(context); - } - return INSTANCE; - } - - private IonHttpEngineFactory(Context context) { - if (context == null) { - throw new NullPointerException("Ion HTTP request with no Context"); - } - - ion = Ion.getDefault(context); - IonClient.setupIon(ion); - } - - /** - * Get the {@link Ion} instance used by default by TOPHE. - */ - @NonNull - public Ion getDefaultIon() { - return ion; - } - - @Nullable - @Override - public HttpEngine createEngine(HttpEngine.Builder builder) { - return createEngine(builder, ion, false); - } - - /** - * - * @param builder - * @param ion - * @param allowBogusSSL Sometimes Ion maybe have problems with SSL, especially with Conscrypt, but you may decide to take the - * risk anyway and use it in conditions where it may fail - * @param - * @param - * @return - */ - @Nullable - public HttpEngine createEngine(HttpEngine.Builder builder, Ion ion, boolean allowBogusSSL) { - if (!allowBogusSSL && IonClient.forbidSSL && "https".equals(builder.getHttpRequest().getUri().getScheme())) { - return null; - } - - if (!canHandleXferTransform(builder.getResponseHandler().contentParser)) - return null; - - if (!errorCompatibleWithData(builder.getResponseHandler())) - // Ion returns the data fully parsed so if we don't have common ground to parse the data and the error data, Ion can't handle the request - return null; - - return new HttpEngineIon(builder, ion); - } - - private static boolean canHandleXferTransform(XferTransform contentParser) { - if (contentParser instanceof XferTransformChain) { - XferTransformChain parser = (XferTransformChain) contentParser; - for (XferTransform transform : parser.transforms) { - if (transform == XferTransformInputStreamHttpStream.INSTANCE) - return false; - } - } - return true; - } - - /** - * See if we can find common ground to parse the data and the error data inside Ion - * @param responseHandler - * @return whether Ion will be able to parse the data and the error in its processing thread - */ - private static boolean errorCompatibleWithData(ResponseHandler responseHandler) { - return Utils.getCommonXferTransform(responseHandler.contentParser, responseHandler.errorParser, false) != null; - } -} diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/internal/HttpResponseIon.java b/Tophe-Ion/src/main/java/co/tophe/ion/internal/HttpResponseIon.java deleted file mode 100644 index c32f992..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/internal/HttpResponseIon.java +++ /dev/null @@ -1,98 +0,0 @@ -package co.tophe.ion.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.Map; - -import org.apache.http.protocol.HTTP; - -import android.text.TextUtils; - -import com.koushikdutta.ion.Response; - -import co.tophe.HttpResponse; -import co.tophe.ServerException; -import co.tophe.parser.XferTransform; - -/** - * @author Created by Steve Lhomme on 09/07/2014. - */ -public class HttpResponseIon implements HttpResponse { - private final Response response; - private final XferTransform commonTransform; - - public HttpResponseIon(Response response, XferTransform commonTransform) { - this.response = response; - this.commonTransform = commonTransform; - } - - @Override - public String getContentType() { - return getHeaderField(HTTP.CONTENT_TYPE); - } - - @Override - public int getResponseCode() { - return response.getHeaders().code(); - } - - @Override - public Map> getHeaderFields() { - return response.getHeaders().getHeaders().getMultiMap(); - } - - @Override - public String getHeaderField(String name) { - return response.getHeaders().getHeaders().get(name); - } - - @Override - public int getContentLength() { - String contentLength = getHeaderField(HTTP.CONTENT_LEN); - if (TextUtils.isEmpty(contentLength)) - return -1; - return Integer.parseInt(contentLength); - } - - @Override - public String getResponseMessage() { - return response.getHeaders().message(); - } - - @Override - public String getContentEncoding() { - return getHeaderField(HTTP.CONTENT_ENCODING); - } - - @Override - public void disconnect() { - // TODO see if we can cancel a Ion response while it's processing - } - - @Override - public InputStream getContentStream() throws IOException { - if (response.getResult() instanceof InputStream) - return (InputStream) response.getResult(); - - if (response.getException() instanceof ServerException) { - ServerException exception = (ServerException) response.getException(); - if (exception.getServerError() instanceof InputStream) - return (InputStream) exception.getServerError(); - } - - throw new IOException("trying to read an InputStream from Ion result:"+response.getResult()+" error:"+response.getException()); - } - - public T getResult() { - return response.getResult(); - } - - Exception getException() { - return response.getException(); - } - - public XferTransform getCommonTransform() { - return commonTransform; - } -} diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/internal/InputStreamPart.java b/Tophe-Ion/src/main/java/co/tophe/ion/internal/InputStreamPart.java deleted file mode 100644 index 5aafa9e..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/internal/InputStreamPart.java +++ /dev/null @@ -1,29 +0,0 @@ -package co.tophe.ion.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; - -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicNameValuePair; - -import com.koushikdutta.async.http.body.StreamPart; - -public class InputStreamPart extends StreamPart { - - private final InputStream inputStream; - - InputStreamPart(String streamName, InputStream value, long length) { - super(streamName, (int) length, new ArrayList() { - { - add(new BasicNameValuePair("filename", "rawstream")); - } - }); - this.inputStream = value; - } - - @Override - protected InputStream getInputStream() throws IOException { - return inputStream; - } -} \ No newline at end of file diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonBody.java b/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonBody.java deleted file mode 100644 index 43a9014..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonBody.java +++ /dev/null @@ -1,10 +0,0 @@ -package co.tophe.ion.internal; - -import com.koushikdutta.ion.builder.Builders; - -/** - * @author Created by Steve Lhomme on 15/07/2014. - */ -public interface IonBody { - void setOutputData(Builders.Any.B requestBuilder); -} diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyJSON.java b/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyJSON.java deleted file mode 100644 index 23f598f..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyJSON.java +++ /dev/null @@ -1,30 +0,0 @@ -package co.tophe.ion.internal; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.koushikdutta.async.http.body.JSONObjectBody; -import com.koushikdutta.ion.builder.Builders; -import co.tophe.body.HttpBodyJSON; - -/** - * @author Created by Steve Lhomme on 15/07/2014. - */ -public class IonHttpBodyJSON extends HttpBodyJSON implements IonBody { - - public IonHttpBodyJSON(HttpBodyJSON sourceBody) { - super(sourceBody); - } - - @Override - public String getContentType() { - return JSONObjectBody.CONTENT_TYPE; - } - - @Override - public void setOutputData(Builders.Any.B requestBuilder) { - if (jsonElement instanceof JsonObject) - requestBuilder.setJsonObjectBody((JsonObject) jsonElement); - else if (jsonElement instanceof JsonArray) - requestBuilder.setJsonArrayBody((JsonArray) jsonElement); - } -} diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyMultiPart.java b/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyMultiPart.java deleted file mode 100644 index 7371c15..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyMultiPart.java +++ /dev/null @@ -1,61 +0,0 @@ -package co.tophe.ion.internal; - -import java.io.File; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -import android.text.TextUtils; - -import com.koushikdutta.async.http.body.FilePart; -import com.koushikdutta.async.http.body.MultipartFormDataBody; -import com.koushikdutta.async.http.body.Part; -import com.koushikdutta.ion.builder.Builders; -import co.tophe.body.HttpBodyMultiPart; - -/** - * @author Created by Steve Lhomme on 15/07/2014. - */ -public class IonHttpBodyMultiPart extends HttpBodyMultiPart implements IonBody { - - public IonHttpBodyMultiPart(HttpBodyMultiPart sourceBody) { - super(sourceBody); - } - - @Override - public String getContentType() { - return MultipartFormDataBody.CONTENT_TYPE; - } - - @Override - public void setOutputData(Builders.Any.B requestBuilder) { - for (HttpParam param : mParams) { - if (param.value instanceof File) { - FilePart part = new FilePart(param.name, (File) param.value); - if (!TextUtils.isEmpty(param.contentType)) - part.setContentType(param.contentType); - part.getRawHeaders().add("Content-Transfer-Encoding", "binary"); - List partList = new ArrayList(1); - partList.add(part); - requestBuilder.addMultipartParts(partList); - /*if (!TextUtils.isEmpty(param.contentType)) - requestBuilder.setMultipartFile(param.name, param.contentType, (File) param.value); - else - requestBuilder.setMultipartFile(param.name, (File) param.value);*/ - } else if (param.value instanceof InputStream) { - InputStreamPart part = new InputStreamPart(param.name, (InputStream) param.value, param.length); - if (!TextUtils.isEmpty(param.contentType)) - part.setContentType(param.contentType); - part.getRawHeaders().add("Content-Transfer-Encoding", "binary"); - List partList = new ArrayList(1); - partList.add(part); - requestBuilder.addMultipartParts(partList); - } - } - for (HttpParam param : mParams) { - if (param.value instanceof String) { - requestBuilder.setMultipartParameter(param.name, (String) param.value); - } - } - } -} diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyString.java b/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyString.java deleted file mode 100644 index 6e516fb..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyString.java +++ /dev/null @@ -1,33 +0,0 @@ -package co.tophe.ion.internal; - -import com.koushikdutta.async.http.body.StringBody; -import com.koushikdutta.ion.builder.Builders; -import co.tophe.body.HttpBodyString; - -/** - * @author Created by Steve Lhomme on 15/07/2014. - */ -public class IonHttpBodyString extends HttpBodyString implements IonBody { - - public IonHttpBodyString(HttpBodyString sourceBody) { - super(sourceBody); - } - - @Override - public String getContentType() { - return StringBody.CONTENT_TYPE; - } - - @Override - public void setOutputData(Builders.Any.B requestBuilder) { - requestBuilder.setStringBody(value); - } - - private class TypedStringBody extends StringBody { - // TODO use this body to use our own Content-Type and get rid of the getContentType() overriding - @Override - public String getContentType() { - return IonHttpBodyString.this.getContentType(); - } - } -} diff --git a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyUrlEncoded.java b/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyUrlEncoded.java deleted file mode 100644 index e373e01..0000000 --- a/Tophe-Ion/src/main/java/co/tophe/ion/internal/IonHttpBodyUrlEncoded.java +++ /dev/null @@ -1,28 +0,0 @@ -package co.tophe.ion.internal; - -import org.apache.http.NameValuePair; - -import com.koushikdutta.async.http.body.UrlEncodedFormBody; -import com.koushikdutta.ion.builder.Builders; -import co.tophe.body.HttpBodyUrlEncoded; - -/** - * @author Created by Steve Lhomme on 15/07/2014. - */ -public class IonHttpBodyUrlEncoded extends HttpBodyUrlEncoded implements IonBody{ - public IonHttpBodyUrlEncoded(HttpBodyUrlEncoded sourceBody) { - super(sourceBody); - } - - @Override - public String getContentType() { - return UrlEncodedFormBody.CONTENT_TYPE; - } - - @Override - public void setOutputData(Builders.Any.B requestBuilder) { - for (NameValuePair param : mParams) { - requestBuilder.setBodyParameter(param.getName(), param.getValue()); - } - } -} diff --git a/Tophe-Ion/src/main/libraries/androidasync-1.4.1-javadoc.jar b/Tophe-Ion/src/main/libraries/androidasync-1.4.1-javadoc.jar deleted file mode 100644 index 9c0fb16..0000000 Binary files a/Tophe-Ion/src/main/libraries/androidasync-1.4.1-javadoc.jar and /dev/null differ diff --git a/Tophe-Ion/src/main/libraries/androidasync-1.4.1-sources.jar b/Tophe-Ion/src/main/libraries/androidasync-1.4.1-sources.jar deleted file mode 100644 index 7e09024..0000000 Binary files a/Tophe-Ion/src/main/libraries/androidasync-1.4.1-sources.jar and /dev/null differ diff --git a/Tophe-Ion/src/main/libraries/ion-1.4.1-javadoc.jar b/Tophe-Ion/src/main/libraries/ion-1.4.1-javadoc.jar deleted file mode 100644 index 92bf346..0000000 Binary files a/Tophe-Ion/src/main/libraries/ion-1.4.1-javadoc.jar and /dev/null differ diff --git a/Tophe-Ion/src/main/libraries/ion-1.4.1-sources.jar b/Tophe-Ion/src/main/libraries/ion-1.4.1-sources.jar deleted file mode 100644 index 005efd9..0000000 Binary files a/Tophe-Ion/src/main/libraries/ion-1.4.1-sources.jar and /dev/null differ diff --git a/Tophe-Ion/src/main/libs/androidasync-1.4.1.jar b/Tophe-Ion/src/main/libs/androidasync-1.4.1.jar deleted file mode 100644 index 44315aa..0000000 Binary files a/Tophe-Ion/src/main/libs/androidasync-1.4.1.jar and /dev/null differ diff --git a/Tophe-Ion/src/main/libs/androidasync-1.4.1.jar.properties b/Tophe-Ion/src/main/libs/androidasync-1.4.1.jar.properties deleted file mode 100644 index 7153148..0000000 --- a/Tophe-Ion/src/main/libs/androidasync-1.4.1.jar.properties +++ /dev/null @@ -1,2 +0,0 @@ -doc=../libraries/androidasync-1.4.1-javadoc.jar -src=../libraries/androidasync-1.4.1-sources.jar diff --git a/Tophe-Ion/src/main/libs/ion-1.4.1.jar b/Tophe-Ion/src/main/libs/ion-1.4.1.jar deleted file mode 100644 index 4dd4c95..0000000 Binary files a/Tophe-Ion/src/main/libs/ion-1.4.1.jar and /dev/null differ diff --git a/Tophe-Ion/src/main/libs/ion-1.4.1.jar.properties b/Tophe-Ion/src/main/libs/ion-1.4.1.jar.properties deleted file mode 100644 index 3bb60b9..0000000 --- a/Tophe-Ion/src/main/libs/ion-1.4.1.jar.properties +++ /dev/null @@ -1,2 +0,0 @@ -doc=../libraries/ion-1.4.1-javadoc.jar -src=../libraries/ion-1.4.1-sources.jar diff --git a/Tophe-Ion/src/main/project.properties b/Tophe-Ion/src/main/project.properties deleted file mode 100644 index 00c8ccb..0000000 --- a/Tophe-Ion/src/main/project.properties +++ /dev/null @@ -1,16 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-19 -android.library.reference.1=../../../Tophe -android.library=true diff --git a/Tophe-Ion/src/main/res/.empty b/Tophe-Ion/src/main/res/.empty deleted file mode 100644 index e69de29..0000000 diff --git a/Tophe-Ion/src/main/src/.empty b/Tophe-Ion/src/main/src/.empty deleted file mode 100644 index e69de29..0000000 diff --git a/Tophe-OAuth1/build.gradle b/Tophe-OAuth1/build.gradle index eef220e..a260d69 100644 --- a/Tophe-OAuth1/build.gradle +++ b/Tophe-OAuth1/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.library' dependencies { - def artifactTophe = 'co.tophe:tophe:1.0.1' + def artifactTophe = 'co.tophe:tophe:1.0.0' def localTophe = ':Tophe' - def useLocalTophe = false + def useLocalTophe = true allprojects { rootProject.allprojects.project.each { @@ -15,12 +15,13 @@ dependencies { } } - if (useLocalTophe) { - println 'Using local Tophe library in ' + project.path - compile project(localTophe) - } else { - compile artifactTophe - } +// if (useLocalTophe) { + // println 'Using local Tophe library in ' + project.path + // compile project(localTophe) + //} else { + // compile artifactTophe + //} + compile artifactTophe compile 'oauth.signpost:signpost-core:1.2.1.2' } diff --git a/Tophe-OAuth1/gradle.properties b/Tophe-OAuth1/gradle.properties index f972362..74136be 100644 --- a/Tophe-OAuth1/gradle.properties +++ b/Tophe-OAuth1/gradle.properties @@ -19,4 +19,4 @@ VERSION_CODE=10001 ANDROID_BUILD_TARGET_SDK_VERSION=21 ANDROID_BUILD_SDK_VERSION=21 ANDROID_BUILD_TOOLS_VERSION=21.1.2 -ANDROID_BUILD_MIN_SDK_VERSION=9 +ANDROID_BUILD_MIN_SDK_VERSION=14 diff --git a/Tophe/AndroidManifest.xml b/Tophe/AndroidManifest.xml index d593784..1dee3f8 100644 --- a/Tophe/AndroidManifest.xml +++ b/Tophe/AndroidManifest.xml @@ -7,7 +7,6 @@ diff --git a/Tophe/build.gradle b/Tophe/build.gradle index fa814c3..8b152f1 100644 --- a/Tophe/build.gradle +++ b/Tophe/build.gradle @@ -1,3 +1,13 @@ +allprojects { + repositories { + mavenCentral() + jcenter() + maven { + url "https://maven.google.com" + } + } +} + apply plugin: 'com.android.library' dependencies { @@ -23,7 +33,7 @@ dependencies { } androidTestCompile 'com.squareup.okio:okio:1.0.1' - androidTestCompile ('com.google.android.gms:play-services-base:6.5.87') { + androidTestCompile ('com.google.android.gms:play-services-base:15.0.0') { exclude group: 'com.android.support', module: 'support-annotations' } } @@ -33,7 +43,7 @@ android { buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION defaultConfig { - minSdkVersion Math.max(9, Integer.parseInt(ANDROID_BUILD_MIN_SDK_VERSION)) + minSdkVersion Math.max(14, Integer.parseInt(ANDROID_BUILD_MIN_SDK_VERSION)) targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) versionCode Integer.parseInt(project.VERSION_CODE) versionName project.VERSION_NAME @@ -58,7 +68,7 @@ android { buildTypes { release { - proguardFiles getDefaultProguardFile('proguard-project.txt'), 'proguard-rules.txt' + proguardFile file('proguard-project.txt') } } diff --git a/Tophe/gradle.properties b/Tophe/gradle.properties index 2d0d179..f541ede 100644 --- a/Tophe/gradle.properties +++ b/Tophe/gradle.properties @@ -19,4 +19,4 @@ VERSION_CODE=10001 ANDROID_BUILD_TARGET_SDK_VERSION=21 ANDROID_BUILD_SDK_VERSION=21 ANDROID_BUILD_TOOLS_VERSION=21.1.2 -ANDROID_BUILD_MIN_SDK_VERSION=9 +ANDROID_BUILD_MIN_SDK_VERSION=14 diff --git a/Tophe/proguard-android.txt b/Tophe/proguard-android.txt new file mode 100644 index 0000000..5ba246e --- /dev/null +++ b/Tophe/proguard-android.txt @@ -0,0 +1,36 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +-keepnames class co.tophe.** { *; } +-keepnames interface co.tophe.** { *; } + +# AndroidAsync +-keepnames class com.koushikdutta.async.** { *; } +-keepnames interface com.koushikdutta.async.** { *; } + +# Ion +-keepnames class com.koushikdutta.ion.** { *; } +-keepnames interface com.koushikdutta.ion.** { *; } + +# Okio oddities +-keepnames class okio.** { *; } +-keepnames interface okio.** { *; } +-dontwarn java.nio.file.* diff --git a/Tophe/src/co/tophe/AbstractHttpEngine.java b/Tophe/src/co/tophe/AbstractHttpEngine.java index 3e255be..b685987 100644 --- a/Tophe/src/co/tophe/AbstractHttpEngine.java +++ b/Tophe/src/co/tophe/AbstractHttpEngine.java @@ -18,6 +18,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; +import android.util.Log; import co.tophe.log.LogManager; import co.tophe.parser.ParserException; @@ -84,7 +85,7 @@ public final HttpRequestInfo getHttpRequest() { *

Usually you don't need to call this yourself, the engine will do it

* @throws HttpSignException */ - public final void prepareEngine() throws HttpSignException { + public void prepareEngine() throws HttpSignException { /* HttpResponse resp = null; try { @@ -97,7 +98,7 @@ public final void prepareEngine() throws HttpSignException { final long contentLength; if (null != request.getBodyParameters()) { - setHeader(HTTP.CONTENT_TYPE, request.getBodyParameters().getContentType()); + //setHeader(HTTP.CONTENT_TYPE, request.getBodyParameters().getContentType()); contentLength = request.getBodyParameters().getContentLength(); } else { contentLength = 0L; @@ -117,6 +118,7 @@ protected void setContentLength(long contentLength) { setHeader(HTTP.CONTENT_LEN, Long.toString(contentLength)); } + /** * Set the HTTP headers on the internal connection, the follow redirect setting and the timeout * @throws HttpException @@ -177,11 +179,11 @@ public final T call() throws SE, HttpException { String expectedMimeType = request.getHeader(HttpRequest.HEADER_ACCEPT); if (!TextUtils.isEmpty(expectedMimeType)) { MediaType expectedType = MediaType.parse(expectedMimeType); - if (null!=expectedType && !expectedType.equalsType(MediaType.parse(httpResponse.getContentType()))) { + /*if (null!=expectedType && !expectedType.equalsType(MediaType.parse(httpResponse.getContentType()))) { HttpMimeException.Builder builder = new HttpMimeException.Builder(request, httpResponse); builder.setErrorMessage("Expected '" + expectedMimeType + "' got '" + httpResponse.getContentType()); throw builder.build(); - } + }*/ } return responseToResult(httpResponse); diff --git a/Tophe/src/co/tophe/BasicHttpConfig.java b/Tophe/src/co/tophe/BasicHttpConfig.java index 14927d2..6a73e32 100644 --- a/Tophe/src/co/tophe/BasicHttpConfig.java +++ b/Tophe/src/co/tophe/BasicHttpConfig.java @@ -11,7 +11,7 @@ */ public class BasicHttpConfig implements HttpConfig { - public static final int READ_TIMEOUT_IN_MS = (int) (6 * DateUtils.SECOND_IN_MILLIS); + public static final int READ_TIMEOUT_IN_MS = (int) (20 * DateUtils.SECOND_IN_MILLIS); public static final int READ_TIMEOUT_LONG_POST_IN_MS = (int) (80 * DateUtils.SECOND_IN_MILLIS); public static final BasicHttpConfig INSTANCE = new BasicHttpConfig(); diff --git a/Tophe/src/co/tophe/RawHttpRequest.java b/Tophe/src/co/tophe/RawHttpRequest.java index 7cd77b8..67788c9 100644 --- a/Tophe/src/co/tophe/RawHttpRequest.java +++ b/Tophe/src/co/tophe/RawHttpRequest.java @@ -149,12 +149,12 @@ public B setUrl(String url, @Nullable HttpUriParameters uriParams) { uriParams.appendUriParameters(uriBuilder); this.uri = uriBuilder.build(); } - try { + /*try { URI utfSafeUri = new URI(this.uri.getScheme(), this.uri.getAuthority(), this.uri.getPath(), this.uri.getQuery(), this.uri.getFragment()); this.uri = Uri.parse(utfSafeUri.toASCIIString()); } catch (URISyntaxException e) { - } + }*/ } return (B) this; diff --git a/Tophe/src/co/tophe/ServerException.java b/Tophe/src/co/tophe/ServerException.java index cc094d5..4171440 100644 --- a/Tophe/src/co/tophe/ServerException.java +++ b/Tophe/src/co/tophe/ServerException.java @@ -2,6 +2,9 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.util.Log; + +import java.io.IOException; /** * Thrown when the server returns an HTTP error. @@ -26,11 +29,22 @@ public class ServerException extends TopheException { public static final int HTTP_STATUS_GATEWAY_TIMEOUT = 504; public static final int HTTP_STATUS_INTERNAL = 506; - private final Object serverError; + private Object serverError; + + public ServerException() { + super(); + serverError = null; + + } public ServerException(@NonNull ImmutableHttpRequest request, @Nullable Object serverError) { - super(request.getHttpRequest(), request.getHttpResponse(), "serverError="+ String.valueOf(serverError)); - this.serverError = serverError; + super(request.getHttpRequest(), request.getHttpResponse(), "serverError=" + String.valueOf(serverError)); + try { + Log.e("", "new ServerError() " + request.getHttpResponse().getResponseCode() + ":" + request.getHttpResponse().getResponseMessage().toString()); + } catch (IOException e) { + e.printStackTrace(); + } + this.serverError = serverError; } @NonNull @@ -47,4 +61,8 @@ public HttpResponse getHttpResponse() { public Object getServerError() { return serverError; } + + protected void setServerError(Object err) { + serverError = err; + } } diff --git a/Tophe/src/co/tophe/TopheClient.java b/Tophe/src/co/tophe/TopheClient.java index e438052..372627d 100644 --- a/Tophe/src/co/tophe/TopheClient.java +++ b/Tophe/src/co/tophe/TopheClient.java @@ -28,13 +28,17 @@ public final class TopheClient { private static Boolean useConscrypt; + public static void nullify() { + HttpEngineFactoryUrlConnection.INSTANCE.nullify(); + } + /** * Setup internal values of the {@link TopheClient} using the provided {@link Context} *

The user agent is deduced from the app name of the {@code context} if it's not {@code null}

* * @param context Used to get a proper User Agent for your app, may be {@code null} */ - public static void setup(Context context) { + public static void setup(final Context context) { if (null != context) { ApplicationInfo applicationInfo = context.getApplicationInfo(); xRequestedWith = applicationInfo.packageName; @@ -64,26 +68,26 @@ public static void setup(Context context) { } catch (PackageManager.NameNotFoundException ignored) { } + if (useConscrypt)*/ - { + + try { + Class providerInstaller = Class.forName("com.google.android.gms.security.ProviderInstaller"); + Method mInsertProvider = providerInstaller.getDeclaredMethod("installIfNeeded", Context.class); + mInsertProvider.invoke(null, context); + + } catch (Throwable ignored) { try { - Class providerInstaller = Class.forName("com.google.android.gms.security.ProviderInstaller"); - Method mInsertProvider = providerInstaller.getDeclaredMethod("installIfNeeded", Context.class); + Context gms = context.createPackageContext("com.google.android.gms", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + Class clazz = gms.getClassLoader().loadClass("com.google.android.gms.common.security.ProviderInstallerImpl"); + Method mInsertProvider = clazz.getDeclaredMethod("insertProvider", Context.class); mInsertProvider.invoke(null, context); - - } catch (Throwable ignored) { - try { - Context gms = context.createPackageContext("com.google.android.gms", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); - Class clazz = gms.getClassLoader().loadClass("com.google.android.gms.common.security.ProviderInstallerImpl"); - Method mInsertProvider = clazz.getDeclaredMethod("insertProvider", Context.class); - mInsertProvider.invoke(null, context); - } catch (Throwable e) { - } + } catch (Throwable e) { } } - } - HttpEngineFactoryUrlConnection.INSTANCE.init(); + HttpEngineFactoryUrlConnection.INSTANCE.init(); + } } } diff --git a/Tophe/src/co/tophe/TopheException.java b/Tophe/src/co/tophe/TopheException.java index 93d5167..03f00ca 100644 --- a/Tophe/src/co/tophe/TopheException.java +++ b/Tophe/src/co/tophe/TopheException.java @@ -1,25 +1,30 @@ package co.tophe; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; - /** * Base exception to catch {@link co.tophe.ServerException} and {@link co.tophe.HttpException} at the same time. * * @author Created by robUx4 on 29/09/2014. */ public abstract class TopheException extends Exception { - private final int httpStatusCode; + private int httpStatusCode; private final HttpResponse response; private final HttpRequestInfo request; + public TopheException() { + response = null; + request = null; + } + /** * Constructor. * @@ -31,7 +36,11 @@ protected TopheException(@NonNull HttpRequestInfo request, @Nullable HttpRespons super(detailMessage); this.request = request; this.response = response; - this.httpStatusCode = getHttpStatusCode(response); + try { + + } catch (Exception e) { + this.httpStatusCode = getHttpStatusCode(response); + } } /** @@ -43,6 +52,10 @@ public int getStatusCode() { return httpStatusCode; } + public void setStatusCode(int code){ + httpStatusCode = code; + } + /** * The {@link HttpRequestInfo} that generated this Exception. */ diff --git a/Tophe/src/co/tophe/async/AsyncTask.java b/Tophe/src/co/tophe/async/AsyncTask.java index e5ce22b..b880b2b 100644 --- a/Tophe/src/co/tophe/async/AsyncTask.java +++ b/Tophe/src/co/tophe/async/AsyncTask.java @@ -1,5 +1,10 @@ package co.tophe.async; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + import java.io.Closeable; import java.io.IOException; import java.util.HashMap; @@ -10,11 +15,6 @@ import java.util.concurrent.Future; import java.util.concurrent.FutureTask; -import android.os.Handler; -import android.os.Looper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - import co.tophe.HttpEngine; import co.tophe.HttpRequest; import co.tophe.ResponseHandler; diff --git a/Tophe/src/co/tophe/body/BasicNameValuePair.java b/Tophe/src/co/tophe/body/BasicNameValuePair.java new file mode 100644 index 0000000..03686ad --- /dev/null +++ b/Tophe/src/co/tophe/body/BasicNameValuePair.java @@ -0,0 +1,175 @@ +package co.tophe.body; + +/** + * Created by Denis Babak on 06/07/16. + */ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicNameValuePair.java $ + * $Revision: 604625 $ + * $Date: 2007-12-16 06:11:11 -0800 (Sun, 16 Dec 2007) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +import android.text.TextUtils; + +/** + * A simple class encapsulating an attribute/value pair. + *

+ * This class comforms to the generic grammar and formatting rules outlined in the + * Section 2.2 + * and + * Section 3.6 + * of RFC 2616 + *

+ * 2.2 Basic Rules + *

+ * The following rules are used throughout this specification to describe basic parsing constructs. + * The US-ASCII coded character set is defined by ANSI X3.4-1986. + *

+ *
+ *     OCTET          = 
+ *     CHAR           = 
+ *     UPALPHA        = 
+ *     LOALPHA        = 
+ *     ALPHA          = UPALPHA | LOALPHA
+ *     DIGIT          = 
+ *     CTL            = 
+ *     CR             = 
+ *     LF             = 
+ *     SP             = 
+ *     HT             = 
+ *     <">            = 
+ * 
+ *

+ * Many HTTP/1.1 header field values consist of words separated by LWS or special + * characters. These special characters MUST be in a quoted string to be used within + * a parameter value (as defined in section 3.6). + *

+ *

+ * token          = 1*
+ * separators     = "(" | ")" | "<" | ">" | "@"
+ *                | "," | ";" | ":" | "\" | <">
+ *                | "/" | "[" | "]" | "?" | "="
+ *                | "{" | "}" | SP | HT
+ * 
+ *

+ * A string of text is parsed as a single word if it is quoted using double-quote marks. + *

+ *
+ * quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
+ * qdtext         = >
+ * 
+ *

+ * The backslash character ("\") MAY be used as a single-character quoting mechanism only + * within quoted-string and comment constructs. + *

+ *
+ * quoted-pair    = "\" CHAR
+ * 
+ * 3.6 Transfer Codings + *

+ * Parameters are in the form of attribute/value pairs. + *

+ *
+ * parameter               = attribute "=" value
+ * attribute               = token
+ * value                   = token | quoted-string
+ * 
+ * + * @author Oleg Kalnichevski + * + */ +public class BasicNameValuePair implements NameValuePair, Cloneable { + + private final String name; + private final String value; + + /** + * Default Constructor taking a name and a value. The value may be null. + * + * @param name The name. + * @param value The value. + */ + public BasicNameValuePair(final String name, final String value) { + super(); + if (name == null) { + throw new IllegalArgumentException("Name may not be null"); + } + this.name = name; + this.value = value; + } + + /** + * Returns the name. + * + * @return String name The name + */ + public String getName() { + return this.name; + } + + /** + * Returns the value. + * + * @return String value The current value. + */ + public String getValue() { + return this.value; + } + + + /** + * Get a string representation of this pair. + * + * @return A string representation. + */ + public String toString() { + return name + "=" + value; + } + + public boolean equals(final Object object) { + if (object == null) return false; + if (this == object) return true; + if (object instanceof NameValuePair) { + BasicNameValuePair that = (BasicNameValuePair) object; + return this.name.equals(that.name) + && TextUtils.equals(this.value, that.value); + } else { + return false; + } + } + + public int hashCode() { + return name.hashCode() ^ value.hashCode(); + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + +} diff --git a/Tophe/src/co/tophe/body/HttpBodyMultiPart.java b/Tophe/src/co/tophe/body/HttpBodyMultiPart.java index b34b4ea..36c0bef 100644 --- a/Tophe/src/co/tophe/body/HttpBodyMultiPart.java +++ b/Tophe/src/co/tophe/body/HttpBodyMultiPart.java @@ -21,13 +21,15 @@ *

Useful to send {@link File} or {@link InputStream}

*/ public class HttpBodyMultiPart implements HttpBodyParameters { - protected final ArrayList mParams; + public final ArrayList mParams; private static final String charset = "UTF-8"; private static final String CRLF = "\r\n"; public static final String boundary = "t0Ph3Multip4rt"; private static final String boundarySplit = "--"; + public long apacheContentLength = -1; + /** * Constructor with an initial amount of parameters to hold * @param capacity amount of parameters the object will get @@ -200,6 +202,8 @@ public void writeBodyTo(OutputStream output, HttpRequestInfo request, UploadProg @Override public long getContentLength() { + if(apacheContentLength >=0) + return apacheContentLength; long contentLength = 0; // everything but strings first in the multipart for (HttpParam param : mParams) @@ -316,7 +320,7 @@ public String getContentType() { return "multipart/form-data; boundary="+boundary; } - protected static class HttpParam { + public static class HttpParam { private static final String TEXT_PLAIN = "text/plain; charset=UTF-8"; public final String name; diff --git a/Tophe/src/co/tophe/body/HttpBodyUrlEncoded.java b/Tophe/src/co/tophe/body/HttpBodyUrlEncoded.java index 36b02d1..22bfa29 100644 --- a/Tophe/src/co/tophe/body/HttpBodyUrlEncoded.java +++ b/Tophe/src/co/tophe/body/HttpBodyUrlEncoded.java @@ -2,13 +2,14 @@ import java.io.IOException; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.util.ArrayList; -import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; import android.support.annotation.NonNull; +import android.util.Log; import co.tophe.HttpRequestInfo; import co.tophe.UploadProgressListener; @@ -47,9 +48,27 @@ public HttpBodyUrlEncoded(HttpBodyUrlEncoded copy) { private byte[] getEncodedParams() { if (null==encodedParams) { - encodedParams = URLEncodedUtils.format(mParams, "UTF-8")/*.replace("*", "∗")*/.getBytes(); + StringBuilder b = new StringBuilder(); + + try { + for (NameValuePair pair: mParams) { + if (pair.getValue() == null) + continue; + + b.append(URLEncoder.encode(pair.getName(), "UTF-8")); + b.append('='); + b.append(URLEncoder.encode(pair.getValue(), "UTF-8").replace("*", "%2A")); + b.append('&'); + } + encodedParams = b.toString().getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return new byte[]{}; + } + mParams.clear(); } + Log.e("", "length " + encodedParams.length); return encodedParams; } diff --git a/Tophe/src/co/tophe/body/NameValuePair.java b/Tophe/src/co/tophe/body/NameValuePair.java new file mode 100644 index 0000000..95921e8 --- /dev/null +++ b/Tophe/src/co/tophe/body/NameValuePair.java @@ -0,0 +1,111 @@ +package co.tophe.body; + +/** + * Created by Denis Babak on 06/07/16. + */ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/NameValuePair.java $ + * $Revision: 496070 $ + * $Date: 2007-01-14 04:18:34 -0800 (Sun, 14 Jan 2007) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +/** + * A simple class encapsulating an attribute/value pair. + *

+ * This class comforms to the generic grammar and formatting rules outlined in the + * Section 2.2 + * and + * Section 3.6 + * of RFC 2616 + *

+ * 2.2 Basic Rules + *

+ * The following rules are used throughout this specification to describe basic parsing constructs. + * The US-ASCII coded character set is defined by ANSI X3.4-1986. + *

+ *
+ *     OCTET          = 
+ *     CHAR           = 
+ *     UPALPHA        = 
+ *     LOALPHA        = 
+ *     ALPHA          = UPALPHA | LOALPHA
+ *     DIGIT          = 
+ *     CTL            = 
+ *     CR             = 
+ *     LF             = 
+ *     SP             = 
+ *     HT             = 
+ *     <">            = 
+ * 
+ *

+ * Many HTTP/1.1 header field values consist of words separated by LWS or special + * characters. These special characters MUST be in a quoted string to be used within + * a parameter value (as defined in section 3.6). + *

+ *

+ * token          = 1*
+ * separators     = "(" | ")" | "<" | ">" | "@"
+ *                | "," | ";" | ":" | "\" | <">
+ *                | "/" | "[" | "]" | "?" | "="
+ *                | "{" | "}" | SP | HT
+ * 
+ *

+ * A string of text is parsed as a single word if it is quoted using double-quote marks. + *

+ *
+ * quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
+ * qdtext         = >
+ * 
+ *

+ * The backslash character ("\") MAY be used as a single-character quoting mechanism only + * within quoted-string and comment constructs. + *

+ *
+ * quoted-pair    = "\" CHAR
+ * 
+ * 3.6 Transfer Codings + *

+ * Parameters are in the form of attribute/value pairs. + *

+ *
+ * parameter               = attribute "=" value
+ * attribute               = token
+ * value                   = token | quoted-string
+ * 
+ * + * @author Oleg Kalnichevski + * + */ +public interface NameValuePair { + + String getName(); + + String getValue(); + +} diff --git a/Tophe/src/co/tophe/engine/HttpEngineFactoryUrlConnection.java b/Tophe/src/co/tophe/engine/HttpEngineFactoryUrlConnection.java index 74089e6..879cc15 100644 --- a/Tophe/src/co/tophe/engine/HttpEngineFactoryUrlConnection.java +++ b/Tophe/src/co/tophe/engine/HttpEngineFactoryUrlConnection.java @@ -47,6 +47,14 @@ public void init() { } } + public void nullify() { + initialized = false; + } + + public boolean isInitialized() { + return initialized; + } + /** * An {@link javax.net.ssl.SSLSocket} that doesn't allow {@code SSLv3} only connections *

fixes https://github.com/koush/ion/issues/386

diff --git a/Tophe/src/co/tophe/parser/XferTransformChain.java b/Tophe/src/co/tophe/parser/XferTransformChain.java index e152d96..b1a65b6 100644 --- a/Tophe/src/co/tophe/parser/XferTransformChain.java +++ b/Tophe/src/co/tophe/parser/XferTransformChain.java @@ -5,6 +5,7 @@ import java.util.Arrays; import android.support.annotation.NonNull; +import android.util.Log; import co.tophe.ImmutableHttpRequest; @@ -111,6 +112,7 @@ public OUTPUT transformData(INPUT input, ImmutableHttpRequest request) throws IO try { intermediate = transform.transformData(intermediate, request); } catch (ClassCastException e) { + e.printStackTrace(); throw new ParserException("Can't cast " + intermediate + " using " + transform + " in " + this, e, null); } } diff --git a/Tophe/src/co/tophe/utils/DateUtils.java b/Tophe/src/co/tophe/utils/DateUtils.java index 3d38591..7036f06 100644 --- a/Tophe/src/co/tophe/utils/DateUtils.java +++ b/Tophe/src/co/tophe/utils/DateUtils.java @@ -1,5 +1,10 @@ package co.tophe.utils; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @@ -8,12 +13,6 @@ import java.util.Map; import java.util.TimeZone; -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; -import android.util.Log; - import co.tophe.BuildConfig; /** @@ -85,7 +84,7 @@ public static Date parseDate(@Nullable String dateString, @NonNull String[] date formatMap.get().put(dateFormat, format); } try { - return format.parse(dateString); + return format.parse(dateString.toUpperCase()); } catch (ParseException ignored) { } } diff --git a/build.gradle b/build.gradle index 674e7ce..df84dff 100644 --- a/build.gradle +++ b/build.gradle @@ -6,14 +6,23 @@ task wrapper(type: Wrapper) { buildscript { repositories { mavenCentral() + jcenter() + maven { + url "https://maven.google.com" + } + google() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:3.3.0-rc02' } } allprojects { repositories { mavenCentral() + jcenter() + maven { + url "https://maven.google.com" + } } } diff --git a/gradle.properties b/gradle.properties index b84817c..7f0bdf1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ ANDROID_BUILD_TARGET_SDK_VERSION=21 ANDROID_BUILD_SDK_VERSION=21 ANDROID_BUILD_TOOLS_VERSION=21.0.2 -ANDROID_BUILD_MIN_SDK_VERSION=9 +ANDROID_BUILD_MIN_SDK_VERSION=14 diff --git a/settings.gradle b/settings.gradle index 2f56f8b..ec97520 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,8 +3,11 @@ include 'Tophe' project(':Tophe').projectDir = new File('Tophe') -include 'Tophe-Ion' -project(':Tophe-Ion').projectDir = new File('Tophe-Ion') +//include 'Tophe-Ion' +//project(':Tophe-Ion').projectDir = new File('Tophe-Ion') include 'Tophe-OAuth1' project(':Tophe-OAuth1').projectDir = new File('Tophe-OAuth1') + +include 'tophe-volley' +project(':tophe-volley').projectDir = new File('tophe-volley') diff --git a/tophe-volley/.gitignore b/tophe-volley/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/tophe-volley/.gitignore @@ -0,0 +1 @@ +/build diff --git a/tophe-volley/build.gradle b/tophe-volley/build.gradle new file mode 100644 index 0000000..2dc3477 --- /dev/null +++ b/tophe-volley/build.gradle @@ -0,0 +1,71 @@ +buildscript { + repositories { + jcenter() + maven { url 'https://maven.fabric.io/public' } + maven { url 'https://oss.jfrog.org/artifactory/oss-snapshot-local/' } + maven { + url "https://maven.google.com" + } + } + + dependencies { + classpath 'io.fabric.tools:gradle:1.25.4' + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.3" + + //useLibrary 'org.apache.http.legacy' + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +repositories { + maven { url 'https://oss.jfrog.org/artifactory/oss-snapshot-local/' } +} + +dependencies { + def localTophe = ':Tophe' + def useLocalTophe = true + + allprojects { + rootProject.allprojects.project.each { + if (it.path == localTophe) { + useLocalTophe = true + } + } + } + + compile project(localTophe) + compile ('com.android.volley:volley:1.1.1-20171227.180452-3') { + exclude group: 'com.android.support', module: 'support-v4' + exclude group: 'com.android.support', module: 'support-annotations' + exclude group: 'support-appcompat-v7' + } + + + //compile fileTree(dir: 'libs', include: ['*.jar']) + //compile files('libs/httpcore-4.4.4.jar') + //compile files('libs/httpclient-4.5.2.jar') + //compile files('libs/httpmime-4.5.2.jar') + dependencies { + compile group: 'cz.msebera.android' , name: 'httpclient', version: '4.4.1.1' + } + testCompile 'junit:junit:4.12' + compile 'com.android.support:appcompat-v7:23.4.0' +} diff --git a/tophe-volley/libs/httpclient-4.5.2.jar b/tophe-volley/libs/httpclient-4.5.2.jar new file mode 100644 index 0000000..701609f Binary files /dev/null and b/tophe-volley/libs/httpclient-4.5.2.jar differ diff --git a/tophe-volley/libs/httpcore-4.4.4.jar b/tophe-volley/libs/httpcore-4.4.4.jar new file mode 100644 index 0000000..ac4a877 Binary files /dev/null and b/tophe-volley/libs/httpcore-4.4.4.jar differ diff --git a/tophe-volley/libs/httpmime-4.5.2.jar b/tophe-volley/libs/httpmime-4.5.2.jar new file mode 100644 index 0000000..474670a Binary files /dev/null and b/tophe-volley/libs/httpmime-4.5.2.jar differ diff --git a/Tophe-Ion/proguard-rules.pro b/tophe-volley/proguard-rules.pro similarity index 84% rename from Tophe-Ion/proguard-rules.pro rename to tophe-volley/proguard-rules.pro index 19bd1f9..00f07db 100644 --- a/Tophe-Ion/proguard-rules.pro +++ b/tophe-volley/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified -# in C:/android-sdk/tools/proguard/proguard-android.txt +# in /media/debugger/22a30ccd-dabe-4c44-8e26-d3da64b50f7a/android-sdk-linux/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # diff --git a/tophe-volley/src/androidTest/java/com/debugger/tophe_volley/ApplicationTest.java b/tophe-volley/src/androidTest/java/com/debugger/tophe_volley/ApplicationTest.java new file mode 100644 index 0000000..81bf0af --- /dev/null +++ b/tophe-volley/src/androidTest/java/com/debugger/tophe_volley/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.debugger.tophe_volley; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/tophe-volley/src/main/AndroidManifest.xml b/tophe-volley/src/main/AndroidManifest.xml new file mode 100644 index 0000000..eda0f28 --- /dev/null +++ b/tophe-volley/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/HttpEngineVolley.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/HttpEngineVolley.java new file mode 100644 index 0000000..fe9fcd1 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/HttpEngineVolley.java @@ -0,0 +1,243 @@ +package com.debugger.tophe_volley.volley; + +import android.util.Log; + +import com.android.volley.DefaultRetryPolicy; +import com.android.volley.Request; +import com.android.volley.RequestQueue; +import com.android.volley.ServerError; +import com.android.volley.toolbox.RequestFuture; +import com.android.volley.toolbox.StringRequest; +import com.debugger.tophe_volley.volley.internal.HttpResponseVolley; +import com.debugger.tophe_volley.volley.internal.VolleyBody; +import com.debugger.tophe_volley.volley.internal.VolleyHttpBodyJSON; +import com.debugger.tophe_volley.volley.internal.VolleyHttpBodyMultiPart; +import com.debugger.tophe_volley.volley.internal.VolleyHttpBodyString; +import com.debugger.tophe_volley.volley.internal.VolleyHttpBodyUrlEncoded; +import com.debugger.tophe_volley.volley.request.HttpBodyMultipartRequest; +import com.debugger.tophe_volley.volley.request.HttpBodyUrlEncodedRequest; +import com.debugger.tophe_volley.volley.request.JSONRequestWithHeaders; +import com.debugger.tophe_volley.volley.request.StringRequestWithHeaders; +import com.debugger.tophe_volley.volley.request.VolleyRequest; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +import co.tophe.AbstractHttpEngine; +import co.tophe.BasicHttpConfig; +import co.tophe.HttpException; +import co.tophe.HttpResponse; +import co.tophe.HttpSignException; +import co.tophe.ServerException; +import co.tophe.body.HttpBodyJSON; +import co.tophe.body.HttpBodyMultiPart; +import co.tophe.body.HttpBodyParameters; +import co.tophe.body.HttpBodyString; +import co.tophe.body.HttpBodyUrlEncoded; +import co.tophe.parser.ParserException; +import co.tophe.parser.Utils; +import co.tophe.parser.XferTransform; +import cz.msebera.android.httpclient.protocol.HTTP; + +/** + * Created by Denis Babak on 10/06/16. + */ +public class HttpEngineVolley extends AbstractHttpEngine> { + + Request volleyRequest; + final VolleyBody volleyBody; + RequestQueue queue; + Builder builder; + RequestFuture requestFuture; + + private void createRequest(RequestFuture future) { + int method = Request.Method.GET; + if(request.getHttpMethod().equalsIgnoreCase("GET")) { + method = Request.Method.GET; + } else if(request.getHttpMethod().equalsIgnoreCase("POST")) { + method = Request.Method.POST; + } else if(request.getHttpMethod().equalsIgnoreCase("DELETE")) { + method = Request.Method.DELETE; + } else if(request.getHttpMethod().equalsIgnoreCase("PUT")) { + method = Request.Method.PUT; + } + if(volleyBody instanceof VolleyHttpBodyString) { + volleyRequest = new StringRequest(method, request.getUri().toString(), future, future); + } else if(volleyBody instanceof VolleyHttpBodyJSON) { + try { + volleyRequest = new JSONRequestWithHeaders(method, request.getUri().toString(), + new JSONObject(((VolleyHttpBodyJSON)volleyBody).getJsonElement().toString()), future, future); + if(method == Request.Method.POST) { + requestHeaders.remove(HTTP.CONTENT_LEN); + requestHeaders.put(HTTP.CONTENT_LEN, String.valueOf(((JSONRequestWithHeaders)volleyRequest).getContentLength())); + } + } catch (JSONException e) { + e.printStackTrace(); + volleyRequest = new JSONRequestWithHeaders(method, request.getUri().toString(), + null, future, future); + } + } else if(volleyBody instanceof VolleyHttpBodyUrlEncoded) { + volleyRequest = new HttpBodyUrlEncodedRequest(method, + request.getUri().toString(), ((VolleyHttpBodyUrlEncoded) volleyBody), future, future); + } else if(volleyBody instanceof VolleyHttpBodyMultiPart) { + volleyRequest = new HttpBodyMultipartRequest(method, + request.getUri().toString(), ((VolleyHttpBodyMultiPart) volleyBody), future, future); + /*((VolleyHttpBodyMultiPart) volleyBody).setContentLength(((HttpBodyMultipartRequest)volleyRequest).getContentLength()); + ((HttpBodyMultiPart)request.getBodyParameters()).apacheContentLength = ((HttpBodyMultipartRequest)volleyRequest).getContentLength();*/ + requestHeaders.remove(HTTP.CONTENT_LEN); + requestHeaders.put(HTTP.CONTENT_LEN, String.valueOf(((HttpBodyMultipartRequest)volleyRequest).getContentLength())); + } else { + volleyRequest = new StringRequestWithHeaders(method, + request.getUri().toString(), volleyBody, future, future); + + } + + ((VolleyRequest) volleyRequest).addHeaders(requestHeaders); + volleyRequest.setRetryPolicy(new DefaultRetryPolicy(BasicHttpConfig.READ_TIMEOUT_IN_MS, 0/*DefaultRetryPolicy.DEFAULT_MAX_RETRIES*/, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); + } + + @Override + public void prepareEngine() throws HttpSignException { + super.prepareEngine(); + requestFuture = RequestFuture.newFuture(); + createRequest(requestFuture); + } + + public HttpEngineVolley(Builder builder, RequestQueue queue) { + super(builder); + + this.queue = queue; + this.builder = builder; + + final HttpBodyParameters sourceBody = request.getBodyParameters(); + if (sourceBody instanceof HttpBodyMultiPart) + volleyBody = new VolleyHttpBodyMultiPart((HttpBodyMultiPart) sourceBody); + else if (sourceBody instanceof HttpBodyJSON) + volleyBody = new VolleyHttpBodyJSON((HttpBodyJSON) sourceBody); + else if (sourceBody instanceof HttpBodyUrlEncoded) { + volleyBody = new VolleyHttpBodyUrlEncoded((HttpBodyUrlEncoded) sourceBody); + } + else if (sourceBody instanceof HttpBodyString) + volleyBody = new VolleyHttpBodyString((HttpBodyString) sourceBody, request.getUri().toString()); + else if (sourceBody != null) + throw new IllegalStateException("Unknown body type "+sourceBody); + else + volleyBody = null; + + /*if (null != volleyBody) { + volleyBody.setOutputData(requestBuilder); + + final UploadProgressListener progressListener = request.getProgressListener(); + if (null != progressListener) { + requestBuilder.progress(new ProgressCallback() { + @Override + public void onProgress(long downloaded, long total) { + progressListener.onParamUploadProgress(request, null, (int) ((100 * downloaded) / total)); + } + }); + } + }*/ + } + + @Override + protected void setHeadersAndConfig() { + /*if(volleyRequest instanceof HttpBodyMultipartRequest && volleyRequest.getMethod() == Request.Method.POST) { + setHeader(HTTP.CONTENT_LEN, String.valueOf(((HttpBodyMultipartRequest)volleyRequest).getContentLength())); + ((VolleyHttpBodyMultiPart) volleyBody).setContentLength(((HttpBodyMultipartRequest)volleyRequest).getContentLength()); + ((HttpBodyMultiPart)request.getBodyParameters()).apacheContentLength = ((HttpBodyMultipartRequest)volleyRequest).getContentLength(); + }*/ + /*if (request.getBodyParameters() instanceof HttpBodyMultiPart) { + setHeader("content-type", null); + }*/ + + /*for (Map.Entry entry : requestHeaders.entrySet()) { + requestBuilder.setHeader(entry.getKey(), entry.getValue()); + }*/ + + /*if (null != responseHandler.followsRedirect()) { + requestBuilder.followRedirect(responseHandler.followsRedirect()); + }*/ + + + } + + @Override + protected String getEngineSignature() { + return "TopheVolley"; + } + + @Override + protected HttpResponseVolley queryResponse() throws ServerException, HttpException { + XferTransform errorParser = responseHandler.errorParser; + XferTransform commonTransforms = Utils.getCommonXferTransform(responseHandler.contentParser, errorParser, true); + //AsyncParser parser = getXferTransformParser(commonTransforms); + //ResponseFuture req = requestBuilder.as(parser); + + queue.add(volleyRequest); + //Future> withResponse = req.withResponse(); + try { + Object response = requestFuture.get(); + HttpResponseVolley volleyResponse = new HttpResponseVolley(response, volleyRequest, ((VolleyRequest) volleyRequest).getResponseCode(), "", commonTransforms); + setRequestResponse(volleyResponse); + + /*Exception e = response.getException(); + if (null != e) { + throw exceptionToHttpException(e).build(); + }*/ + + if (isHttpError(volleyResponse)) { + XferTransform transformToResult = Utils.skipCommonTransforms(errorParser, commonTransforms); + SE errorData; + if (null == transformToResult) + errorData = (SE) response; + else + errorData = (SE) transformToResult.transformData(response, this); + throw errorData; + } + return volleyResponse; + + } catch (InterruptedException e) { + e.printStackTrace(); + throw exceptionToHttpException(e).build(); + + } catch (ExecutionException e) { + e.printStackTrace(); + throw exceptionToHttpException(e).build(); + + } catch (ParserException e) { + e.printStackTrace(); + throw exceptionToHttpException(e).build(); + + } catch (IOException e) { + e.printStackTrace(); + throw exceptionToHttpException(e).build(); + + } + } + + @Override + protected T responseToResult(HttpResponseVolley response) throws ParserException, IOException { + try { + Object data = response.getResponse(); + XferTransform transformToResult = Utils.skipCommonTransforms(responseHandler.contentParser, response.getCommonTransform()); + if (null == transformToResult) { + return (T) response.getContentStream(); + } + + return (T) transformToResult.transformData(response.getContentStream(), this); + + } catch(ParserException e) { + e.printStackTrace(); + throw e; + } catch (IOException e) { + e.printStackTrace(); + throw e; + } + + //return response.getResponse(); + } + +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/VolleyClient.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/VolleyClient.java new file mode 100644 index 0000000..4fecebc --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/VolleyClient.java @@ -0,0 +1,21 @@ +package com.debugger.tophe_volley.volley; + +import android.content.Context; + +import com.android.volley.RequestQueue; + +import co.tophe.TopheClient; +import co.tophe.engine.HttpEngineFactoryFallback; + +/** + * Created by Denis Babak on 13/06/16. + */ +public class VolleyClient { + + public static void setup(Context context, RequestQueue queue) { + VolleyHttpEngineFactory factory = VolleyHttpEngineFactory.getInstance(context, queue); + TopheClient.setHttpEngineFactory(new HttpEngineFactoryFallback(factory, TopheClient.getHttpEngineFactory())); + TopheClient.setup(context); + } + +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/VolleyHttpEngineFactory.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/VolleyHttpEngineFactory.java new file mode 100644 index 0000000..193388d --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/VolleyHttpEngineFactory.java @@ -0,0 +1,105 @@ +package com.debugger.tophe_volley.volley; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.android.volley.RequestQueue; +import com.android.volley.Response; +import com.android.volley.toolbox.Volley; + +import co.tophe.HttpEngine; +import co.tophe.HttpEngineFactory; +import co.tophe.HttpResponse; +import co.tophe.ResponseHandler; +import co.tophe.ServerException; +import co.tophe.parser.Utils; +import co.tophe.parser.XferTransform; +import co.tophe.parser.XferTransformChain; +import co.tophe.parser.XferTransformInputStreamHttpStream; + +/** + * Created by Denis Babak on 13/06/16. + */ +public class VolleyHttpEngineFactory implements HttpEngineFactory { + + private static VolleyHttpEngineFactory INSTANCE; + //public static final int BOGUS_CONSCRYPT_DUAL_FEEDLY = 6587000; // see https://github.com/koush/ion/issues/443 + //public static final int CONSCRYPT_LACKS_SNI = 6599038; // 6587030 to 6599038 don't have it see https://github.com/koush/ion/issues/428 + + private RequestQueue volleyQueue; + + public static VolleyHttpEngineFactory getInstance(Context context, RequestQueue queue) { + if (null == INSTANCE) { + INSTANCE = new VolleyHttpEngineFactory(context); + INSTANCE.volleyQueue = queue; + } + return INSTANCE; + } + + private VolleyHttpEngineFactory(Context context) { + if (context == null) { + throw new NullPointerException("Volley HTTP request with no Context"); + } + + //volleyQueue = Volley.newRequestQueue(context); + //IonClient.setupIon(ion); + } + + /** + * Get the {@link RequestQueue} instance used by default by TOPHE. + */ + @NonNull + public RequestQueue getDefaultVolley() { + return volleyQueue; + } + + @Nullable + @Override + public HttpEngine createEngine(HttpEngine.Builder builder) { + return createEngine(builder, volleyQueue, false); + } + + /** + * + * @param builder + * @param volleyQueue + * @param allowBogusSSL Sometimes Ion maybe have problems with SSL, especially with Conscrypt, but you may decide to take the + * risk anyway and use it in conditions where it may fail + * @param + * @param + * @return + */ + @Nullable + public HttpEngine createEngine(HttpEngine.Builder builder, RequestQueue volleyQueue, boolean allowBogusSSL) { + if (!canHandleXferTransform(builder.getResponseHandler().contentParser)) + return null; + + if (!errorCompatibleWithData(builder.getResponseHandler())) + // Ion returns the data fully parsed so if we don't have common ground to parse the data and the error data, Ion can't handle the request + return null; + + return new HttpEngineVolley(builder, volleyQueue); + } + + private static boolean canHandleXferTransform(XferTransform contentParser) { + if (contentParser instanceof XferTransformChain) { + XferTransformChain parser = (XferTransformChain) contentParser; + for (XferTransform transform : parser.transforms) { + if (transform == XferTransformInputStreamHttpStream.INSTANCE) + return false; + } + } + return true; + } + + /** + * See if we can find common ground to parse the data and the error data inside Ion + * @param responseHandler + * @return whether Ion will be able to parse the data and the error in its processing thread + */ + private static boolean errorCompatibleWithData(ResponseHandler responseHandler) { + return true;//Utils.getCommonXferTransform(responseHandler.contentParser, responseHandler.errorParser, false) != null; + } + +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/ByteBufferList.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/ByteBufferList.java new file mode 100644 index 0000000..5d511fe --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/ByteBufferList.java @@ -0,0 +1,562 @@ +package com.debugger.tophe_volley.volley.internal; + +import android.annotation.TargetApi; +import android.os.Build; +import android.os.Looper; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayDeque; +import java.util.Comparator; +import java.util.PriorityQueue; + +/** + * Created by Denis Babak on 17/06/16. + */ +@TargetApi(Build.VERSION_CODES.GINGERBREAD) +public class ByteBufferList { + ArrayDeque mBuffers = new ArrayDeque(); + + ByteOrder order = ByteOrder.BIG_ENDIAN; + public ByteOrder order() { + return order; + } + + public ByteBufferList order(ByteOrder order) { + this.order = order; + return this; + } + + public ByteBufferList() { + } + + public ByteBufferList(ByteBuffer... b) { + addAll(b); + } + + public ByteBufferList(byte[] buf) { + super(); + ByteBuffer b = ByteBuffer.wrap(buf); + add(b); + } + + public ByteBufferList addAll(ByteBuffer... bb) { + for (ByteBuffer b: bb) + add(b); + return this; + } + + public ByteBufferList addAll(ByteBufferList... bb) { + for (ByteBufferList b: bb) + b.get(this); + return this; + } + + public byte[] getBytes(int length) { + byte[] ret = new byte[length]; + get(ret); + return ret; + } + + public byte[] getAllByteArray() { + // fast path to return the contents of the first and only byte buffer, + // if that's what we're looking for. avoids allocation. + if (mBuffers.size() == 1) { + ByteBuffer peek = mBuffers.peek(); + if (peek.capacity() == remaining() && peek.isDirect()) { + remaining = 0; + return mBuffers.remove().array(); + } + } + + byte[] ret = new byte[remaining()]; + get(ret); + + return ret; + } + + public ByteBuffer[] getAllArray() { + ByteBuffer[] ret = new ByteBuffer[mBuffers.size()]; + ret = mBuffers.toArray(ret); + mBuffers.clear(); + remaining = 0; + return ret; + } + + public boolean isEmpty() { + return remaining == 0; + } + + private int remaining = 0; + public int remaining() { + return remaining; + } + + public boolean hasRemaining() { + return remaining() > 0; + } + + public short peekShort() { + return read(2).duplicate().getShort(); + } + + public int peekInt() { + return read(4).duplicate().getInt(); + } + + public long peekLong() { + return read(8).duplicate().getLong(); + } + + public byte[] peekBytes(int size) { + byte[] ret = new byte[size]; + read(size).duplicate().get(ret); + return ret; + } + + public ByteBufferList skip(int length) { + get(null, 0, length); + return this; + } + + public int getInt() { + int ret = read(4).getInt(); + remaining -= 4; + return ret; + } + + public char getByteChar() { + char ret = (char)read(1).get(); + remaining--; + return ret; + } + + public short getShort() { + short ret = read(2).getShort(); + remaining -= 2; + return ret; + } + + public byte get() { + byte ret = read(1).get(); + remaining--; + return ret; + } + + public long getLong() { + long ret = read(8).getLong(); + remaining -= 8; + return ret; + } + + public void get(byte[] bytes) { + get(bytes, 0, bytes.length); + } + + public void get(byte[] bytes, int offset, int length) { + if (remaining() < length) + throw new IllegalArgumentException("length"); + + int need = length; + while (need > 0) { + ByteBuffer b = mBuffers.peek(); + int read = Math.min(b.remaining(), need); + if (bytes != null) + b.get(bytes, offset, read); + need -= read; + offset += read; + if (b.remaining() == 0) { + ByteBuffer removed = mBuffers.remove(); + assert b == removed; + reclaim(b); + } + } + + remaining -= length; + } + + public void get(ByteBufferList into, int length) { + if (remaining() < length) + throw new IllegalArgumentException("length"); + int offset = 0; + + while (offset < length) { + ByteBuffer b = mBuffers.remove(); + int remaining = b.remaining(); + + if (remaining == 0) { + reclaim(b); + continue; + } + + if (offset + remaining > length) { + int need = length - offset; + // this is shared between both + ByteBuffer subset = obtain(need); + subset.limit(need); + b.get(subset.array(), 0, need); + into.add(subset); + mBuffers.addFirst(b); + assert subset.capacity() >= need; + assert subset.position() == 0; + break; + } + else { + // this belongs to the new list + into.add(b); + } + + offset += remaining; + } + + remaining -= length; + } + + public void get(ByteBufferList into) { + get(into, remaining()); + } + + public ByteBufferList get(int length) { + ByteBufferList ret = new ByteBufferList(); + get(ret, length); + return ret.order(order); + } + + public ByteBuffer getAll() { + if (remaining() == 0) + return EMPTY_BYTEBUFFER; + read(remaining()); + return remove(); + } + + private ByteBuffer read(int count) { + if (remaining() < count) + throw new IllegalArgumentException("count : " + remaining() + "/" + count); + + ByteBuffer first = mBuffers.peek(); + while (first != null && !first.hasRemaining()) { + reclaim(mBuffers.remove()); + first = mBuffers.peek(); + } + + if (first == null) { + return EMPTY_BYTEBUFFER; + } + + if (first.remaining() >= count) { + return first.order(order); + } + + ByteBuffer ret = obtain(count); + ret.limit(count); + byte[] bytes = ret.array(); + int offset = 0; + ByteBuffer bb = null; + while (offset < count) { + bb = mBuffers.remove(); + int toRead = Math.min(count - offset, bb.remaining()); + bb.get(bytes, offset, toRead); + offset += toRead; + if (bb.remaining() == 0) { + reclaim(bb); + bb = null; + } + } + // if there was still data left in the last buffer we popped + // toss it back into the head + if (bb != null && bb.remaining() > 0) + mBuffers.addFirst(bb); + mBuffers.addFirst(ret); + return ret.order(order); + } + + public void trim() { + // this clears out buffers that are empty in the beginning of the list + read(0); + } + + public ByteBufferList add(ByteBufferList b) { + b.get(this); + return this; + } + + public ByteBufferList add(ByteBuffer b) { + if (b.remaining() <= 0) { +// System.out.println("reclaiming remaining: " + b.remaining()); + reclaim(b); + return this; + } + addRemaining(b.remaining()); + // see if we can fit the entirety of the buffer into the end + // of the current last buffer + if (mBuffers.size() > 0) { + ByteBuffer last = mBuffers.getLast(); + if (last.capacity() - last.limit() >= b.remaining()) { + last.mark(); + last.position(last.limit()); + last.limit(last.capacity()); + last.put(b); + last.limit(last.position()); + last.reset(); + reclaim(b); + trim(); + return this; + } + } + mBuffers.add(b); + trim(); + return this; + } + + public void addFirst(ByteBuffer b) { + if (b.remaining() <= 0) { + reclaim(b); + return; + } + addRemaining(b.remaining()); + // see if we can fit the entirety of the buffer into the beginning + // of the current first buffer + if (mBuffers.size() > 0) { + ByteBuffer first = mBuffers.getFirst(); + if (first.position() >= b.remaining()) { + first.position(first.position() - b.remaining()); + first.mark(); + first.put(b); + first.reset(); + reclaim(b); + return; + } + } + mBuffers.addFirst(b); + } + + private void addRemaining(int remaining) { + if (this.remaining() >= 0) + this.remaining += remaining; + } + + public void recycle() { + while (mBuffers.size() > 0) { + reclaim(mBuffers.remove()); + } + assert mBuffers.size() == 0; + remaining = 0; + } + + public ByteBuffer remove() { + ByteBuffer ret = mBuffers.remove(); + remaining -= ret.remaining(); + return ret; + } + + public int size() { + return mBuffers.size(); + } + + public void spewString() { + System.out.println(peekString()); + } + + public String peekString() { + return peekString(null); + } + + // not doing toString as this is really nasty in the debugger... + public String peekString(Charset charset) { + if (charset == null) + charset = Charset.forName("US-ASCII"); + StringBuilder builder = new StringBuilder(); + for (ByteBuffer bb: mBuffers) { + byte[] bytes; + int offset; + int length; + if (bb.isDirect()) { + bytes = new byte[bb.remaining()]; + offset = 0; + length = bb.remaining(); + bb.get(bytes); + } + else { + bytes = bb.array(); + offset = bb.arrayOffset() + bb.position(); + length = bb.remaining(); + } + builder.append(new String(bytes, offset, length, charset)); + } + return builder.toString(); + } + + public String readString() { + return readString(null); + } + + public String readString(Charset charset) { + String ret = peekString(charset); + recycle(); + return ret; + } + + static class Reclaimer implements Comparator { + @Override + public int compare(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) { + // keep the smaller ones at the head, so they get tossed out quicker + if (byteBuffer.capacity() == byteBuffer2.capacity()) + return 0; + if (byteBuffer.capacity() > byteBuffer2.capacity()) + return 1; + return -1; + } + } + + static PriorityQueue reclaimed = new PriorityQueue(8, new Reclaimer()); + + private static PriorityQueue getReclaimed() { + Looper mainLooper = Looper.getMainLooper(); + if (mainLooper != null) { + if (Thread.currentThread() == mainLooper.getThread()) + return null; + } + return reclaimed; + } + + private static int MAX_SIZE = 1024 * 1024; + public static int MAX_ITEM_SIZE = 1024 * 256; + static int currentSize = 0; + static int maxItem = 0; + + public static void setMaxPoolSize(int size) { + MAX_SIZE = size; + } + + public static void setMaxItemSize(int size) { + MAX_ITEM_SIZE = size; + } + + private static boolean reclaimedContains(ByteBuffer b) { + for (ByteBuffer other: reclaimed) { + if (other == b) + return true; + } + return false; + } + + public static void reclaim(ByteBuffer b) { + if (b == null || b.isDirect()) + return; + if (b.arrayOffset() != 0 || b.array().length != b.capacity()) + return; + if (b.capacity() < 8192) + return; + if (b.capacity() > MAX_ITEM_SIZE) + return; + + PriorityQueue r = getReclaimed(); + if (r == null) + return; + + synchronized (LOCK) { + while (currentSize > MAX_SIZE && r.size() > 0 && r.peek().capacity() < b.capacity()) { +// System.out.println("removing for better: " + b.capacity()); + ByteBuffer head = r.remove(); + currentSize -= head.capacity(); + } + + if (currentSize > MAX_SIZE) { +// System.out.println("too full: " + b.capacity()); + return; + } + + assert !reclaimedContains(b); + + b.position(0); + b.limit(b.capacity()); + currentSize += b.capacity(); + + r.add(b); + assert r.size() != 0 ^ currentSize == 0; + + maxItem = Math.max(maxItem, b.capacity()); + } + } + + private static final Object LOCK = new Object(); + + public static ByteBuffer obtain(int size) { + if (size <= maxItem) { + PriorityQueue r = getReclaimed(); + if (r != null) { + synchronized (LOCK) { + while (r.size() > 0) { + ByteBuffer ret = r.remove(); + if (r.size() == 0) + maxItem = 0; + currentSize -= ret.capacity(); + assert r.size() != 0 ^ currentSize == 0; + if (ret.capacity() >= size) { +// System.out.println("using " + ret.capacity()); + return ret; + } +// System.out.println("dumping " + ret.capacity()); + } + } + } + } + +// System.out.println("alloc for " + size); + ByteBuffer ret = ByteBuffer.allocate(Math.max(8192, size)); + return ret; + } + + public static void obtainArray(ByteBuffer[] arr, int size) { + PriorityQueue r = getReclaimed(); + int index = 0; + int total = 0; + + if (r != null) { + synchronized (LOCK) { + while (r.size() > 0 && total < size && index < arr.length - 1) { + ByteBuffer b = r.remove(); + currentSize -= b.capacity(); + assert r.size() != 0 ^ currentSize == 0; + int needed = Math.min(size - total, b.capacity()); + total += needed; + arr[index++] = b; + } + } + } + + if (total < size) { + ByteBuffer b = ByteBuffer.allocate(Math.max(8192, size - total)); + arr[index++] = b; + } + + for (int i = index; i < arr.length; i++) { + arr[i] = EMPTY_BYTEBUFFER; + } + } + + public static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0); + + public static void writeOutputStream(OutputStream out, ByteBuffer b) throws IOException { + byte[] bytes; + int offset; + int length; + if (b.isDirect()) { + bytes = new byte[b.remaining()]; + offset = 0; + length = b.remaining(); + b.get(bytes); + } + else { + bytes = b.array(); + offset = b.arrayOffset() + b.position(); + length = b.remaining(); + } + out.write(bytes, offset, length); + } +} + diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/ByteBufferListInputStream.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/ByteBufferListInputStream.java new file mode 100644 index 0000000..f93f3d6 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/ByteBufferListInputStream.java @@ -0,0 +1,35 @@ +package com.debugger.tophe_volley.volley.internal; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by Denis Babak on 17/06/16. + */ +public class ByteBufferListInputStream extends InputStream { + ByteBufferList bb; + public ByteBufferListInputStream(ByteBufferList bb) { + this.bb = bb; + } + + @Override + public int read() throws IOException { + if (bb.remaining() <= 0) + return -1; + return bb.get(); + } + + @Override + public int read(byte[] buffer) throws IOException { + return this.read(buffer, 0, buffer.length); + } + + @Override + public int read(byte[] buffer, int offset, int length) throws IOException { + if (bb.remaining() <= 0) + return -1; + int toRead = Math.min(length, bb.remaining()); + bb.get(buffer, offset, toRead); + return toRead; + } +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/HttpResponseVolley.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/HttpResponseVolley.java new file mode 100644 index 0000000..35937f1 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/HttpResponseVolley.java @@ -0,0 +1,151 @@ +package com.debugger.tophe_volley.volley.internal; + +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import com.android.volley.AuthFailureError; +import com.android.volley.Request; +import com.debugger.tophe_volley.volley.request.JSONRequestWithHeaders; +import com.debugger.tophe_volley.volley.request.StringRequestWithHeaders; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import co.tophe.HttpResponse; +import co.tophe.ServerException; +import co.tophe.parser.XferTransform; + +/** + * Created by Denis Babak on 10/06/16. + */ +public class HttpResponseVolley implements HttpResponse { + + T response; + Request request; + int code; + String message; + ByteBufferListInputStream is; + Map responseHeaders; + private final XferTransform commonTransform; + + public HttpResponseVolley(T response, Request request, int code, String message, XferTransform commonTransform) { + this.response = response; + this.request = request; + this.code = code; + this.message = message; + this.commonTransform = commonTransform; + if(request instanceof StringRequestWithHeaders) { + this.is = ((StringRequestWithHeaders) request).is; + this.responseHeaders = ((StringRequestWithHeaders) request).getResponseHeaders(); + } else if(request instanceof JSONRequestWithHeaders) { + this.is = ((JSONRequestWithHeaders) request).bbIs; + this.responseHeaders = ((JSONRequestWithHeaders) request).getResponseHeaders(); + } + } + + public T getResponse() { + return response; + } + + private Map> constructHeaders() throws AuthFailureError { + Map> result = new HashMap<>(); + for(Object key : request.getHeaders().keySet()) { + List params = new ArrayList<>(1); + params.add((String)request.getHeaders().get((String)key)); + result.put((String)key, params); + } + for(Object key : responseHeaders.keySet()) { + List params = new ArrayList<>(1); + params.add(responseHeaders.get(key)); + result.put((String)key, params); + } + return result; + } + + @Nullable + @Override + public String getContentType() { + if(responseHeaders.containsKey("content-type")) { + return responseHeaders.get("content-type"); + } + if(responseHeaders.containsKey("Content-Type")) { + return responseHeaders.get("Content-Type"); + } + return request.getBodyContentType(); + } + + @Override + public int getResponseCode() throws IOException { + return code; + } + + @Override + public Map> getHeaderFields() { + try { + return constructHeaders(); + } catch (AuthFailureError e) { + return null; + } + } + + @Nullable + @Override + public String getHeaderField(String name) { + return responseHeaders.get(name); + } + + @Override + public int getContentLength() { + String contentLength = getHeaderField("Content-Length"); + if (TextUtils.isEmpty(contentLength)) + return -1; + return Integer.parseInt(contentLength); + } + + @Override + public String getResponseMessage() throws IOException { + return message; + } + + @Nullable + @Override + public String getContentEncoding() { + return getHeaderField("Content-Encoding"); + } + + @Override + public void disconnect() { + } + + @Override + public InputStream getContentStream() throws IOException { + if (response instanceof InputStream) + return (InputStream) response; + + /*if(response instanceof String) { + InputStream stream = new ByteArrayInputStream(((String)response).getBytes("UTF-8")); + return stream; + }*/ + if(is != null) { + return is; + } + + /*if (response.getException() instanceof ServerException) { + ServerException exception = (ServerException) response.getException(); + if (exception.getServerError() instanceof InputStream) + return (InputStream) exception.getServerError(); + }*/ + + throw new IOException("trying to read an InputStream from Volley result:"+response+" error:"); + } + + public XferTransform getCommonTransform() { + return commonTransform; + } +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/MultipartRequest.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/MultipartRequest.java new file mode 100644 index 0000000..972e159 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/MultipartRequest.java @@ -0,0 +1,67 @@ +package com.debugger.tophe_volley.volley.internal; + +import android.util.Log; + +import com.android.volley.AuthFailureError; +import com.android.volley.NetworkResponse; +import com.android.volley.ParseError; +import com.android.volley.Request; +import com.android.volley.Response; +import com.android.volley.VolleyError; +import com.android.volley.toolbox.HttpHeaderParser; + +import java.util.Map; + +public class MultipartRequest extends Request { + private final Response.Listener mListener; + private final Response.ErrorListener mErrorListener; + private final Map mHeaders; + private final String mMimeType; + private final byte[] mMultipartBody; + + public MultipartRequest(String url, Map headers, String mimeType, byte[] multipartBody, Response.Listener listener, Response.ErrorListener errorListener) { + super(Method.POST, url, errorListener); + this.mListener = listener; + this.mErrorListener = errorListener; + this.mHeaders = headers; + this.mMimeType = mimeType; + this.mMultipartBody = multipartBody; + } + + @Override + public Map getHeaders() throws AuthFailureError { + return (mHeaders != null) ? mHeaders : super.getHeaders(); + } + + @Override + public String getBodyContentType() { + return mMimeType; + } + + @Override + public byte[] getBody() throws AuthFailureError { + return mMultipartBody; + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + try { + return Response.success( + response, + HttpHeaderParser.parseCacheHeaders(response)); + } catch (Exception e) { + return Response.error(new ParseError(e)); + } + } + + @Override + protected void deliverResponse(NetworkResponse response) { + mListener.onResponse(response); + } + + @Override + public void deliverError(VolleyError error) { + Log.e("", "Volley + " + error.getMessage()); + mErrorListener.onErrorResponse(error); + } +} \ No newline at end of file diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyBody.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyBody.java new file mode 100644 index 0000000..17cd797 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyBody.java @@ -0,0 +1,13 @@ +package com.debugger.tophe_volley.volley.internal; + +import com.android.volley.Request; +import com.android.volley.toolbox.RequestFuture; + +/** + * Created by Denis Babak on 14/06/16. + */ +public interface VolleyBody { + + void setOutputData(Request requestBuilder); + +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyGenericHttpRequest.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyGenericHttpRequest.java new file mode 100644 index 0000000..4165c58 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyGenericHttpRequest.java @@ -0,0 +1,221 @@ +package com.debugger.tophe_volley.volley.internal; + +/** + * Created by Denis Babak on 04/07/16. + */ + +import com.android.volley.AuthFailureError; +import com.android.volley.NetworkResponse; +import com.android.volley.ParseError; +import com.android.volley.Request; +import com.android.volley.Response; +import com.android.volley.toolbox.HttpHeaderParser; +import com.android.volley.toolbox.JsonRequest; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by laurentmeyer on 25/07/15. + */ +public class VolleyGenericHttpRequest extends JsonRequest { + + private final Gson gson = new Gson(); + private final Class clazz; + private final Map headers; + // Used for request which do not return anything from the server + private boolean muteRequest = false; + + /** + * Basically, this is the constructor which is called by the others. + * It allows you to send an object of type A to the server and expect a JSON representing a object of type B. + * The problem with the #JsonObjectRequest is that you expect a JSON at the end. + * We can do better than that, we can directly receive our POJO. + * That's what this class does. + * + * @param method: HTTP Method + * @param classtype: Classtype to parse the JSON coming from the server + * @param url: url to be called + * @param requestBody: The body being sent + * @param listener: Listener of the request + * @param errorListener: Error handler of the request + * @param headers: Added headers + */ + private VolleyGenericHttpRequest(int method, Class classtype, String url, String requestBody, + Response.Listener listener, Response.ErrorListener errorListener, Map headers) { + super(method, url, requestBody, listener, + errorListener); + clazz = classtype; + this.headers = headers; + configureRequest(); + } + + /** + * Method to be called if you want to send some objects to your server via body in JSON of the request (with headers and not muted) + * + * @param method: HTTP Method + * @param url: URL to be called + * @param classtype: Classtype to parse the JSON returned from the server + * @param toBeSent: Object which will be transformed in JSON via Gson and sent to the server + * @param listener: Listener of the request + * @param errorListener: Error handler of the request + * @param headers: Added headers + */ + public VolleyGenericHttpRequest(int method, String url, Class classtype, Object toBeSent, + Response.Listener listener, Response.ErrorListener errorListener, Map headers) { + this(method, classtype, url, new Gson().toJson(toBeSent), listener, + errorListener, headers); + } + + /** + * Method to be called if you want to send some objects to your server via body in JSON of the request (without header and not muted) + * + * @param method: HTTP Method + * @param url: URL to be called + * @param classtype: Classtype to parse the JSON returned from the server + * @param toBeSent: Object which will be transformed in JSON via Gson and sent to the server + * @param listener: Listener of the request + * @param errorListener: Error handler of the request + */ + public VolleyGenericHttpRequest(int method, String url, Class classtype, Object toBeSent, + Response.Listener listener, Response.ErrorListener errorListener) { + this(method, classtype, url, new Gson().toJson(toBeSent), listener, + errorListener, new HashMap()); + } + + /** + * Method to be called if you want to send something to the server but not with a JSON, just with a defined String (without header and not muted) + * + * @param method: HTTP Method + * @param url: URL to be called + * @param classtype: Classtype to parse the JSON returned from the server + * @param requestBody: String to be sent to the server + * @param listener: Listener of the request + * @param errorListener: Error handler of the request + */ + public VolleyGenericHttpRequest(int method, String url, Class classtype, String requestBody, + Response.Listener listener, Response.ErrorListener errorListener) { + this(method, classtype, url, requestBody, listener, + errorListener, new HashMap()); + } + + /** + * Method to be called if you want to GET something from the server and receive the POJO directly after the call (no JSON). (Without header) + * + * @param url: URL to be called + * @param classtype: Classtype to parse the JSON returned from the server + * @param listener: Listener of the request + * @param errorListener: Error handler of the request + */ + public VolleyGenericHttpRequest(String url, Class classtype, Response.Listener listener, Response.ErrorListener errorListener) { + this(Request.Method.GET, url, classtype, "", listener, errorListener); + } + + /** + * Method to be called if you want to GET something from the server and receive the POJO directly after the call (no JSON). (With headers) + * + * @param url: URL to be called + * @param classtype: Classtype to parse the JSON returned from the server + * @param listener: Listener of the request + * @param errorListener: Error handler of the request + * @param headers: Added headers + */ + public VolleyGenericHttpRequest(String url, Class classtype, Response.Listener listener, Response.ErrorListener errorListener, Map headers) { + this(Request.Method.GET, classtype, url, "", listener, errorListener, headers); + } + + /** + * Method to be called if you want to send some objects to your server via body in JSON of the request (with headers and muted) + * + * @param method: HTTP Method + * @param url: URL to be called + * @param classtype: Classtype to parse the JSON returned from the server + * @param toBeSent: Object which will be transformed in JSON via Gson and sent to the server + * @param listener: Listener of the request + * @param errorListener: Error handler of the request + * @param headers: Added headers + * @param mute: Muted (put it to true, to make sense) + */ + public VolleyGenericHttpRequest(int method, String url, Class classtype, Object toBeSent, + Response.Listener listener, Response.ErrorListener errorListener, Map headers, boolean mute) { + this(method, classtype, url, new Gson().toJson(toBeSent), listener, + errorListener, headers); + this.muteRequest = mute; + } + + /** + * Method to be called if you want to send some objects to your server via body in JSON of the request (without header and muted) + * + * @param method: HTTP Method + * @param url: URL to be called + * @param classtype: Classtype to parse the JSON returned from the server + * @param toBeSent: Object which will be transformed in JSON via Gson and sent to the server + * @param listener: Listener of the request + * @param errorListener: Error handler of the request + * @param mute: Muted (put it to true, to make sense) + */ + public VolleyGenericHttpRequest(int method, String url, Class classtype, Object toBeSent, + Response.Listener listener, Response.ErrorListener errorListener, boolean mute) { + this(method, classtype, url, new Gson().toJson(toBeSent), listener, + errorListener, new HashMap()); + this.muteRequest = mute; + + } + + /** + * Method to be called if you want to send something to the server but not with a JSON, just with a defined String (without header and not muted) + * + * @param method: HTTP Method + * @param url: URL to be called + * @param classtype: Classtype to parse the JSON returned from the server + * @param requestBody: String to be sent to the server + * @param listener: Listener of the request + * @param errorListener: Error handler of the request + * @param mute: Muted (put it to true, to make sense) + */ + public VolleyGenericHttpRequest(int method, String url, Class classtype, String requestBody, + Response.Listener listener, Response.ErrorListener errorListener, boolean mute) { + this(method, classtype, url, requestBody, listener, + errorListener, new HashMap()); + this.muteRequest = mute; + + } + + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + // The magic of the mute request happens here + if (muteRequest) { + if (response.statusCode >= 200 && response.statusCode <= 299) { + // If the status is correct, we return a success but with a null object, because the server didn't return anything + return Response.success(null, HttpHeaderParser.parseCacheHeaders(response)); + } + } else { + try { + // If it's not muted; we just need to create our POJO from the returned JSON and handle correctly the errors + String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); + T parsedObject = gson.fromJson(json, clazz); + return Response.success(parsedObject, HttpHeaderParser.parseCacheHeaders(response)); + } catch (UnsupportedEncodingException e) { + return Response.error(new ParseError(e)); + } catch (JsonSyntaxException e) { + return Response.error(new ParseError(e)); + } + } + return null; + } + + @Override + public Map getHeaders() throws AuthFailureError { + return headers != null ? headers : super.getHeaders(); + } + + private void configureRequest() { + // Set retry policy + // Add headers, for auth for example + // ... + } +} \ No newline at end of file diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyJSON.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyJSON.java new file mode 100644 index 0000000..4bec139 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyJSON.java @@ -0,0 +1,25 @@ +package com.debugger.tophe_volley.volley.internal; + +import com.android.volley.Request; +import com.android.volley.toolbox.JsonObjectRequest; + +import co.tophe.body.HttpBodyJSON; + +/** + * Created by Denis Babak on 13/06/16. + */ +public class VolleyHttpBodyJSON extends HttpBodyJSON implements VolleyBody { + + public VolleyHttpBodyJSON(HttpBodyJSON sourceBody) { + super(sourceBody); + } + + @Override + public String getContentType() { + return "application/json"; + } + + @Override + public void setOutputData(Request requestBuilder) { + } +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyMultiPart.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyMultiPart.java new file mode 100644 index 0000000..db57073 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyMultiPart.java @@ -0,0 +1,68 @@ +package com.debugger.tophe_volley.volley.internal; + +import android.util.Log; + +import com.android.volley.Request; +import com.android.volley.VolleyLog; + +import java.io.File; + +import co.tophe.body.HttpBodyMultiPart; + +/** + * Created by Denis Babak on 14/06/16. + */ +public class VolleyHttpBodyMultiPart extends HttpBodyMultiPart implements VolleyBody { + + private long contentLength = -1; + + public VolleyHttpBodyMultiPart(HttpBodyMultiPart sourceBody) { + super(sourceBody); + /*for (HttpParam param : mParams) { + if (param.value instanceof File) { + FilePart part = new FilePart(param.name, (File) param.value); + if (!TextUtils.isEmpty(param.contentType)) + part.setContentType(param.contentType); + part.getRawHeaders().add("Content-Transfer-Encoding", "binary"); + List partList = new ArrayList(1); + partList.add(part); + requestBuilder.addMultipartParts(partList); + } else if (param.value instanceof InputStream) { + InputStreamPart part = new InputStreamPart(param.name, (InputStream) param.value, param.length); + if (!TextUtils.isEmpty(param.contentType)) + part.setContentType(param.contentType); + part.getRawHeaders().add("Content-Transfer-Encoding", "binary"); + List partList = new ArrayList(1); + partList.add(part); + requestBuilder.addMultipartParts(partList); + } + } + for (HttpParam param : mParams) { + if (param.value instanceof String) { + requestBuilder.setMultipartParameter(param.name, (String) param.value); + } + }*/ + } + + @Override + public String getContentType() { + return "multipart/form-data; boundary=t0Ph3Multip4rt; charset=UTF-8"; + } + + public void setContentLength(long length) { + this.contentLength = length; + } + + @Override + public long getContentLength() { + if(contentLength >= 0) + return contentLength; + else + return super.getContentLength(); + + } + + @Override + public void setOutputData(Request requestBuilder) { + } +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyString.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyString.java new file mode 100644 index 0000000..db81d0b --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyString.java @@ -0,0 +1,32 @@ +package com.debugger.tophe_volley.volley.internal; + +import com.android.volley.Request; +import com.android.volley.Response; +import com.android.volley.VolleyError; +import com.android.volley.toolbox.StringRequest; + +import co.tophe.body.HttpBodyJSON; +import co.tophe.body.HttpBodyString; + +/** + * Created by Denis Babak on 14/06/16. + */ +public class VolleyHttpBodyString extends HttpBodyString implements VolleyBody { + + String url; + + public VolleyHttpBodyString(HttpBodyString sourceBody, String url) { + super(sourceBody); + this.url = url; + } + + @Override + public String getContentType() { + return "text/plain"; + } + + @Override + public void setOutputData(Request requestBuilder) { + //requestBuilder.setStringBody(value); + } +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyUrlEncoded.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyUrlEncoded.java new file mode 100644 index 0000000..3ac8701 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/internal/VolleyHttpBodyUrlEncoded.java @@ -0,0 +1,46 @@ +package com.debugger.tophe_volley.volley.internal; + +import com.android.volley.Request; + +import java.util.HashMap; +import java.util.Map; + +import co.tophe.body.HttpBodyUrlEncoded; +import co.tophe.body.NameValuePair; + +/** + * Created by Denis Babak on 14/06/16. + */ +public class VolleyHttpBodyUrlEncoded extends HttpBodyUrlEncoded implements VolleyBody { + + public VolleyHttpBodyUrlEncoded(HttpBodyUrlEncoded sourceBody) { + super(sourceBody); + } + + protected Map params = new HashMap(); + + public Map getParams() { + if(params.isEmpty()) + for (NameValuePair param : mParams) { + params.put(param.getName(), param.getValue()); + //requestBuilder.setBodyParameter(param.getName(), param.getValue()); + } + return params; + } + + @Override + public String getContentType() { + return "application/x-www-form-urlencoded; charset=utf-8"; + //return "application/json;"; + } + + @Override + public long getContentLength() { + return super.getContentLength(); + } + + @Override + public void setOutputData(Request requestBuilder) { + } + +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/HttpBodyMultipartRequest.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/HttpBodyMultipartRequest.java new file mode 100644 index 0000000..4fb8fad --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/HttpBodyMultipartRequest.java @@ -0,0 +1,133 @@ +package com.debugger.tophe_volley.volley.request; + +import com.android.volley.AuthFailureError; +import com.android.volley.Response; +import com.android.volley.VolleyLog; +import com.debugger.tophe_volley.volley.internal.VolleyHttpBodyMultiPart; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import co.tophe.body.HttpBodyMultiPart; +import cz.msebera.android.httpclient.HttpEntity; +import cz.msebera.android.httpclient.entity.ContentType; +import cz.msebera.android.httpclient.entity.mime.HttpMultipartMode; +import cz.msebera.android.httpclient.entity.mime.MultipartEntityBuilder; + +/** + * Created by Denis Babak on 07/07/16. + */ +public class HttpBodyMultipartRequest extends StringRequestWithHeaders { + + public HttpBodyMultipartRequest(int method, String url, VolleyHttpBodyMultiPart params, Response.Listener listener, Response.ErrorListener errorListener) { + super(method, url, params, listener, errorListener); + mListener = listener; + this.mFilePartData = new ArrayList<>(); + this.mStringPart = new HashMap<>(); + for(HttpBodyMultiPart.HttpParam p : params.mParams) { + if (p.value instanceof File) { + mFilePartData.add(p); + } else if(p.value instanceof InputStream) { + //??? + } + } + for(HttpBodyMultiPart.HttpParam p : params.mParams) { + if(p.value instanceof String) { + mStringPart.put(p.name, (String)p.value); + } + } + /*this.mFilePartData = mFilePartData; + this.mStringPart = mStringPart; + this.mHeaderPart = mHeaderPart; + this.mContext = mContext;*/ + mEntityBuilder.setMode(HttpMultipartMode.STRICT); + mEntityBuilder.setBoundary(HttpBodyMultiPart.boundary); + mEntityBuilder.setCharset(Charset.forName("UTF-8")); + mEntityBuilder.seContentType(ContentType.MULTIPART_FORM_DATA); + buildMultipartFileEntity(); + buildMultipartTextEntity(); + mHttpEntity = mEntityBuilder.build(); + params.setContentLength(mHttpEntity.getContentLength()); + params.apacheContentLength = mHttpEntity.getContentLength(); + } + + public long getContentLength() { + return mHttpEntity.getContentLength(); + } + + @Override + public String getBodyContentType() { + //return "multipart/form-data;boundary=" + HttpBodyMultiPart.boundary + "; charset=UTF-8"; + return mHttpEntity.getContentType().getValue(); + } + + private final Response.Listener mListener; + private final List mFilePartData; + private final Map mStringPart; + + private MultipartEntityBuilder mEntityBuilder = MultipartEntityBuilder.create(); + private HttpEntity mHttpEntity; + + /*public static String getMimeType(Context context, String url) { + Uri uri = Uri.fromFile(new File(url)); + String mimeType = null; + if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) { + ContentResolver cr = context.getContentResolver(); + mimeType = cr.getType(uri); + } else { + String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri + .toString()); + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( + fileExtension.toLowerCase()); + } + return mimeType; + }*/ + + private void buildMultipartFileEntity() { + for (HttpBodyMultiPart.HttpParam entry : mFilePartData) { + try { + String key = entry.name; + File file = (File)entry.value; + String mimeType = entry.contentType; + mEntityBuilder.addBinaryBody(key, file, ContentType.create(mimeType), file.getName()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private void buildMultipartTextEntity() { + for (Map.Entry entry : mStringPart.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (key != null && value != null) + mEntityBuilder.addTextBody(key, value); + } + } + + + /*@Override + public String getBodyContentType() { + return mHttpEntity.getContentType().getValue(); + }*/ + + @Override + public byte[] getBody() throws AuthFailureError { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try { + mHttpEntity.writeTo(bos); + } catch (IOException e) { + VolleyLog.e("IOException writing to ByteArrayOutputStream"); + } + return bos.toByteArray(); + } + + +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/HttpBodyUrlEncodedRequest.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/HttpBodyUrlEncodedRequest.java new file mode 100644 index 0000000..2aee0c9 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/HttpBodyUrlEncodedRequest.java @@ -0,0 +1,61 @@ +package com.debugger.tophe_volley.volley.request; + +import android.util.Log; + +import com.android.volley.AuthFailureError; +import com.android.volley.Response; +import com.debugger.tophe_volley.volley.internal.VolleyHttpBodyUrlEncoded; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Map; + +/** + * Created by Denis Babak on 06/07/16. + */ +public class HttpBodyUrlEncodedRequest extends StringRequestWithHeaders { + + public HttpBodyUrlEncodedRequest(int method, String url, VolleyHttpBodyUrlEncoded params, Response.Listener listener, Response.ErrorListener errorListener) { + super(method, url, params, listener, errorListener); + this.params = params.getParams(); + } + + public HttpBodyUrlEncodedRequest(String url, Response.Listener listener, Response.ErrorListener errorListener) { + super(url, listener, errorListener); + } + + @Override + protected Map getParams() throws AuthFailureError { + return super.getParams(); + } + + private byte[] encodeParameters(Map params, String paramsEncoding) { + StringBuilder encodedParams = new StringBuilder(); + try { + for (Map.Entry entry : params.entrySet()) { + encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding)); + encodedParams.append('='); + encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding).replace("*", "%2A")); + encodedParams.append('&'); + } + return encodedParams.toString().getBytes(paramsEncoding); + } catch (UnsupportedEncodingException uee) { + throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee); + } + } + + @Override + public byte[] getBody() throws AuthFailureError { + Log.e("", "BODY " + super.getBody().toString()); + Map params = getParams(); + if (params != null && params.size() > 0) { + return encodeParameters(params, getParamsEncoding()); + } + return null; + } + + @Override + public String getBodyContentType() { + return "application/x-www-form-urlencoded; charset=" + getParamsEncoding(); + } +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/JSONRequestWithHeaders.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/JSONRequestWithHeaders.java new file mode 100644 index 0000000..d35c0e2 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/JSONRequestWithHeaders.java @@ -0,0 +1,93 @@ +package com.debugger.tophe_volley.volley.request; + +import com.android.volley.AuthFailureError; +import com.android.volley.NetworkResponse; +import com.android.volley.ParseError; +import com.android.volley.Response; +import com.android.volley.toolbox.HttpHeaderParser; +import com.android.volley.toolbox.JsonObjectRequest; +import com.android.volley.toolbox.JsonRequest; +import com.debugger.tophe_volley.volley.internal.ByteBufferList; +import com.debugger.tophe_volley.volley.internal.ByteBufferListInputStream; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Denis Babak on 16/06/16. + */ +public class JSONRequestWithHeaders extends JsonObjectRequest implements VolleyRequest { + + public JSONRequestWithHeaders(int method, String url, JSONObject jsonRequest, Response.Listener listener, Response.ErrorListener errorListener) { + super(method, url, jsonRequest, listener, errorListener); + } + + public JSONRequestWithHeaders(String url, JSONObject jsonRequest, Response.Listener listener, Response.ErrorListener errorListener) { + super(url, jsonRequest, listener, errorListener); + } + + ByteBufferList bbList; + public ByteBufferListInputStream bbIs; + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + bbList = new ByteBufferList(response.data); + bbIs = new ByteBufferListInputStream(bbList); + code = response.statusCode; + responseHeaders = new HashMap<>(); + responseHeaders.putAll(response.headers); + + try { + String jsonString = new String(response.data, + HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET)); + + JSONObject result = null; + + if (jsonString != null && jsonString.length() > 0) + result = new JSONObject(jsonString); + + return Response.success(result, + HttpHeaderParser.parseCacheHeaders(response)); + } catch (UnsupportedEncodingException e) { + return Response.error(new ParseError(e)); + } catch (JSONException je) { + return Response.error(new ParseError(je)); + } + } + + public long getContentLength() { + return getBody().length; + } + + @Override + public int getResponseCode() { + return code; + } + + @Override + public Map getHeaders() throws AuthFailureError { + if(customHeaders != null && customHeaders.size() > 0) { + customHeaders.putAll(super.getHeaders()); + return customHeaders; + } + return super.getHeaders(); + } + + private Map customHeaders = new HashMap<>(); + private Map responseHeaders; + private int code = -1; + + @Override + public void addHeaders(Map collection) { + this.customHeaders.putAll(collection); + } + + @Override + public Map getResponseHeaders() { + return responseHeaders; + } +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/StringRequestWithHeaders.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/StringRequestWithHeaders.java new file mode 100644 index 0000000..d015fd6 --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/StringRequestWithHeaders.java @@ -0,0 +1,70 @@ +package com.debugger.tophe_volley.volley.request; + +import com.android.volley.AuthFailureError; +import com.android.volley.NetworkResponse; +import com.android.volley.Response; +import com.android.volley.toolbox.StringRequest; +import com.debugger.tophe_volley.volley.internal.ByteBufferList; +import com.debugger.tophe_volley.volley.internal.ByteBufferListInputStream; +import com.debugger.tophe_volley.volley.internal.VolleyBody; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Denis Babak on 15/06/16. + */ +public class StringRequestWithHeaders extends StringRequest implements VolleyRequest { + public StringRequestWithHeaders(int method, String url, VolleyBody params, Response.Listener listener, Response.ErrorListener errorListener) { + super(method, url, listener, errorListener); + } + + public StringRequestWithHeaders(String url, Response.Listener listener, Response.ErrorListener errorListener) { + super(url, listener, errorListener); + } + + protected Map customHeaders = new HashMap<>(); + protected Map params; + + @Override + public void addHeaders(Map collection) { + this.customHeaders.putAll(collection); + } + + @Override + protected Map getParams() throws AuthFailureError { + return params; + } + + @Override + public int getResponseCode() { + return code; + } + + @Override + public Map getResponseHeaders() { + return responseHeaders; + } + + @Override + public Map getHeaders() throws AuthFailureError { + if(customHeaders != null && customHeaders.size() > 0) { + return customHeaders; + } + return super.getHeaders(); + } + + public ByteBufferListInputStream is; + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + code = response.statusCode; + responseHeaders = new HashMap<>(); + responseHeaders.putAll(response.headers); + is = new ByteBufferListInputStream(new ByteBufferList(response.data)); + return super.parseNetworkResponse(response); + } + + protected int code = -1; + protected Map responseHeaders; +} diff --git a/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/VolleyRequest.java b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/VolleyRequest.java new file mode 100644 index 0000000..1d5202a --- /dev/null +++ b/tophe-volley/src/main/java/com/debugger/tophe_volley/volley/request/VolleyRequest.java @@ -0,0 +1,13 @@ +package com.debugger.tophe_volley.volley.request; + +import java.util.Map; + +/** + * Created by Denis Babak on 15/06/16. + */ +public interface VolleyRequest { + + public int getResponseCode(); + public Map getResponseHeaders(); + public void addHeaders(Map headers); +} diff --git a/tophe-volley/src/main/res/values/strings.xml b/tophe-volley/src/main/res/values/strings.xml new file mode 100644 index 0000000..b135ccc --- /dev/null +++ b/tophe-volley/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Tophe-Volley + diff --git a/tophe-volley/src/test/java/com/debugger/tophe_volley/ExampleUnitTest.java b/tophe-volley/src/test/java/com/debugger/tophe_volley/ExampleUnitTest.java new file mode 100644 index 0000000..d53394b --- /dev/null +++ b/tophe-volley/src/test/java/com/debugger/tophe_volley/ExampleUnitTest.java @@ -0,0 +1,15 @@ +package com.debugger.tophe_volley; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * To work on unit tests, switch the Test Artifact in the Build Variants view. + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file