diff --git a/.classpath b/.classpath
deleted file mode 100644
index 14fcf527f..000000000
--- a/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml
new file mode 100644
index 000000000..405a2b306
--- /dev/null
+++ b/.github/workflows/gradle-wrapper-validation.yml
@@ -0,0 +1,10 @@
+name: "Validate Gradle Wrapper"
+on: [push, pull_request]
+
+jobs:
+ validation:
+ name: "Validation"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: gradle/wrapper-validation-action@v1
diff --git a/.gitignore b/.gitignore
old mode 100644
new mode 100755
index 0900c481f..d3b225642
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,40 @@
+# Custom
_site
+
+# Ant
MANIFEST.MF
-*.jar
+./*.jar
build.num
build
+
+# ADT
+.classpath
+.project
+.settings
local.properties
-bin/
-gen/
+bin
+gen
_layouts
+proguard.cfg
+
+# OSX
.DS_Store
-gh-pages
\ No newline at end of file
+
+# Github
+gh-pages
+
+# Gradle
+.gradle
+build
+
+# IDEA
+*.iml
+*.ipr
+*.iws
+out
+.idea
+
+# Maven
+target
+release.properties
+pom.xml.*
diff --git a/.project b/.project
deleted file mode 100644
index a97931397..000000000
--- a/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
- android-async-http
-
-
-
-
-
- com.android.ide.eclipse.adt.ResourceManagerBuilder
-
-
-
-
- com.android.ide.eclipse.adt.PreCompilerBuilder
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- com.android.ide.eclipse.adt.ApkBuilder
-
-
-
-
-
- com.android.ide.eclipse.adt.AndroidNature
- org.eclipse.jdt.core.javanature
-
-
diff --git a/.travis.yml b/.travis.yml
new file mode 100755
index 000000000..ddc10848e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,16 @@
+language: android
+sudo: false
+dist: trusty
+jdk: openjdk8
+android:
+ components:
+ - platform-tools
+ - tools
+ - build-tools-28.0.3
+ - android-28
+ - extra-android-m2repository
+ - extra-google-m2repository
+ licenses:
+ - '.+'
+script:
+ - ./gradlew clean assemble check
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
deleted file mode 100644
index fbeab8b4d..000000000
--- a/AndroidManifest.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..72c4b65f6
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,161 @@
+# CHANGELOG
+
+## 1.4.11
+
+ - fix SNI issue on lower android device with Conscrypt
+
+## 1.4.10
+
+ - Fixed IP/name resolution errors #998
+ - Fixed SNI compatibility
+ - Upgraded library HttpClient 4.5.8 from 4.3.6
+
+## 1.4.9 (released 19. 9. 2015)
+
+Complete list of commits included is here [https://github.com/loopj/android-async-http/commits/1.4.9](https://github.com/loopj/android-async-http/commits/1.4.9)
+List of closed issues is here [https://github.com/loopj/android-async-http/issues?milestone=8&state=closed](https://github.com/loopj/android-async-http/issues?milestone=8&state=closed)
+
+ - **IMPORTANT**, We've switched library from using `org.apache.http` to use `cz.msebera.android.httpclient`, you have to update all your code
+ - Library is from now on using upstream version of HttpClient libraries, provided by repackaging project https://github.com/smarek/httpclient-android/
+ - Achieved API23 Compatibility, see #830 for more info
+ - Added HeadSample into sample application, to verify Head request works as it should
+ - FileAsyncHttpResponseHandler now has constructor with `usePoolThread` param, which causes callbacks to be fired from ThreadPool instead of main looper
+
+## 1.4.8 (released 17. 7. 2015)
+
+Complete list of commits included is here [https://github.com/loopj/android-async-http/commits/1.4.8](https://github.com/loopj/android-async-http/commits/1.4.8)
+List of closed issues is here [https://github.com/loopj/android-async-http/issues?milestone=7&state=closed](https://github.com/loopj/android-async-http/issues?milestone=7&state=closed)
+
+ - New constructor for BinaryHttpResponseHandler which takes Looper as argument (thanks to @ScottFrank)
+ - SaxAsyncHttpResponseHandler can be now provided with custom charset, instead of just using default one
+ - Library LogCat tags now use shorter form (forced through Lint checks), appendix "ResponseHandler" shortened to "RH"
+ - Updated documentation on `RequestHandle.cancel(boolean)` and returning correct response according to handle state
+ - SaxAsyncHttpResponseHandler onFailure(int, Header[], byte[], Throwable) used wrong fallback to onSuccess(int, Header[], T), fixed to onFailure(int, Header[], T), where T extends SAX DefaultHandler
+ - Regression fix on onProgress(int,int) documentation
+ - Sample application now can be built with LeakCanary, use i.e. `gradle :sample:installWithLeakCanaryDebug` to use it
+ - Updated RequestParams documentation on handling arrays, sets and maps, along with new RequestParamsDebug sample
+ - Added BlackholeHttpResponseHandler implementation, which discards all response contents and silents all various log messages (see #416)
+ - Added LogInterface, it's default implementation and interface option to disable/enable logging library-wide and set logging verbosity
+ - Added option to TAG RequestHandle and cancel all requests matching specified TAG through `AsyncHttpClient.cancelRequestsByTAG(Object TAG)`
+ - Removed deprecated `getTimeout()` replaced by `getConnectTimeout()` and `getResponseTimeout()` respectively
+ - Removed deprecated `clearBasicAuth()` replaced by `clearCredentialsProvider()`
+
+## 1.4.7 (released 9. 5. 2015)
+
+Complete list of commits included is here [https://github.com/loopj/android-async-http/commits/1.4.7](https://github.com/loopj/android-async-http/commits/1.4.7)
+List of closed issues is here [https://github.com/loopj/android-async-http/issues?milestone=6&state=closed](https://github.com/loopj/android-async-http/issues?milestone=6&state=closed)
+
+ - Fixed crash when canceling through RequestHandle from UI Thread (NetworkOnMainThreadException)
+ - Fixed URL encoding feature, that was breaking whole URL, not just path and query parts
+ - FileAsyncHttpResponseHandler now checks that target file path is available or can be created
+ - DataAsyncHttpResponseHandler was sending cancel notification instead of progress notification, fixed
+ - Added support for HTTP PATCH requests
+ - Fixed Assert exception when mkdirs in FileAsyncHttpResponseHandler tries to create dirs that already exists
+ - Provided option to easily override ClientConnectionManager provision in AsyncHttpClient
+ - Changed onProgress from (int,int) to (long,long) for dealing with large transfers
+ - Renamed typo of `preemtive` to `preemptive` (preemptive basic auth)
+ - Added option to put File array in RequestParams
+ - RequestParams now support forcing Content-Type into `multipart/form-data` even if there are no files/streams to be multiparted
+ - Gradle added support for installing to local maven repository, through `gradle installArchives` task
+ - Added support for Json RFC5179 in JsonHttpResponseHandler
+
+## 1.4.6 (released 7. 9. 2014)
+
+Complete list of commits included is here [https://github.com/loopj/android-async-http/commits/1.4.6](https://github.com/loopj/android-async-http/commits/1.4.6)
+List of closed issues is here [https://github.com/loopj/android-async-http/issues?milestone=4&state=closed](https://github.com/loopj/android-async-http/issues?milestone=2&state=closed)
+
+ - Fixed missing boundary when passing content-type as call param along with HttpEntity
+ - Added warnings for not overriden calls in JsonHttpResponseHandler (and others)
+ - RequestParams now implement Serializable, to support storing them and passing them along
+ - Added option to add File part with custom file name (overriding the real file name)
+ - Fixed not-escaped contents in JsonStreamEntity
+ - Separated connect and response timeout settings
+ - Allowed to pass Looper into *HttpResponseHandler classes
+ - Fixed reporting progress on GZIP compressed down-streams
+ - Added more samples (eg. AsyncBackgroundThreadSample.java, ContentTypeForHttpEntitySample.java, PrePostProcessingSample.java)
+ - Added option to pre- and post- process data in AsyncHttpRequest.java via subclass (see PrePostProcessingSample.java)
+ - Fixed ConcurrentModificationException on AsyncHttpClient.cancelRequests
+ - Fixed handling BOM in decoding response in TextHttpResponseHandler and JsonHttpResponseHandler
+
+## 1.4.5 (released 22. 6. 2014)
+
+Complete list of commits included is here [https://github.com/loopj/android-async-http/commits/1.4.5](https://github.com/loopj/android-async-http/commits/1.4.5)
+List of closed issues is here [https://github.com/loopj/android-async-http/issues?milestone=2&state=closed](https://github.com/loopj/android-async-http/issues?milestone=2&state=closed)
+
+ - Support for circular and relative redirects
+ - Added support for SAX parsing, see `SaxAsyncHttpResponseHandler`
+ - Fixed Threading issue when used in Service or non-UI Thread context
+ - Fixed GZIPInputStream issue when running in StrictMode
+ - Removed unnecessary (ambiguous) callback methods (were deprecated in 1.4.4)
+ - Added JsonStreamerEntity to allow up streaming JSON data
+ - Added possibility to modify blacklisted/whitelisted exceptions (see `RetryHandler`)
+ - Using `newCachedThreadPool()` as default ExecutorService in library, with option to change it via main interface
+ - Added `ResponseHandlerInterface` to support completely custom implementations of response handlers
+ - Added `onProgress(int,int)` callback, which is used for upstream progress logging (eg. Progressbar dialogs)
+ - Fixed "division by zero" in default response handler
+ - Added DataAsyncHttpResponseHandler, which has extra callback method which receives partially received data
+ - Fixed problem with uploading more than 2 files (changes in SimpleMultipartEntity)
+ - Fixed problem where on GarbageCollectors activity there was no callback received
+ - Added warning for cases, where headers overwrite each other (between common headers and per-request headers)
+ - Safely closing IO streams (both upstream and downstream)
+ - Fixed PersistentCookieStore issue, where non-persistent cookies were stored and added option to delete stored cookie
+ - Fixed networkOnMainThreadException when canceling requests (`AsyncHttpClient#cancel(boolean)`)
+ - Removed default User-Agent definition from library
+ - Fixed handling null URLs in default interface calls
+ - Allowed to subclass AsyncHttpClient and simply provide custom AsyncHttpRequest object (`AsyncHttpClient#newAsyncHttpRequest`)
+ - Changed project structure to be default Intellij IDEA structure (eg. library/src/main/java)
+ - Catching UnsupportedEncodingException default Async and Text response handlers
+ - Added strict Lint checking for both Library and Sample application
+ - Added example implementations in Sample application
+ - Requests threading (ThreadPool usage, 6 seconds delay on response)
+ - Synchronous request (from Activity and IntentService)
+ - SAX Parsing the response
+ - Retry request sample
+ - Handling 302 redirects
+ - RangeResponse (working with partially received data)
+ - Basic usage of GET, POST, PUT, DELETE
+ - JSON response parsing
+ - GZIP compressed communication
+ - Binary handler (receives `byte[]` without parsing/converting)
+ - File response handler (saving response directly into given File)
+ - Self-signed CA sample (how to pin SSL certificate or add custom trust-chain for requests)
+ - Persistent cookies store (persisting cookies between requests)
+ - Post multi-part encoded Files (SimpleMultipartEntity)
+ - Jackson JSON integration
+
+## 1.4.4 (released 28. 10. 2013)
+
+Complete list of commits included is here [https://github.com/loopj/android-async-http/commits/1.4.4](https://github.com/loopj/android-async-http/commits/1.4.4)
+List of closed issues is here [https://github.com/loopj/android-async-http/issues?milestone=1&state=closed](https://github.com/loopj/android-async-http/issues?milestone=1&state=closed)
+
+ - Added FileAsyncHttpResponseHandler for direct saving response into File instead of device memory
+ - JsonHttpResponseHandler now parsing JSON in separate thread
+ - Interface method to allow/deny handling of http redirects
+ - Added method to delete previously set header (AsyncHttpClient.java)
+ - Not creating new Thread if call initiated outside of UI Thread (broken, fixed in 1.4.5)
+ - Support for changing response Charset (default still UTF-8)
+ - Allow setting maximum retries count (AsyncHttpClient.java)
+ - SimpleMultipartEntity now allows repeated usage (`HttpEntity.isRepeatable()`)
+ - Added custom SSLSocketFactory to allow certificate pinning and accepting self-signed or untrusted SSL certificates
+ - Callbacks to return HTTP status code and response Headers
+ - Added support for unsetting Basic Auth params
+ - Added support for non-standard HTTP and HTTPS ports (new constructors of `AsyncHttpClient`)
+ - Allowed to change dynamically allowed content-types for BinaryHttpResponseHandler per-response-handler (was static previously)
+ - Added support for setting proxy, optionally with authentication
+ - `AsyncHttpClient#setProxy(String hostname, int port, String username, String password)`
+ - Support for passing Maps, Sets and Lists via RequestParams
+ - Properly chaining callback methods (onSuccess, onFailure, ...) in descendant order by number of function params
+ - Fixed incorrect handling of URLs with spaces after redirect
+ - now sanitizes spaces within URL both on-request and on-redirect
+ - Added RequestHandle which can be used to cancel and/or check request status
+ - Is returned for each call (`.post(...)`, `.get(...)`, `.head(...)`, `.put(...)`, etc..)
+ - Added BaseJsonHttpResponseHandler to simplify integration with Jackson JSON, Gson and other JSON parsing libraries
+ - Added Sample application to demonstrate functions and usage
+ - Using [https://httpbin.org/](https://httpbin.org/) to test methods
+ - Enforcing INTERNET permission
+ - Support for Gradle buildscript
+ - Support for Travis CI (Continuous Integration) testing
+ - Dropped support for Eclipse ADT usage (obsolete)
+ - Added HTTP HEAD method support
+ - Releasing both AAR and JAR (+javadoc and sources) into Maven Central repository
+ - Added tons of mising Javadoc for both classes and methods
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..fa99f5847
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,47 @@
+CONTRIBUTING
+============
+AsyncHttpClient is an open-source project made by developers for developers!
+
+If you would like to contribute to the project, it's really great. You can contribute in a variety of ways:
+
+ * Help us with test cases and examples for the Wiki (and kindly follow our [Coding Standards](#coding-standards))
+ * If you have a good idea/patch for the project, create a [pull request](#pull-requests)
+ * Found a bug? You're more than welcome to [submit an issue](#issues)
+ * Help other fellow developers solve their problems, you're welcome to do so in issues
+
+We do require certain guidelines to be followed so that the quality of the project remains top-notch:
+
+PULL requests
+-------------
+When you submit a patch or a new functionality for the project, you must open a pull request. We will get to the pull request as soon as possible, investigate what functionality or bug fixes have been added and decide whether to include it in the library or not -- for the benefit of everyone.
+
+**You agree that all contributions that you make to the library will be distributed further under the same license as the library itself (Apache V2).**
+
+Don't be discouraged if your pull request is rejected. This is not a deadline and sometimes with a proper explanation on your side, we are persuaded to merge in the request. Just remember that this is a library for everyone and as such must meet certain, generic rules that we would like to believe are following.
+
+ISSUES
+---------
+
+
+
+The issues system is the place to report bugs and not for submitting patches or new functionality. As helpful as we would like to be, we cannot replace the developer and we certainly do not see what you're seeing. So when you come to report an issue, follow these simple rules:
+
+ * Report bugs in the English language only
+ * Use Markdown to format your issue in a fashionable way (easier to read): [Familiarize yourself](https://help.github.com/articles/github-flavored-markdown)
+ * If the issue is due to a crash, include the stack trace -- `throwable.printStackTrace()` -- and any other detail that will shed light on the problem
+ * We need to see the source code (minus certain details that you think are confidential) that caused the problem in the first place, so include it too
+
+Opening issues without providing us with the information necessary to debug and fix it is useless; so we will close such issues within 7 days period
+
+CODING STANDARDS
+----------------
+We need you to follow certain rules when sending source code contributions. These are the basic principles that we ourselves abide to and we require that you do so as well:
+
+ * Do not use the Tab character (it's in first place for a reason)
+ * Indentation is 4 spaces
+ * Include the copyright info (as in other files) at the top of the class file
+ * You must provide proper Javadoc, including description, in English for both public and protected methods, classes and properties
+ * Group packages that belong to the same top-level package together, followed by an empty line
+ * Add an empty line after and before class/interface declarations, methods and constructors
+ * Add an empty line before and after a group of properties
+ * Do not catch generic Exception/Throwable errors, but always catch the most specific type of the exception/error
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 000000000..792ce018f
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,9 @@
+Android Async Http Client library
+Copyright (c) 2011-2015 James Smith
+https://loopj.com
+
+Copyright (c) 2015-2019 Marek Sebera
+htts://msebera.cz
+
+This product includes software developed by
+The Apache Software Foundation (https://www.apache.org/).
diff --git a/PUBLISHING.md b/PUBLISHING.md
new file mode 100644
index 000000000..9ac180688
--- /dev/null
+++ b/PUBLISHING.md
@@ -0,0 +1,48 @@
+# Publish to oss.sonatype.org staging repository
+
+```
+gradle clean
+# edit build.gradle to update allprojects.version
+# edit gradle.properties to update VERSION_NAME and VERSION_CODE
+# edit CHANGELOG.md and add changes for published version
+# edit sample/src/main/AndroidManifest.xml and update both versionCode and versionName attributes
+# edit README.md and update paths, latest version, repository links and sample codes
+gradle check
+# fix all possible errors and warnings before publishing
+cd library
+# publishing only library, so following tasks are run in "library" sub-folder
+gradle generateJavadocJar
+# this will create javadoc archive check the contents via following cmd (use different name and/or path if needed)
+# jar -tf ./library/build/libs/android-async-http-null-Release-1.4.11-javadoc.jar
+gradle publish
+```
+
+# Publish to maven central
+
+*For Nexus Repository Manager 2.14+*
+
+ - Login into https://oss.sonatype.org/
+ - Navigation, choose Build Promotion > Staging Repositories
+ - Explore if repo was automatically created and if contents do match expectations
+ - Select repository and use "Close" action, to run pre-publishing checks
+ - Wait a bit
+ - Refresh the panel with repositories
+ - Select repository and use "Release" action, if not available, there are issues, that need to be fixed before publishing
+
+# In GIT
+
+
+**example code using 1.4.11 as released version**
+```
+git tag 1.4.11
+git push origin --tags
+```
+
+# Github
+
+in *releases* https://github.com/android-async-http/android-async-http/releases
+
+ - Create new release from appropriate tag (see GIT above)
+ - Describe in similar terms as in CHANGELOG.md what is being done
+ - Upload JAR (library, sources and javadoc) and AAR (library) along with the release
+ - Publish by saving form
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index 1602a5343..1ff6c9f85
--- a/README.md
+++ b/README.md
@@ -1,8 +1,22 @@
Asynchronous Http Client for Android
====================================
+[](https://travis-ci.org/android-async-http/android-async-http)
-An asynchronous, callback-based Http client for Android built on top of Apache's [HttpClient](http://hc.apache.org/httpcomponents-client-ga/) libraries.
+An asynchronous, callback-based Http client for Android built on top of Apache's [HttpClient](https://hc.apache.org/httpcomponents-client-ga/) libraries.
+Changelog
+---------
+
+See what is new in version 1.4.11 released on 29th June 2020
+
+https://github.com/android-async-http/android-async-http/blob/1.4.11/CHANGELOG.md
+
+Javadoc
+-------
+
+Latest Javadoc for 1.4.11 release are available here (also included in Maven repository):
+
+https://android-async-http.github.io/android-async-http/doc/
Features
--------
@@ -11,15 +25,75 @@ Features
- Requests use a **threadpool** to cap concurrent resource usage
- GET/POST **params builder** (RequestParams)
- **Multipart file uploads** with no additional third party libraries
-- Tiny size overhead to your application, only **19kb** for everything
+- Tiny size overhead to your application, only **60kb** for everything
- Automatic smart **request retries** optimized for spotty mobile connections
- Automatic **gzip** response decoding support for super-fast requests
- Optional built-in response parsing into **JSON** (JsonHttpResponseHandler)
- Optional **persistent cookie store**, saves cookies into your app's SharedPreferences
+- Support sni with Conscrypt on older android device ([wiki](https://github.com/android-async-http/android-async-http/wiki/Support-SNI-on-lower-android-device))
+
+Examples
+--------
+
+For inspiration and testing on device we've provided Sample Application.
+See individual samples [here on Github](https://github.com/android-async-http/android-async-http/tree/1.4.11/sample/src/main/java/com/loopj/android/http/sample)
+To run Sample application, simply clone the repository and run this command, to install it on connected device
+```java
+gradle :sample:installDebug
+```
+
+Maven
+-----
+You can now integrate this library in your project via Maven. There are available two kind of builds.
+
+**releases, maven central**
+
+https://repo1.maven.org/maven2/com/loopj/android/android-async-http/
+```
+Maven URL: https://repo1.maven.org/maven2/
+GroupId: com.loopj.android
+ArtifactId: android-async-http
+Version: 1.4.11
+Packaging: JAR or AAR
+```
+Gradle
+```groovy
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation 'com.loopj.android:android-async-http:1.4.11'
+}
+```
+
+**development snapshots**
+snapshot might not be published yet
+
+https://oss.sonatype.org/content/repositories/snapshots/com/loopj/android/android-async-http/
+```
+Maven URL: https://oss.sonatype.org/content/repositories/snapshots/
+GroupId: com.loopj.android
+ArtifactId: android-async-http
+Version: 1.4.12-SNAPSHOT
+Packaging: JAR or AAR
+```
+Gradle
+```groovy
+repositories {
+ maven {
+ url 'https://oss.sonatype.org/content/repositories/snapshots/'
+ }
+}
+dependencies {
+ implementation 'com.loopj.android:android-async-http:1.4.11-SNAPSHOT'
+}
+```
Documentation, Features and Examples
------------------------------------
Full details and documentation can be found on the project page here:
-http://loopj.com/android-async-http/
\ No newline at end of file
+https://android-async-http.github.io/android-async-http/
+
diff --git a/build.gradle b/build.gradle
new file mode 100755
index 000000000..2b8e1368e
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,40 @@
+buildscript {
+ repositories {
+ jcenter()
+ google()
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
+ maven { url "https://plugins.gradle.org/m2/" }
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.0.0'
+ classpath 'com.vanniktech:gradle-android-javadoc-plugin:0.4.0-SNAPSHOT'
+ classpath 'digital.wup:android-maven-publish:3.6.2'
+ classpath "gradle.plugin.com.dorongold.plugins:task-tree:1.4"
+ }
+}
+
+def isReleaseBuild() {
+ return version.contains("SNAPSHOT") == false
+}
+
+allprojects {
+ group = 'com.loopj.android'
+ version = '1.4.11'
+
+ repositories {
+ google()
+ jcenter()
+ mavenCentral()
+ }
+
+ tasks.withType(JavaCompile) {
+ options.encoding = "UTF-8"
+ options.compilerArgs << "-Xlint:unchecked"
+ options.compilerArgs << "-Xlint:deprecation"
+ }
+}
+
+apply plugin: 'android-reporting'
+apply plugin: 'com.vanniktech.android.javadoc'
+apply plugin: 'com.dorongold.task-tree'
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 316d6b940..000000000
--- a/build.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/examples/ExampleUsage.java b/examples/ExampleUsage.java
deleted file mode 100644
index 2b7a4fa0a..000000000
--- a/examples/ExampleUsage.java
+++ /dev/null
@@ -1,14 +0,0 @@
-import com.loopj.android.http.*;
-
-public class ExampleUsage {
- public static void makeRequest() {
- AsyncHttpClient client = new AsyncHttpClient();
-
- client.get("http://www.google.com/", new AsyncHttpResponseHandler() {
- @Override
- public void onSuccess(String response) {
- System.out.println(response);
- }
- });
- }
-}
\ No newline at end of file
diff --git a/examples/TwitterRestClient.java b/examples/TwitterRestClient.java
deleted file mode 100644
index 387a87114..000000000
--- a/examples/TwitterRestClient.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Static wrapper library around AsyncHttpClient
-
-import com.loopj.android.http.*;
-
-public class TwitterRestClient {
- private static final String BASE_URL = "http://api.twitter.com/1/";
-
- private static AsyncHttpClient client = new AsyncHttpClient();
-
- public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
- client.get(getAbsoluteUrl(url), params, responseHandler);
- }
-
- public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
- client.post(getAbsoluteUrl(url), params, responseHandler);
- }
-
- private static String getAbsoluteUrl(String relativeUrl) {
- return BASE_URL + relativeUrl;
- }
-}
\ No newline at end of file
diff --git a/examples/TwitterRestClientUsage.java b/examples/TwitterRestClientUsage.java
deleted file mode 100644
index a4c89c8ce..000000000
--- a/examples/TwitterRestClientUsage.java
+++ /dev/null
@@ -1,21 +0,0 @@
-import org.json.*;
-import com.loopj.android.http.*;
-
-class TwitterRestClientUsage {
- public void getPublicTimeline() {
- TwitterRestClient.get("statuses/public_timeline.json", null, new JsonHttpResponseHandler() {
- @Override
- public void onSuccess(JSONArray timeline) {
- try {
- JSONObject firstEvent = (JSONObject)timeline.get(0);
- String tweetText = firstEvent.getString("text");
-
- // Do something with the response
- System.out.println(tweetText);
- } catch(JSONException e) {
- e.printStackTrace();
- }
- }
- });
- }
-}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100755
index 000000000..c9086631a
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,16 @@
+VERSION_NAME=1.4.11
+VERSION_CODE=1411
+GROUP=com.loopj.android
+
+POM_DESCRIPTION=An Asynchronous HTTP Library for Android
+POM_URL=https://android-async-http.github.io/android-async-http/
+POM_SCM_URL=https://github.com/android-async-http/android-async-http
+POM_SCM_CONNECTION=scm:git@github.com:android-async-http/android-async-http.git
+POM_SCM_DEV_CONNECTION=scm:git@github.com:android-async-http/android-async-http.git
+POM_LICENCE_NAME=The Apache Software License, Version 2.0
+POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt
+POM_LICENCE_DIST=repo
+
+POM_DEVELOPER_ID=jamessmith
+POM_DEVELOPER_NAME=James Smith
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..30d399d8d
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..7bfd9f1e4
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Jun 28 22:59:06 PDT 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 000000000..91a7e269e
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 000000000..aec99730b
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/library/build.gradle b/library/build.gradle
new file mode 100755
index 000000000..d63952f0b
--- /dev/null
+++ b/library/build.gradle
@@ -0,0 +1,109 @@
+apply plugin: 'com.android.library'
+apply plugin: 'digital.wup.android-maven-publish'
+apply plugin: 'signing'
+
+android {
+ compileSdkVersion 28
+
+ defaultConfig {
+ minSdkVersion 9
+ targetSdkVersion 28
+ consumerProguardFiles 'proguard.txt'
+ }
+
+ lintOptions {
+ xmlReport false
+ warningsAsErrors true
+ quiet false
+ showAll true
+ disable 'OldTargetApi'
+ }
+}
+
+dependencies {
+ api 'cz.msebera.android:httpclient:4.5.8'
+ compileOnly 'org.conscrypt:conscrypt-android:2.4.0'
+}
+
+project.afterEvaluate { project ->
+
+ android.libraryVariants.all { variant ->
+ def name = variant.buildType.name
+ def task = project.tasks.create "jar${name.capitalize()}", Jar
+ task.dependsOn variant.javaCompileProvider.get()
+ task.from variant.javaCompileProvider.get().destinationDir
+ }
+
+ task sourcesJar(type: Jar) {
+ from android.sourceSets.main.java.srcDirs
+ archiveClassifier = 'sources'
+ }
+
+ task javadocJar(type: Jar, dependsOn: tasks.findAll { task -> task.name.contains('Javadoc') }) {
+ archiveClassifier = 'javadoc'
+ from 'build/docs/javadoc/release/'
+ }
+
+ publishing {
+ publications {
+ maven(MavenPublication) {
+ artifactId = POM_ARTIFACT_ID
+ artifact javadocJar
+ artifact sourcesJar
+ artifact jarRelease
+ from components.android
+
+ pom {
+ name = POM_NAME
+ description = POM_DESCRIPTION
+ packaging = POM_PACKAGING
+ url = POM_URL
+
+ scm {
+ connection = POM_SCM_CONNECTION
+ developerConnection = POM_SCM_DEV_CONNECTION
+ url = POM_SCM_URL
+ }
+
+ licenses {
+ license {
+ name = POM_LICENCE_NAME
+ url = POM_LICENCE_URL
+ distribution = POM_LICENCE_DIST
+ }
+ }
+
+ developers {
+ developer {
+ id = 'mareksebera'
+ name = 'Marek Sebera'
+ }
+ }
+ }
+
+ pom.name = POM_NAME
+ pom.description = POM_DESCRIPTION
+ pom.url = POM_URL
+ pom.packaging = POM_PACKAGING
+ }
+ }
+ repositories {
+ maven {
+ def releaseUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
+ def snapshotUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
+ url = version.endsWith('SNAPSHOT') ? snapshotUrl : releaseUrl
+ credentials {
+ def NexusUsername = project.hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ''
+ def NexusPassword = project.hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ''
+ username NexusUsername
+ password NexusPassword
+ }
+ }
+ }
+ }
+
+ signing {
+ sign publishing.publications.maven
+ }
+}
+
diff --git a/library/gradle.properties b/library/gradle.properties
new file mode 100755
index 000000000..96e35d668
--- /dev/null
+++ b/library/gradle.properties
@@ -0,0 +1,3 @@
+POM_NAME=android-async-http Library
+POM_ARTIFACT_ID=android-async-http
+POM_PACKAGING=aar
diff --git a/library/proguard.txt b/library/proguard.txt
new file mode 100644
index 000000000..e3ab81252
--- /dev/null
+++ b/library/proguard.txt
@@ -0,0 +1,2 @@
+-keep class cz.msebera.android.httpclient.** { *; }
+-keep class com.loopj.android.http.** { *; }
diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml
new file mode 100755
index 000000000..7af3e5711
--- /dev/null
+++ b/library/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java b/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java
new file mode 100755
index 000000000..f38c4fe10
--- /dev/null
+++ b/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java
@@ -0,0 +1,1696 @@
+package com.loopj.android.http;
+
+/*
+ Android Asynchronous Http Client
+ Copyright (c) 2011 James Smith
+ https://github.com/android-async-http/android-async-http
+ 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
+ https://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.
+*/
+
+import android.content.Context;
+import android.os.Looper;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.lang.reflect.Field;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.zip.GZIPInputStream;
+
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HeaderElement;
+import cz.msebera.android.httpclient.HttpEntity;
+import cz.msebera.android.httpclient.HttpException;
+import cz.msebera.android.httpclient.HttpHost;
+import cz.msebera.android.httpclient.HttpRequest;
+import cz.msebera.android.httpclient.HttpRequestInterceptor;
+import cz.msebera.android.httpclient.HttpResponse;
+import cz.msebera.android.httpclient.HttpResponseInterceptor;
+import cz.msebera.android.httpclient.HttpVersion;
+import cz.msebera.android.httpclient.auth.AuthSchemeRegistry;
+import cz.msebera.android.httpclient.auth.AuthScope;
+import cz.msebera.android.httpclient.auth.AuthState;
+import cz.msebera.android.httpclient.auth.Credentials;
+import cz.msebera.android.httpclient.auth.UsernamePasswordCredentials;
+import cz.msebera.android.httpclient.client.CookieStore;
+import cz.msebera.android.httpclient.client.CredentialsProvider;
+import cz.msebera.android.httpclient.client.HttpClient;
+import cz.msebera.android.httpclient.client.RedirectHandler;
+import cz.msebera.android.httpclient.client.methods.HttpEntityEnclosingRequestBase;
+import cz.msebera.android.httpclient.client.methods.HttpHead;
+import cz.msebera.android.httpclient.client.methods.HttpOptions;
+import cz.msebera.android.httpclient.client.methods.HttpPatch;
+import cz.msebera.android.httpclient.client.methods.HttpPost;
+import cz.msebera.android.httpclient.client.methods.HttpPut;
+import cz.msebera.android.httpclient.client.methods.HttpUriRequest;
+import cz.msebera.android.httpclient.client.params.ClientPNames;
+import cz.msebera.android.httpclient.client.protocol.ClientContext;
+import cz.msebera.android.httpclient.conn.ClientConnectionManager;
+import cz.msebera.android.httpclient.conn.params.ConnManagerParams;
+import cz.msebera.android.httpclient.conn.params.ConnPerRouteBean;
+import cz.msebera.android.httpclient.conn.params.ConnRoutePNames;
+import cz.msebera.android.httpclient.conn.scheme.PlainSocketFactory;
+import cz.msebera.android.httpclient.conn.scheme.Scheme;
+import cz.msebera.android.httpclient.conn.scheme.SchemeRegistry;
+import cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory;
+import cz.msebera.android.httpclient.entity.HttpEntityWrapper;
+import cz.msebera.android.httpclient.impl.auth.BasicScheme;
+import cz.msebera.android.httpclient.impl.client.DefaultHttpClient;
+import cz.msebera.android.httpclient.impl.conn.tsccm.ThreadSafeClientConnManager;
+import cz.msebera.android.httpclient.params.BasicHttpParams;
+import cz.msebera.android.httpclient.params.HttpConnectionParams;
+import cz.msebera.android.httpclient.params.HttpParams;
+import cz.msebera.android.httpclient.params.HttpProtocolParams;
+import cz.msebera.android.httpclient.protocol.BasicHttpContext;
+import cz.msebera.android.httpclient.protocol.ExecutionContext;
+import cz.msebera.android.httpclient.protocol.HttpContext;
+import cz.msebera.android.httpclient.protocol.SyncBasicHttpContext;
+
+
+/**
+ * The AsyncHttpClient can be used to make asynchronous GET, POST, PUT and DELETE HTTP requests in
+ * your Android applications. Requests can be made with additional parameters by passing a {@link
+ * RequestParams} instance, and responses can be handled by passing an anonymously overridden {@link
+ * ResponseHandlerInterface} instance.
+ *
+ * @see com.loopj.android.http.AsyncHttpResponseHandler
+ * @see com.loopj.android.http.ResponseHandlerInterface
+ * @see com.loopj.android.http.RequestParams
+ */
+public class AsyncHttpClient {
+
+ public static final String LOG_TAG = "AsyncHttpClient";
+
+ public static final String HEADER_CONTENT_TYPE = "Content-Type";
+ public static final String HEADER_CONTENT_RANGE = "Content-Range";
+ public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
+ public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
+ public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
+ public static final String ENCODING_GZIP = "gzip";
+
+ public static final int DEFAULT_MAX_CONNECTIONS = 10;
+ public static final int DEFAULT_SOCKET_TIMEOUT = 10 * 1000;
+ public static final int DEFAULT_MAX_RETRIES = 5;
+ public static final int DEFAULT_RETRY_SLEEP_TIME_MILLIS = 1500;
+ public static final int DEFAULT_SOCKET_BUFFER_SIZE = 8192;
+ public static LogInterface log = new LogHandler();
+ private final DefaultHttpClient httpClient;
+ private final HttpContext httpContext;
+ private final Map> requestMap;
+ private final Map clientHeaderMap;
+ private int maxConnections = DEFAULT_MAX_CONNECTIONS;
+ private int connectTimeout = DEFAULT_SOCKET_TIMEOUT;
+ private int responseTimeout = DEFAULT_SOCKET_TIMEOUT;
+ private ExecutorService threadPool;
+ private boolean isUrlEncodingEnabled = true;
+
+ /**
+ * Creates a new AsyncHttpClient with default constructor arguments values
+ */
+ public AsyncHttpClient() {
+ this(false, 80, 443);
+ }
+
+ /**
+ * Creates a new AsyncHttpClient.
+ *
+ * @param httpPort non-standard HTTP-only port
+ */
+ public AsyncHttpClient(int httpPort) {
+ this(false, httpPort, 443);
+ }
+
+ /**
+ * Creates a new AsyncHttpClient.
+ *
+ * @param httpPort non-standard HTTP-only port
+ * @param httpsPort non-standard HTTPS-only port
+ */
+ public AsyncHttpClient(int httpPort, int httpsPort) {
+ this(false, httpPort, httpsPort);
+ }
+
+ /**
+ * Creates new AsyncHttpClient using given params
+ *
+ * @param fixNoHttpResponseException Whether to fix issue or not, by omitting SSL verification
+ * @param httpPort HTTP port to be used, must be greater than 0
+ * @param httpsPort HTTPS port to be used, must be greater than 0
+ */
+ public AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort) {
+ this(getDefaultSchemeRegistry(fixNoHttpResponseException, httpPort, httpsPort));
+ }
+
+ /**
+ * Creates a new AsyncHttpClient.
+ *
+ * @param schemeRegistry SchemeRegistry to be used
+ */
+ public AsyncHttpClient(SchemeRegistry schemeRegistry) {
+
+ BasicHttpParams httpParams = new BasicHttpParams();
+
+ ConnManagerParams.setTimeout(httpParams, connectTimeout);
+ ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(maxConnections));
+ ConnManagerParams.setMaxTotalConnections(httpParams, DEFAULT_MAX_CONNECTIONS);
+
+ HttpConnectionParams.setSoTimeout(httpParams, responseTimeout);
+ HttpConnectionParams.setConnectionTimeout(httpParams, connectTimeout);
+ HttpConnectionParams.setTcpNoDelay(httpParams, true);
+ HttpConnectionParams.setSocketBufferSize(httpParams, DEFAULT_SOCKET_BUFFER_SIZE);
+
+ HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
+
+ ClientConnectionManager cm = createConnectionManager(schemeRegistry, httpParams);
+ Utils.asserts(cm != null, "Custom implementation of #createConnectionManager(SchemeRegistry, BasicHttpParams) returned null");
+
+ threadPool = getDefaultThreadPool();
+ requestMap = Collections.synchronizedMap(new WeakHashMap>());
+ clientHeaderMap = new HashMap();
+
+ httpContext = new SyncBasicHttpContext(new BasicHttpContext());
+ httpClient = new DefaultHttpClient(cm, httpParams);
+ httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
+ @Override
+ public void process(HttpRequest request, HttpContext context) {
+ if (!request.containsHeader(HEADER_ACCEPT_ENCODING)) {
+ request.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
+ }
+ for (String header : clientHeaderMap.keySet()) {
+ if (request.containsHeader(header)) {
+ Header overwritten = request.getFirstHeader(header);
+ log.d(LOG_TAG,
+ String.format("Headers were overwritten! (%s | %s) overwrites (%s | %s)",
+ header, clientHeaderMap.get(header),
+ overwritten.getName(), overwritten.getValue())
+ );
+
+ //remove the overwritten header
+ request.removeHeader(overwritten);
+ }
+ request.addHeader(header, clientHeaderMap.get(header));
+ }
+ }
+ });
+
+ httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
+ @Override
+ public void process(HttpResponse response, HttpContext context) {
+ final HttpEntity entity = response.getEntity();
+ if (entity == null) {
+ return;
+ }
+ final Header encoding = entity.getContentEncoding();
+ if (encoding != null) {
+ for (HeaderElement element : encoding.getElements()) {
+ if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) {
+ response.setEntity(new InflatingEntity(entity));
+ break;
+ }
+ }
+ }
+ }
+ });
+
+ httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
+ @Override
+ public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
+
+ AuthSchemeRegistry authSchemeRegistry = new AuthSchemeRegistry();
+ authSchemeRegistry.register("Bearer", new BearerAuthSchemeFactory());
+ context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry);
+
+ AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
+ CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
+ ClientContext.CREDS_PROVIDER);
+ HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+
+ if (authState.getAuthScheme() == null) {
+ AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
+ Credentials creds = credsProvider.getCredentials(authScope);
+ if (creds instanceof TokenCredentials) {
+ authState.setAuthScheme(new BearerAuthSchemeFactory.BearerAuthScheme());
+ authState.setCredentials(creds);
+ } else if (creds != null) {
+ authState.setAuthScheme(new BasicScheme());
+ authState.setCredentials(creds);
+ }
+ }
+ }
+ }, 0);
+
+ httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_SLEEP_TIME_MILLIS));
+ }
+
+ /**
+ * Returns default instance of SchemeRegistry
+ *
+ * @param fixNoHttpResponseException Whether to fix issue or not, by omitting SSL verification
+ * @param httpPort HTTP port to be used, must be greater than 0
+ * @param httpsPort HTTPS port to be used, must be greater than 0
+ */
+ private static SchemeRegistry getDefaultSchemeRegistry(boolean fixNoHttpResponseException, int httpPort, int httpsPort) {
+ if (fixNoHttpResponseException) {
+ log.d(LOG_TAG, "Beware! Using the fix is insecure, as it doesn't verify SSL certificates.");
+ }
+
+ if (httpPort < 1) {
+ httpPort = 80;
+ log.d(LOG_TAG, "Invalid HTTP port number specified, defaulting to 80");
+ }
+
+ if (httpsPort < 1) {
+ httpsPort = 443;
+ log.d(LOG_TAG, "Invalid HTTPS port number specified, defaulting to 443");
+ }
+
+ // Fix to SSL flaw in API < ICS
+ // See https://code.google.com/p/android/issues/detail?id=13117
+ SSLSocketFactory sslSocketFactory;
+ if (fixNoHttpResponseException) {
+ sslSocketFactory = MySSLSocketFactory.getFixedSocketFactory();
+ } else {
+ sslSocketFactory = SSLSocketFactory.getSocketFactory();
+ }
+
+ SchemeRegistry schemeRegistry = new SchemeRegistry();
+ schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), httpPort));
+ schemeRegistry.register(new Scheme("https", sslSocketFactory, httpsPort));
+
+ return schemeRegistry;
+ }
+
+ public static void allowRetryExceptionClass(Class> cls) {
+ if (cls != null) {
+ RetryHandler.addClassToWhitelist(cls);
+ }
+ }
+
+ public static void blockRetryExceptionClass(Class> cls) {
+ if (cls != null) {
+ RetryHandler.addClassToBlacklist(cls);
+ }
+ }
+
+ /**
+ * Will encode url, if not disabled, and adds params on the end of it
+ *
+ * @param url String with URL, should be valid URL without params
+ * @param params RequestParams to be appended on the end of URL
+ * @param shouldEncodeUrl whether url should be encoded (replaces spaces with %20)
+ * @return encoded url if requested with params appended if any available
+ */
+ public static String getUrlWithQueryString(boolean shouldEncodeUrl, String url, RequestParams params) {
+ if (url == null)
+ return null;
+
+ if (shouldEncodeUrl) {
+ try {
+ String decodedURL = URLDecoder.decode(url, "UTF-8");
+ URL _url = new URL(decodedURL);
+ URI _uri = new URI(_url.getProtocol(), _url.getUserInfo(), _url.getHost(), _url.getPort(), _url.getPath(), _url.getQuery(), _url.getRef());
+ url = _uri.toASCIIString();
+ } catch (Exception ex) {
+ // Should not really happen, added just for sake of validity
+ log.e(LOG_TAG, "getUrlWithQueryString encoding URL", ex);
+ }
+ }
+
+ if (params != null) {
+ // Construct the query string and trim it, in case it
+ // includes any excessive white spaces.
+ String paramString = params.getParamString().trim();
+
+ // Only add the query string if it isn't empty and it
+ // isn't equal to '?'.
+ if (!paramString.equals("") && !paramString.equals("?")) {
+ url += url.contains("?") ? "&" : "?";
+ url += paramString;
+ }
+ }
+
+ return url;
+ }
+
+ /**
+ * Checks the InputStream if it contains GZIP compressed data
+ *
+ * @param inputStream InputStream to be checked
+ * @return true or false if the stream contains GZIP compressed data
+ * @throws java.io.IOException if read from inputStream fails
+ */
+ public static boolean isInputStreamGZIPCompressed(final PushbackInputStream inputStream) throws IOException {
+ if (inputStream == null)
+ return false;
+
+ byte[] signature = new byte[2];
+ int count = 0;
+ try {
+ while (count < 2) {
+ int readCount = inputStream.read(signature, count, 2 - count);
+ if (readCount < 0) return false;
+ count = count + readCount;
+ }
+ } finally {
+ inputStream.unread(signature, 0, count);
+ }
+ int streamHeader = ((int) signature[0] & 0xff) | ((signature[1] << 8) & 0xff00);
+ return GZIPInputStream.GZIP_MAGIC == streamHeader;
+ }
+
+ /**
+ * A utility function to close an input stream without raising an exception.
+ *
+ * @param is input stream to close safely
+ */
+ public static void silentCloseInputStream(InputStream is) {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
+ log.w(LOG_TAG, "Cannot close input stream", e);
+ }
+ }
+
+ /**
+ * A utility function to close an output stream without raising an exception.
+ *
+ * @param os output stream to close safely
+ */
+ public static void silentCloseOutputStream(OutputStream os) {
+ try {
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException e) {
+ log.w(LOG_TAG, "Cannot close output stream", e);
+ }
+ }
+
+ /**
+ * This horrible hack is required on Android, due to implementation of BasicManagedEntity, which
+ * doesn't chain call consumeContent on underlying wrapped HttpEntity
+ *
+ * @param entity HttpEntity, may be null
+ */
+ public static void endEntityViaReflection(HttpEntity entity) {
+ if (entity instanceof HttpEntityWrapper) {
+ try {
+ Field f = null;
+ Field[] fields = HttpEntityWrapper.class.getDeclaredFields();
+ for (Field ff : fields) {
+ if (ff.getName().equals("wrappedEntity")) {
+ f = ff;
+ break;
+ }
+ }
+ if (f != null) {
+ f.setAccessible(true);
+ HttpEntity wrapped = (HttpEntity) f.get(entity);
+ if (wrapped != null) {
+ wrapped.consumeContent();
+ }
+ }
+ } catch (Throwable t) {
+ log.e(LOG_TAG, "wrappedEntity consume", t);
+ }
+ }
+ }
+
+ /**
+ * Get the underlying HttpClient instance. This is useful for setting additional fine-grained
+ * settings for requests by accessing the client's ConnectionManager, HttpParams and
+ * SchemeRegistry.
+ *
+ * @return underlying HttpClient instance
+ */
+ public HttpClient getHttpClient() {
+ return this.httpClient;
+ }
+
+ /**
+ * Get the underlying HttpContext instance. This is useful for getting and setting fine-grained
+ * settings for requests by accessing the context's attributes such as the CookieStore.
+ *
+ * @return underlying HttpContext instance
+ */
+ public HttpContext getHttpContext() {
+ return this.httpContext;
+ }
+
+ /**
+ * Returns logging enabled flag from underlying LogInterface instance
+ * Default setting is logging enabled.
+ *
+ * @return boolean whether is logging across the library currently enabled
+ */
+ public boolean isLoggingEnabled() {
+ return log.isLoggingEnabled();
+ }
+
+ /**
+ * Will set logging enabled flag on underlying LogInterface instance.
+ * Default setting is logging enabled.
+ *
+ * @param loggingEnabled whether the logging should be enabled or not
+ */
+ public void setLoggingEnabled(boolean loggingEnabled) {
+ log.setLoggingEnabled(loggingEnabled);
+ }
+
+ /**
+ * Retrieves current log level from underlying LogInterface instance.
+ * Default setting is VERBOSE log level.
+ *
+ * @return int log level currently in effect
+ */
+ public int getLoggingLevel() {
+ return log.getLoggingLevel();
+ }
+
+ /**
+ * Sets log level to be used across all library default implementation
+ * Default setting is VERBOSE log level.
+ *
+ * @param logLevel int log level, either from LogInterface interface or from {@link android.util.Log}
+ */
+ public void setLoggingLevel(int logLevel) {
+ log.setLoggingLevel(logLevel);
+ }
+
+ /**
+ * Will return current LogInterface used in AsyncHttpClient instance
+ *
+ * @return LogInterface currently used by AsyncHttpClient instance
+ */
+ public LogInterface getLogInterface() {
+ return log;
+ }
+
+ /**
+ * Sets default LogInterface (similar to std Android Log util class) instance,
+ * to be used in AsyncHttpClient instance
+ *
+ * @param logInterfaceInstance LogInterface instance, if null, nothing is done
+ */
+ public void setLogInterface(LogInterface logInterfaceInstance) {
+ if (logInterfaceInstance != null) {
+ log = logInterfaceInstance;
+ }
+ }
+
+ /**
+ * Sets an optional CookieStore to use when making requests
+ *
+ * @param cookieStore The CookieStore implementation to use, usually an instance of {@link
+ * PersistentCookieStore}
+ */
+ public void setCookieStore(CookieStore cookieStore) {
+ httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
+ }
+
+ /**
+ * Returns the current executor service used. By default, Executors.newCachedThreadPool() is
+ * used.
+ *
+ * @return current executor service used
+ */
+ public ExecutorService getThreadPool() {
+ return threadPool;
+ }
+
+ /**
+ * Overrides the threadpool implementation used when queuing/pooling requests. By default,
+ * Executors.newCachedThreadPool() is used.
+ *
+ * @param threadPool an instance of {@link ExecutorService} to use for queuing/pooling
+ * requests.
+ */
+ public void setThreadPool(ExecutorService threadPool) {
+ this.threadPool = threadPool;
+ }
+
+ /**
+ * Get the default threading pool to be used for this HTTP client.
+ *
+ * @return The default threading pool to be used
+ */
+ protected ExecutorService getDefaultThreadPool() {
+ return Executors.newCachedThreadPool();
+ }
+
+ /**
+ * Provided so it is easier for developers to provide custom ThreadSafeClientConnManager implementation
+ *
+ * @param schemeRegistry SchemeRegistry, usually provided by {@link #getDefaultSchemeRegistry(boolean, int, int)}
+ * @param httpParams BasicHttpParams
+ * @return ClientConnectionManager instance
+ */
+ protected ClientConnectionManager createConnectionManager(SchemeRegistry schemeRegistry, BasicHttpParams httpParams) {
+ return new ThreadSafeClientConnManager(httpParams, schemeRegistry);
+ }
+
+ /**
+ * Simple interface method, to enable or disable redirects. If you set manually RedirectHandler
+ * on underlying HttpClient, effects of this method will be canceled.
Default
+ * setting is to disallow redirects.
+ *
+ * @param enableRedirects boolean
+ * @param enableRelativeRedirects boolean
+ * @param enableCircularRedirects boolean
+ */
+ public void setEnableRedirects(final boolean enableRedirects, final boolean enableRelativeRedirects, final boolean enableCircularRedirects) {
+ httpClient.getParams().setBooleanParameter(ClientPNames.REJECT_RELATIVE_REDIRECT, !enableRelativeRedirects);
+ httpClient.getParams().setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, enableCircularRedirects);
+ httpClient.setRedirectHandler(new MyRedirectHandler(enableRedirects));
+ }
+
+ /**
+ * Circular redirects are enabled by default
+ *
+ * @param enableRedirects boolean
+ * @param enableRelativeRedirects boolean
+ * @see #setEnableRedirects(boolean, boolean, boolean)
+ */
+ public void setEnableRedirects(final boolean enableRedirects, final boolean enableRelativeRedirects) {
+ setEnableRedirects(enableRedirects, enableRelativeRedirects, true);
+ }
+
+ /**
+ * @param enableRedirects boolean
+ * @see #setEnableRedirects(boolean, boolean, boolean)
+ */
+ public void setEnableRedirects(final boolean enableRedirects) {
+ setEnableRedirects(enableRedirects, enableRedirects, enableRedirects);
+ }
+
+ /**
+ * Allows you to set custom RedirectHandler implementation, if the default provided doesn't suit
+ * your needs
+ *
+ * @param customRedirectHandler RedirectHandler instance
+ * @see com.loopj.android.http.MyRedirectHandler
+ */
+ public void setRedirectHandler(final RedirectHandler customRedirectHandler) {
+ httpClient.setRedirectHandler(customRedirectHandler);
+ }
+
+ /**
+ * Sets the User-Agent header to be sent with each request. By default, "Android Asynchronous
+ * Http Client/VERSION (https://github.com/android-async-http/android-async-http/)" is used.
+ *
+ * @param userAgent the string to use in the User-Agent header.
+ */
+ public void setUserAgent(String userAgent) {
+ HttpProtocolParams.setUserAgent(this.httpClient.getParams(), userAgent);
+ }
+
+ /**
+ * Returns current limit of parallel connections
+ *
+ * @return maximum limit of parallel connections, default is 10
+ */
+ public int getMaxConnections() {
+ return maxConnections;
+ }
+
+ /**
+ * Sets maximum limit of parallel connections
+ *
+ * @param maxConnections maximum parallel connections, must be at least 1
+ */
+ public void setMaxConnections(int maxConnections) {
+ if (maxConnections < 1)
+ maxConnections = DEFAULT_MAX_CONNECTIONS;
+ this.maxConnections = maxConnections;
+ final HttpParams httpParams = this.httpClient.getParams();
+ ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(this.maxConnections));
+ }
+
+ /**
+ * Set both the connection and socket timeouts. By default, both are set to
+ * 10 seconds.
+ *
+ * @param value the connect/socket timeout in milliseconds, at least 1 second
+ * @see #setConnectTimeout(int)
+ * @see #setResponseTimeout(int)
+ */
+ public void setTimeout(int value) {
+ value = value < 1000 ? DEFAULT_SOCKET_TIMEOUT : value;
+ setConnectTimeout(value);
+ setResponseTimeout(value);
+ }
+
+ /**
+ * Returns current connection timeout limit (milliseconds). By default, this
+ * is set to 10 seconds.
+ *
+ * @return Connection timeout limit in milliseconds
+ */
+ public int getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ /**
+ * Set connection timeout limit (milliseconds). By default, this is set to
+ * 10 seconds.
+ *
+ * @param value Connection timeout in milliseconds, minimal value is 1000 (1 second).
+ */
+ public void setConnectTimeout(int value) {
+ connectTimeout = value < 1000 ? DEFAULT_SOCKET_TIMEOUT : value;
+ final HttpParams httpParams = httpClient.getParams();
+ ConnManagerParams.setTimeout(httpParams, connectTimeout);
+ HttpConnectionParams.setConnectionTimeout(httpParams, connectTimeout);
+ }
+
+ /**
+ * Returns current response timeout limit (milliseconds). By default, this
+ * is set to 10 seconds.
+ *
+ * @return Response timeout limit in milliseconds
+ */
+ public int getResponseTimeout() {
+ return responseTimeout;
+ }
+
+ /**
+ * Set response timeout limit (milliseconds). By default, this is set to
+ * 10 seconds.
+ *
+ * @param value Response timeout in milliseconds, minimal value is 1000 (1 second).
+ */
+ public void setResponseTimeout(int value) {
+ responseTimeout = value < 1000 ? DEFAULT_SOCKET_TIMEOUT : value;
+ final HttpParams httpParams = httpClient.getParams();
+ HttpConnectionParams.setSoTimeout(httpParams, responseTimeout);
+ }
+
+ /**
+ * Sets the Proxy by it's hostname and port
+ *
+ * @param hostname the hostname (IP or DNS name)
+ * @param port the port number. -1 indicates the scheme default port.
+ */
+ public void setProxy(String hostname, int port) {
+ final HttpHost proxy = new HttpHost(hostname, port);
+ final HttpParams httpParams = this.httpClient.getParams();
+ httpParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
+ }
+
+ /**
+ * Sets the Proxy by it's hostname,port,username and password
+ *
+ * @param hostname the hostname (IP or DNS name)
+ * @param port the port number. -1 indicates the scheme default port.
+ * @param username the username
+ * @param password the password
+ */
+ public void setProxy(String hostname, int port, String username, String password) {
+ httpClient.getCredentialsProvider().setCredentials(
+ new AuthScope(hostname, port),
+ new UsernamePasswordCredentials(username, password));
+ final HttpHost proxy = new HttpHost(hostname, port);
+ final HttpParams httpParams = this.httpClient.getParams();
+ httpParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
+ }
+
+ /**
+ * Sets the SSLSocketFactory to user when making requests. By default, a new, default
+ * SSLSocketFactory is used.
+ *
+ * @param sslSocketFactory the socket factory to use for https requests.
+ */
+ public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
+ this.httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", sslSocketFactory, 443));
+ }
+
+ /**
+ * Sets the maximum number of retries and timeout for a particular Request.
+ *
+ * @param retries maximum number of retries per request
+ * @param timeout sleep between retries in milliseconds
+ */
+ public void setMaxRetriesAndTimeout(int retries, int timeout) {
+ this.httpClient.setHttpRequestRetryHandler(new RetryHandler(retries, timeout));
+ }
+
+ /**
+ * Will, before sending, remove all headers currently present in AsyncHttpClient instance, which
+ * applies on all requests this client makes
+ */
+ public void removeAllHeaders() {
+ clientHeaderMap.clear();
+ }
+
+ /**
+ * Sets headers that will be added to all requests this client makes (before sending).
+ *
+ * @param header the name of the header
+ * @param value the contents of the header
+ */
+ public void addHeader(String header, String value) {
+ clientHeaderMap.put(header, value);
+ }
+
+ /**
+ * Remove header from all requests this client makes (before sending).
+ *
+ * @param header the name of the header
+ */
+ public void removeHeader(String header) {
+ clientHeaderMap.remove(header);
+ }
+
+ /**
+ * Sets bearer authentication for the request. Uses AuthScope.ANY. This is the same as
+ * setBearerAuth('token',AuthScope.ANY, false)
+ *
+ * @param token Bearer Token
+ */
+ public void setBearerAuth(String token) {
+ setBearerAuth(token, AuthScope.ANY, false);
+ }
+
+
+ /**
+ * Sets bearer authentication for the request. You should pass in your AuthScope for security. It
+ * should be like this setBearerAuth("token", new AuthScope("host",port,AuthScope.ANY_REALM), false)
+ *
+ * @param token Bearer Token
+ * @param scope an AuthScope object
+ * @param preemptive sets authorization in preemptive manner
+ */
+ public void setBearerAuth(String token, AuthScope scope, boolean preemptive) {
+ TokenCredentials credentials = new TokenCredentials(token);
+ setCredentials(scope, credentials);
+ setAuthenticationPreemptive(preemptive);
+ }
+
+ /**
+ * Sets basic authentication for the request. Uses AuthScope.ANY. This is the same as
+ * setBasicAuth('username','password',AuthScope.ANY)
+ *
+ * @param username Basic Auth username
+ * @param password Basic Auth password
+ */
+ public void setBasicAuth(String username, String password) {
+ setBasicAuth(username, password, false);
+ }
+
+ /**
+ * Sets basic authentication for the request. Uses AuthScope.ANY. This is the same as
+ * setBasicAuth('username','password',AuthScope.ANY)
+ *
+ * @param username Basic Auth username
+ * @param password Basic Auth password
+ * @param preemptive sets authorization in preemptive manner
+ */
+ public void setBasicAuth(String username, String password, boolean preemptive) {
+ setBasicAuth(username, password, null, preemptive);
+ }
+
+ /**
+ * Sets basic authentication for the request. You should pass in your AuthScope for security. It
+ * should be like this setBasicAuth("username","password", new AuthScope("host",port,AuthScope.ANY_REALM))
+ *
+ * @param username Basic Auth username
+ * @param password Basic Auth password
+ * @param scope - an AuthScope object
+ */
+ public void setBasicAuth(String username, String password, AuthScope scope) {
+ setBasicAuth(username, password, scope, false);
+ }
+
+ /**
+ * Sets basic authentication for the request. You should pass in your AuthScope for security. It
+ * should be like this setBasicAuth("username","password", new AuthScope("host",port,AuthScope.ANY_REALM))
+ *
+ * @param username Basic Auth username
+ * @param password Basic Auth password
+ * @param scope an AuthScope object
+ * @param preemptive sets authorization in preemptive manner
+ */
+ public void setBasicAuth(String username, String password, AuthScope scope, boolean preemptive) {
+ UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
+ setCredentials(scope, credentials);
+ setAuthenticationPreemptive(preemptive);
+ }
+
+ public void setCredentials(AuthScope authScope, Credentials credentials) {
+ if (credentials == null) {
+ log.d(LOG_TAG, "Provided credentials are null, not setting");
+ return;
+ }
+ this.httpClient.getCredentialsProvider().setCredentials(authScope == null ? AuthScope.ANY : authScope, credentials);
+ }
+
+ /**
+ * Sets HttpRequestInterceptor which handles authorization in preemptive way, as workaround you
+ * can use call `AsyncHttpClient.addHeader("Authorization","Basic base64OfUsernameAndPassword==")`
+ *
+ * @param isPreemptive whether the authorization is processed in preemptive way
+ */
+ public void setAuthenticationPreemptive(boolean isPreemptive) {
+ if (isPreemptive) {
+ httpClient.addRequestInterceptor(new PreemptiveAuthorizationHttpRequestInterceptor(), 0);
+ } else {
+ httpClient.removeRequestInterceptorByClass(PreemptiveAuthorizationHttpRequestInterceptor.class);
+ }
+ }
+
+ // [+] HTTP HEAD
+
+ /**
+ * Removes previously set auth credentials
+ */
+ public void clearCredentialsProvider() {
+ this.httpClient.getCredentialsProvider().clear();
+ }
+
+ /**
+ * Cancels any pending (or potentially active) requests associated with the passed Context.
+ *
Note: This will only affect requests which were created with a non-null
+ * android Context. This method is intended to be used in the onDestroy method of your android
+ * activities to destroy all requests which are no longer required.
+ *
+ * @param context the android Context instance associated to the request.
+ * @param mayInterruptIfRunning specifies if active requests should be cancelled along with
+ * pending requests.
+ */
+ public void cancelRequests(final Context context, final boolean mayInterruptIfRunning) {
+ if (context == null) {
+ log.e(LOG_TAG, "Passed null Context to cancelRequests");
+ return;
+ }
+
+ final List requestList = requestMap.get(context);
+ requestMap.remove(context);
+
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ cancelRequests(requestList, mayInterruptIfRunning);
+ }
+ };
+ threadPool.submit(runnable);
+ } else {
+ cancelRequests(requestList, mayInterruptIfRunning);
+ }
+ }
+
+ private void cancelRequests(final List requestList, final boolean mayInterruptIfRunning) {
+ if (requestList != null) {
+ for (RequestHandle requestHandle : requestList) {
+ requestHandle.cancel(mayInterruptIfRunning);
+ }
+ }
+ }
+
+ /**
+ * Cancels all pending (or potentially active) requests.
Note: This will
+ * only affect requests which were created with a non-null android Context. This method is
+ * intended to be used in the onDestroy method of your android activities to destroy all
+ * requests which are no longer required.
+ *
+ * @param mayInterruptIfRunning specifies if active requests should be cancelled along with
+ * pending requests.
+ */
+ public void cancelAllRequests(boolean mayInterruptIfRunning) {
+ for (List requestList : requestMap.values()) {
+ if (requestList != null) {
+ for (RequestHandle requestHandle : requestList) {
+ requestHandle.cancel(mayInterruptIfRunning);
+ }
+ }
+ }
+ requestMap.clear();
+ }
+
+ /**
+ * Allows you to cancel all requests currently in queue or running, by set TAG,
+ * if passed TAG is null, will not attempt to cancel any requests, if TAG is null
+ * on RequestHandle, it cannot be canceled by this call
+ *
+ * @param TAG TAG to be matched in RequestHandle
+ * @param mayInterruptIfRunning specifies if active requests should be cancelled along with
+ * pending requests.
+ */
+ public void cancelRequestsByTAG(Object TAG, boolean mayInterruptIfRunning) {
+ if (TAG == null) {
+ log.d(LOG_TAG, "cancelRequestsByTAG, passed TAG is null, cannot proceed");
+ return;
+ }
+ for (List requestList : requestMap.values()) {
+ if (requestList != null) {
+ for (RequestHandle requestHandle : requestList) {
+ if (TAG.equals(requestHandle.getTag()))
+ requestHandle.cancel(mayInterruptIfRunning);
+ }
+ }
+ }
+ }
+
+ // [-] HTTP HEAD
+ // [+] HTTP OPTIONS
+
+ /**
+ * Perform a HTTP OPTIONS request, without any parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle options(String url, ResponseHandlerInterface responseHandler) {
+ return options(null, url, null, responseHandler);
+ }
+
+ public RequestHandle options(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return sendRequest(httpClient, httpContext, new HttpOptions(getUrlWithQueryString(isUrlEncodingEnabled(), url, params)), null, responseHandler, context);
+ }
+
+ // [-] HTTP OPTIONS
+ // [+] HTTP GET
+
+ /**
+ * Perform a HTTP HEAD request, without any parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle head(String url, ResponseHandlerInterface responseHandler) {
+ return head(null, url, null, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP HEAD request with parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param params additional HEAD parameters to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle head(String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return head(null, url, params, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP HEAD request without any parameters and track the Android Context which
+ * initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle head(Context context, String url, ResponseHandlerInterface responseHandler) {
+ return head(context, url, null, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP HEAD request and track the Android Context which initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param params additional HEAD parameters to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle head(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return sendRequest(httpClient, httpContext, new HttpHead(getUrlWithQueryString(isUrlEncodingEnabled, url, params)), null, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP HEAD request and track the Android Context which initiated the request with
+ * customized headers
+ *
+ * @param context Context to execute request against
+ * @param url the URL to send the request to.
+ * @param headers set headers only for this request
+ * @param params additional HEAD parameters to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle head(Context context, String url, Header[] headers, RequestParams params, ResponseHandlerInterface responseHandler) {
+ HttpUriRequest request = new HttpHead(getUrlWithQueryString(isUrlEncodingEnabled, url, params));
+ if (headers != null) request.setHeaders(headers);
+ return sendRequest(httpClient, httpContext, request, null, responseHandler,
+ context);
+ }
+
+ /**
+ * Perform a HTTP GET request, without any parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle get(String url, ResponseHandlerInterface responseHandler) {
+ return get(null, url, null, responseHandler);
+ }
+
+ // [-] HTTP GET
+ // [+] HTTP POST
+
+ /**
+ * Perform a HTTP GET request with parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param params additional GET parameters to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle get(String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return get(null, url, params, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP GET request without any parameters and track the Android Context which
+ * initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle get(Context context, String url, ResponseHandlerInterface responseHandler) {
+ return get(context, url, null, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP GET request and track the Android Context which initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param params additional GET parameters to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle get(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return sendRequest(httpClient, httpContext, new HttpGet(getUrlWithQueryString(isUrlEncodingEnabled, url, params)), null, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP GET request and track the Android Context which initiated the request with
+ * customized headers
+ *
+ * @param context Context to execute request against
+ * @param url the URL to send the request to.
+ * @param headers set headers only for this request
+ * @param params additional GET parameters to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle get(Context context, String url, Header[] headers, RequestParams params, ResponseHandlerInterface responseHandler) {
+ HttpUriRequest request = new HttpGet(getUrlWithQueryString(isUrlEncodingEnabled, url, params));
+ if (headers != null) request.setHeaders(headers);
+ return sendRequest(httpClient, httpContext, request, null, responseHandler,
+ context);
+ }
+
+ /**
+ * Perform a HTTP GET request and track the Android Context which initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param entity a raw {@link cz.msebera.android.httpclient.HttpEntity} to send with the request, for
+ * example, use this to send string/json/xml payloads to a server by
+ * passing a {@link cz.msebera.android.httpclient.entity.StringEntity}.
+ * @param contentType the content type of the payload you are sending, for example
+ * application/json if sending a json payload.
+ * @param responseHandler the response ha ndler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle get(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) {
+ return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpGet(URI.create(url).normalize()), entity), contentType, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP POST request, without any parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle post(String url, ResponseHandlerInterface responseHandler) {
+ return post(null, url, null, responseHandler);
+ }
+
+ // [-] HTTP POST
+ // [+] HTTP PUT
+
+ /**
+ * Perform a HTTP POST request with parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param params additional POST parameters or files to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle post(String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return post(null, url, params, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP POST request and track the Android Context which initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param params additional POST parameters or files to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle post(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return post(context, url, paramsToEntity(params, responseHandler), null, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP POST request and track the Android Context which initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param entity a raw {@link cz.msebera.android.httpclient.HttpEntity} to send with the request, for
+ * example, use this to send string/json/xml payloads to a server by
+ * passing a {@link cz.msebera.android.httpclient.entity.StringEntity}.
+ * @param contentType the content type of the payload you are sending, for example
+ * application/json if sending a json payload.
+ * @param responseHandler the response ha ndler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle post(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) {
+ return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpPost(getURI(url)), entity), contentType, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP POST request and track the Android Context which initiated the request. Set
+ * headers only for this request
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param headers set headers only for this request
+ * @param params additional POST parameters to send with the request.
+ * @param contentType the content type of the payload you are sending, for example
+ * application/json if sending a json payload.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle post(Context context, String url, Header[] headers, RequestParams params, String contentType,
+ ResponseHandlerInterface responseHandler) {
+ HttpEntityEnclosingRequestBase request = new HttpPost(getURI(url));
+ if (params != null) request.setEntity(paramsToEntity(params, responseHandler));
+ if (headers != null) request.setHeaders(headers);
+ return sendRequest(httpClient, httpContext, request, contentType,
+ responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP POST request and track the Android Context which initiated the request. Set
+ * headers only for this request
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param headers set headers only for this request
+ * @param entity a raw {@link HttpEntity} to send with the request, for example, use
+ * this to send string/json/xml payloads to a server by passing a {@link
+ * cz.msebera.android.httpclient.entity.StringEntity}.
+ * @param contentType the content type of the payload you are sending, for example
+ * application/json if sending a json payload.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle post(Context context, String url, Header[] headers, HttpEntity entity, String contentType,
+ ResponseHandlerInterface responseHandler) {
+ HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPost(getURI(url)), entity);
+ if (headers != null) request.setHeaders(headers);
+ return sendRequest(httpClient, httpContext, request, contentType, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP PUT request, without any parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle put(String url, ResponseHandlerInterface responseHandler) {
+ return put(null, url, null, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP PUT request with parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param params additional PUT parameters or files to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle put(String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return put(null, url, params, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP PUT request and track the Android Context which initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param params additional PUT parameters or files to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle put(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return put(context, url, paramsToEntity(params, responseHandler), null, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP PUT request and track the Android Context which initiated the request. And set
+ * one-time headers for the request
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param entity a raw {@link HttpEntity} to send with the request, for example, use
+ * this to send string/json/xml payloads to a server by passing a {@link
+ * cz.msebera.android.httpclient.entity.StringEntity}.
+ * @param contentType the content type of the payload you are sending, for example
+ * application/json if sending a json payload.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle put(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) {
+ return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpPut(getURI(url)), entity), contentType, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP PUT request and track the Android Context which initiated the request. And set
+ * one-time headers for the request
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param headers set one-time headers for this request
+ * @param entity a raw {@link HttpEntity} to send with the request, for example, use
+ * this to send string/json/xml payloads to a server by passing a {@link
+ * cz.msebera.android.httpclient.entity.StringEntity}.
+ * @param contentType the content type of the payload you are sending, for example
+ * application/json if sending a json payload.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle put(Context context, String url, Header[] headers, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) {
+ HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPut(getURI(url)), entity);
+ if (headers != null) request.setHeaders(headers);
+ return sendRequest(httpClient, httpContext, request, contentType, responseHandler, context);
+ }
+
+ // [-] HTTP PUT
+ // [+] HTTP DELETE
+
+ /**
+ * Perform a HTTP
+ * request, without any parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle patch(String url, ResponseHandlerInterface responseHandler) {
+ return patch(null, url, null, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP PATCH request with parameters.
+ *
+ * @param url the URL to send the request to.
+ * @param params additional PUT parameters or files to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle patch(String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return patch(null, url, params, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP PATCH request and track the Android Context which initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param params additional PUT parameters or files to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle patch(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {
+ return patch(context, url, paramsToEntity(params, responseHandler), null, responseHandler);
+ }
+
+ /**
+ * Perform a HTTP PATCH request and track the Android Context which initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @param entity a raw {@link HttpEntity} to send with the request, for example, use
+ * this to send string/json/xml payloads to a server by passing a {@link
+ * cz.msebera.android.httpclient.entity.StringEntity}
+ * @param contentType the content type of the payload you are sending, for example
+ * "application/json" if sending a json payload.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle patch(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) {
+ return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpPatch(getURI(url)), entity), contentType, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP PATCH request and track the Android Context which initiated the request. And set
+ * one-time headers for the request
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param headers set one-time headers for this request
+ * @param entity a raw {@link HttpEntity} to send with the request, for example, use
+ * this to send string/json/xml payloads to a server by passing a {@link
+ * cz.msebera.android.httpclient.entity.StringEntity}.
+ * @param contentType the content type of the payload you are sending, for example
+ * application/json if sending a json payload.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle patch(Context context, String url, Header[] headers, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) {
+ HttpEntityEnclosingRequestBase request = addEntityToRequestBase(new HttpPatch(getURI(url)), entity);
+ if (headers != null) request.setHeaders(headers);
+ return sendRequest(httpClient, httpContext, request, contentType, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP DELETE request.
+ *
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle delete(String url, ResponseHandlerInterface responseHandler) {
+ return delete(null, url, responseHandler);
+ }
+
+ // [-] HTTP DELETE
+
+ /**
+ * Perform a HTTP DELETE request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle delete(Context context, String url, ResponseHandlerInterface responseHandler) {
+ final HttpDelete delete = new HttpDelete(getURI(url));
+ return sendRequest(httpClient, httpContext, delete, null, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP DELETE request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param headers set one-time headers for this request
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle delete(Context context, String url, Header[] headers, ResponseHandlerInterface responseHandler) {
+ final HttpDelete delete = new HttpDelete(getURI(url));
+ if (headers != null) delete.setHeaders(headers);
+ return sendRequest(httpClient, httpContext, delete, null, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP DELETE request.
+ *
+ * @param url the URL to send the request to.
+ * @param params additional DELETE parameters or files to send with the request.
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle delete(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
+ final HttpDelete delete = new HttpDelete(getUrlWithQueryString(isUrlEncodingEnabled, url, params));
+ return sendRequest(httpClient, httpContext, delete, null, responseHandler, null);
+ }
+
+ /**
+ * Perform a HTTP DELETE request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param headers set one-time headers for this request
+ * @param params additional DELETE parameters or files to send along with request
+ * @param responseHandler the response handler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle delete(Context context, String url, Header[] headers, RequestParams params, ResponseHandlerInterface responseHandler) {
+ HttpDelete httpDelete = new HttpDelete(getUrlWithQueryString(isUrlEncodingEnabled, url, params));
+ if (headers != null) httpDelete.setHeaders(headers);
+ return sendRequest(httpClient, httpContext, httpDelete, null, responseHandler, context);
+ }
+
+ /**
+ * Perform a HTTP DELETE request and track the Android Context which initiated the request.
+ *
+ * @param context the Android Context which initiated the request.
+ * @param url the URL to send the request to.
+ * @param entity a raw {@link cz.msebera.android.httpclient.HttpEntity} to send with the request, for
+ * example, use this to send string/json/xml payloads to a server by
+ * passing a {@link cz.msebera.android.httpclient.entity.StringEntity}.
+ * @param contentType the content type of the payload you are sending, for example
+ * application/json if sending a json payload.
+ * @param responseHandler the response ha ndler instance that should handle the response.
+ * @return RequestHandle of future request process
+ */
+ public RequestHandle delete(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) {
+ return sendRequest(httpClient, httpContext, addEntityToRequestBase(new HttpDelete(URI.create(url).normalize()), entity), contentType, responseHandler, context);
+ }
+
+ /**
+ * Instantiate a new asynchronous HTTP request for the passed parameters.
+ *
+ * @param client HttpClient to be used for request, can differ in single requests
+ * @param contentType MIME body type, for POST and PUT requests, may be null
+ * @param context Context of Android application, to hold the reference of request
+ * @param httpContext HttpContext in which the request will be executed
+ * @param responseHandler ResponseHandler or its subclass to put the response into
+ * @param uriRequest instance of HttpUriRequest, which means it must be of HttpDelete,
+ * HttpPost, HttpGet, HttpPut, etc.
+ * @return AsyncHttpRequest ready to be dispatched
+ */
+ protected AsyncHttpRequest newAsyncHttpRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) {
+ return new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler);
+ }
+
+ /**
+ * Puts a new request in queue as a new thread in pool to be executed
+ *
+ * @param client HttpClient to be used for request, can differ in single requests
+ * @param contentType MIME body type, for POST and PUT requests, may be null
+ * @param context Context of Android application, to hold the reference of request
+ * @param httpContext HttpContext in which the request will be executed
+ * @param responseHandler ResponseHandler or its subclass to put the response into
+ * @param uriRequest instance of HttpUriRequest, which means it must be of HttpDelete,
+ * HttpPost, HttpGet, HttpPut, etc.
+ * @return RequestHandle of future request process
+ */
+ protected RequestHandle sendRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) {
+ if (uriRequest == null) {
+ throw new IllegalArgumentException("HttpUriRequest must not be null");
+ }
+
+ if (responseHandler == null) {
+ throw new IllegalArgumentException("ResponseHandler must not be null");
+ }
+
+ if (responseHandler.getUseSynchronousMode() && !responseHandler.getUsePoolThread()) {
+ throw new IllegalArgumentException("Synchronous ResponseHandler used in AsyncHttpClient. You should create your response handler in a looper thread or use SyncHttpClient instead.");
+ }
+
+ if (contentType != null) {
+ if (uriRequest instanceof HttpEntityEnclosingRequestBase && ((HttpEntityEnclosingRequestBase) uriRequest).getEntity() != null && uriRequest.containsHeader(HEADER_CONTENT_TYPE)) {
+ log.w(LOG_TAG, "Passed contentType will be ignored because HttpEntity sets content type");
+ } else {
+ uriRequest.setHeader(HEADER_CONTENT_TYPE, contentType);
+ }
+ }
+
+ responseHandler.setRequestHeaders(uriRequest.getAllHeaders());
+ responseHandler.setRequestURI(uriRequest.getURI());
+
+ AsyncHttpRequest request = newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context);
+ threadPool.submit(request);
+ RequestHandle requestHandle = new RequestHandle(request);
+
+ if (context != null) {
+ List requestList;
+ // Add request to request map
+ synchronized (requestMap) {
+ requestList = requestMap.get(context);
+ if (requestList == null) {
+ requestList = Collections.synchronizedList(new LinkedList());
+ requestMap.put(context, requestList);
+ }
+ }
+
+ requestList.add(requestHandle);
+
+ Iterator iterator = requestList.iterator();
+ while (iterator.hasNext()) {
+ if (iterator.next().shouldBeGarbageCollected()) {
+ iterator.remove();
+ }
+ }
+ }
+
+ return requestHandle;
+ }
+
+ /**
+ * Returns a {@link URI} instance for the specified, absolute URL string.
+ *
+ * @param url absolute URL string, containing scheme, host and path
+ * @return URI instance for the URL string
+ */
+ protected URI getURI(String url) {
+ return URI.create(url).normalize();
+ }
+
+ /**
+ * Sets state of URL encoding feature, see bug #227, this method allows you to turn off and on
+ * this auto-magic feature on-demand.
+ *
+ * @param enabled desired state of feature
+ */
+ public void setURLEncodingEnabled(boolean enabled) {
+ this.isUrlEncodingEnabled = enabled;
+ }
+
+ /**
+ * Returns HttpEntity containing data from RequestParams included with request declaration.
+ * Allows also passing progress from upload via provided ResponseHandler
+ *
+ * @param params additional request params
+ * @param responseHandler ResponseHandlerInterface or its subclass to be notified on progress
+ */
+ private HttpEntity paramsToEntity(RequestParams params, ResponseHandlerInterface responseHandler) {
+ HttpEntity entity = null;
+
+ try {
+ if (params != null) {
+ entity = params.getEntity(responseHandler);
+ }
+ } catch (IOException e) {
+ if (responseHandler != null) {
+ responseHandler.sendFailureMessage(0, null, null, e);
+ } else {
+ e.printStackTrace();
+ }
+ }
+
+ return entity;
+ }
+
+ public boolean isUrlEncodingEnabled() {
+ return isUrlEncodingEnabled;
+ }
+
+ /**
+ * Applicable only to HttpRequest methods extending HttpEntityEnclosingRequestBase, which is for
+ * example not DELETE
+ *
+ * @param entity entity to be included within the request
+ * @param requestBase HttpRequest instance, must not be null
+ */
+ private HttpEntityEnclosingRequestBase addEntityToRequestBase(HttpEntityEnclosingRequestBase requestBase, HttpEntity entity) {
+ if (entity != null) {
+ requestBase.setEntity(entity);
+ }
+
+ return requestBase;
+ }
+
+ /**
+ * Enclosing entity to hold stream of gzip decoded data for accessing HttpEntity contents
+ */
+ private static class InflatingEntity extends HttpEntityWrapper {
+
+ InputStream wrappedStream;
+ PushbackInputStream pushbackStream;
+ GZIPInputStream gzippedStream;
+
+ public InflatingEntity(HttpEntity wrapped) {
+ super(wrapped);
+ }
+
+ @Override
+ public InputStream getContent() throws IOException {
+ wrappedStream = wrappedEntity.getContent();
+ pushbackStream = new PushbackInputStream(wrappedStream, 2);
+ if (isInputStreamGZIPCompressed(pushbackStream)) {
+ gzippedStream = new GZIPInputStream(pushbackStream);
+ return gzippedStream;
+ } else {
+ return pushbackStream;
+ }
+ }
+
+ @Override
+ public long getContentLength() {
+ return wrappedEntity == null ? 0 : wrappedEntity.getContentLength();
+ }
+
+ @Override
+ public void consumeContent() throws IOException {
+ AsyncHttpClient.silentCloseInputStream(wrappedStream);
+ AsyncHttpClient.silentCloseInputStream(pushbackStream);
+ AsyncHttpClient.silentCloseInputStream(gzippedStream);
+ super.consumeContent();
+ }
+ }
+
+ /**
+ * Call this method if your app target android below 4.4
+ * This method enable sni in android below 4.4
+ */
+ public static void useConscryptSSLProvider(){
+ ConscryptSSLProvider.install();
+ }
+}
diff --git a/library/src/main/java/com/loopj/android/http/AsyncHttpRequest.java b/library/src/main/java/com/loopj/android/http/AsyncHttpRequest.java
new file mode 100755
index 000000000..346cde73e
--- /dev/null
+++ b/library/src/main/java/com/loopj/android/http/AsyncHttpRequest.java
@@ -0,0 +1,257 @@
+/*
+ Android Asynchronous Http Client
+ Copyright (c) 2011 James Smith
+ https://github.com/android-async-http/android-async-http
+
+ 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
+
+ https://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.
+*/
+
+package com.loopj.android.http;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.UnknownHostException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import cz.msebera.android.httpclient.HttpResponse;
+import cz.msebera.android.httpclient.client.HttpRequestRetryHandler;
+import cz.msebera.android.httpclient.client.methods.HttpUriRequest;
+import cz.msebera.android.httpclient.impl.client.AbstractHttpClient;
+import cz.msebera.android.httpclient.protocol.HttpContext;
+
+/**
+ * Internal class, representing the HttpRequest, done in asynchronous manner
+ */
+public class AsyncHttpRequest implements Runnable {
+ private final AbstractHttpClient client;
+ private final HttpContext context;
+ private final HttpUriRequest request;
+ private final ResponseHandlerInterface responseHandler;
+ private final AtomicBoolean isCancelled = new AtomicBoolean();
+ private int executionCount;
+ private boolean cancelIsNotified;
+ private volatile boolean isFinished;
+ private boolean isRequestPreProcessed;
+
+ public AsyncHttpRequest(AbstractHttpClient client, HttpContext context, HttpUriRequest request, ResponseHandlerInterface responseHandler) {
+ this.client = Utils.notNull(client, "client");
+ this.context = Utils.notNull(context, "context");
+ this.request = Utils.notNull(request, "request");
+ this.responseHandler = Utils.notNull(responseHandler, "responseHandler");
+ }
+
+ /**
+ * This method is called once by the system when the request is about to be
+ * processed by the system. The library makes sure that a single request
+ * is pre-processed only once.
+ *
+ * Please note: pre-processing does NOT run on the main thread, and thus
+ * any UI activities that you must perform should be properly dispatched to
+ * the app's UI thread.
+ *
+ * @param request The request to pre-process
+ */
+ public void onPreProcessRequest(AsyncHttpRequest request) {
+ // default action is to do nothing...
+ }
+
+ /**
+ * This method is called once by the system when the request has been fully
+ * sent, handled and finished. The library makes sure that a single request
+ * is post-processed only once.
+ *
+ * Please note: post-processing does NOT run on the main thread, and thus
+ * any UI activities that you must perform should be properly dispatched to
+ * the app's UI thread.
+ *
+ * @param request The request to post-process
+ */
+ public void onPostProcessRequest(AsyncHttpRequest request) {
+ // default action is to do nothing...
+ }
+
+ @Override
+ public void run() {
+ if (isCancelled()) {
+ return;
+ }
+
+ // Carry out pre-processing for this request only once.
+ if (!isRequestPreProcessed) {
+ isRequestPreProcessed = true;
+ onPreProcessRequest(this);
+ }
+
+ if (isCancelled()) {
+ return;
+ }
+
+ responseHandler.sendStartMessage();
+
+ if (isCancelled()) {
+ return;
+ }
+
+ try {
+ makeRequestWithRetries();
+ } catch (IOException e) {
+ if (!isCancelled()) {
+ responseHandler.sendFailureMessage(0, null, null, e);
+ } else {
+ AsyncHttpClient.log.e("AsyncHttpRequest", "makeRequestWithRetries returned error", e);
+ }
+ }
+
+ if (isCancelled()) {
+ return;
+ }
+
+ responseHandler.sendFinishMessage();
+
+ if (isCancelled()) {
+ return;
+ }
+
+ // Carry out post-processing for this request.
+ onPostProcessRequest(this);
+
+ isFinished = true;
+ }
+
+ private void makeRequest() throws IOException {
+ if (isCancelled()) {
+ return;
+ }
+
+ // Fixes #115
+ if (request.getURI().getScheme() == null) {
+ // subclass of IOException so processed in the caller
+ throw new MalformedURLException("No valid URI scheme was provided");
+ }
+
+ if (responseHandler instanceof RangeFileAsyncHttpResponseHandler) {
+ ((RangeFileAsyncHttpResponseHandler) responseHandler).updateRequestHeaders(request);
+ }
+
+ HttpResponse response = client.execute(request, context);
+
+ if (isCancelled()) {
+ return;
+ }
+
+ // Carry out pre-processing for this response.
+ responseHandler.onPreProcessResponse(responseHandler, response);
+
+ if (isCancelled()) {
+ return;
+ }
+
+ // The response is ready, handle it.
+ responseHandler.sendResponseMessage(response);
+
+ if (isCancelled()) {
+ return;
+ }
+
+ // Carry out post-processing for this response.
+ responseHandler.onPostProcessResponse(responseHandler, response);
+ }
+
+ private void makeRequestWithRetries() throws IOException {
+ boolean retry = true;
+ IOException cause = null;
+ HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();
+ try {
+ while (retry) {
+ try {
+ makeRequest();
+ return;
+ } catch (UnknownHostException e) {
+ // switching between WI-FI and mobile data networks can cause a retry which then results in an UnknownHostException
+ // while the WI-FI is initialising. The retry logic will be invoked here, if this is NOT the first retry
+ // (to assist in genuine cases of unknown host) which seems better than outright failure
+ cause = new IOException("UnknownHostException exception: " + e.getMessage(), e);
+ retry = (executionCount > 0) && retryHandler.retryRequest(e, ++executionCount, context);
+ } catch (NullPointerException e) {
+ // there's a bug in HttpClient 4.0.x that on some occasions causes
+ // DefaultRequestExecutor to throw an NPE, see
+ // https://code.google.com/p/android/issues/detail?id=5255
+ cause = new IOException("NPE in HttpClient: " + e.getMessage());
+ retry = retryHandler.retryRequest(cause, ++executionCount, context);
+ } catch (IOException e) {
+ if (isCancelled()) {
+ // Eating exception, as the request was cancelled
+ return;
+ }
+ cause = e;
+ retry = retryHandler.retryRequest(cause, ++executionCount, context);
+ }
+ if (retry) {
+ responseHandler.sendRetryMessage(executionCount);
+ }
+ }
+ } catch (Exception e) {
+ // catch anything else to ensure failure message is propagated
+ AsyncHttpClient.log.e("AsyncHttpRequest", "Unhandled exception origin cause", e);
+ cause = new IOException("Unhandled exception: " + e.getMessage(), cause);
+ }
+
+ // cleaned up to throw IOException
+ throw (cause);
+ }
+
+ public boolean isCancelled() {
+ boolean cancelled = isCancelled.get();
+ if (cancelled) {
+ sendCancelNotification();
+ }
+ return cancelled;
+ }
+
+ private synchronized void sendCancelNotification() {
+ if (!isFinished && isCancelled.get() && !cancelIsNotified) {
+ cancelIsNotified = true;
+ responseHandler.sendCancelMessage();
+ }
+ }
+
+ public boolean isDone() {
+ return isCancelled() || isFinished;
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ isCancelled.set(true);
+ request.abort();
+ return isCancelled();
+ }
+
+ /**
+ * Will set Object as TAG to this request, wrapped by WeakReference
+ *
+ * @param TAG Object used as TAG to this RequestHandle
+ * @return this AsyncHttpRequest to allow fluid syntax
+ */
+ public AsyncHttpRequest setRequestTag(Object TAG) {
+ this.responseHandler.setTag(TAG);
+ return this;
+ }
+
+ /**
+ * Will return TAG of this AsyncHttpRequest
+ *
+ * @return Object TAG, can be null, if it's been already garbage collected
+ */
+ public Object getTag() {
+ return this.responseHandler.getTag();
+ }
+}
diff --git a/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java
new file mode 100755
index 000000000..096020d9e
--- /dev/null
+++ b/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java
@@ -0,0 +1,516 @@
+/*
+ Android Asynchronous Http Client
+ Copyright (c) 2011 James Smith
+ https://github.com/android-async-http/android-async-http
+
+ 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
+
+ https://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.
+*/
+
+package com.loopj.android.http;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.util.Locale;
+
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HttpEntity;
+import cz.msebera.android.httpclient.HttpResponse;
+import cz.msebera.android.httpclient.StatusLine;
+import cz.msebera.android.httpclient.client.HttpResponseException;
+import cz.msebera.android.httpclient.util.ByteArrayBuffer;
+
+/**
+ * Used to intercept and handle the responses from requests made using {@link AsyncHttpClient}. The
+ * {@link #onSuccess(int, cz.msebera.android.httpclient.Header[], byte[])} method is designed to be anonymously
+ * overridden with your own response handling code.
Additionally, you can override the
+ * {@link #onFailure(int, cz.msebera.android.httpclient.Header[], byte[], Throwable)}, {@link #onStart()}, {@link
+ * #onFinish()}, {@link #onRetry(int)} and {@link #onProgress(long, long)} methods as required.
+ *