Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend HttpOpener. #463

Merged
merged 12 commits into from
Sep 8, 2022
156 changes: 144 additions & 12 deletions metafacture-io/src/main/java/org/metafacture/io/HttpOpener.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013, 2014 Deutsche Nationalbibliothek
* Copyright 2013, 2022 Deutsche Nationalbibliothek et al
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
Expand All @@ -24,18 +24,21 @@
import org.metafacture.framework.annotations.Out;
import org.metafacture.framework.helpers.DefaultObjectPipe;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.SequenceInputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
* Opens a {@link URLConnection} and passes a reader to the receiver.
* Opens an {@link HttpURLConnection} and passes a reader to the receiver.
*
* @author Christoph Böhme
* @author Jan Schnasse
Expand All @@ -51,18 +54,55 @@ public final class HttpOpener extends DefaultObjectPipe<String, ObjectReceiver<R

private static final String ACCEPT_HEADER = "accept";
private static final String ENCODING_HEADER = "accept-charset";

blackwinter marked this conversation as resolved.
Show resolved Hide resolved
private static final String ACCEPT_DEFAULT = "*/*";
private static final String CONTENT_TYPE_HEADER = "content-Type";
blackwinter marked this conversation as resolved.
Show resolved Hide resolved
private static final String CONTENT_TYPE_DEFAULT = "application/json";
blackwinter marked this conversation as resolved.
Show resolved Hide resolved
private static final String ENCODING_DEFAULT = "UTF-8";
private static final String INPUT_DESIGNATOR = "@-";
private static final String DEFAULT_PREFIX = "ERROR: ";

private static final Method DEFAULT_METHOD = Method.GET;

private final Map<String, String> headers = new HashMap<>();

private Method method;
private String body;
private String errorPrefix;
private String url;
private boolean inputUsed;

public enum Method {

DELETE(false),
GET(false),
HEAD(false),
OPTIONS(false),
POST(true),
PUT(true),
TRACE(false);

private final boolean inputAsBody;

Method(final boolean inputAsBody) {
this.inputAsBody = inputAsBody;
}

private boolean getInputAsBody() {
return inputAsBody;
}

}

/**
* Creates an instance of {@link HttpOpener}.
*/
public HttpOpener() {
setAccept(ACCEPT_DEFAULT);
setContentType(CONTENT_TYPE_DEFAULT);
setEncoding(ENCODING_DEFAULT);
setErrorPrefix(DEFAULT_PREFIX);
setMethod(DEFAULT_METHOD);
setUrl(INPUT_DESIGNATOR);
}

/**
Expand All @@ -76,6 +116,25 @@ public void setAccept(final String accept) {
setHeader(ACCEPT_HEADER, accept);
}

/**
* Sets the HTTP request body.
*
* @param body the request body
*/
public void setBody(final String body) {
this.body = body;
}

/**
* Sets the HTTP content type header. This is a mime-type such as text/plain,
* text/html. The default is application/json.
*
* @param contentType mime-type to use for the HTTP contentType header
*/
public void setContentType(final String contentType) {
setHeader(CONTENT_TYPE_HEADER, contentType);
}

/**
* Sets the preferred encoding of the HTTP response. This value is in the
* accept-charset header. Additonally, the encoding is used for reading the
Expand All @@ -89,6 +148,15 @@ public void setEncoding(final String encoding) {
setHeader(ENCODING_HEADER, encoding);
}

/**
* Sets the error prefix.
*
* @param errorPrefix the error prefix
*/
public void setErrorPrefix(final String errorPrefix) {
this.errorPrefix = errorPrefix;
}

/**
* Sets a request property, or multiple request properties separated by
* {@code \n}.
Expand Down Expand Up @@ -117,21 +185,85 @@ public void setHeader(final String key, final String value) {
headers.put(key.toLowerCase(), value);
}

/**
* Sets the HTTP request method.
*
* @param method the request method
*/
public void setMethod(final Method method) {
this.method = method;
}

/**
* Sets the HTTP request URL.
*
* @param url the request URL
*/
public void setUrl(final String url) {
this.url = url;
}

@Override
public void process(final String urlStr) {
public void process(final String input) {
try {
final URL url = new URL(urlStr);
final URLConnection con = url.openConnection();
headers.forEach(con::addRequestProperty);
String enc = con.getContentEncoding();
if (enc == null) {
enc = headers.get(ENCODING_HEADER);
final String requestUrl = getInput(input, url);
final String requestBody = getInput(input,
body == null && method.getInputAsBody() ? INPUT_DESIGNATOR : body);

final HttpURLConnection connection =
(HttpURLConnection) new URL(requestUrl).openConnection();

connection.setRequestMethod(method.name());
headers.forEach(connection::addRequestProperty);

if (requestBody != null) {
connection.setDoOutput(true);
connection.getOutputStream().write(requestBody.getBytes());
}
getReceiver().process(new InputStreamReader(con.getInputStream(), enc));

final InputStream errorStream = connection.getErrorStream();
final InputStream inputStream;

if (errorStream != null) {
if (errorPrefix != null) {
final InputStream errorPrefixStream = new ByteArrayInputStream(errorPrefix.getBytes());
inputStream = new SequenceInputStream(errorPrefixStream, errorStream);
}
else {
inputStream = errorStream;
}
}
else {
inputStream = connection.getInputStream();
}

final String contentEncoding = getEncoding(connection.getContentEncoding());
getReceiver().process(new InputStreamReader(inputStream, contentEncoding));
}
catch (final IOException e) {
throw new MetafactureException(e);
}
}

private String getInput(final String input, final String value) {
final String result;

if (!INPUT_DESIGNATOR.equals(value)) {
result = value;
}
else if (inputUsed) {
result = null;
}
else {
inputUsed = true;
result = input;
}

return result;
}

private String getEncoding(final String contentEncoding) {
return contentEncoding != null ? contentEncoding : headers.get(ENCODING_HEADER);
}

}