]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/dav/DavHttpHandler.java
Additional HTTP headers
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / dav / DavHttpHandler.java
1 package org.argeo.cms.dav;
2
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;
9
10 import javax.xml.namespace.NamespaceContext;
11
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;
18
19 import com.sun.net.httpserver.HttpExchange;
20 import com.sun.net.httpserver.HttpHandler;
21
22 /**
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.
26 */
27 public abstract class DavHttpHandler implements HttpHandler {
28 private final static CmsLog log = CmsLog.getLog(DavHttpHandler.class);
29
30 @Override
31 public void handle(HttpExchange exchange) throws IOException {
32 String subPath = HttpServerUtils.subPath(exchange);
33 String method = exchange.getRequestMethod();
34 try {
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);
42 if (depth == null) {
43 // default, as per http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
44 depth = DavDepth.DEPTH_INFINITY;
45 }
46 DavPropfind davPropfind;
47 try (InputStream in = exchange.getRequestBody()) {
48 davPropfind = DavPropfind.load(depth, in);
49 }
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());
57 }
58 } else {
59 throw new IllegalArgumentException("Unsupported method " + method);
60 }
61 } catch (ContentNotFoundException e) {
62 exchange.sendResponseHeaders(HttpStatus.NOT_FOUND.getCode(), -1);
63 }
64 // TODO return a structured error message
65 // TODO better filter application errors and failed login etc.
66 catch (UnsupportedOperationException e) {
67 e.printStackTrace();
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);
72 }
73
74 }
75
76 protected abstract NamespaceContext getNamespaceContext(HttpExchange httpExchange, String path);
77
78 protected abstract CompletableFuture<Void> handlePROPFIND(HttpExchange exchange, String path,
79 DavPropfind davPropfind, Consumer<DavResponse> consumer) throws IOException;
80
81 protected abstract void handleGET(HttpExchange exchange, String path) throws IOException;
82
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());
92 // TODO :
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());
98
99 exchange.getResponseHeaders().add(HttpHeader.ALLOW.getHeaderName(), methods.toString());
100 }
101
102 }