Saturday 15 February 2014

android - Retrofit POST request w/ Digest HTTP Authentication: “Cannot retry streamed HTTP body” -



android - Retrofit POST request w/ Digest HTTP Authentication: “Cannot retry streamed HTTP body” -

i'm trying implement digest authentication using retrofit. first solution sets implementation of okhttp's authenticator on okhttpclient:

class myauthenticator implements authenticator { private final digestscheme digestscheme = new digestscheme(); private final credentials credentials = new usernamepasswordcredentials("user", "pass"); @override public request authenticate(proxy proxy, response response) throws ioexception { seek { digestscheme.processchallenge(new basicheader("www-authenticate", response.header("www-authenticate"))); httprequest request = new basichttprequest(response.request().method(), response.request().uri().tostring()); string authheader = digestscheme.authenticate(credentials, request).getvalue(); homecoming response.request().newbuilder() .addheader("authorization", authheader) .build(); } grab (exception e) { throw new assertionerror(e); } } @override public request authenticateproxy(proxy proxy, response response) throws ioexception { homecoming null; } }

this works requests through retrofit. however, described in this stackoverflow question, post requests result in "cannot retry streamed http body" exception:

caused by: java.net.httpretryexception: cannot retry streamed http body @ com.squareup.okhttp.internal.http.httpurlconnectionimpl.getresponse(httpurlconnectionimpl.java:324) @ com.squareup.okhttp.internal.http.httpurlconnectionimpl.getresponsecode(httpurlconnectionimpl.java:508) @ com.squareup.okhttp.internal.http.httpsurlconnectionimpl.getresponsecode(httpsurlconnectionimpl.java:136) @ retrofit.client.urlconnectionclient.readresponse(urlconnectionclient.java:94) @ retrofit.client.urlconnectionclient.execute(urlconnectionclient.java:49) @ retrofit.restadapter$resthandler.invokerequest(restadapter.java:357) @ retrofit.restadapter$resthandler.invoke(restadapter.java:282) @ $proxy3.login(native method) @ com.audax.paths.job.loginjob.onruninbackground(loginjob.java:41) @ com.audax.library.job.axjob.onrun(axjob.java:25) @ com.path.android.jobqueue.basejob.saferun(basejob.java:108) @ com.path.android.jobqueue.jobholder.saferun(jobholder.java:60) @ com.path.android.jobqueue.executor.jobconsumerexecutor$jobconsumer.run(jobconsumerexecutor.java:172) @ java.lang.thread.run(thread.java:841)

jesse wilson explains can't resend our request after authenticating, because post body has been thrown out. need returned www-authenticate header because of digest authentication, can't utilize requestinterceptor add together header. maybe it's possible perform separate http request in requestinterceptor, , utilize www-authenticate header in response, seems hacky.

is there way around this?

as workaround, ended swapping out okhttp apache's httpclient, has built-in digest authentication. provide implementation of retrofit.client.client delegates requests apache's httpclient:

import retrofit.client.client; import org.apache.http.impl.client.closeablehttpclient; import org.apache.http.auth.credentials; import org.apache.http.auth.authscope; import org.apache.http.client.credentialsprovider; import org.apache.http.impl.client.basiccredentialsprovider; import org.apache.http.impl.client.httpclientbuilder; import retrofit.client.request; import retrofit.client.response; public class myclient implements client { private final closeablehttpclient delegate; public myclient(string user, string pass, string hostname, string scope) { credentials credentials = new usernamepasswordcredentials(user, pass); authscope authscope = new authscope(hostname, 443, scope); credentialsprovider credentialsprovider = new basiccredentialsprovider(); credentialsprovider.setcredentials(authscope, credentials); delegate = httpclientbuilder.create() .setdefaultcredentialsprovider(credentialsprovider) .build(); } @override public response execute(request request) { // // we're getting retrofit request, need execute apache // httpurirequest instead. utilize info in retrofit request create // apache httpurirequest. // string method = req.getmethod(); bytearrayoutputstream bos = new bytearrayoutputstream(); if (request.getbody() != null) { request.getbody().writeto(bos); } string body = new string(bos.tobytearray()); httpurirequest wrappedrequest; switch (method) { case "get": wrappedrequest = new httpget(request.geturl()); break; case "post": wrappedrequest = new httppost(request.geturl()); wrappedrequest.addheader("content-type", "application/xml"); ((httppost) wrappedrequest).setentity(new stringentity(body)); break; case "put": wrappedrequest = new httpput(request.geturl()); wrappedrequest.addheader("content-type", "application/xml"); ((httpput) wrappedrequest).setentity(new stringentity(body)); break; case "delete": wrappedrequest = new httpdelete(request.geturl()); break; default: throw new assertionerror("http operation not supported."); } // // execute request `delegate.execute(urirequest)`. // // ... // }

then set new client implementation on restadapter.builder:

restadapter restadapter = new restadapter.builder() .setclient(new myclient("jason", "pass", "something.com", "some scope")) .setendpoint("https://something.com/api") .build();

android authentication retrofit okhttp

No comments:

Post a Comment