diff --git a/CHANGELOG.md b/CHANGELOG.md index 59efa08fb..09f21beed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ List of closed issues is here [https://github.com/loopj/android-async-http/issue ## 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/commits1.4.7) +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) diff --git a/library/build.gradle b/library/build.gradle index d463344f6..7a55b32cf 100755 --- a/library/build.gradle +++ b/library/build.gradle @@ -7,6 +7,7 @@ android { defaultConfig { minSdkVersion 3 targetSdkVersion 23 + consumerProguardFiles 'proguard.txt' } lintOptions { 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/java/com/loopj/android/http/AsyncHttpClient.java b/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java index 8f816adc0..6644f5c26 100755 --- a/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java +++ b/library/src/main/java/com/loopj/android/http/AsyncHttpClient.java @@ -1,14 +1,13 @@ +package com.loopj.android.http; + /* Android Asynchronous Http Client Copyright (c) 2011 James Smith https://loopj.com - 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. @@ -16,8 +15,6 @@ limitations under the License. */ -package com.loopj.android.http; - import android.content.Context; import android.os.Looper; @@ -50,6 +47,7 @@ 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; @@ -60,6 +58,7 @@ 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; @@ -251,6 +250,11 @@ public void process(HttpResponse response, HttpContext context) { 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); @@ -259,7 +263,10 @@ public void process(final HttpRequest request, final HttpContext context) throws if (authState.getAuthScheme() == null) { AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort()); Credentials creds = credsProvider.getCredentials(authScope); - if (creds != null) { + if (creds instanceof TokenCredentials) { + authState.setAuthScheme(new BearerAuthSchemeFactory.BearerAuthScheme()); + authState.setCredentials(creds); + } else if (creds != null) { authState.setAuthScheme(new BasicScheme()); authState.setCredentials(creds); } @@ -791,6 +798,31 @@ 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) @@ -957,6 +989,24 @@ public void cancelRequestsByTAG(Object TAG, boolean 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 /** @@ -1413,10 +1463,11 @@ public RequestHandle delete(Context context, String url, Header[] headers, Respo * @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 void delete(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) { + public RequestHandle delete(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) { final HttpDelete delete = new HttpDelete(getUrlWithQueryString(isUrlEncodingEnabled, url, params)); - sendRequest(httpClient, httpContext, delete, null, responseHandler, null); + return sendRequest(httpClient, httpContext, delete, null, responseHandler, null); } /** @@ -1634,4 +1685,4 @@ public void consumeContent() throws IOException { super.consumeContent(); } } -} +} \ No newline at end of file diff --git a/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java b/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java index 609cc21a7..88f105a20 100755 --- a/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java +++ b/library/src/main/java/com/loopj/android/http/AsyncHttpResponseHandler.java @@ -78,7 +78,7 @@ * }); * */ -@SuppressWarnings("ALL") +@SuppressWarnings("DesignForExtension") public abstract class AsyncHttpResponseHandler implements ResponseHandlerInterface { public static final String DEFAULT_CHARSET = "UTF-8"; @@ -117,13 +117,8 @@ public AsyncHttpResponseHandler() { * @param looper The looper to work with */ public AsyncHttpResponseHandler(Looper looper) { - this.looper = looper == null ? Looper.myLooper() : looper; - - // Use asynchronous mode by default. - setUseSynchronousMode(false); - // Do not use the pool's thread to fire callbacks by default. - setUsePoolThread(false); + this(looper == null ? Looper.myLooper() : looper, false); } /** @@ -133,17 +128,24 @@ public AsyncHttpResponseHandler(Looper looper) { * @param usePoolThread Whether to use the pool's thread to fire callbacks */ public AsyncHttpResponseHandler(boolean usePoolThread) { - // Whether to use the pool's thread to fire callbacks. - setUsePoolThread(usePoolThread); - - // When using the pool's thread, there's no sense in having a looper. - if (!getUsePoolThread()) { - // Use the current thread's looper. - this.looper = Looper.myLooper(); + this(usePoolThread ? null : Looper.myLooper(), usePoolThread); + } - // Use asynchronous mode by default. - setUseSynchronousMode(false); + private AsyncHttpResponseHandler(Looper looper, boolean usePoolThread) { + if (!usePoolThread) { + Utils.asserts(looper != null, "use looper thread, must call Looper.prepare() first!"); + this.looper = looper; + // Create a handler on current thread to submit tasks + this.handler = new ResponderHandler(this, looper); + } else { + Utils.asserts(looper == null, "use pool thread, looper should be null!"); + // If pool thread is to be used, there's no point in keeping a reference + // to the looper and handler. + this.looper = null; + this.handler = null; } + + this.usePoolThread = usePoolThread; } @Override diff --git a/library/src/main/java/com/loopj/android/http/BearerAuthSchemeFactory.java b/library/src/main/java/com/loopj/android/http/BearerAuthSchemeFactory.java new file mode 100644 index 000000000..f31065275 --- /dev/null +++ b/library/src/main/java/com/loopj/android/http/BearerAuthSchemeFactory.java @@ -0,0 +1,89 @@ +/* + Android Asynchronous Http Client + Copyright (c) 2011 James Smith + https://loopj.com + + 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 cz.msebera.android.httpclient.Header; +import cz.msebera.android.httpclient.HttpRequest; +import cz.msebera.android.httpclient.auth.AUTH; +import cz.msebera.android.httpclient.auth.AuthScheme; +import cz.msebera.android.httpclient.auth.AuthSchemeFactory; +import cz.msebera.android.httpclient.auth.AuthenticationException; +import cz.msebera.android.httpclient.auth.ContextAwareAuthScheme; +import cz.msebera.android.httpclient.auth.Credentials; +import cz.msebera.android.httpclient.auth.MalformedChallengeException; +import cz.msebera.android.httpclient.message.BufferedHeader; +import cz.msebera.android.httpclient.params.HttpParams; +import cz.msebera.android.httpclient.protocol.HttpContext; +import cz.msebera.android.httpclient.util.CharArrayBuffer; + +public class BearerAuthSchemeFactory implements AuthSchemeFactory { + + @Override + public AuthScheme newInstance(HttpParams params) { + return new BearerAuthScheme(); + } + + public static class BearerAuthScheme implements ContextAwareAuthScheme { + private boolean complete = false; + + @Override + public void processChallenge(Header header) throws MalformedChallengeException { + this.complete = true; + } + + @Override + public Header authenticate(Credentials credentials, HttpRequest request) throws AuthenticationException { + return authenticate(credentials, request, null); + } + + @Override + public Header authenticate(Credentials credentials, HttpRequest request, HttpContext httpContext) + throws AuthenticationException { + CharArrayBuffer buffer = new CharArrayBuffer(32); + buffer.append(AUTH.WWW_AUTH_RESP); + buffer.append(": Bearer "); + buffer.append(credentials.getUserPrincipal().getName()); + return new BufferedHeader(buffer); + } + + @Override + public String getSchemeName() { + return "Bearer"; + } + + @Override + public String getParameter(String name) { + return null; + } + + @Override + public String getRealm() { + return null; + } + + @Override + public boolean isConnectionBased() { + return false; + } + + @Override + public boolean isComplete() { + return this.complete; + } + } +} \ No newline at end of file diff --git a/library/src/main/java/com/loopj/android/http/TokenCredentials.java b/library/src/main/java/com/loopj/android/http/TokenCredentials.java new file mode 100644 index 000000000..7e8270bbe --- /dev/null +++ b/library/src/main/java/com/loopj/android/http/TokenCredentials.java @@ -0,0 +1,42 @@ +/* + Android Asynchronous Http Client + Copyright (c) 2011 James Smith + https://loopj.com + + 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.security.Principal; + +import cz.msebera.android.httpclient.auth.BasicUserPrincipal; +import cz.msebera.android.httpclient.auth.Credentials; + +public class TokenCredentials implements Credentials { + private Principal userPrincipal; + + public TokenCredentials(String token) { + this.userPrincipal = new BasicUserPrincipal(token); + } + + @Override + public Principal getUserPrincipal() { + return userPrincipal; + } + + @Override + public String getPassword() { + return null; + } + +}