mirror of
https://github.com/etesync/android
synced 2024-11-26 09:58:11 +00:00
Handle HTTP redirections (fixes #83)
This commit is contained in:
parent
1678873885
commit
cf40cb2ebc
@ -88,7 +88,7 @@
|
|||||||
o comprar-lo.</p>
|
o comprar-lo.</p>
|
||||||
|
|
||||||
<h1>Llicència</h1>
|
<h1>Llicència</h1>
|
||||||
<p>Copyright (c) 2013 – 2014 Richard Hirner (<a href="http://www.bitfire.at">bitfire web engineering</a>). Tots els drets reservats.
|
<p>Copyright (c) 2013 – 2014 Ricki Hirner (<a href="http://www.bitfire.at">bitfire web engineering</a>). Tots els drets reservats.
|
||||||
Aquest programa i tots els materials que l\'acompanyen estan disponibles sota els termes de la GNU Public License v3.0 que acompanya
|
Aquest programa i tots els materials que l\'acompanyen estan disponibles sota els termes de la GNU Public License v3.0 que acompanya
|
||||||
aquesta distribució i està disponible a <a
|
aquesta distribució i està disponible a <a
|
||||||
href="http://www.gnu.org/licenses/gpl.html">http://www.gnu.org/licenses/gpl.html</a>. Respecte al Google Play, Samsung
|
href="http://www.gnu.org/licenses/gpl.html">http://www.gnu.org/licenses/gpl.html</a>. Respecte al Google Play, Samsung
|
||||||
|
@ -82,7 +82,7 @@
|
|||||||
<a href="http://davdroid.bitfire.at/donate?pk_campaign=davdroid-app&pk_kwd=main-activity">für DAVdroid spenden</a> oder die App kaufen.</p>
|
<a href="http://davdroid.bitfire.at/donate?pk_campaign=davdroid-app&pk_kwd=main-activity">für DAVdroid spenden</a> oder die App kaufen.</p>
|
||||||
|
|
||||||
<h1>Lizenz</h1>
|
<h1>Lizenz</h1>
|
||||||
<p>Copyright (c) 2013 – 2014 Richard Hirner (<a href="http://www.bitfire.at">bitfire web engineering</a>), alle Rechte
|
<p>Copyright (c) 2013 – 2014 Ricki Hirner (<a href="http://www.bitfire.at">bitfire web engineering</a>), alle Rechte
|
||||||
vorbehalten. Dieses Programm ist freie Software. Sie können es unter den Bedingungen der <a href="http://www.gnu.org/licenses/gpl.html">GNU
|
vorbehalten. Dieses Programm ist freie Software. Sie können es unter den Bedingungen der <a href="http://www.gnu.org/licenses/gpl.html">GNU
|
||||||
General Public License Version 3</a>, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren.
|
General Public License Version 3</a>, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren.
|
||||||
Sofern Google Play oder Samsung Store andere Bedingungen benötigen, gelten für über den jeweiligen Markt heruntergeladene
|
Sofern Google Play oder Samsung Store andere Bedingungen benötigen, gelten für über den jeweiligen Markt heruntergeladene
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
Si vous voulez aider ce projet <a href="http://davdroid.bitfire.at/donate?pk_campaign=davdroid-app&pk_kwd=main-activity">faites un don à DAVdroid</a> ou achetez le</p>
|
Si vous voulez aider ce projet <a href="http://davdroid.bitfire.at/donate?pk_campaign=davdroid-app&pk_kwd=main-activity">faites un don à DAVdroid</a> ou achetez le</p>
|
||||||
|
|
||||||
<h1>License</h1>
|
<h1>License</h1>
|
||||||
<p>Copyright (c) 2013 – 2014 Richard Hirner (<a href="http://www.bitfire.at">bitfire web engineering</a>). All rights reserved.
|
<p>Copyright (c) 2013 – 2014 Ricki Hirner (<a href="http://www.bitfire.at">bitfire web engineering</a>). All rights reserved.
|
||||||
Ce programme et les documents qui l\'accompagnent sont mis à disposition sous les termes de la Licence Public GNU v3.0 qui
|
Ce programme et les documents qui l\'accompagnent sont mis à disposition sous les termes de la Licence Public GNU v3.0 qui
|
||||||
accompagne cette distribution, et est disponible à <a
|
accompagne cette distribution, et est disponible à <a
|
||||||
href="http://www.gnu.org/licenses/gpl.html">http://www.gnu.org/licenses/gpl.html</a>. En ce qui concerne Google Play ou Samsung Store, les conditions respectives s\'appliquent pour les versions qui sont téléchargées via ces services.</p>
|
href="http://www.gnu.org/licenses/gpl.html">http://www.gnu.org/licenses/gpl.html</a>. En ce qui concerne Google Play ou Samsung Store, les conditions respectives s\'appliquent pour les versions qui sont téléchargées via ces services.</p>
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
or purchasing it.</p>
|
or purchasing it.</p>
|
||||||
|
|
||||||
<h1>License</h1>
|
<h1>License</h1>
|
||||||
<p>Copyright (c) 2013 – 2014 Richard Hirner (<a href="http://www.bitfire.at">bitfire web engineering</a>). All rights reserved.
|
<p>Copyright (c) 2013 – 2014 Ricki Hirner (<a href="http://www.bitfire.at">bitfire web engineering</a>). All rights reserved.
|
||||||
This program and the accompanying materials are made available under the terms of the GNU Public License v3.0 which
|
This program and the accompanying materials are made available under the terms of the GNU Public License v3.0 which
|
||||||
accompanies this distribution, and is available at <a
|
accompanies this distribution, and is available at <a
|
||||||
href="http://www.gnu.org/licenses/gpl.html">http://www.gnu.org/licenses/gpl.html</a>. As far as Google Play, Samsung
|
href="http://www.gnu.org/licenses/gpl.html">http://www.gnu.org/licenses/gpl.html</a>. As far as Google Play, Samsung
|
||||||
|
@ -48,7 +48,7 @@ public abstract class RemoteCollection<T extends Resource> {
|
|||||||
public RemoteCollection(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
|
public RemoteCollection(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
|
|
||||||
collection = new WebDavResource(httpClient, new URI(baseURL), user, password, preemptiveAuth, true);
|
collection = new WebDavResource(httpClient, new URI(baseURL), user, password, preemptiveAuth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,14 +125,15 @@ public class EnterCredentialsFragment extends Fragment implements TextWatcher {
|
|||||||
editUserName.getText().length() > 0 &&
|
editUserName.getText().length() > 0 &&
|
||||||
editPassword.getText().length() > 0;
|
editPassword.getText().length() > 0;
|
||||||
|
|
||||||
// check host name
|
if (ok)
|
||||||
try {
|
// check host name
|
||||||
URI uri = new URI(URIUtils.sanitize(protocol + editBaseURL.getText().toString()));
|
try {
|
||||||
if (StringUtils.isBlank(uri.getHost()))
|
URI uri = new URI(URIUtils.sanitize(protocol + editBaseURL.getText().toString()));
|
||||||
|
if (StringUtils.isBlank(uri.getHost()))
|
||||||
|
ok = false;
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
ok = false;
|
ok = false;
|
||||||
} catch (URISyntaxException e) {
|
}
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuItem item = menu.findItem(R.id.next);
|
MenuItem item = menu.findItem(R.id.next);
|
||||||
item.setEnabled(ok);
|
item.setEnabled(ok);
|
||||||
|
@ -117,7 +117,7 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
|||||||
try {
|
try {
|
||||||
// (1/5) detect capabilities
|
// (1/5) detect capabilities
|
||||||
WebDavResource base = new WebDavResource(httpClient, new URI(serverInfo.getProvidedURL()), serverInfo.getUserName(),
|
WebDavResource base = new WebDavResource(httpClient, new URI(serverInfo.getProvidedURL()), serverInfo.getUserName(),
|
||||||
serverInfo.getPassword(), serverInfo.isAuthPreemptive(), true);
|
serverInfo.getPassword(), serverInfo.isAuthPreemptive());
|
||||||
base.options();
|
base.options();
|
||||||
|
|
||||||
serverInfo.setCardDAV(base.supportsDAV("addressbook"));
|
serverInfo.setCardDAV(base.supportsDAV("addressbook"));
|
||||||
|
@ -52,6 +52,7 @@ public class DavHttpClient {
|
|||||||
.setConnectionManager(connectionManager)
|
.setConnectionManager(connectionManager)
|
||||||
.setDefaultRequestConfig(defaultRqConfig)
|
.setDefaultRequestConfig(defaultRqConfig)
|
||||||
.setRetryHandler(DavHttpRequestRetryHandler.INSTANCE)
|
.setRetryHandler(DavHttpRequestRetryHandler.INSTANCE)
|
||||||
|
.setRedirectStrategy(DavRedirectStrategy.INSTANCE)
|
||||||
.setUserAgent("DAVdroid/" + Constants.APP_VERSION)
|
.setUserAgent("DAVdroid/" + Constants.APP_VERSION)
|
||||||
.disableCookieManagement();
|
.disableCookieManagement();
|
||||||
|
|
||||||
|
94
src/at/bitfire/davdroid/webdav/DavRedirectStrategy.java
Normal file
94
src/at/bitfire/davdroid/webdav/DavRedirectStrategy.java
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package at.bitfire.davdroid.webdav;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import ch.boye.httpclientandroidlib.Header;
|
||||||
|
import ch.boye.httpclientandroidlib.HttpRequest;
|
||||||
|
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||||
|
import ch.boye.httpclientandroidlib.ProtocolException;
|
||||||
|
import ch.boye.httpclientandroidlib.RequestLine;
|
||||||
|
import ch.boye.httpclientandroidlib.client.RedirectStrategy;
|
||||||
|
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
|
||||||
|
import ch.boye.httpclientandroidlib.client.methods.RequestBuilder;
|
||||||
|
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
|
||||||
|
import ch.boye.httpclientandroidlib.protocol.HttpContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom Redirect Strategy that handles 30x for CalDAV/CardDAV-specific requests correctly
|
||||||
|
*/
|
||||||
|
public class DavRedirectStrategy implements RedirectStrategy {
|
||||||
|
private final static String TAG = "davdroid.DavRedirectStrategy";
|
||||||
|
final static DavRedirectStrategy INSTANCE = new DavRedirectStrategy();
|
||||||
|
|
||||||
|
protected final static String REDIRECTABLE_METHODS[] = {
|
||||||
|
"OPTIONS", "GET", "PUT", "DELETE"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
|
||||||
|
RequestLine line = request.getRequestLine();
|
||||||
|
|
||||||
|
String location = getLocation(request, response, context).toString();
|
||||||
|
Log.i(TAG, "Following redirection: " + line.getMethod() + " " + line.getUri() + " -> " + location);
|
||||||
|
|
||||||
|
return RequestBuilder.copy(request)
|
||||||
|
.setUri(location)
|
||||||
|
.removeHeaders("Content-Length") // Content-Length will be set again automatically, if required;
|
||||||
|
// remove it now to avoid duplicate header
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a response indicates a redirection and if it does, whether to follow this redirection.
|
||||||
|
* PROPFIND and REPORT must handle redirections explicitely because multi-status processing requires knowledge of the content location.
|
||||||
|
* @return true for 3xx responses on OPTIONS, GET, PUT, DELETE requests that have a valid Location header; false otherwise
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
|
||||||
|
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
||||||
|
boolean redirectable = false;
|
||||||
|
for (String method : REDIRECTABLE_METHODS)
|
||||||
|
if (method.equalsIgnoreCase(request.getRequestLine().getMethod())) {
|
||||||
|
redirectable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return redirectable && getLocation(request, response, context) != null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the destination of a redirection
|
||||||
|
* @return absolute URL of new location; null if not available
|
||||||
|
*/
|
||||||
|
static URI getLocation(HttpRequest request, HttpResponse response, HttpContext context) {
|
||||||
|
Header locationHdr = response.getFirstHeader("Location");
|
||||||
|
if (locationHdr == null) {
|
||||||
|
Log.e(TAG, "Received redirection without Location header, ignoring");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
URI location = new URI(locationHdr.getValue());
|
||||||
|
|
||||||
|
// some servers don't return absolute URLs as required by RFC 2616
|
||||||
|
if (!location.isAbsolute()) {
|
||||||
|
Log.w(TAG, "Received invalid redirection with relative URL, repairing");
|
||||||
|
|
||||||
|
// determine original URL
|
||||||
|
final HttpClientContext clientContext = HttpClientContext.adapt(context);
|
||||||
|
final URI originalURI = new URI(clientContext.getTargetHost() + request.getRequestLine().getUri());
|
||||||
|
|
||||||
|
// determine new location relative to original URL
|
||||||
|
location = originalURI.resolve(location);
|
||||||
|
}
|
||||||
|
return location;
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
Log.e(TAG, "Received redirection from/to invalid URL, ignoring", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -95,19 +95,16 @@ public class WebDavResource {
|
|||||||
protected HttpClientContext context;
|
protected HttpClientContext context;
|
||||||
|
|
||||||
|
|
||||||
public WebDavResource(CloseableHttpClient httpClient, URI baseURL, boolean trailingSlash) throws URISyntaxException {
|
public WebDavResource(CloseableHttpClient httpClient, URI baseURL) throws URISyntaxException {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
location = baseURL.normalize();
|
location = baseURL.normalize();
|
||||||
|
|
||||||
if (trailingSlash && !location.getRawPath().endsWith("/"))
|
|
||||||
location = new URI(location.getScheme(), location.getSchemeSpecificPart() + "/", null);
|
|
||||||
|
|
||||||
context = HttpClientContext.create();
|
context = HttpClientContext.create();
|
||||||
context.setCredentialsProvider(new BasicCredentialsProvider());
|
context.setCredentialsProvider(new BasicCredentialsProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebDavResource(CloseableHttpClient httpClient, URI baseURL, String username, String password, boolean preemptive, boolean trailingSlash) throws URISyntaxException {
|
public WebDavResource(CloseableHttpClient httpClient, URI baseURL, String username, String password, boolean preemptive) throws URISyntaxException {
|
||||||
this(httpClient, baseURL, trailingSlash);
|
this(httpClient, baseURL);
|
||||||
|
|
||||||
HttpHost host = new HttpHost(baseURL.getHost(), baseURL.getPort(), baseURL.getScheme());
|
HttpHost host = new HttpHost(baseURL.getHost(), baseURL.getPort(), baseURL.getScheme());
|
||||||
context.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
|
context.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
|
||||||
@ -250,67 +247,75 @@ public class WebDavResource {
|
|||||||
/* collection operations */
|
/* collection operations */
|
||||||
|
|
||||||
public void propfind(HttpPropfind.Mode mode) throws IOException, DavException, HttpException {
|
public void propfind(HttpPropfind.Mode mode) throws IOException, DavException, HttpException {
|
||||||
HttpPropfind propfind = new HttpPropfind(location, mode);
|
CloseableHttpResponse response = null;
|
||||||
CloseableHttpResponse response = httpClient.execute(propfind, context);
|
|
||||||
try {
|
// processMultiStatus() requires knowledge of the actual content location,
|
||||||
checkResponse(response);
|
// so we have to handle redirections manually and create a new request for the new location
|
||||||
|
for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) {
|
||||||
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_MULTI_STATUS)
|
HttpPropfind propfind = new HttpPropfind(location, mode);
|
||||||
throw new DavNoMultiStatusException();
|
response = httpClient.execute(propfind, context);
|
||||||
|
|
||||||
HttpEntity entity = response.getEntity();
|
|
||||||
if (entity == null)
|
|
||||||
throw new DavNoContentException();
|
|
||||||
@Cleanup InputStream content = entity.getContent();
|
|
||||||
|
|
||||||
DavMultistatus multistatus;
|
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
||||||
try {
|
location = DavRedirectStrategy.getLocation(propfind, response, context);
|
||||||
Serializer serializer = new Persister();
|
Log.i(TAG, "Redirection on PROPFIND; trying again at new content URL: " + location);
|
||||||
multistatus = serializer.read(DavMultistatus.class, content, false);
|
// don't forget to throw away the unneeded response content
|
||||||
} catch (Exception ex) {
|
HttpEntity entity = response.getEntity();
|
||||||
throw new DavException("Couldn't parse Multi-Status response on PROPFIND", ex);
|
if (entity != null) { @Cleanup InputStream content = entity.getContent(); }
|
||||||
}
|
} else
|
||||||
processMultiStatus(multistatus);
|
break; // answer was NOT a redirection, continue
|
||||||
|
}
|
||||||
|
if (response == null)
|
||||||
|
throw new DavNoContentException();
|
||||||
|
|
||||||
|
try {
|
||||||
|
checkResponse(response); // will also handle Content-Location
|
||||||
|
processMultiStatus(response);
|
||||||
} finally {
|
} finally {
|
||||||
response.close();
|
response.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void multiGet(DavMultiget.Type type, String[] names) throws IOException, DavException, HttpException {
|
public void multiGet(DavMultiget.Type type, String[] names) throws IOException, DavException, HttpException {
|
||||||
List<String> hrefs = new LinkedList<String>();
|
CloseableHttpResponse response = null;
|
||||||
for (String name : names)
|
|
||||||
hrefs.add(location.resolve(name).getRawPath());
|
|
||||||
DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[0]));
|
|
||||||
|
|
||||||
Serializer serializer = new Persister();
|
// processMultiStatus() requires knowledge of the actual content location,
|
||||||
StringWriter writer = new StringWriter();
|
// so we have to handle redirections manually and create a new request for the new location
|
||||||
try {
|
for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) {
|
||||||
serializer.write(multiget, writer);
|
// build multi-get XML request
|
||||||
} catch (Exception ex) {
|
List<String> hrefs = new LinkedList<String>();
|
||||||
Log.e(TAG, "Couldn't create XML multi-get request", ex);
|
for (String name : names)
|
||||||
throw new DavException("Couldn't create multi-get request");
|
hrefs.add(location.resolve(name).getRawPath());
|
||||||
}
|
DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[0]));
|
||||||
|
|
||||||
HttpReport report = new HttpReport(location, writer.toString());
|
|
||||||
CloseableHttpResponse response = httpClient.execute(report, context);
|
|
||||||
try {
|
|
||||||
checkResponse(response);
|
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_MULTI_STATUS)
|
StringWriter writer = new StringWriter();
|
||||||
throw new DavNoMultiStatusException();
|
|
||||||
|
|
||||||
HttpEntity entity = response.getEntity();
|
|
||||||
if (entity == null)
|
|
||||||
throw new DavNoContentException();
|
|
||||||
@Cleanup InputStream content = entity.getContent();
|
|
||||||
|
|
||||||
DavMultistatus multiStatus;
|
|
||||||
try {
|
try {
|
||||||
multiStatus = serializer.read(DavMultistatus.class, content, false);
|
Serializer serializer = new Persister();
|
||||||
|
serializer.write(multiget, writer);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new DavException("Couldn't parse Multi-Status response on REPORT multi-get", ex);
|
Log.e(TAG, "Couldn't create XML multi-get request", ex);
|
||||||
|
throw new DavException("Couldn't create multi-get request");
|
||||||
}
|
}
|
||||||
processMultiStatus(multiStatus);
|
|
||||||
|
// submit REPORT request
|
||||||
|
HttpReport report = new HttpReport(location, writer.toString());
|
||||||
|
response = httpClient.execute(report, context);
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
||||||
|
location = DavRedirectStrategy.getLocation(report, response, context);
|
||||||
|
Log.i(TAG, "Redirection on REPORT multi-get; trying again at new content URL: " + location);
|
||||||
|
|
||||||
|
// don't forget to throw away the unneeded response content
|
||||||
|
HttpEntity entity = response.getEntity();
|
||||||
|
if (entity != null) { @Cleanup InputStream content = entity.getContent(); }
|
||||||
|
} else
|
||||||
|
break; // answer was NOT a redirection, continue
|
||||||
|
}
|
||||||
|
if (response == null)
|
||||||
|
throw new DavNoContentException();
|
||||||
|
|
||||||
|
try {
|
||||||
|
checkResponse(response); // will also handle Content-Location
|
||||||
|
processMultiStatus(response);
|
||||||
} finally {
|
} finally {
|
||||||
response.close();
|
response.close();
|
||||||
}
|
}
|
||||||
@ -383,8 +388,19 @@ public class WebDavResource {
|
|||||||
|
|
||||||
/* helpers */
|
/* helpers */
|
||||||
|
|
||||||
protected static void checkResponse(HttpResponse response) throws HttpException {
|
protected void checkResponse(HttpResponse response) throws HttpException {
|
||||||
checkResponse(response.getStatusLine());
|
checkResponse(response.getStatusLine());
|
||||||
|
|
||||||
|
// handle Content-Location header (see RFC 4918 5.2 Collection Resources)
|
||||||
|
Header contentLocationHdr = response.getFirstHeader("Content-Location");
|
||||||
|
if (contentLocationHdr != null)
|
||||||
|
try {
|
||||||
|
// Content-Location was set, update location correspondingly
|
||||||
|
location = location.resolve(new URI(contentLocationHdr.getValue()));
|
||||||
|
Log.d(TAG, "Set Content-Location to " + location);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
Log.w(TAG, "Ignoring invalid Content-Location", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void checkResponse(StatusLine statusLine) throws HttpException {
|
protected static void checkResponse(StatusLine statusLine) throws HttpException {
|
||||||
@ -404,14 +420,30 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processMultiStatus(DavMultistatus multistatus) throws HttpException, DavException {
|
protected void processMultiStatus(HttpResponse response) throws IOException, HttpException, DavException {
|
||||||
if (multistatus.response == null) // empty response
|
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_MULTI_STATUS)
|
||||||
|
throw new DavNoMultiStatusException();
|
||||||
|
|
||||||
|
HttpEntity entity = response.getEntity();
|
||||||
|
if (entity == null)
|
||||||
|
throw new DavNoContentException();
|
||||||
|
@Cleanup InputStream content = entity.getContent();
|
||||||
|
|
||||||
|
DavMultistatus multiStatus;
|
||||||
|
try {
|
||||||
|
Serializer serializer = new Persister();
|
||||||
|
multiStatus = serializer.read(DavMultistatus.class, content, false);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new DavException("Couldn't parse Multi-Status response on REPORT multi-get", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multiStatus.response == null) // empty response
|
||||||
throw new DavNoContentException();
|
throw new DavNoContentException();
|
||||||
|
|
||||||
// member list will be built from response
|
// member list will be built from response
|
||||||
List<WebDavResource> members = new LinkedList<WebDavResource>();
|
List<WebDavResource> members = new LinkedList<WebDavResource>();
|
||||||
|
|
||||||
for (DavResponse singleResponse : multistatus.response) {
|
for (DavResponse singleResponse : multiStatus.response) {
|
||||||
URI href;
|
URI href;
|
||||||
try {
|
try {
|
||||||
href = location.resolve(URIUtils.sanitize(singleResponse.getHref().href));
|
href = location.resolve(URIUtils.sanitize(singleResponse.getHref().href));
|
||||||
@ -423,12 +455,28 @@ public class WebDavResource {
|
|||||||
|
|
||||||
// about which resource is this response?
|
// about which resource is this response?
|
||||||
WebDavResource referenced = null;
|
WebDavResource referenced = null;
|
||||||
|
|
||||||
|
// "this" resource is either at "location" …
|
||||||
if (location.equals(href)) { // -> ourselves
|
if (location.equals(href)) { // -> ourselves
|
||||||
referenced = this;
|
referenced = this;
|
||||||
|
} else {
|
||||||
|
// … or at location + "/" (in case of a collection where the server has implicitly appended the trailing slash)
|
||||||
|
if (!location.getRawPath().endsWith("/")) // this is only possible if location doesn't have a trailing slash
|
||||||
|
try {
|
||||||
|
URI locationAsCollection = new URI(location.getScheme(), location.getAuthority(), location.getPath() + "/", location.getQuery(), null);
|
||||||
|
if (locationAsCollection.equals(href)) {
|
||||||
|
Log.d(TAG, "Server implicitly appended trailing slash to " + locationAsCollection);
|
||||||
|
referenced = this;
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
Log.wtf(TAG, "Couldn't understand our own URI", e);
|
||||||
|
}
|
||||||
|
|
||||||
} else { // -> about a member
|
// otherwise, the referenced resource is a member
|
||||||
referenced = new WebDavResource(this, href);
|
if (referenced == null) {
|
||||||
members.add(referenced);
|
referenced = new WebDavResource(this, href);
|
||||||
|
members.add(referenced);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (DavPropstat singlePropstat : singleResponse.getPropstat()) {
|
for (DavPropstat singlePropstat : singleResponse.getPropstat()) {
|
||||||
|
@ -4,11 +4,23 @@ exports.getBodyParts = function(conf) {
|
|||||||
return {
|
return {
|
||||||
heads: [
|
heads: [
|
||||||
new RoboHydraHead({
|
new RoboHydraHead({
|
||||||
path: "/redirect",
|
path: "/redirect/301",
|
||||||
|
handler: function(req,res,next) {
|
||||||
|
res.statusCode = 301;
|
||||||
|
var location = req.queryParams['to'] || '/assets/test.random';
|
||||||
|
res.headers = {
|
||||||
|
Location: location
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new RoboHydraHead({
|
||||||
|
path: "/redirect/302",
|
||||||
handler: function(req,res,next) {
|
handler: function(req,res,next) {
|
||||||
res.statusCode = 302;
|
res.statusCode = 302;
|
||||||
|
var location = req.queryParams['to'] || '/assets/test.random';
|
||||||
res.headers = {
|
res.headers = {
|
||||||
location: 'http://www.example.com'
|
Location: location
|
||||||
}
|
}
|
||||||
res.end();
|
res.end();
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import at.bitfire.davdroid.webdav.DavHttpClient;
|
|||||||
import at.bitfire.davdroid.webdav.DavMultiget;
|
import at.bitfire.davdroid.webdav.DavMultiget;
|
||||||
import at.bitfire.davdroid.webdav.HttpException;
|
import at.bitfire.davdroid.webdav.HttpException;
|
||||||
import at.bitfire.davdroid.webdav.HttpPropfind;
|
import at.bitfire.davdroid.webdav.HttpPropfind;
|
||||||
|
import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
|
||||||
import at.bitfire.davdroid.webdav.NotFoundException;
|
import at.bitfire.davdroid.webdav.NotFoundException;
|
||||||
import at.bitfire.davdroid.webdav.PreconditionFailedException;
|
import at.bitfire.davdroid.webdav.PreconditionFailedException;
|
||||||
import at.bitfire.davdroid.webdav.WebDavResource;
|
import at.bitfire.davdroid.webdav.WebDavResource;
|
||||||
@ -52,13 +53,13 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
assetMgr = getInstrumentation().getContext().getResources().getAssets();
|
assetMgr = getInstrumentation().getContext().getResources().getAssets();
|
||||||
|
|
||||||
simpleFile = new WebDavResource(httpClient, new URI(ROBOHYDRA_BASE + "assets/test.random"), false);
|
simpleFile = new WebDavResource(httpClient, new URI(ROBOHYDRA_BASE + "assets/test.random"));
|
||||||
|
|
||||||
davCollection = new WebDavResource(httpClient, new URI(ROBOHYDRA_BASE + "dav"), true);
|
davCollection = new WebDavResource(httpClient, new URI(ROBOHYDRA_BASE + "dav/"));
|
||||||
davNonExistingFile = new WebDavResource(davCollection, "collection/new.file");
|
davNonExistingFile = new WebDavResource(davCollection, "collection/new.file");
|
||||||
davExistingFile = new WebDavResource(davCollection, "collection/existing.file");
|
davExistingFile = new WebDavResource(davCollection, "collection/existing.file");
|
||||||
|
|
||||||
davInvalid = new WebDavResource(httpClient, new URI(ROBOHYDRA_BASE + "dav-invalid"), true);
|
davInvalid = new WebDavResource(httpClient, new URI(ROBOHYDRA_BASE + "dav-invalid/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -112,6 +113,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
try {
|
try {
|
||||||
simpleFile.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
|
simpleFile.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
|
||||||
fail();
|
fail();
|
||||||
|
|
||||||
} catch(DavException ex) {
|
} catch(DavException ex) {
|
||||||
}
|
}
|
||||||
assertNull(simpleFile.getCurrentUserPrincipal());
|
assertNull(simpleFile.getCurrentUserPrincipal());
|
||||||
@ -154,8 +156,14 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
/* test normal HTTP/WebDAV */
|
/* test normal HTTP/WebDAV */
|
||||||
|
|
||||||
public void testFollowGetRedirections() throws URISyntaxException, IOException, DavException, HttpException {
|
public void testRedirections() throws URISyntaxException, IOException, DavException, HttpException {
|
||||||
WebDavResource redirection = new WebDavResource(httpClient, new URI(ROBOHYDRA_BASE + "redirect"), false);
|
// PROPFIND redirection
|
||||||
|
WebDavResource redirection = new WebDavResource(httpClient, new URI(ROBOHYDRA_BASE + "redirect/301?to=/dav/"));
|
||||||
|
redirection.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||||
|
assertEquals("/dav/", redirection.getLocation().getPath());
|
||||||
|
|
||||||
|
// normal GET redirection
|
||||||
|
redirection = new WebDavResource(httpClient, new URI(ROBOHYDRA_BASE + "redirect/301"));
|
||||||
redirection.get();
|
redirection.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +175,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testGetHttpsWithSni() throws URISyntaxException, HttpException, IOException, DavException {
|
public void testGetHttpsWithSni() throws URISyntaxException, HttpException, IOException, DavException {
|
||||||
WebDavResource file = new WebDavResource(httpClient, new URI("https://sni.velox.ch"), false);
|
WebDavResource file = new WebDavResource(httpClient, new URI("https://sni.velox.ch"));
|
||||||
|
|
||||||
boolean sniWorking = false;
|
boolean sniWorking = false;
|
||||||
try {
|
try {
|
||||||
|
Loading…
Reference in New Issue
Block a user