org.asynchttpclient.shaded.webdav.WebDavCompletionHandlerBase Maven / Gradle / Ivy
/*
* Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package org.asynchttpclient.shaded.webdav;
import org.asynchttpclient.shaded.io.netty.handler.codec.http.HttpHeaders;
import org.asynchttpclient.shaded.AsyncHandler;
import org.asynchttpclient.shaded.HttpResponseBodyPart;
import org.asynchttpclient.shaded.HttpResponseStatus;
import org.asynchttpclient.shaded.netty.NettyResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Simple {@link AsyncHandler} that add support for WebDav's response manipulation.
*
* @param the result type
*/
public abstract class WebDavCompletionHandlerBase implements AsyncHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(WebDavCompletionHandlerBase.class);
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY;
private final List bodyParts = Collections.synchronizedList(new ArrayList<>());
private HttpResponseStatus status;
private HttpHeaders headers;
static {
DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
if (Boolean.getBoolean("org.asynchttpclient.shaded.webdav.enableDtd")) {
try {
DOCUMENT_BUILDER_FACTORY.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
} catch (ParserConfigurationException e) {
LOGGER.error("Failed to disable doctype declaration");
throw new ExceptionInInitializerError(e);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public final State onBodyPartReceived(final HttpResponseBodyPart content) {
bodyParts.add(content);
return State.CONTINUE;
}
/**
* {@inheritDoc}
*/
@Override
public final State onStatusReceived(final HttpResponseStatus status) {
this.status = status;
return State.CONTINUE;
}
/**
* {@inheritDoc}
*/
@Override
public final State onHeadersReceived(final HttpHeaders headers) {
this.headers = headers;
return State.CONTINUE;
}
private Document readXMLResponse(InputStream stream) {
Document document;
try {
document = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse(stream);
parse(document);
} catch (SAXException | IOException | ParserConfigurationException e) {
LOGGER.error(e.getMessage(), e);
throw new RuntimeException(e);
}
return document;
}
private void parse(Document document) {
Element element = document.getDocumentElement();
NodeList statusNode = element.getElementsByTagName("status");
for (int i = 0; i < statusNode.getLength(); i++) {
Node node = statusNode.item(i);
String value = node.getFirstChild().getNodeValue();
int statusCode = Integer.parseInt(value.substring(value.indexOf(" "), value.lastIndexOf(" ")).trim());
String statusText = value.substring(value.lastIndexOf(" "));
status = new HttpStatusWrapper(status, statusText, statusCode);
}
}
/**
* {@inheritDoc}
*/
@Override
public final T onCompleted() throws Exception {
if (status != null) {
Document document = null;
if (status.getStatusCode() == 207) {
document = readXMLResponse(new NettyResponse(status, headers, bodyParts).getResponseBodyAsStream());
}
// recompute response as readXMLResponse->parse might have updated it
return onCompleted(new WebDavResponse(new NettyResponse(status, headers, bodyParts), document));
} else {
throw new IllegalStateException("Status is null");
}
}
/**
* {@inheritDoc}
*/
@Override
public void onThrowable(Throwable t) {
LOGGER.debug(t.getMessage(), t);
}
/**
* Invoked once the HTTP response has been fully read.
*
* @param response The {@link org.asynchttpclient.shaded.Response}
* @return Type of the value that will be returned by the associated {@link java.util.concurrent.Future}
* @throws Exception if something wrong happens
*/
abstract public T onCompleted(WebDavResponse response) throws Exception;
private static class HttpStatusWrapper extends HttpResponseStatus {
private final HttpResponseStatus wrapped;
private final String statusText;
private final int statusCode;
HttpStatusWrapper(HttpResponseStatus wrapper, String statusText, int statusCode) {
super(wrapper.getUri());
this.wrapped = wrapper;
this.statusText = statusText;
this.statusCode = statusCode;
}
@Override
public int getStatusCode() {
return (statusText == null ? wrapped.getStatusCode() : statusCode);
}
@Override
public String getStatusText() {
return (statusText == null ? wrapped.getStatusText() : statusText);
}
@Override
public String getProtocolName() {
return wrapped.getProtocolName();
}
@Override
public int getProtocolMajorVersion() {
return wrapped.getProtocolMajorVersion();
}
@Override
public int getProtocolMinorVersion() {
return wrapped.getProtocolMinorVersion();
}
@Override
public String getProtocolText() {
return wrapped.getStatusText();
}
@Override
public SocketAddress getRemoteAddress() {
return wrapped.getRemoteAddress();
}
@Override
public SocketAddress getLocalAddress() {
return wrapped.getLocalAddress();
}
}
}