mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 18:51:26 +00:00
some Windows WebDAV compatibility fixes
This commit is contained in:
@@ -16,7 +16,6 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.frontend.Frontend;
|
||||
import org.cryptomator.frontend.FrontendCreationFailedException;
|
||||
@@ -36,7 +35,6 @@ import org.slf4j.LoggerFactory;
|
||||
public class WebDavServer implements FrontendFactory {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WebDavServer.class);
|
||||
private static final String LOCALHOST = SystemUtils.IS_OS_WINDOWS ? "::1" : "localhost";
|
||||
private static final int MAX_PENDING_REQUESTS = 200;
|
||||
private static final int MAX_THREADS = 200;
|
||||
private static final int MIN_THREADS = 4;
|
||||
@@ -57,8 +55,8 @@ public class WebDavServer implements FrontendFactory {
|
||||
this.servletCollection = new ContextHandlerCollection();
|
||||
this.servletContextFactory = servletContextFactory;
|
||||
this.webdavMounterProvider = webdavMounterProvider;
|
||||
|
||||
localConnector.setHost(LOCALHOST);
|
||||
|
||||
servletCollection.addHandler(WindowsCompatibilityServlet.createServletContextHandler());
|
||||
server.setConnectors(new Connector[] {localConnector});
|
||||
server.setHandler(servletCollection);
|
||||
}
|
||||
@@ -111,7 +109,7 @@ public class WebDavServer implements FrontendFactory {
|
||||
}
|
||||
final URI uri;
|
||||
try {
|
||||
uri = new URI("http", null, LOCALHOST, getPort(), contextPath, null, null);
|
||||
uri = new URI("http", null, "localhost", getPort(), contextPath, null, null);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.frontend.webdav.filters.AcceptRangeFilter;
|
||||
import org.cryptomator.frontend.webdav.filters.LoopbackFilter;
|
||||
import org.cryptomator.frontend.webdav.filters.MacChunkedPutCompatibilityFilter;
|
||||
import org.cryptomator.frontend.webdav.filters.MkcolComplianceFilter;
|
||||
import org.cryptomator.frontend.webdav.filters.UriNormalizationFilter;
|
||||
@@ -65,6 +66,7 @@ class WebDavServletContextFactory {
|
||||
final ServletContextHandler servletContext = new ServletContextHandler(null, contextPath, ServletContextHandler.SESSIONS);
|
||||
final ServletHolder servletHolder = new ServletHolder(contextPath, new WebDavServlet(contextRoot, root));
|
||||
servletContext.addServlet(servletHolder, WILDCARD);
|
||||
servletContext.addFilter(LoopbackFilter.class, WILDCARD, EnumSet.of(DispatcherType.REQUEST));
|
||||
servletContext.addFilter(MkcolComplianceFilter.class, WILDCARD, EnumSet.of(DispatcherType.REQUEST));
|
||||
servletContext.addFilter(AcceptRangeFilter.class, WILDCARD, EnumSet.of(DispatcherType.REQUEST));
|
||||
servletContext.addFilter(new FilterHolder(new UriNormalizationFilter(resourceTypeChecker)), WILDCARD, EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.cryptomator.frontend.webdav;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.cryptomator.frontend.webdav.filters.LoopbackFilter;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
|
||||
/**
|
||||
* The server needs to respond to requests to the root resource, because Windows is stupid.
|
||||
*/
|
||||
public class WindowsCompatibilityServlet extends HttpServlet {
|
||||
|
||||
private static final String ROOT_PATH = "/";
|
||||
|
||||
@Override
|
||||
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
resp.addHeader("DAV", "1, 2");
|
||||
resp.addHeader("MS-Author-Via", "DAV");
|
||||
// resp.addHeader("Allow", "OPTIONS, GET, HEAD, POST, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, PUT, DELETE, MOVE, LOCK, UNLOCK");
|
||||
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
}
|
||||
|
||||
public static ServletContextHandler createServletContextHandler() {
|
||||
final ServletContextHandler servletContext = new ServletContextHandler(null, ROOT_PATH, ServletContextHandler.NO_SESSIONS);
|
||||
final ServletHolder servletHolder = new ServletHolder(ROOT_PATH, WindowsCompatibilityServlet.class);
|
||||
servletContext.addServlet(servletHolder, ROOT_PATH);
|
||||
servletContext.addFilter(LoopbackFilter.class, ROOT_PATH, EnumSet.of(DispatcherType.REQUEST));
|
||||
return servletContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.cryptomator.frontend.webdav.filters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Blocks all requests from external hosts.
|
||||
*/
|
||||
public class LoopbackFilter implements HttpFilter {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
if (InetAddress.getByName(request.getRemoteAddr()).isLoopbackAddress()) {
|
||||
chain.doFilter(request, response);
|
||||
} else {
|
||||
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Can only access drive from localhost.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Sebastian Stenzel and others.
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.frontend.webdav;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
|
||||
public class WindowsCompatibilityServletTest {
|
||||
|
||||
@Test
|
||||
public void testFactory() throws ServletException {
|
||||
ServletHolder[] holders = WindowsCompatibilityServlet.createServletContextHandler().getServletHandler().getServlets();
|
||||
Assert.assertEquals(1, holders.length);
|
||||
ServletHolder holder = holders[0];
|
||||
|
||||
Servlet servlet = holder.getServlet();
|
||||
Assert.assertTrue(servlet instanceof WindowsCompatibilityServlet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponse() throws IOException, ServletException {
|
||||
final WindowsCompatibilityServlet servlet = new WindowsCompatibilityServlet();
|
||||
final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
|
||||
final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
|
||||
|
||||
servlet.doOptions(request, response);
|
||||
|
||||
Mockito.verify(response).addHeader("MS-Author-Via", "DAV");
|
||||
Mockito.verify(response).addHeader("DAV", "1, 2");
|
||||
Mockito.verify(response).setStatus(204);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Sebastian Stenzel and others.
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.frontend.webdav.filters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.experimental.theories.DataPoints;
|
||||
import org.junit.experimental.theories.Theories;
|
||||
import org.junit.experimental.theories.Theory;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
@RunWith(Theories.class)
|
||||
public class LoopbackFilterTest {
|
||||
|
||||
@DataPoints
|
||||
public static final Iterable<String> HOST_NAMES = Arrays.asList("127.0.0.1", "0::1", "1.2.3.4", "google.com");
|
||||
|
||||
private LoopbackFilter filter;
|
||||
private FilterChain chain;
|
||||
private HttpServletRequest request;
|
||||
private HttpServletResponse response;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
filter = new LoopbackFilter();
|
||||
chain = Mockito.mock(FilterChain.class);
|
||||
request = Mockito.mock(HttpServletRequest.class);
|
||||
response = Mockito.mock(HttpServletResponse.class);
|
||||
}
|
||||
|
||||
@Theory
|
||||
public void testWithLoopbackAddress(String hostname) throws IOException, ServletException {
|
||||
Assume.assumeTrue(InetAddress.getByName(hostname).isLoopbackAddress());
|
||||
Mockito.when(request.getRemoteAddr()).thenReturn(hostname);
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
Mockito.verify(chain).doFilter(request, response);
|
||||
}
|
||||
|
||||
@Theory
|
||||
public void testWithExternalAddress(String hostname) throws IOException, ServletException {
|
||||
Assume.assumeFalse(InetAddress.getByName(hostname).isLoopbackAddress());
|
||||
Mockito.when(request.getRemoteAddr()).thenReturn(hostname);
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
ArgumentCaptor<Integer> statusCode = ArgumentCaptor.forClass(Integer.class);
|
||||
Mockito.verify(response).sendError(statusCode.capture(), Mockito.anyString());
|
||||
Assert.assertEquals(405, statusCode.getValue().intValue());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user