Skip to content

Commit

Permalink
WIP 2
Browse files Browse the repository at this point in the history
  • Loading branch information
dr0i committed Dec 1, 2023
1 parent f18027a commit 8dde42d
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 41 deletions.
111 changes: 86 additions & 25 deletions metafacture-io/src/main/java/org/metafacture/io/HttpOpener.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,25 @@
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.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
* Opens an {@link HttpURLConnection} and passes a reader to the receiver.
*
* @author Christoph Böhme
* @author Jan Schnasse
* @author Jens Wille
* @author Pascal Christoph (dr0i)
*/
@Description("Opens an HTTP resource. Supports setting HTTP header fields `Accept`, `Accept-Charset` and `Content-Type`, as well as generic headers (separated by `\\n`). Defaults: request `method` = `GET`, request `url` = `@-` (input data), request `body` = `@-` (input data) if request method supports body and input data not already used, `Accept` header = `*/*`, `Accept-Charset` header (`encoding`) = `UTF-8`, `errorPrefix` = `ERROR: `.")
@Description("Opens an HTTP resource. Supports setting HTTP header fields `Accept`, `Accept-Charset`, `Accept-Encoding` and `Content-Type`, as well as generic headers (separated by `\\n`). Defaults: request `method` = `GET`, request `url` = `@-` (input data), request `body` = `@-` (input data) if request method supports body and input data not already used, `Accept` header = `*/*`, `Accept-Charset` header (`encoding`) = `UTF-8`, `errorPrefix` = `ERROR: `.")
@In(String.class)
@Out(Reader.class)
@FluxCommand("open-http")
Expand All @@ -53,9 +51,12 @@ public final class HttpOpener extends DefaultObjectPipe<String, ObjectReceiver<R
public static final String ACCEPT_DEFAULT = "*/*";
public static final String ACCEPT_HEADER = "accept";
public static final String CONTENT_TYPE_HEADER = "content-type";
public static final String ACCEPT_ENCODING_HEADER = "accept-encoding";
public static final String ENCODING_HEADER = "content-encoding";
public static final String DEFAULT_PREFIX = "ERROR: ";
public static final String ENCODING_DEFAULT = "UTF-8";
public static final String ENCODING_HEADER = "accept-charset";

public static final String CHARSET_DEFAULT = "UTF-8";
public static final String ACCEPT_CHARSET_HEADER = "accept-charset";
public static final String INPUT_DESIGNATOR = "@-";

