diff --git a/pom.xml b/pom.xml index 0fa6f72c..413e33ca 100644 --- a/pom.xml +++ b/pom.xml @@ -129,6 +129,12 @@ test + + org.hamcrest + hamcrest + 2.2 + test + diff --git a/src/test/java/org/icatproject/ids/integration/BaseTest.java b/src/test/java/org/icatproject/ids/integration/BaseTest.java index 2d4bca96..fbfdb64a 100644 --- a/src/test/java/org/icatproject/ids/integration/BaseTest.java +++ b/src/test/java/org/icatproject/ids/integration/BaseTest.java @@ -6,6 +6,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -20,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.NoSuchElementException; import java.util.Set; import java.util.UUID; import java.util.zip.CRC32; @@ -369,6 +371,16 @@ protected void checkStream(InputStream stream, long id) throws IOException { assertTrue(found); } + protected long fileLength(long id) { + for (Entry e : ids.entrySet()) { + if (id == e.getValue()) { + var fileContents = contents.get(e.getKey()); + return fileContents.getBytes(StandardCharsets.UTF_8).length; + } + } + throw new NoSuchElementException("No file with id " + id); + } + protected void writeToFile(Datafile df, String content, String key) throws IOException, IcatException_Exception, NoSuchAlgorithmException { Path path = setup.getStorageDir().resolve(df.getLocation()); diff --git a/src/test/java/org/icatproject/ids/integration/one/GetDataExplicitTest.java b/src/test/java/org/icatproject/ids/integration/one/GetDataExplicitTest.java index 16026ef5..0816e3d7 100644 --- a/src/test/java/org/icatproject/ids/integration/one/GetDataExplicitTest.java +++ b/src/test/java/org/icatproject/ids/integration/one/GetDataExplicitTest.java @@ -2,6 +2,7 @@ import java.io.InputStream; import java.util.Arrays; +import org.apache.http.HttpResponse; import static org.junit.Assert.assertEquals; import org.junit.BeforeClass; @@ -15,6 +16,8 @@ import org.icatproject.ids.integration.util.client.InsufficientPrivilegesException; import org.icatproject.ids.integration.util.client.NotFoundException; import org.icatproject.ids.integration.util.client.TestingClient.Flag; +import static org.icatproject.ids.integration.util.client.TestingClient.isNotChunkedEncoded; +import static org.icatproject.ids.integration.util.client.TestingClient.hasHeader; public class GetDataExplicitTest extends BaseTest { @@ -98,25 +101,36 @@ public void forbiddenTest() throws Exception { @Test public void correctBehaviourTestNone() throws Exception { - try (InputStream stream = testingClient.getData(sessionId, new DataSelection().addDatafiles(datafileIds), - Flag.NONE, 0, 200)) { + try (InputStream stream = testingClient + .assertingHttpResponse(isNotChunkedEncoded()) // assumes caching + .getData(sessionId, new DataSelection().addDatafiles(datafileIds), + Flag.NONE, 0, 200)) { checkZipStream(stream, datafileIds, 57L, 0); } - try (InputStream stream = testingClient.getData(sessionId, new DataSelection().addDatafile(datafileIds.get(0)), - Flag.NONE, 0, 200)) { + var datafileId = datafileIds.get(0); + var fileLength = fileLength(datafileId); + try (InputStream stream = testingClient + .assertingHttpResponse(isNotChunkedEncoded(), // assumes caching + hasHeader("Content-Length", fileLength)) + .getData(sessionId, new DataSelection().addDatafile(datafileId), + Flag.NONE, 0, 200)) { checkStream(stream, datafileIds.get(0)); } } @Test public void correctBehaviourTestCompress() throws Exception { - try (InputStream stream = testingClient.getData(sessionId, new DataSelection().addDatafiles(datafileIds), - Flag.COMPRESS, 0, 200)) { + try (InputStream stream = testingClient + .assertingHttpResponse(isNotChunkedEncoded()) // assumes caching + .getData(sessionId, new DataSelection().addDatafiles(datafileIds), + Flag.COMPRESS, 0, 200)) { checkZipStream(stream, datafileIds, 36L, 0); } - try (InputStream stream = testingClient.getData(sessionId, new DataSelection().addDatafile(datafileIds.get(0)), + try (InputStream stream = testingClient + .assertingHttpResponse(isNotChunkedEncoded()) // assumes caching + .getData(sessionId, new DataSelection().addDatafile(datafileIds.get(0)), Flag.COMPRESS, 0, 200)) { checkStream(stream, datafileIds.get(0)); } @@ -124,26 +138,34 @@ public void correctBehaviourTestCompress() throws Exception { @Test public void correctBehaviourTestZip() throws Exception { - try (InputStream stream = testingClient.getData(sessionId, new DataSelection().addDatafiles(datafileIds), - Flag.ZIP, 0, 200)) { + try (InputStream stream = testingClient + .assertingHttpResponse(isNotChunkedEncoded()) // assumes caching + .getData(sessionId, new DataSelection().addDatafiles(datafileIds), + Flag.ZIP, 0, 200)) { checkZipStream(stream, datafileIds, 57L, 0); } - try (InputStream stream = testingClient.getData(sessionId, new DataSelection().addDatafile(datafileIds.get(0)), - Flag.ZIP, 0, 200)) { + try (InputStream stream = testingClient + .assertingHttpResponse(isNotChunkedEncoded()) // assumes caching + .getData(sessionId, new DataSelection().addDatafile(datafileIds.get(0)), + Flag.ZIP, 0, 200)) { checkZipStream(stream, datafileIds.subList(0, 1), 57L, 0); } } @Test public void correctBehaviourTestZipAndCompress() throws Exception { - try (InputStream stream = testingClient.getData(sessionId, new DataSelection().addDatafiles(datafileIds), - Flag.ZIP_AND_COMPRESS, 0, 200)) { + try (InputStream stream = testingClient + .assertingHttpResponse(isNotChunkedEncoded()) // assumes caching + .getData(sessionId, new DataSelection().addDatafiles(datafileIds), + Flag.ZIP_AND_COMPRESS, 0, 200)) { checkZipStream(stream, datafileIds, 36L, 0); } - try (InputStream stream = testingClient.getData(sessionId, new DataSelection().addDatafile(datafileIds.get(0)), - Flag.ZIP_AND_COMPRESS, 0, 200)) { + try (InputStream stream = testingClient + .assertingHttpResponse(isNotChunkedEncoded()) // assumes caching + .getData(sessionId, new DataSelection().addDatafile(datafileIds.get(0)), + Flag.ZIP_AND_COMPRESS, 0, 200)) { checkZipStream(stream, datafileIds.subList(0, 1), 36L, 0); } } @@ -173,5 +195,4 @@ public void correctBehaviourInvestigations() throws Exception { checkZipStream(stream, datafileIds.subList(0, 1), 57L, 0); } } - } diff --git a/src/test/java/org/icatproject/ids/integration/util/HeaderMatcher.java b/src/test/java/org/icatproject/ids/integration/util/HeaderMatcher.java new file mode 100644 index 00000000..13326018 --- /dev/null +++ b/src/test/java/org/icatproject/ids/integration/util/HeaderMatcher.java @@ -0,0 +1,92 @@ +package org.icatproject.ids.integration.util; + +import static java.util.Objects.requireNonNull; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import static org.hamcrest.Matchers.equalToIgnoringCase; + +/** + * A custom header that matches HTTP headers in some HttpResponse object. It + * supports two kind of matches: that the header is missing or that the header + * is present and the value matches some other Matcher. + */ +public class HeaderMatcher extends BaseMatcher { + + private final String headerName; + private final Matcher matcher; + + public HeaderMatcher(String headerName, Matcher valueMatcher) { + this.headerName = requireNonNull(headerName); + this.matcher = valueMatcher; + } + + private boolean isHeaderExpected() { + return matcher != null; + } + + @Override + public boolean matches(Object item) { + HttpResponse response = (HttpResponse) item; // It is a bug if item is not an HttpResponse. + + Header header = response.getFirstHeader(headerName); + + if (!isHeaderExpected()) { + return header == null; + } + + if (header == null) { + return false; + } + + String headerValue = header.getValue(); + + return matcher.matches(headerValue); + } + + @Override + public void describeTo(Description description) { + if (isHeaderExpected()) { + description.appendText("a '") + .appendText(headerName) + .appendText("' header that ") + .appendDescriptionOf(matcher); + } else { + description.appendText("no '").appendText(headerName).appendText("' header"); + } + } + + /** + * Match an HttpResponse that does not contain the named header. + * @param header The name of the HTTP response header. + * @return a Matcher that verifies expected response. + */ + public static Matcher hasNoHeader(String header) { + return new HeaderMatcher(header, null); + } + + /** + * Match an HttpResponse that contains the named header with a value that + * matches the supplied matcher. + * @param header The name of the HTTP response header. + * @param matcher The matcher for the header value. + * @return a Matcher that verifies expected response. + */ + public static Matcher hasHeaderMatching(String header, Matcher matcher) { + return new HeaderMatcher(header, matcher); + } + + /** + * Match an HttpResponse that contains the named header with the specific + * value (ignoring case). + * @param header The name of the HTTP response header. + * @param value The expected value. + * @return a Matcher that verifies expected response. + */ + public static Matcher hasHeader(String header, Object value) { + Matcher m = equalToIgnoringCase(String.valueOf(value)); + return hasHeaderMatching(header, m); + } +} diff --git a/src/test/java/org/icatproject/ids/integration/util/LongValue.java b/src/test/java/org/icatproject/ids/integration/util/LongValue.java new file mode 100644 index 00000000..b879e5c7 --- /dev/null +++ b/src/test/java/org/icatproject/ids/integration/util/LongValue.java @@ -0,0 +1,33 @@ +package org.icatproject.ids.integration.util; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +/** + * A Matcher that checks whether a String argument is a Long value. The code is + * heavily based on an answer from Ezequiel to + * + * a stack overview question. + */ +public class LongValue extends TypeSafeMatcher { + + @Override + protected boolean matchesSafely(String s) { + try { + Long.valueOf(s); + } catch (NumberFormatException nfe) { + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("is long"); + } + + public static Matcher longValue() { + return new LongValue(); + } +} \ No newline at end of file diff --git a/src/test/java/org/icatproject/ids/integration/util/client/TestingClient.java b/src/test/java/org/icatproject/ids/integration/util/client/TestingClient.java index 2f826866..810e52b3 100644 --- a/src/test/java/org/icatproject/ids/integration/util/client/TestingClient.java +++ b/src/test/java/org/icatproject/ids/integration/util/client/TestingClient.java @@ -10,13 +10,17 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import static java.util.Arrays.stream; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import static java.util.Objects.requireNonNull; import java.util.Set; +import static java.util.stream.Collectors.reducing; +import java.util.function.Consumer; import java.util.zip.CRC32; import java.util.zip.CheckedInputStream; @@ -47,7 +51,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; +import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; +import org.hamcrest.Matcher; +import static org.hamcrest.MatcherAssert.assertThat; +import org.icatproject.ids.integration.util.HeaderMatcher; +import static org.icatproject.ids.integration.util.LongValue.longValue; import static org.junit.Assert.fail; public class TestingClient { @@ -111,6 +120,7 @@ public enum Status { private String basePath; private URI idsUri; + private Consumer responseAssertion = initialAssertions(); public TestingClient(URL idsUrl) { @@ -120,7 +130,24 @@ public TestingClient(URL idsUrl) { } catch (URISyntaxException e) { throw new RuntimeException(e); } + } + + /** + * Some assertions that are always present. + */ + private static Consumer initialAssertions() { + return TestingClient::checkResponseConformity; + } + /** + * Add additional assertions to the HTTP response from IDS. Once the + * response is verified, the assertions are removed. + * @param assertions Assertions made of the HttpResponse from IDS. + */ + public TestingClient assertingHttpResponse(Consumer... assertions) { + requireNonNull(assertions); + responseAssertion = stream(assertions).collect(reducing(responseAssertion, Consumer::andThen)); + return this; } public void archive(String sessionId, DataSelection data, Integer sc) throws NotImplementedException, @@ -132,7 +159,7 @@ public void archive(String sessionId, DataSelection data, Integer sc) throws Not for (Entry entry : data.getParameters().entrySet()) { formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpPost httpPost = new HttpPost(uri); httpPost.setEntity(new UrlEncodedFormEntity(formparams)); try (CloseableHttpResponse response = httpclient.execute(httpPost)) { @@ -223,7 +250,7 @@ public void delete(String sessionId, DataSelection data, Integer sc) } URI uri = getUri(uriBuilder); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpDelete httpDelete = new HttpDelete(uri); try (CloseableHttpResponse response = httpclient.execute(httpDelete)) { expectNothing(response, sc); @@ -249,10 +276,9 @@ private void expectNothing(CloseableHttpResponse response, Integer sc) throws In public URL getIcatUrl(int sc) throws InternalException, ParseException, NotImplementedException { URI uri = getUri(getUriBuilder("getIcatUrl")); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - checkResponseConformity(response); return new URL(getString(response, sc)); } catch (IOException | InsufficientStorageException | DataNotOnlineException | BadRequestException | InsufficientPrivilegesException | NotFoundException e) { @@ -289,10 +315,9 @@ public InputStream getData(String sessionId, DataSelection data, Flag flags, lon } boolean closeNeeded = true; try { - httpclient = HttpClients.createDefault(); + httpclient = createClient(); response = httpclient.execute(httpGet); checkStatus(response, sc); - checkResponseConformity(response); closeNeeded = false; return new HttpInputStream(httpclient, response); } catch (IOException | InsufficientStorageException e) { @@ -331,10 +356,9 @@ public InputStream getData(String preparedId, long offset, Integer sc) } boolean closeNeeded = true; try { - httpclient = HttpClients.createDefault(); + httpclient = createClient(); response = httpclient.execute(httpGet); checkStatus(response, sc); - checkResponseConformity(response); closeNeeded = false; return new HttpInputStream(httpclient, response); } catch (IOException | InsufficientStorageException e) { @@ -366,11 +390,10 @@ public Path getLink(String sessionId, long datafileId, String username, int sc) formparams.add(new BasicNameValuePair("datafileId", Long.toString(datafileId))); formparams.add(new BasicNameValuePair("username", username)); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpPost httpPost = new HttpPost(uri); httpPost.setEntity(new UrlEncodedFormEntity(formparams)); try (CloseableHttpResponse response = httpclient.execute(httpPost)) { - checkResponseConformity(response); return Paths.get(getString(response, sc)); } catch (InsufficientStorageException e) { throw new InternalException(e.getClass() + " " + e.getMessage()); @@ -387,11 +410,10 @@ public ServiceStatus getServiceStatus(String sessionId, Integer sc) uriBuilder.setParameter("sessionId", sessionId); URI uri = getUri(uriBuilder); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - checkResponseConformity(response); String result = getString(response, sc); try (JsonReader jsonReader = Json.createReader(new StringReader(result))) { ServiceStatus serviceStatus = new ServiceStatus(); @@ -435,10 +457,9 @@ public long getSize(String sessionId, DataSelection data, int sc) throws BadRequ } URI uri = getUri(uriBuilder); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - checkResponseConformity(response); return Long.parseLong(getString(response, sc)); } catch (IOException | InsufficientStorageException | DataNotOnlineException e) { throw new InternalException(e.getClass() + " " + e.getMessage()); @@ -455,7 +476,7 @@ public long getSize(String preparedId, int sc) throws BadRequestException, NotFo uriBuilder.setParameter("preparedId", preparedId); URI uri = getUri(uriBuilder); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { return Long.parseLong(getString(response, sc)); @@ -479,11 +500,10 @@ public Status getStatus(String sessionId, DataSelection data, Integer sc) throws } URI uri = getUri(uriBuilder); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - checkResponseConformity(response); return Status.valueOf(getString(response, sc)); } catch (InsufficientStorageException | DataNotOnlineException e) { throw new InternalException(e.getClass() + " " + e.getMessage()); @@ -501,7 +521,7 @@ public Status getStatus(String preparedId, Integer sc) throws BadRequestExceptio uriBuilder.setParameter("preparedId", preparedId); URI uri = getUri(uriBuilder); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { @@ -545,11 +565,10 @@ public boolean isPrepared(String preparedId, Integer sc) uriBuilder.setParameter("preparedId", preparedId); URI uri = getUri(uriBuilder); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - checkResponseConformity(response); return Boolean.parseBoolean(getString(response, sc)); } catch (InsufficientStorageException | DataNotOnlineException | InsufficientPrivilegesException e) { throw new InternalException(e.getClass() + " " + e.getMessage()); @@ -561,10 +580,9 @@ public boolean isPrepared(String preparedId, Integer sc) public boolean isReadOnly(int sc) throws InternalException, NotImplementedException { URI uri = getUri(getUriBuilder("isReadOnly")); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - checkResponseConformity(response); return Boolean.parseBoolean(getString(response, sc)); } catch (IOException | InsufficientStorageException | DataNotOnlineException | BadRequestException | InsufficientPrivilegesException | NotFoundException e) { @@ -577,10 +595,9 @@ public boolean isReadOnly(int sc) throws InternalException, NotImplementedExcept public boolean isTwoLevel(int sc) throws InternalException, NotImplementedException { URI uri = getUri(getUriBuilder("isTwoLevel")); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - checkResponseConformity(response); return Boolean.parseBoolean(getString(response, sc)); } catch (IOException | InsufficientStorageException | DataNotOnlineException | BadRequestException | InsufficientPrivilegesException | NotFoundException e) { @@ -594,10 +611,9 @@ public boolean isTwoLevel(int sc) throws InternalException, NotImplementedExcept public void ping(Integer sc) throws InternalException, NotFoundException, NotImplementedException { URI uri = getUri(getUriBuilder("ping")); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - checkResponseConformity(response); String result = getString(response, sc); if (!result.equals("IdsOK")) { throw new NotFoundException("Server gave invalid response: " + result); @@ -627,12 +643,11 @@ public String prepareData(String sessionId, DataSelection data, Flag flags, Inte if (flags == Flag.COMPRESS || flags == Flag.ZIP_AND_COMPRESS) { formparams.add(new BasicNameValuePair("compress", "true")); } - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpEntity entity = new UrlEncodedFormEntity(formparams); HttpPost httpPost = new HttpPost(uri); httpPost.setEntity(entity); try (CloseableHttpResponse response = httpclient.execute(httpPost)) { - checkResponseConformity(response); return getString(response, sc); } catch (InsufficientStorageException | DataNotOnlineException e) { throw new InternalException(e.getClass() + " " + e.getMessage()); @@ -678,13 +693,12 @@ public Long put(String sessionId, InputStream inputStream, String name, long dat } URI uri = getUri(uriBuilder); - CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpClient httpclient = createClient(); HttpPut httpPut = new HttpPut(uri); httpPut.setEntity(new InputStreamEntity(inputStream, ContentType.APPLICATION_OCTET_STREAM)); try (CloseableHttpResponse response = httpclient.execute(httpPut)) { String result = getString(response, sc); - checkResponseConformity(response); try (JsonReader jsonReader = Json.createReader(new StringReader(result))) { JsonObject rootNode = jsonReader.readObject(); if (rootNode.getJsonNumber("checksum").longValueExact() != crc.getValue()) { @@ -738,12 +752,11 @@ public Long putAsPost(String sessionId, InputStream inputStream, String name, lo ContentType.APPLICATION_OCTET_STREAM, "unreliable"); HttpEntity entity = reqEntityBuilder.addPart("file", body).build(); - CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpClient httpclient = createClient(); HttpPost httpPost = new HttpPost(uri); httpPost.setEntity(entity); try (CloseableHttpResponse response = httpclient.execute(httpPost)) { - checkResponseConformity(response); String result = getString(response, sc); String prefix = ""; @@ -774,7 +787,7 @@ public void restore(String sessionId, DataSelection data, Integer sc) throws Not for (Entry entry : data.getParameters().entrySet()) { formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpEntity entity = new UrlEncodedFormEntity(formparams); HttpPost httpPost = new HttpPost(uri); httpPost.setEntity(entity); @@ -795,12 +808,11 @@ public List getDatafileIds(String preparedId, Integer sc) uriBuilder.setParameter("preparedId", preparedId); URI uri = getUri(uriBuilder); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { String result = getString(response, sc); - checkResponseConformity(response); try (JsonReader jsonReader = Json.createReader(new StringReader(result))) { JsonObject rootNode = jsonReader.readObject(); @@ -833,11 +845,10 @@ public List getDatafileIds(String sessionId, DataSelection data, Integer s } URI uri = getUri(uriBuilder); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpGet httpGet = new HttpGet(uri); try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - checkResponseConformity(response); String result = getString(response, sc); try (JsonReader jsonReader = Json.createReader(new StringReader(result))) { @@ -868,7 +879,7 @@ public void reset(String preparedId, Integer sc) throws InternalException, BadRe List formparams = new ArrayList<>(); formparams.add(new BasicNameValuePair("preparedId", preparedId)); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpEntity entity = new UrlEncodedFormEntity(formparams); HttpPost httpPost = new HttpPost(uri); httpPost.setEntity(entity); @@ -890,7 +901,7 @@ public void reset(String sessionId, DataSelection data, int sc) throws InternalE for (Entry entry : data.getParameters().entrySet()) { formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpEntity entity = new UrlEncodedFormEntity(formparams); HttpPost httpPost = new HttpPost(uri); httpPost.setEntity(entity); @@ -914,7 +925,7 @@ public void write(String sessionId, DataSelection data, Integer sc) throws NotIm for (Entry entry : data.getParameters().entrySet()) { formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + try (CloseableHttpClient httpclient = createClient()) { HttpPost httpPost = new HttpPost(uri); httpPost.setEntity(new UrlEncodedFormEntity(formparams)); try (CloseableHttpResponse response = httpclient.execute(httpPost)) { @@ -927,21 +938,53 @@ public void write(String sessionId, DataSelection data, Integer sc) throws NotIm } } - private void checkResponseConformity(HttpResponse response) throws InternalException { - + private CloseableHttpClient createClient() { + return HttpClients.custom() + .addInterceptorLast((HttpResponse r, HttpContext c) -> { + responseAssertion.accept(r); + responseAssertion = initialAssertions(); + }) + .build(); + } + + private static void checkResponseConformity(HttpResponse response) { + StatusLine status = response.getStatusLine(); var statusCode = status.getStatusCode(); if(statusCode == 200 && response.getEntity() != null) { //we have a status of 200 and a payload. Check for header conformity Boolean chunked = response.getFirstHeader("Transfer-Encoding") != null ? response.getFirstHeader("Transfer-Encoding").getValue().contains("chunked") : false; - + if ( (response.getFirstHeader("Content-Length") == null && !chunked) || (response.getFirstHeader("Content-Length") != null && chunked ) ) { - throw new InternalException("Responses with payload should either contain the Content-Length header or the 'Transfer-Encoding: chunked' header. It needs exactly one of them."); + fail("Responses with payload should either contain the Content-Length header or the 'Transfer-Encoding: chunked' header. It needs exactly one of them."); } } } + public static Consumer isChunkedEncoded() { + return hasNoHeader("Content-Length") + .andThen(hasHeader("Transfer-Encoding", "chunked")); + } + + public static Consumer isNotChunkedEncoded() { + return hasHeaderMatching("Content-Length", longValue()) + .andThen(hasNoHeader("Transfer-Encoding")); + } + + public static Consumer hasNoHeader(String header) { + return r-> assertThat(r, HeaderMatcher.hasNoHeader(header)); + } + + public static Consumer hasHeaderMatching(String header, + Matcher valueMatcher) { + return r-> assertThat(r, HeaderMatcher.hasHeaderMatching(header, valueMatcher)); + } + + public static Consumer hasHeader(String header, + Object expectedValue) { + return r-> assertThat(r, HeaderMatcher.hasHeader(header, expectedValue)); + } }