1 package org
.argeo
.cms
.dav
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.io
.OutputStream
;
6 import java
.util
.StringJoiner
;
7 import java
.util
.concurrent
.CompletableFuture
;
8 import java
.util
.function
.Consumer
;
10 import javax
.xml
.namespace
.NamespaceContext
;
12 import org
.argeo
.api
.acr
.ContentNotFoundException
;
13 import org
.argeo
.api
.cms
.CmsLog
;
14 import org
.argeo
.cms
.http
.HttpHeader
;
15 import org
.argeo
.cms
.http
.HttpMethod
;
16 import org
.argeo
.cms
.http
.HttpStatus
;
17 import org
.argeo
.cms
.http
.server
.HttpServerUtils
;
19 import com
.sun
.net
.httpserver
.HttpExchange
;
20 import com
.sun
.net
.httpserver
.HttpHandler
;
23 * Centralise patterns which are not ACR specific. Not really meant as a
24 * framework for building WebDav servers, but rather to make upper-level of
25 * ACR-specific code more readable and maintainable.
27 public abstract class DavHttpHandler
implements HttpHandler
{
28 private final static CmsLog log
= CmsLog
.getLog(DavHttpHandler
.class);
31 public void handle(HttpExchange exchange
) throws IOException
{
32 String subPath
= HttpServerUtils
.subPath(exchange
);
33 String method
= exchange
.getRequestMethod();
35 if (HttpMethod
.GET
.name().equals(method
)) {
36 handleGET(exchange
, subPath
);
37 } else if (HttpMethod
.OPTIONS
.name().equals(method
)) {
38 handleOPTIONS(exchange
, subPath
);
39 exchange
.sendResponseHeaders(HttpStatus
.NO_CONTENT
.getCode(), -1);
40 } else if (HttpMethod
.PROPFIND
.name().equals(method
)) {
41 DavDepth depth
= DavDepth
.fromHttpExchange(exchange
);
43 // default, as per http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
44 depth
= DavDepth
.DEPTH_INFINITY
;
46 DavPropfind davPropfind
;
47 try (InputStream in
= exchange
.getRequestBody()) {
48 davPropfind
= DavPropfind
.load(depth
, in
);
50 MultiStatusWriter multiStatusWriter
= new MultiStatusWriter(exchange
.getProtocol());
51 CompletableFuture
<Void
> published
= handlePROPFIND(exchange
, subPath
, davPropfind
, multiStatusWriter
);
52 exchange
.sendResponseHeaders(HttpStatus
.MULTI_STATUS
.getCode(), 0l);
53 NamespaceContext namespaceContext
= getNamespaceContext(exchange
, subPath
);
54 try (OutputStream out
= exchange
.getResponseBody()) {
55 multiStatusWriter
.process(namespaceContext
, out
, published
.minimalCompletionStage(),
56 davPropfind
.isPropname());
59 throw new IllegalArgumentException("Unsupported method " + method
);
61 } catch (ContentNotFoundException e
) {
62 exchange
.sendResponseHeaders(HttpStatus
.NOT_FOUND
.getCode(), -1);
64 // TODO return a structured error message
65 // TODO better filter application errors and failed login etc.
66 catch (UnsupportedOperationException e
) {
68 exchange
.sendResponseHeaders(HttpStatus
.NOT_IMPLEMENTED
.getCode(), -1);
69 } catch (Exception e
) {
70 log
.error("Failed HTTP exchange " + exchange
.getRequestURI(), e
);
71 exchange
.sendResponseHeaders(HttpStatus
.INTERNAL_SERVER_ERROR
.getCode(), -1);
76 protected abstract NamespaceContext
getNamespaceContext(HttpExchange httpExchange
, String path
);
78 protected abstract CompletableFuture
<Void
> handlePROPFIND(HttpExchange exchange
, String path
,
79 DavPropfind davPropfind
, Consumer
<DavResponse
> consumer
) throws IOException
;
81 protected abstract void handleGET(HttpExchange exchange
, String path
) throws IOException
;
83 protected void handleOPTIONS(HttpExchange exchange
, String path
) throws IOException
{
84 exchange
.getResponseHeaders().set(HttpHeader
.DAV
.getHeaderName(), "1, 3");
85 StringJoiner methods
= new StringJoiner(",");
86 methods
.add(HttpMethod
.OPTIONS
.name());
87 methods
.add(HttpMethod
.HEAD
.name());
88 methods
.add(HttpMethod
.GET
.name());
89 methods
.add(HttpMethod
.POST
.name());
90 methods
.add(HttpMethod
.PUT
.name());
91 methods
.add(HttpMethod
.PROPFIND
.name());
93 methods
.add(HttpMethod
.PROPPATCH
.name());
94 methods
.add(HttpMethod
.MKCOL
.name());
95 methods
.add(HttpMethod
.DELETE
.name());
96 methods
.add(HttpMethod
.MOVE
.name());
97 methods
.add(HttpMethod
.COPY
.name());
99 exchange
.getResponseHeaders().add(HttpHeader
.ALLOW
.getHeaderName(), methods
.toString());