@Override public boolean call(HttpConnection connection) throws IOException { iamTokenResponse.set(connection.responseAsString()); return true; } }
public static boolean isCouchDBV2(URI uri) throws URISyntaxException, IOException { URI root = new URI(uri.getScheme() + "://" + uri.getAuthority()); HttpConnection connection = Http.GET(root); String response = connection.execute().responseAsString(); return response.contains("\"version\":\"2."); }
@TestTemplate public void testReadBeforeExecute() throws Exception { HttpConnection conn = new HttpConnection("POST", new URL(dbResource.getDbURIWithUserInfo()), "application/json"); ByteArrayInputStream bis = new ByteArrayInputStream(data.getBytes()); // nothing read from stream assertEquals(data.getBytes().length, bis.available()); conn.setRequestBody(bis); try { String response = conn.responseAsString(); fail("IOException not thrown as expected instead had response " + response); } catch (IOException ioe) { ; // "Attempted to read response from server before calling execute()" } // stream was not read because execute() was not called assertEquals(data.getBytes().length, bis.available()); }
@TestTemplate public void testWriteToServerOk() throws Exception { HttpConnection conn = new HttpConnection("POST", new URL(dbResource.getDbURIWithUserInfo()), "application/json"); ByteArrayInputStream bis = new ByteArrayInputStream(data.getBytes()); // nothing read from stream assertEquals(data.getBytes().length, bis.available()); conn.setRequestBody(bis); HttpConnection response = conn.execute(); // Consume response stream String responseStr = response.responseAsString(); String okPattern = ".*\"ok\"\\s*:\\s*true.*"; assertTrue(Pattern.compile(okPattern, Pattern.DOTALL).matcher(responseStr).matches(), "There should be an ok response: " + "" + responseStr); // stream was read to end assertEquals(0, bis.available()); assertEquals(2, response.getConnection().getResponseCode () / 100, "Should be a 2XX response code"); }
@Test public void httpLoggingEnabled() throws Exception { logger = setupLogger(HttpConnection.class, Level.ALL); client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); assertTrue(handler.logEntries.size() > 0, "There should be at least 1 log entry"); }
@Test public void urlRegexLogging() throws Exception { //Set the regex filter property on the LogManager and assert it was set String urlFilterPropName = "com.cloudant.http.filter.url"; String urlFilterPropValue = ".*/testdb.*"; setAndAssertLogProperty(urlFilterPropName, urlFilterPropValue); // Configure HttpConnection logging and get a client logger = setupLogger(HttpConnection.class, Level.FINE); // Make a request to testdb client.executeRequest(Http.GET(new URL(client.getBaseUri().toString() + "/testdb"))) .responseAsString(); // Check there were two log messages one for request and one for response assertEquals(2, handler.logEntries.size(), "There should be 2 log messages"); // Check the messages were the ones we expected assertHttpMessage("GET .*/testdb request", 0); assertHttpMessage("GET .*/testdb response 200 OK", 1); // Store the current log size int logsize = handler.logEntries.size(); // Make a second request to a different URL and check that nothing else was logged client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); assertEquals(logsize, handler.logEntries.size(), "There should have been no more log " + "entries"); }
@Override public void execute() throws Throwable { // Get a client pointing to an https proxy CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder (mockWebServer) .proxyURL(new URL("https://192.0.2.0")).build(); String response = client.executeRequest(Http.GET(client.getBaseUri())) .responseAsString(); fail("There should be an IllegalStateException for an https proxy."); } });
@Test public void httpMethodFilterLogging() throws Exception { setAndAssertLogProperty(methodFilterPropName, "GET"); logger = setupLogger(HttpConnection.class, Level.FINE); // Make a GET request client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); // Check there were two log messages one for request and one for response assertEquals(2, handler.logEntries.size(), "There should be 2 log messages"); // Check the messages were the ones we expected assertHttpMessage("GET .* request", 0); assertHttpMessage("GET .* response 200 OK", 1); // Store the current log size int logsize = handler.logEntries.size(); // Make a PUT request to a different URL and check that nothing else was logged client.executeRequest(Http.PUT(client.getBaseUri(), "text/plain").setRequestBody("")) .responseAsString(); assertEquals(logsize, handler.logEntries.size(), "There should have been no more log " + "entries"); }
@Test public void testReadBeforeExecute() throws Exception { CouchConfig config = getCouchConfig("httptest" + System.currentTimeMillis()); CouchClient client = new CouchClient(config.getRootUri(), config.getRequestInterceptors() , config.getResponseInterceptors()); client.createDb(); HttpConnection conn = postAndAssertNothingReadBeforeSettingBodyGenerator(config); try { conn.responseAsString(); Assert.fail("IOException not thrown as expected"); } catch (IOException ioe) { ; // "Attempted to read response from server before calling execute()" } // stream was not read because execute() was not called Assert.assertEquals(bis.available(), data.getBytes().length); client.deleteDb(); }
@Test public void httpMethodFilterLoggingList() throws Exception { setAndAssertLogProperty(methodFilterPropName, "PUT,GET"); logger = setupLogger(HttpConnection.class, Level.FINE); // Make a GET request client.executeRequest(Http.GET(client.getBaseUri())).responseAsString(); // Check there were two log messages one for request and one for response assertEquals(2, handler.logEntries.size(), "There should be 2 log messages"); // Check the messages were the ones we expected assertHttpMessage("GET .* request", 0); assertHttpMessage("GET .* response 200 OK", 1); // Make a PUT request to a different URL and check that new messages are logged client.executeRequest(Http.PUT(client.getBaseUri(), "text/plain").setRequestBody("")) .responseAsString(); assertEquals(4, handler.logEntries.size(), "There should now be 4 log messages"); // Check the messages were the ones we expected assertHttpMessage("PUT .* request", 2); assertHttpMessage("PUT .* response 200 OK", 3); }
/** * Assert that requests have the User-Agent header added. This test runs a local HTTP server * process that can handle a single request to receive the request and validate the header. */ @Test public void testUserAgentHeaderIsAddedToRequest() throws Exception { //send back an OK 200 server.enqueue(new MockResponse()); //instantiating the client performs a single post request CloudantClient client = CloudantClientHelper.newMockWebServerClientBuilder(server) .build(); String response = client.executeRequest(createPost(client.getBaseUri(), null, "application/json")).responseAsString(); assertTrue(response.isEmpty(), "There should be no response body on the mock response"); //assert that the request had the expected header String userAgentHeader = server.takeRequest(10, TimeUnit.SECONDS) .getHeader("User-Agent"); assertNotNull(userAgentHeader, "The User-Agent header should be present on the request"); assertTrue(userAgentHeader.matches(userAgentUnknownRegex), "The value of the User-Agent " + "header " + userAgentHeader + " on the request" + " should match the format " + userAgentFormat); }
/** * Test that a request is replayed in response to a 429. * * @throws Exception */ @TestTemplate public void test429Backoff() throws Exception { mockWebServer.enqueue(MockWebServerResources.get429()); mockWebServer.enqueue(new MockResponse()); CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) .interceptors(Replay429Interceptor.WITH_DEFAULTS) .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); assertTrue(response.isEmpty(), "There should be no response body on the mock response"); assertEquals(2, mockWebServer.getRequestCount(), "There should be 2 requests"); }
@TestTemplate public void testCookieRenewOnPost() throws Exception { mockWebServer.enqueue(MockWebServerResources.OK_COOKIE); mockWebServer.enqueue(new MockResponse().setResponseCode(403).setBody ("{\"error\":\"credentials_expired\", \"reason\":\"Session expired\"}\r\n")); mockWebServer.enqueue(MockWebServerResources.OK_COOKIE); mockWebServer.enqueue(new MockResponse()); CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) .username("a") .password("b") .build(); HttpConnection request = Http.POST(mockWebServer.url("/").url(), "application/json"); request.setRequestBody("{\"some\": \"json\"}"); HttpConnection response = c.executeRequest(request); String responseStr = response.responseAsString(); assertTrue(responseStr.isEmpty(), "There should be no response body on the mock response"); response.getConnection().getResponseCode(); }
@Override public void execute() throws Throwable { // Respond with a cookie init to the first request to _session mockWebServer.enqueue(MockWebServerResources.OK_COOKIE); // Respond to the executeRequest GET of / with a 403 with no body mockWebServer.enqueue(new MockResponse().setResponseCode(403)); CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) .username("a") .password("b") .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); fail("There should be an exception, but received response " + response); } });
@TestTemplate public void test429IgnoreRetryAfter() throws Exception { mockWebServer.enqueue(MockWebServerResources.get429().addHeader("Retry-After", "1")); mockWebServer.enqueue(new MockResponse()); TestTimer t = TestTimer.startTimer(); CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) .interceptors(new Replay429Interceptor(1, 1, false)) .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); assertTrue(response.isEmpty(), "There should be no response body on the mock response"); long duration = t.stopTimer(TimeUnit.MILLISECONDS); assertTrue(duration < 1000, "The duration should be less than 1000 ms, but was " + duration); assertEquals(2, mockWebServer .getRequestCount(), "There should be 2 request attempts"); }
/** * Test that an integer number of seconds delay specified by a Retry-After header is honoured. * * @throws Exception */ @TestTemplate public void test429BackoffRetryAfter() throws Exception { mockWebServer.enqueue(MockWebServerResources.get429().addHeader("Retry-After", "1")); mockWebServer.enqueue(new MockResponse()); TestTimer t = TestTimer.startTimer(); CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) .interceptors(Replay429Interceptor.WITH_DEFAULTS) .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); assertTrue(response.isEmpty(), "There should be no response body on the mock response"); long duration = t.stopTimer(TimeUnit.MILLISECONDS); assertTrue(duration >= 1000, "The duration should be at least 1000 ms, but was " + duration); assertEquals(2, mockWebServer .getRequestCount(), "There should be 2 request attempts"); }
/** * Test that the outer number of configured retries takes precedence. * * @throws Exception */ @TestTemplate public void test429BackoffMaxMoreThanRetriesAllowed() throws Exception { // Always respond 429 for this test mockWebServer.setDispatcher(MockWebServerResources.ALL_429); try { CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) .interceptors(new Replay429Interceptor(10, 1, true)) .build(); String response = c.executeRequest(Http.GET(c.getBaseUri()).setNumberOfRetries(3)) .responseAsString(); fail("There should be a TooManyRequestsException instead had response " + response); } catch (TooManyRequestsException e) { assertEquals(3, mockWebServer .getRequestCount(), "There should be 3 request attempts"); } }
/** * Test that the configured maximum number of retries is reached and the backoff is of at least * the expected duration. * * @throws Exception */ @TestTemplate public void test429BackoffMaxConfigured() throws Exception { // Always respond 429 for this test mockWebServer.setDispatcher(MockWebServerResources.ALL_429); TestTimer t = TestTimer.startTimer(); try { CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) .interceptors(new Replay429Interceptor(10, 1, true)) .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); fail("There should be a TooManyRequestsException instead had response " + response); } catch (TooManyRequestsException e) { long duration = t.stopTimer(TimeUnit.MILLISECONDS); // 9 backoff periods for 10 attempts: 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 = 511 ms assertTrue(duration >= 511, "The duration should be at least 511 ms, but was " + duration); assertEquals(10, mockWebServer .getRequestCount(), "There should be 10 request attempts"); } }
/** * Test IAM token and cookie flow: * - GET a resource on the cloudant server * - Cookie jar empty, so get IAM token followed by session cookie * - GET now proceeds as normal, expected cookie value is sent in header * * @throws Exception */ @Test public void iamTokenAndCookieSuccessful() throws Exception { // Request sequence // _iam_session request to get Cookie // GET request -> 200 with a Set-Cookie mockWebServer.enqueue(OK_IAM_COOKIE); mockWebServer.enqueue(new MockResponse().setResponseCode(200) .setBody(hello)); mockIamServer.enqueue(new MockResponse().setResponseCode(200).setBody(IAM_TOKEN)); CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) .iamApiKey(IAM_API_KEY) .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); assertEquals(hello, response, "The expected response should be received"); MockWebServerResources.assertMockIamRequests(mockWebServer, mockIamServer); }
@Test public void iamTokenWithValidClientIdAndSecretAndCookieSuccessful() throws Exception { // Request sequence // _iam_session request to get Cookie // GET request -> 200 with a Set-Cookie mockWebServer.enqueue(OK_IAM_COOKIE); mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(hello)); mockIamServer.enqueue(new MockResponse().setResponseCode(200).setBody(IAM_TOKEN)); final String mockIAMUser = "iamServerUser"; final String mockIAMPass = "iamServerPass"; final String mockAuthHeader = "Basic aWFtU2VydmVyVXNlcjppYW1TZXJ2ZXJQYXNz"; CloudantClient c = CloudantClientHelper.newMockWebServerClientBuilder(mockWebServer) .iamApiKey(IAM_API_KEY, mockIAMUser, mockIAMPass) .build(); String response = c.executeRequest(Http.GET(c.getBaseUri())).responseAsString(); assertEquals(hello, response, "The expected response should be received"); assertMockIamCloudantRequests(takeN(mockWebServer, 2)); RecordedRequest recordedIAMRequest = takeN(mockIamServer, 1)[0]; assertMockIamServerRequests(recordedIAMRequest); assertEquals(recordedIAMRequest.getHeader("Authorization"), mockAuthHeader); }