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
.util
.http
.HttpHeader
;
14 import org
.argeo
.util
.http
.HttpMethod
;
15 import org
.argeo
.util
.http
.HttpStatus
;
16 import org
.argeo
.util
.http
.HttpServerUtils
;
18 import com
.sun
.net
.httpserver
.HttpExchange
;
19 import com
.sun
.net
.httpserver
.HttpHandler
;
22 * Centralise patterns which are not ACR specific. Not really meant as a
23 * framework for building WebDav servers, but rather to make upper-level of
24 * ACR-specific code more readable and maintainable.
26 public abstract class DavHttpHandler
implements HttpHandler
{
29 public void handle(HttpExchange exchange
) throws IOException
{
30 String subPath
= HttpServerUtils
.subPath(exchange
);
31 String method
= exchange
.getRequestMethod();
33 if (HttpMethod
.GET
.name().equals(method
)) {
34 handleGET(exchange
, subPath
);
35 } else if (HttpMethod
.OPTIONS
.name().equals(method
)) {
36 handleOPTIONS(exchange
, subPath
);
37 exchange
.sendResponseHeaders(HttpStatus
.NO_CONTENT
.getCode(), -1);
38 } else if (HttpMethod
.PROPFIND
.name().equals(method
)) {
39 DavDepth depth
= DavDepth
.fromHttpExchange(exchange
);
41 // default, as per http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
42 depth
= DavDepth
.DEPTH_INFINITY
;
44 DavPropfind davPropfind
;
45 try (InputStream in
= exchange
.getRequestBody()) {
46 davPropfind
= DavPropfind
.load(depth
, in
);
48 MultiStatusWriter multiStatusWriter
= new MultiStatusWriter(exchange
.getProtocol());
49 CompletableFuture
<Void
> published
= handlePROPFIND(exchange
, subPath
, davPropfind
, multiStatusWriter
);
50 exchange
.sendResponseHeaders(HttpStatus
.MULTI_STATUS
.getCode(), 0l);
51 NamespaceContext namespaceContext
= getNamespaceContext(exchange
, subPath
);
52 try (OutputStream out
= exchange
.getResponseBody()) {
53 multiStatusWriter
.process(namespaceContext
, out
, published
.minimalCompletionStage(),
54 davPropfind
.isPropname());
57 throw new IllegalArgumentException("Unsupported method " + method
);
59 } catch (ContentNotFoundException e
) {
60 exchange
.sendResponseHeaders(HttpStatus
.NOT_FOUND
.getCode(), -1);
62 // TODO return a structured error message
63 catch (UnsupportedOperationException e
) {
65 exchange
.sendResponseHeaders(HttpStatus
.NOT_IMPLEMENTED
.getCode(), -1);
66 } catch (Exception e
) {
67 exchange
.sendResponseHeaders(HttpStatus
.INTERNAL_SERVER_ERROR
.getCode(), -1);
72 protected abstract NamespaceContext
getNamespaceContext(HttpExchange httpExchange
, String path
);
74 protected abstract CompletableFuture
<Void
> handlePROPFIND(HttpExchange exchange
, String path
,
75 DavPropfind davPropfind
, Consumer
<DavResponse
> consumer
) throws IOException
;
77 protected abstract void handleGET(HttpExchange exchange
, String path
) throws IOException
;
79 protected void handleOPTIONS(HttpExchange exchange
, String path
) throws IOException
{
80 exchange
.getResponseHeaders().set(HttpHeader
.DAV
.getHeaderName(), "1, 3");
81 StringJoiner methods
= new StringJoiner(",");
82 methods
.add(HttpMethod
.OPTIONS
.name());
83 methods
.add(HttpMethod
.HEAD
.name());
84 methods
.add(HttpMethod
.GET
.name());
85 methods
.add(HttpMethod
.POST
.name());
86 methods
.add(HttpMethod
.PUT
.name());
87 methods
.add(HttpMethod
.PROPFIND
.name());
89 methods
.add(HttpMethod
.PROPPATCH
.name());
90 methods
.add(HttpMethod
.MKCOL
.name());
91 methods
.add(HttpMethod
.DELETE
.name());
92 methods
.add(HttpMethod
.MOVE
.name());
93 methods
.add(HttpMethod
.COPY
.name());
95 exchange
.getResponseHeaders().add(HttpHeader
.ALLOW
.getHeaderName(), methods
.toString());