public static final String DEFAULT_METHOD_NAME = "GET";
Expand Down Expand Up @@ -118,7 +119,7 @@ public boolean getResponseHasBody() {
*/
public HttpOpener() {
setAccept(ACCEPT_DEFAULT);
setEncoding(ENCODING_DEFAULT);
setAcceptCharset(CHARSET_DEFAULT);
setErrorPrefix(DEFAULT_PREFIX);
setMethod(DEFAULT_METHOD);
setUrl(INPUT_DESIGNATOR);
Expand Down Expand Up @@ -163,17 +164,49 @@ public void setContentType(final String contentType) {
setHeader(CONTENT_TYPE_HEADER, contentType);
}

/**
* Sets the HTTP {@value ACCEPT_CHARSET_HEADER} header value. This is the
* preferred charset for the HTTP response.
* The default charset is {@value CHARSET_DEFAULT}.
*
* @param charset name of the charset used for the accept-charset HTTP header
*/
public void setAcceptCharset(final String charset) {
setHeader(ACCEPT_CHARSET_HEADER, charset);
}

/**
* @deprecated Use {@link #setAcceptCharset} instead.
*/
@Deprecated
public void setEncoding(final String charset) {
setAcceptCharset(charset);
}

/**
* Sets the HTTP {@value ACCEPT_ENCODING_HEADER} header value. This is the
* preferred content encoding for the HTTP response. It accepts HTTP compression.
* Allowed values are i.a. "gzip" and "Brotli".
* The default for the content encoding is null, which means "no compression".
*
* @param contentEncoding name of content encoding used for the accept-encoding HTTP
* header
*/
public void setAcceptContentEncoding(final String contentEncoding) {
setHeader(ACCEPT_ENCODING_HEADER, contentEncoding);
}

/**
* Sets the HTTP {@value ENCODING_HEADER} header value. This is the
* preferred encoding for the HTTP response. Additionally, the encoding
* is used for reading the HTTP response if it does not specify a content
* encoding. The default for the encoding is {@value ENCODING_DEFAULT}.
* content encoding for the HTTP POST. It enables HTTP compression.
* Allowed values are "gzip".
* The default for the content encoding is null, which means "no compression".
*
* @param encoding name of the encoding used for the accept-charset HTTP
* @param contentEncoding name of content encoding used for the content-encoding HTTP
* header
*/
public void setEncoding(final String encoding) {
setHeader(ENCODING_HEADER, encoding);
public void setContentEncoding(final String contentEncoding) {
setHeader(ENCODING_HEADER, contentEncoding);
}

/**
Expand Down Expand Up @@ -248,19 +281,51 @@ public void process(final String input) {

final HttpURLConnection connection =
(HttpURLConnection) new URL(requestUrl).openConnection();
connection.setRequestProperty("Accept-Encoding", "gzip");
connection.setRequestMethod(method.name());
headers.forEach(connection::addRequestProperty);
headers.forEach(connection::setRequestProperty);

if (requestBody != null) {
connection.setDoOutput(true);
connection.getOutputStream().write(requestBody.getBytes());
String contentEncoding = headers.get(ENCODING_HEADER);
if (contentEncoding != null && contentEncoding.equals("gzip")) {
ByteArrayOutputStream rstBao = new ByteArrayOutputStream();
GZIPOutputStream zos = new GZIPOutputStream(rstBao);
zos.write(requestBody.getBytes());
zos.flush();
connection.getOutputStream().write(rstBao.toByteArray());
rstBao.flush();
rstBao.close();
zos.close();
} else {
connection.getOutputStream().write(requestBody.getBytes());
}
}

final InputStream inputStream = getInputStream(connection);
final String contentEncoding = getEncoding(connection.getContentEncoding());

getReceiver().process(new InputStreamReader(inputStream, contentEncoding));
// StringBuilder textBuilder = new StringBuilder();

Reader reader = null;
if ("gzip".equals(headers.get(ENCODING_HEADER))) {

reader = new InputStreamReader(new GZIPInputStream(inputStream));

}

else {

reader = new InputStreamReader(inputStream, headers.get(ACCEPT_CHARSET_HEADER));

}

// try ( BufferedReader breader = new BufferedReader(reader)) {
// int c = 0;
// while ((c = breader.read()) != -1) {
// textBuilder.append((char) c);
// }
// }

getReceiver().process(reader);
}
catch (final IOException e) {
throw new MetafactureException(e);
Expand Down Expand Up @@ -312,8 +377,4 @@ private InputStream getErrorStream(final InputStream errorStream) {
}
}

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

}
79 changes: 63 additions & 16 deletions metafacture-io/src/test/java/org/metafacture/io/HttpOpenerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@

package org.metafacture.io;

import org.metafacture.commons.ResourceUtil;
import org.metafacture.framework.ObjectReceiver;

import com.github.tomakehurst.wiremock.client.MappingBuilder;
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
import com.github.tomakehurst.wiremock.client.WireMock;
Expand All @@ -32,21 +29,25 @@
import org.junit.ComparisonFailure;
import org.junit.Rule;
import org.junit.Test;
import org.metafacture.commons.ResourceUtil;
import org.metafacture.framework.ObjectReceiver;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import static org.mockito.Mockito.times;

import java.io.IOException;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import static org.mockito.Mockito.times;

/**
* Tests for class {@link HttpOpener}.
*
Expand Down Expand Up @@ -226,16 +227,16 @@ public void shouldPerformPostRequestWithContentTypeParameter() throws IOExceptio
}

@Test
public void shouldPerformPostRequestWithEncodingParameter() throws IOException {
final String encoding = "ISO-8859-1";
public void shouldPerformPostRequestWithCharsetParameter() throws IOException {
final String charset = "ISO-8859-1";
final String header = "Accept-Charset";
final StringValuePattern value = WireMock.equalTo(encoding);
final StringValuePattern value = WireMock.equalTo(charset);

try {
shouldPerformRequest(REQUEST_BODY, HttpOpener.Method.POST, (o, u) -> {
o.setMethod(HttpOpener.Method.POST);
o.setUrl(u);
o.setEncoding(encoding);
o.setAcceptCharset(charset);
}, s -> s.withHeader(header, value), q -> q.withHeader(header, value), null);
}
catch (final ComparisonFailure e) {
Expand All @@ -245,18 +246,18 @@ public void shouldPerformPostRequestWithEncodingParameter() throws IOException {

@Test
public void shouldPerformPostRequestWithEncodingParameterAndContentEncodingResponseHeader() throws IOException {
final String encoding = "ISO-8859-1";
final String header = "Accept-Charset";
final StringValuePattern value = WireMock.equalTo(encoding);
final String charset = "ISO-8859-1";
final StringValuePattern value = WireMock.equalTo(charset);

shouldPerformRequest(REQUEST_BODY, HttpOpener.Method.POST, (o, u) -> {
o.setMethod(HttpOpener.Method.POST);
o.setUrl(u);
o.setEncoding(encoding);
o.setAcceptCharset(charset);
o.setHeader(HttpOpener.ENCODING_HEADER, "gzip");
},
s -> s.withHeader(header, value),
q -> q.withHeader(header, value),
r -> r.withHeader("Content-Encoding", "UTF-8")
s -> s.withHeader(HttpOpener.ACCEPT_CHARSET_HEADER, value),
q -> q.withHeader("Content-Encoding", WireMock.equalTo("gzip")),
r -> r.withHeader("Content-Encoding", "gzip")
);
}

Expand All @@ -278,6 +279,11 @@ public void shouldPerformGetRequestWithErrorResponseAndWithoutErrorPrefixParamet
null, null, WireMock.badRequest().withBody(RESPONSE_BODY), RESPONSE_BODY);
}

@Test
public void shouldPerformGetRequestWithGzipedContentEncoding() throws IOException {
shouldPerformRequest(TEST_URL, HttpOpener.Method.GET, (o, u) -> o.setAcceptContentEncoding("gzip"),
null, null, WireMock.ok().withBody(RESPONSE_BODY), RESPONSE_BODY);
}
private void shouldPerformRequest(final String input, final HttpOpener.Method method, final BiConsumer<HttpOpener, String> consumer, final String... headers) throws IOException {
shouldPerformRequest(input, method, consumer,
s -> Arrays.stream(headers).forEach(h -> s.withHeader(h, TEST_VALUE)),
Expand All @@ -290,11 +296,52 @@ private void shouldPerformRequest(final String input, final HttpOpener.Method me
responseConsumer.accept(response);
}

// final HttpURLConnection connection =
// (HttpURLConnection) new URL("https://schema.org").openConnection();
// Object obj = connection.getContent();
// final InputStream inputStream = connection.getInputStream();
// StringBuilder textBuilder = new StringBuilder();
//
// Reader reader = null;
//
// if ("gzip".equals(connection.getContentEncoding())) {
//
// reader = new InputStreamReader(new GZIPInputStream(connection.getInputStream()));
//
// }
//
// else {
//
// reader = new InputStreamReader(connection.getInputStream());
//
// }
//
// try ( BufferedReader breader = new BufferedReader(reader)) {
// int c = 0;
// while ((c = breader.read()) != -1) {
// textBuilder.append((char) c);
// }
// }
shouldPerformRequest(input, method,
consumer, stubConsumer, requestConsumer,
response, method.getResponseHasBody() ? RESPONSE_BODY : "");
}

private void shouldPerformGzipRequest(final String input, final HttpOpener.Method method,
final BiConsumer<HttpOpener, String> consumer, final Consumer<MappingBuilder> stubConsumer, final Consumer<RequestPatternBuilder> requestConsumer, final Consumer<ResponseDefinitionBuilder> responseConsumer) throws IOException {
final ResponseDefinitionBuilder response = WireMock.ok().withBody(RESPONSE_BODY);
if (responseConsumer != null) {
responseConsumer.accept(response);
}

final HttpURLConnection connection =
(HttpURLConnection) new URL("http://schema.org").openConnection();
Object obj = connection.getContent();
shouldPerformRequest(input, method,
consumer, stubConsumer, requestConsumer,
response, method.getResponseHasBody() ? RESPONSE_BODY : "");
}

private void shouldPerformRequest(final String input, final HttpOpener.Method method, final BiConsumer<HttpOpener, String> consumer, final Consumer<MappingBuilder> stubConsumer, final Consumer<RequestPatternBuilder> requestConsumer, final ResponseDefinitionBuilder response, final String responseBody) throws IOException {
final String baseUrl = wireMockRule.baseUrl();
final String url = String.format(TEST_URL, baseUrl);
Expand Down

0 comments on commit 8dde42d

Please sign in to comment.