package com.linkedin.databus.client.netty; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import junit.framework.Assert; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.map.JsonMappingException; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.group.ChannelGroup; import org.jboss.netty.channel.group.DefaultChannelGroup; import org.jboss.netty.channel.local.LocalAddress; import org.jboss.netty.handler.codec.http.DefaultHttpChunk; import org.jboss.netty.handler.codec.http.DefaultHttpResponse; import org.jboss.netty.handler.codec.http.HttpChunk; import org.jboss.netty.handler.codec.http.HttpHeaders; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.jboss.netty.handler.codec.http.HttpServerCodec; import org.jboss.netty.handler.codec.http.HttpVersion; import org.jboss.netty.handler.logging.LoggingHandler; import org.jboss.netty.logging.InternalLogLevel; import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.logging.Log4JLoggerFactory; import org.jboss.netty.util.HashedWheelTimer; import org.jboss.netty.util.Timer; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.linkedin.databus.client.DatabusHttpClientImpl; import com.linkedin.databus.client.pub.ServerInfo; import com.linkedin.databus.core.Checkpoint; import com.linkedin.databus.core.CheckpointMult; import com.linkedin.databus.core.DbusEvent; import com.linkedin.databus.core.DbusEventBuffer; import com.linkedin.databus.core.DbusEventBuffer.AllocationPolicy; import com.linkedin.databus.core.DbusEventFactory; import com.linkedin.databus.core.DbusEventKey; import com.linkedin.databus.core.DbusEventV2Factory; import com.linkedin.databus.core.InvalidEventException; import com.linkedin.databus.core.OffsetNotFoundException; import com.linkedin.databus.core.ScnNotFoundException; import com.linkedin.databus.core.data_model.PhysicalPartition; import com.linkedin.databus.core.util.InvalidConfigException; import com.linkedin.databus2.core.container.DatabusHttpHeaders; import com.linkedin.databus2.core.container.request.RegisterResponseEntry; import com.linkedin.databus2.core.container.request.RegisterResponseMetadataEntry; import com.linkedin.databus2.test.ConditionCheck; import com.linkedin.databus2.test.TestUtil; import com.linkedin.databus2.test.container.SimpleObjectCaptureHandler; import com.linkedin.databus2.test.container.SimpleTestServerConnection; public class TestNettyHttpDatabusRelayConnection { static final String SOURCE1_SCHEMA_STR = "{\"name\":\"source1\",\"type\":\"record\",\"fields\":[{\"name\":\"s\",\"type\":\"string\"}]}"; // "{\"name\":\"source2_v1\",\"namespace\":\"test3\",\"type\":\"record\",\"fields\":[{\"type\":\"string\",\"name\":\"strField\"}]}"; static final String KEY1_SCHEMA_STR = "{\"name\":\"key1\",\"type\":\"record\",\"fields\":[{\"name\":\"strField\",\"type\":\"string\"}]}"; static final String METADATA1_SCHEMA_STR = "{\"name\":\"metadata\",\"namespace\":\"test_namespace\",\"type\":\"record\",\"fields\":[{\"name\":\"randomStrField\",\"type\":\"string\"}]}"; static final ExecutorService BOSS_POOL = Executors.newCachedThreadPool(); static final ExecutorService IO_POOL = Executors.newCachedThreadPool(); static final int SERVER_ADDRESS_ID = 14466; static final LocalAddress SERVER_ADDRESS = new LocalAddress(SERVER_ADDRESS_ID); static final Timer NETWORK_TIMER = new HashedWheelTimer(10, TimeUnit.MILLISECONDS); static final ChannelGroup TEST_CHANNELS_GROUP = new DefaultChannelGroup(); static final long DEFAULT_READ_TIMEOUT_MS = 10000; static final long DEFAULT_WRITE_TIMEOUT_MS = 10000; static final String SOURCE1_NAME = "test.source1"; static final ServerInfo RELAY_SERVER_INFO = new ServerInfo("testRelay", "master", new InetSocketAddress(SERVER_ADDRESS_ID), SOURCE1_NAME); static NettyHttpConnectionFactory CONN_FACTORY; static SimpleTestServerConnection _dummyServer; static DbusEventBuffer.StaticConfig _bufCfg; static List<Object> _sourceObjectsList; static List<Object> _keyObjectsList; static List<Object> _metadataObjectsList; static HashMap<String, List<Object>> _fullV4ResponseMap; static int MAX_EVENT_VERSION = DbusEventFactory.DBUS_EVENT_V2; private Level _logLevel = Level.INFO; @BeforeClass public void setUpClass() throws InvalidConfigException { TestUtil.setupLoggingWithTimestampedFile(true, "/tmp/TestNettyHttpDatabusRelayConnection_" , ".log" , Level.INFO); InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory()); _dummyServer = new SimpleTestServerConnection(new DbusEventV2Factory().getByteOrder(), SimpleTestServerConnection.ServerType.NIO); _dummyServer.setPipelineFactory(new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new LoggingHandler(InternalLogLevel.DEBUG), new HttpServerCodec(), new LoggingHandler(InternalLogLevel.DEBUG), new SimpleObjectCaptureHandler()); } }); _dummyServer.start(SERVER_ADDRESS_ID); DatabusHttpClientImpl.Config clientCfgBuilder = new DatabusHttpClientImpl.Config(); clientCfgBuilder.getContainer().setReadTimeoutMs(DEFAULT_READ_TIMEOUT_MS); clientCfgBuilder.getContainer().setWriteTimeoutMs(DEFAULT_WRITE_TIMEOUT_MS); CONN_FACTORY = new NettyHttpConnectionFactory(BOSS_POOL, IO_POOL, null, NETWORK_TIMER, clientCfgBuilder.getContainer().getWriteTimeoutMs(), clientCfgBuilder.getContainer().getReadTimeoutMs(), clientCfgBuilder.getContainer().getBstReadTimeoutMs(), 4, // protocolVersion MAX_EVENT_VERSION, TEST_CHANNELS_GROUP); DbusEventBuffer.Config bufCfgBuilder = new DbusEventBuffer.Config(); bufCfgBuilder.setAllocationPolicy(AllocationPolicy.HEAP_MEMORY.toString()); bufCfgBuilder.setMaxSize(100000); bufCfgBuilder.setScnIndexSize(128); bufCfgBuilder.setAverageEventSize(1); _bufCfg = bufCfgBuilder.build(); initSchemaObjectsLists(); _fullV4ResponseMap = new HashMap<String, List<Object>>(); _fullV4ResponseMap.put(RegisterResponseEntry.SOURCE_SCHEMAS_KEY, _sourceObjectsList); _fullV4ResponseMap.put(RegisterResponseEntry.KEY_SCHEMAS_KEY, _keyObjectsList); _fullV4ResponseMap.put(RegisterResponseMetadataEntry.METADATA_SCHEMAS_KEY, _metadataObjectsList); } @SuppressWarnings("unchecked") private static void initSchemaObjectsLists() { List<RegisterResponseEntry> sourceSchemasList = new ArrayList<RegisterResponseEntry>(); sourceSchemasList.add(new RegisterResponseEntry(1L, (short)1, SOURCE1_SCHEMA_STR)); _sourceObjectsList = (List<Object>)(List<?>)sourceSchemasList; List<RegisterResponseEntry> keySchemasList = new ArrayList<RegisterResponseEntry>(); keySchemasList.add(new RegisterResponseEntry(1L, (short)7, KEY1_SCHEMA_STR)); _keyObjectsList = (List<Object>)(List<?>)keySchemasList; List<RegisterResponseMetadataEntry> metadataSchemasList = new ArrayList<RegisterResponseMetadataEntry>(); byte[] tbdCrc32 = new byte[DbusEvent.CRC32_DIGEST_LEN]; final short schemaVersion = 5; metadataSchemasList.add(new RegisterResponseMetadataEntry(schemaVersion, METADATA1_SCHEMA_STR, tbdCrc32)); _metadataObjectsList = (List<Object>)(List<?>)metadataSchemasList; } @AfterClass public void tearDownClass() { _dummyServer.stop(); BOSS_POOL.shutdownNow(); IO_POOL.shutdownNow(); } @Test public void testHappyPath() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testHappyPath"); //NettyHttpDatabusRelayConnection.LOG.setLevel(Level.DEBUG); DbusEventBuffer buf = createSimpleBuffer(); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); conn.getHandler().getLog().setLevel(Level.DEBUG); Assert.assertEquals(MAX_EVENT_VERSION, conn.getMaxEventVersion()); // verify the version - current DBUS_EVENT_V2 try { runHappyPathIteration(log, buf, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerSourcesDisconnect() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerSourcesDisconnect"); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerSourcesDisconnectIteration(log, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerRegisterDisconnect() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerRegisterDisconnect"); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerRegisterDisconnectIteration(log, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerRegisterReqDisconnect() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerRegisterReqDisconnect"); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerRegisterReqDisconnectIteration(log, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerStreamDisconnect() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerStreamDisconnect"); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerStreamDisconnectIteration(log, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerStreamReqDisconnect() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerStreamReqDisconnect"); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerStreamReqDisconnectIteration(log, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerPartialDisconnect() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerPartialDisconnect"); DbusEventBuffer buf = createSimpleBuffer(); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerPartialStreamIteration(log, buf, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerSourcesTimeout() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerSourcesTimeout"); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerSourcesReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerRegisterTimeout() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerRegisterTimeout"); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerRegisterReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerStreamTimeout() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerStreamTimeout"); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerStreamReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } @Test public void testServerPartialTimeout() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerPartialResponse"); DbusEventBuffer buf = createSimpleBuffer(); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runServerPartialStreamTimeoutIteration(log, buf, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } /** * <p>Test scenario * <p> * * <ul> * <li> relay disconnected on /sources * <li> relay timed out on /sources * <li> relay disconnected on /sources again * <li> relay timed out on /register * <li> relay disconnected on /register * <li> relay timed out on /register again * <li> relay disconnected on /stream * <li> relay disconnected on /stream again * <li> relay succeeded on /stream * <li> relay succeeded on /stream * <li> relay timed out on partial /stream * <li> relay succeeded on /stream * <li> relay timed out on /stream * <li> relay succeeded on /stream * </ul> */ @Test public void testServerFixedScenario() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerFixedScenario"); DbusEventBuffer buf = createSimpleBuffer(); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHappyPath"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); conn.getHandler().getLog().setLevel(_logLevel); try { log.info("********* 1. relay disconnected on /sources ********"); runServerSourcesDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 2. relay timed out on /sources ********"); runServerSourcesReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 3. relay disconnected on /sources ********"); runServerSourcesDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 4. relay timed out on /register ********"); runServerRegisterReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 5. relay disconnected on /register ********"); runServerRegisterDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 6. relay timed out on /register ********"); runServerRegisterReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 7. relay disconnected on /stream ********"); runServerStreamDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 8. relay disconnected on /stream ********"); runServerStreamDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 9. relay success on /stream ********"); runHappyPathIteration(log, buf, callback, remoteExceptionHandler, conn); log.info("********* 10. relay success on /stream ********"); runHappyPathIteration(log, buf, callback, remoteExceptionHandler, conn); log.info("********* 11. relay timed out on partial /stream ********"); runServerPartialStreamTimeoutIteration(log, buf, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 12. relay success on /stream ********"); runHappyPathIteration(log, buf, callback, remoteExceptionHandler, conn); log.info("********* 13. relay timed out on /stream ********"); runServerStreamReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); log.info("********* 14. relay success on /stream ********"); runHappyPathIteration(log, buf, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } /** * <p>Random sequence of test iterations */ @Test public void testServerRandomScenario() throws IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testServerRandomScenario"); log.info("in"); DbusEventBuffer buf = createSimpleBuffer(); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testServerRandomScenario"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); conn.getHandler().getLog().setLevel(_logLevel); try { final int iterNum = 15; Random rng = new Random(); for (int i = 1; i<= iterNum; ++i ) { int testId = rng.nextInt(10); switch (testId) { case 0: { log.info("======> step " + i + ": runHappyPathIteration"); runHappyPathIteration(log, buf, callback, remoteExceptionHandler, conn); break; } case 1: { log.info("======> step " + i + ": runServerSourcesDisconnectIteration"); runServerSourcesDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); break; } case 2: { log.info("======> step " + i + ": runServerSourcesReadTimeoutIteration"); runServerSourcesReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); break; } case 3: { log.info("======> step " + i + ": runServerRegisterDisconnectIteration"); runServerRegisterDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); break; } case 4: { log.info("======> step " + i + ": runServerRegisterReqDisconnectIteration"); runServerRegisterReqDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); break; } case 5: { log.info("======> step " + i + ": runServerRegisterReadTimeoutIteration"); runServerRegisterReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); break; } case 6: { log.info("======> step " + i + ": runServerStreamDisconnectIteration"); runServerStreamDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); break; } case 7: { log.info("======> step " + i + ": runServerStreamReqDisconnectIteration"); runServerStreamReqDisconnectIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); break; } case 8: { log.info("======> step " + i + ": runServerStreamReadTimeoutIteration"); runServerStreamReadTimeoutIteration(log, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); break; } case 9: { log.info("======> step " + i + ": runServerPartialStreamTimeoutIteration"); runServerPartialStreamTimeoutIteration(log, buf, callback, remoteExceptionHandler, conn); conn.getHandler().reset(); break; } default: { Assert.fail("step " + i + ": unknown test id: " + testId); } log.info("======> step " + i + ": complete."); if (0 != testId) { //if it was an iteration with an error, sleep a bit until we make sure the client //channel has been disconnected TestUtil.assertWithBackoff(new ConditionCheck() { @Override public boolean check() { return !conn._channel.isConnected(); } }, "wait for child channel closure ", 1024, log); } } } } finally { conn.close(); callback.shutdown(); log.info("out"); } } @Test public void testHttpRequestLoggingHandlerRequest() throws Exception { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection.testHttpRequestLoggingHandlerRequest"); DbusEventBuffer buf = createSimpleBuffer(); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testHttpRequestLoggingHandlerRequest"); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); try { runHappyPathIteration(log, buf, callback, remoteExceptionHandler, conn); } finally { conn.close(); callback.shutdown(); } } /** * Tests "normal" client-relay protocol v4 happy path: v4 request (hardcoded in * NettyHttpDatabusRelayConnection) leads to v4 response with all three schema types. */ @Test public void testRegisterV4HappyPath_FullV4Response() throws IOException, ScnNotFoundException, OffsetNotFoundException { String responseStr = NettyTestUtils.generateRegisterResponseV4(_fullV4ResponseMap); runRegisterV4("testRegisterV4HappyPath_FullV4Response", "4", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_SUCCESS); } /** * Tests client-relay protocol v4 happy path in which the relay omits the (optional) * key schemas. */ @Test public void testRegisterV4HappyPath_V4ResponseNoKeySchemas() throws IOException, ScnNotFoundException, OffsetNotFoundException { HashMap<String, List<Object>> entries = new HashMap<String, List<Object>>(); entries.put(RegisterResponseEntry.SOURCE_SCHEMAS_KEY, _sourceObjectsList); entries.put(RegisterResponseMetadataEntry.METADATA_SCHEMAS_KEY, _metadataObjectsList); String responseStr = NettyTestUtils.generateRegisterResponseV4(entries); runRegisterV4("testRegisterV4HappyPath_V4ResponseNoKeySchemas", "4", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_SUCCESS); } /** * Tests client-relay protocol v4 happy path in which the relay omits the (optional) * metadata schema(s). */ @Test public void testRegisterV4HappyPath_V4ResponseNoMetadataSchemas() throws IOException, ScnNotFoundException, OffsetNotFoundException { HashMap<String, List<Object>> entries = new HashMap<String, List<Object>>(); entries.put(RegisterResponseEntry.SOURCE_SCHEMAS_KEY, _sourceObjectsList); entries.put(RegisterResponseEntry.KEY_SCHEMAS_KEY, _keyObjectsList); String responseStr = NettyTestUtils.generateRegisterResponseV4(entries); runRegisterV4("testRegisterV4HappyPath_V4ResponseNoMetadataSchemas", "4", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_SUCCESS); } /** * Tests variant of client-relay protocol v4 happy path: v4 request (hardcoded in * NettyHttpDatabusRelayConnection) to old relay leads to v3 response with list of * RegisterResponseEntry. */ @Test public void testRegisterV4HappyPath_V3Response() throws IOException, ScnNotFoundException, OffsetNotFoundException { String responseStr = NettyTestUtils.generateRegisterResponse(new RegisterResponseEntry(1L, (short)1, SOURCE1_SCHEMA_STR)); runRegisterV4("testRegisterV4HappyPath_V3Response", "3", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_SUCCESS); } /** * Tests variant of client-relay protocol v4 happy path: v4 request (hardcoded in * NettyHttpDatabusRelayConnection) to old relay leads to v2 response with list of * RegisterResponseEntry. */ @Test public void testRegisterV4HappyPath_V2Response() throws IOException, ScnNotFoundException, OffsetNotFoundException { String responseStr = NettyTestUtils.generateRegisterResponse(new RegisterResponseEntry(1L, (short)1, SOURCE1_SCHEMA_STR)); runRegisterV4("testRegisterV4HappyPath_V2Response", "2", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_SUCCESS); } /** * Tests client-relay protocol v4 unhappy path: relay sends back a non-numeric * protocol-version header. */ @Test public void testRegisterV4UnhappyPath_NonNumericProtocolVersionHeader() throws IOException, ScnNotFoundException, OffsetNotFoundException { String responseStr = NettyTestUtils.generateRegisterResponseV4(_fullV4ResponseMap); runRegisterV4("testRegisterV4UnhappyPath_NonNumericProtocolVersionHeader", "xyzzy", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR); } /** * Tests client-relay protocol v4 unhappy path: relay sends back a protocol-version * header specifying version 1 (which doesn't exist in this part of the codebase). */ @Test public void testRegisterV4UnhappyPath_InvalidV1Response() throws IOException, ScnNotFoundException, OffsetNotFoundException { String responseStr = NettyTestUtils.generateRegisterResponseV4(_fullV4ResponseMap); runRegisterV4("testRegisterV4UnhappyPath_InvalidV1Response", "1", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR); } /** * Tests client-relay protocol v4 unhappy path: relay sends back a protocol-version * header specifying version 7 (which doesn't yet exist). */ @Test public void testRegisterV4UnhappyPath_InvalidV7Response() throws IOException, ScnNotFoundException, OffsetNotFoundException { String responseStr = NettyTestUtils.generateRegisterResponseV4(_fullV4ResponseMap); runRegisterV4("testRegisterV4UnhappyPath_InvalidV7Response", "7", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR); } /** * Tests client-relay protocol v4 unhappy path: v4 request (hardcoded in * NettyHttpDatabusRelayConnection) leads to claimed v3 response, but not * with expected list of RegisterResponseEntry. */ @Test public void testRegisterV4UnhappyPath_V3ResponseNotList() throws IOException, ScnNotFoundException, OffsetNotFoundException { String responseStr = NettyTestUtils.generateRegisterResponseV4(_fullV4ResponseMap); // wrong type for v3 runRegisterV4("testRegisterV4UnhappyPath_V3ResponseNotList", "3", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR); } /** * Tests client-relay protocol v4 unhappy path: v4 request (hardcoded in * NettyHttpDatabusRelayConnection) leads to claimed v4 response, but not * with expected map of strings to lists of objects. */ @Test public void testRegisterV4UnhappyPath_V4ResponseNotMap() throws IOException, ScnNotFoundException, OffsetNotFoundException { // this generates wrong type for v4: String responseStr = NettyTestUtils.generateRegisterResponse(new RegisterResponseEntry(1L, (short)1, SOURCE1_SCHEMA_STR)); runRegisterV4("testRegisterV4UnhappyPath_V4ResponseNotMap", "4", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR); } /** * Tests client-relay protocol v4 unhappy path: v4 response that doesn't * include mandatory source schemas. */ @Test public void testRegisterV4UnhappyPath_V4ResponseNoSourceSchemas() throws IOException, ScnNotFoundException, OffsetNotFoundException { HashMap<String, List<Object>> entries = new HashMap<String, List<Object>>(); entries.put(RegisterResponseEntry.KEY_SCHEMAS_KEY, _keyObjectsList); entries.put(RegisterResponseMetadataEntry.METADATA_SCHEMAS_KEY, _metadataObjectsList); String responseStr = NettyTestUtils.generateRegisterResponseV4(entries); runRegisterV4("testRegisterV4UnhappyPath_V4ResponseNoSourceSchemas", "4", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR); } /** * Tests client-relay protocol v4 unhappy path: v4 response (map) includes * source-schemas key but with wrong value type. */ @Test public void testRegisterV4UnhappyPath_V4ResponseSourceSchemasWrongListType() throws IOException, ScnNotFoundException, OffsetNotFoundException { List<String> sourceSchemasList = new ArrayList<String>(); sourceSchemasList.add("foobly"); @SuppressWarnings("unchecked") List<Object> sourceObjectsList = (List<Object>)(List<?>)sourceSchemasList; HashMap<String, List<Object>> entries = new HashMap<String, List<Object>>(); entries.put(RegisterResponseEntry.SOURCE_SCHEMAS_KEY, sourceObjectsList); entries.put(RegisterResponseEntry.KEY_SCHEMAS_KEY, _keyObjectsList); entries.put(RegisterResponseMetadataEntry.METADATA_SCHEMAS_KEY, _metadataObjectsList); String responseStr = NettyTestUtils.generateRegisterResponseV4(entries); runRegisterV4("testRegisterV4UnhappyPath_V4ResponseSourceSchemasWrongListType", "4", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR); } /** * Tests client-relay protocol v4 unhappy path: v4 response (map) includes * source-schemas key with correct value type (list of RRE), but list is empty. */ @Test public void testRegisterV4UnhappyPath_V4ResponseSourceSchemasEmptyList() throws IOException, ScnNotFoundException, OffsetNotFoundException { List<RegisterResponseEntry> sourceSchemasList = new ArrayList<RegisterResponseEntry>(); @SuppressWarnings("unchecked") List<Object> sourceObjectsList = (List<Object>)(List<?>)sourceSchemasList; HashMap<String, List<Object>> entries = new HashMap<String, List<Object>>(); entries.put(RegisterResponseEntry.SOURCE_SCHEMAS_KEY, sourceObjectsList); entries.put(RegisterResponseEntry.KEY_SCHEMAS_KEY, _keyObjectsList); entries.put(RegisterResponseMetadataEntry.METADATA_SCHEMAS_KEY, _metadataObjectsList); String responseStr = NettyTestUtils.generateRegisterResponseV4(entries); runRegisterV4("testRegisterV4UnhappyPath_V4ResponseSourceSchemasEmptyList", "4", responseStr, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR); } private void runRegisterV4(final String subtestName, final String protocolVersionHeader, final String responseStr, final TestResponseProcessors.TestConnectionStateMessage.State expectedRegisterState) throws IOException, ScnNotFoundException, OffsetNotFoundException //throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { final Logger log = Logger.getLogger("TestNettyHttpDatabusRelayConnection." + subtestName); //log.setLevel(Level.DEBUG); TestingConnectionCallback callback = TestingConnectionCallback.createAndStart(subtestName); DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler(); final NettyHttpDatabusRelayConnection conn = (NettyHttpDatabusRelayConnection) CONN_FACTORY.createRelayConnection(RELAY_SERVER_INFO, callback, remoteExceptionHandler); Assert.assertEquals(MAX_EVENT_VERSION, conn.getMaxEventVersion()); // verify the version - current DBUS_EVENT_V1 try { // connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); // introspect connection to server Channel channel = conn._channel; final SocketAddress clientAddr = channel.getLocalAddress(); TestUtil.assertWithBackoff(new ConditionCheck() { @Override public boolean check() { return null != _dummyServer.getChildChannel(clientAddr); } }, "client connection established", 1000, log); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); // verify server gets the /sources request HttpResponse sourcesResp = runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); // send /register and check result doRegisterV4(log, callback, remoteExceptionHandler, conn, msg, clientAddr, objCapture, sourcesResp, protocolVersionHeader, responseStr, expectedRegisterState); callback.clearLastMsg(); objCapture.clear(); } finally { conn.close(); callback.shutdown(); } } private void doRegisterV4(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn, TestResponseProcessors.TestConnectionStateMessage msg, SocketAddress clientAddr, SimpleObjectCaptureHandler objCapture, HttpResponse sourcesResp, final String protocolVersionHeader, final String responseStr, final TestResponseProcessors.TestConnectionStateMessage.State expectedRegisterState) throws JsonGenerationException, JsonMappingException, IOException { HttpRequest msgReq; HttpChunk body; objCapture.clear(); conn.requestRegister("1", msg); // calls createRegisterUrl(), which appends protocolVersion=4 // verify server gets the /register request msgReq = captureRequest(objCapture); Assert.assertTrue(msgReq.getUri().startsWith("/register")); Assert.assertTrue(msgReq.getUri().indexOf(DatabusHttpHeaders.PROTOCOL_VERSION_PARAM + "=4") >= 0); sourcesResp.setHeader(DatabusHttpHeaders.DBUS_CLIENT_RELAY_PROTOCOL_VERSION_HDR, protocolVersionHeader); body = new DefaultHttpChunk(ChannelBuffers.wrappedBuffer(responseStr.getBytes(Charset.defaultCharset()))); NettyTestUtils.sendServerResponses(_dummyServer, clientAddr, sourcesResp, body); waitForCallback(callback, expectedRegisterState, log); Assert.assertNull(remoteExceptionHandler.getLastException()); } private void runServerSourcesReadTimeoutIteration(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); Assert.assertTrue(objCapture.waitForMessage(1000, 0)); Object msgObj = objCapture.getMessages().get(0); Assert.assertTrue(msgObj instanceof HttpRequest); HttpRequest msgReq = (HttpRequest)msgObj; Assert.assertTrue(msgReq.getUri().startsWith("/sources")); // now has "?protocolVersion=X" appended //Trigger a read timeout TestUtil.sleep(DEFAULT_READ_TIMEOUT_MS + 100); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.SOURCES_RESPONSE_ERROR, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runServerRegisterReadTimeoutIteration(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); //verify server gets the /source request runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); conn.requestRegister("1", msg); //verify server gets the /register request HttpRequest msgReq = captureRequest(objCapture); Assert.assertTrue(msgReq.getUri().startsWith("/register")); //Trigger a read timeout TestUtil.sleep(DEFAULT_READ_TIMEOUT_MS + 100); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runServerStreamReadTimeoutIteration(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); //verify server gets the /source request HttpResponse sourcesResp = runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); //send /register runHappyPathRegister(log, callback, remoteExceptionHandler, conn, msg, clientAddr, objCapture, sourcesResp); //send partial /stream callback.clearLastMsg(); objCapture.clear(); Checkpoint cp = new Checkpoint(); cp.setFlexible(); CheckpointMult cpm = new CheckpointMult(); cpm.addCheckpoint(PhysicalPartition.ANY_PHYSICAL_PARTITION, cp); conn.requestStream("1", null, 1000, cpm, null, msg); //////// verify server gets the /stream request HttpRequest msgReq = captureRequest(objCapture); Assert.assertTrue(msgReq.getUri().startsWith("/stream")); //Trigger a read timeout TestUtil.sleep(DEFAULT_READ_TIMEOUT_MS + 100); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.STREAM_RESPONSE_ERROR, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runServerPartialStreamTimeoutIteration(final Logger log, DbusEventBuffer buf, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); //verify server gets the /source request HttpResponse sourcesResp = runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); //send /register runHappyPathRegister(log, callback, remoteExceptionHandler, conn, msg, clientAddr, objCapture, sourcesResp); //send partial /stream callback.clearLastMsg(); objCapture.clear(); Checkpoint cp = new Checkpoint(); cp.setFlexible(); CheckpointMult cpm = new CheckpointMult(); cpm.addCheckpoint(PhysicalPartition.ANY_PHYSICAL_PARTITION, cp); conn.requestStream("1", null, 1000, cpm, null, msg); //////// verify server gets the /stream request HttpRequest msgReq = captureRequest(objCapture); Assert.assertTrue(msgReq.getUri().startsWith("/stream")); ////// send back some partial response ChannelBuffer tmpBuf = NettyTestUtils.streamToChannelBuffer(buf, cp, 10000, null); _dummyServer.sendServerResponse(clientAddr, sourcesResp, 1000); _dummyServer.sendServerResponse(clientAddr, new DefaultHttpChunk(tmpBuf), 1000); //Trigger a read timeout TestUtil.sleep(DEFAULT_READ_TIMEOUT_MS + 100); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.STREAM_RESPONSE_SUCCESS, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runServerSourcesDisconnectIteration(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); Assert.assertTrue(objCapture.waitForMessage(1000, 0)); Object msgObj = objCapture.getMessages().get(0); Assert.assertTrue(msgObj instanceof HttpRequest); HttpRequest msgReq = (HttpRequest)msgObj; Assert.assertTrue(msgReq.getUri().startsWith("/sources")); // now has "?protocolVersion=X" appended serverChannel.close(); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.SOURCES_RESPONSE_ERROR, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runServerRegisterDisconnectIteration(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); final SocketAddress finalClientAddr = clientAddr; TestUtil.assertWithBackoff(new ConditionCheck() { @Override public boolean check() { return _dummyServer.getChildChannel(finalClientAddr) != null; } }, "client connected", 100, log); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); //verify server gets the /source request runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); callback.clearLastMsg(); objCapture.clear(); conn.requestRegister("1", msg); //verify server gets the /register request HttpRequest msgReq = captureRequest(objCapture); Assert.assertTrue(msgReq.getUri().startsWith("/register")); serverChannel.close(); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_RESPONSE_ERROR, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runServerRegisterReqDisconnectIteration(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); //verify server gets the /source request runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); callback.clearLastMsg(); objCapture.clear(); serverChannel.close(); conn.requestRegister("1", msg); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_REQUEST_ERROR, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runServerStreamDisconnectIteration(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); //verify server gets the /source request HttpResponse sourcesResp = runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); //send /register runHappyPathRegister(log, callback, remoteExceptionHandler, conn, msg, clientAddr, objCapture, sourcesResp); //send partial /stream callback.clearLastMsg(); objCapture.clear(); Checkpoint cp = new Checkpoint(); cp.setFlexible(); CheckpointMult cpm = new CheckpointMult(); cpm.addCheckpoint(PhysicalPartition.ANY_PHYSICAL_PARTITION, cp); conn.requestStream("1", null, 1000, cpm, null, msg); //////// verify server gets the /stream request HttpRequest msgReq = captureRequest(objCapture); Assert.assertTrue(msgReq.getUri().startsWith("/stream")); serverChannel.close(); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.STREAM_RESPONSE_ERROR, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runServerStreamReqDisconnectIteration(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); //verify server gets the /source request HttpResponse sourcesResp = runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); //send /register runHappyPathRegister(log, callback, remoteExceptionHandler, conn, msg, clientAddr, objCapture, sourcesResp); //send partial /stream callback.clearLastMsg(); objCapture.clear(); serverChannel.close(); Checkpoint cp = new Checkpoint(); cp.setFlexible(); CheckpointMult cpm = new CheckpointMult(); cpm.addCheckpoint(PhysicalPartition.ANY_PHYSICAL_PARTITION, cp); conn.requestStream("1", null, 1000, cpm, null, msg); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.STREAM_REQUEST_ERROR, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runServerPartialStreamIteration(final Logger log, DbusEventBuffer buf, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; SocketAddress clientAddr = channel.getLocalAddress(); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); //verify server gets the /source request HttpResponse sourcesResp = runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); //send /register runHappyPathRegister(log, callback, remoteExceptionHandler, conn, msg, clientAddr, objCapture, sourcesResp); //send partial /stream callback.clearLastMsg(); objCapture.clear(); Checkpoint cp = new Checkpoint(); cp.setFlexible(); CheckpointMult cpm = new CheckpointMult(); cpm.addCheckpoint(PhysicalPartition.ANY_PHYSICAL_PARTITION, cp); conn.requestStream("1", null, 1000, cpm, null, msg); //////// verify server gets the /stream request HttpRequest msgReq = captureRequest(objCapture); Assert.assertTrue(msgReq.getUri().startsWith("/stream")); ////// send back some partial response ChannelBuffer tmpBuf = NettyTestUtils.streamToChannelBuffer(buf, cp, 10000, null); _dummyServer.sendServerResponse(clientAddr, sourcesResp, 1000); _dummyServer.sendServerResponse(clientAddr, new DefaultHttpChunk(tmpBuf), 1000); serverChannel.close(); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.STREAM_RESPONSE_SUCCESS, log); Assert.assertNull(remoteExceptionHandler.getLastException()); Assert.assertEquals(1, callback.getAllMsgs().size()); callback.clearLastMsg(); objCapture.clear(); } private void runHappyPathIteration(final Logger log, DbusEventBuffer buf, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn) throws JsonGenerationException, JsonMappingException, IOException, ScnNotFoundException, OffsetNotFoundException { //connect to server and send /sources TestResponseProcessors.TestConnectionStateMessage msg = new TestResponseProcessors.TestConnectionStateMessage(); conn.requestSources(msg); waitForServerConnection(conn, log); //introspect connection to server Channel channel = conn._channel; final SocketAddress clientAddr = channel.getLocalAddress(); TestUtil.assertWithBackoff(new ConditionCheck() { @Override public boolean check() { return null != _dummyServer.getChildChannel(clientAddr); } }, "client connection established", 1000, log); Channel serverChannel = _dummyServer.getChildChannel(clientAddr); ChannelPipeline serverPipeline = serverChannel.getPipeline(); SimpleObjectCaptureHandler objCapture = (SimpleObjectCaptureHandler)serverPipeline.get("3"); //verify server gets the /source request HttpResponse sourcesResp = runHappyPathSources(log, callback, remoteExceptionHandler, clientAddr, objCapture); //send /register runHappyPathRegister(log, callback, remoteExceptionHandler, conn, msg, clientAddr, objCapture, sourcesResp); //send /stream runHappyPathStream(log, buf, callback, remoteExceptionHandler, conn, msg, clientAddr, objCapture, sourcesResp); callback.clearLastMsg(); objCapture.clear(); } private void runHappyPathStream(final Logger log, DbusEventBuffer buf, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn, TestResponseProcessors.TestConnectionStateMessage msg, SocketAddress clientAddr, SimpleObjectCaptureHandler objCapture, HttpResponse sourcesResp) throws ScnNotFoundException, OffsetNotFoundException, IOException { HttpRequest msgReq; objCapture.clear(); Checkpoint cp = new Checkpoint(); cp.setFlexible(); CheckpointMult cpm = new CheckpointMult(); cpm.addCheckpoint(PhysicalPartition.ANY_PHYSICAL_PARTITION, cp); conn.requestStream("1", null, 1000, cpm, null, msg); //verify server gets the /stream request msgReq = captureRequest(objCapture); Assert.assertTrue(msgReq.getUri().startsWith("/stream")); // verify url construction for adding max event version String expectedVersion = "&" + DatabusHttpHeaders.MAX_EVENT_VERSION + "=" + MAX_EVENT_VERSION; Assert.assertTrue(msgReq.getUri().contains(expectedVersion)); //send back some response ChannelBuffer tmpBuf = NettyTestUtils.streamToChannelBuffer(buf, cp, 10000, null); NettyTestUtils.sendServerResponses(_dummyServer, clientAddr, sourcesResp, new DefaultHttpChunk(tmpBuf)); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.STREAM_RESPONSE_SUCCESS, log); Assert.assertNull(remoteExceptionHandler.getLastException()); //wait for the readable byte channel to process the response and verify nothing has changed TestUtil.sleep(1000); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.STREAM_RESPONSE_SUCCESS, log); Assert.assertNull(remoteExceptionHandler.getLastException()); } private void runHappyPathRegister(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, final NettyHttpDatabusRelayConnection conn, TestResponseProcessors.TestConnectionStateMessage msg, SocketAddress clientAddr, SimpleObjectCaptureHandler objCapture, HttpResponse sourcesResp) throws JsonGenerationException, JsonMappingException, IOException { HttpRequest msgReq; HttpChunk body; objCapture.clear(); conn.requestRegister("1", msg); //verify server gets the /register request msgReq = captureRequest(objCapture); Assert.assertTrue(msgReq.getUri().startsWith("/register")); //send back some response RegisterResponseEntry entry = new RegisterResponseEntry(1L, (short)1, SOURCE1_SCHEMA_STR); String responseStr = NettyTestUtils.generateRegisterResponse(entry); body = new DefaultHttpChunk( ChannelBuffers.wrappedBuffer(responseStr.getBytes(Charset.defaultCharset()))); NettyTestUtils.sendServerResponses(_dummyServer, clientAddr, sourcesResp, body); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.REGISTER_SUCCESS, log); Assert.assertNull(remoteExceptionHandler.getLastException()); } private HttpResponse runHappyPathSources(final Logger log, TestingConnectionCallback callback, DummyRemoteExceptionHandler remoteExceptionHandler, SocketAddress clientAddr, SimpleObjectCaptureHandler objCapture) { Assert.assertTrue(objCapture.waitForMessage(1000, 0)); Object msgObj = objCapture.getMessages().get(0); Assert.assertTrue(msgObj instanceof HttpRequest); HttpRequest msgReq = (HttpRequest)msgObj; Assert.assertTrue(msgReq.getUri().startsWith("/sources")); // now has "?protocolVersion=X" appended callback.clearLastMsg(); objCapture.clear(); //send back some response HttpResponse sourcesResp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); sourcesResp.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); sourcesResp.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); HttpChunk body = new DefaultHttpChunk(ChannelBuffers.wrappedBuffer("[{\"id\":1,\"name\":\"test.source1\"}]".getBytes(Charset.defaultCharset()))); NettyTestUtils.sendServerResponses(_dummyServer, clientAddr, sourcesResp, body); waitForCallback(callback, TestResponseProcessors.TestConnectionStateMessage.State.SOURCES_SUCCESS, log); Assert.assertNull(remoteExceptionHandler.getLastException()); callback.clearLastMsg(); objCapture.clear(); return sourcesResp; } private HttpRequest captureRequest(SimpleObjectCaptureHandler objCapture) { Object msgObj; Assert.assertTrue(objCapture.waitForMessage(1000, 0)); msgObj = objCapture.getMessages().get(0); Assert.assertTrue(msgObj instanceof HttpRequest); HttpRequest msgReq = (HttpRequest)msgObj; return msgReq; } private DbusEventBuffer createSimpleBuffer() { DbusEventBuffer buf = new DbusEventBuffer(_bufCfg); buf.start(0); buf.startEvents(); buf.appendEvent(new DbusEventKey(1), (short)1, (short)1, System.nanoTime(), (short)1, new byte[16], new byte[100], false, null); buf.appendEvent(new DbusEventKey(2), (short)1, (short)1, System.nanoTime(), (short)1, new byte[16], new byte[100], false, null); buf.endEvents(10); return buf; } static void waitForServerConnection(final NettyHttpDatabusRelayConnection conn, final Logger log) { TestUtil.assertWithBackoff(new ConditionCheck() { @Override public boolean check() { return null != conn._channel && conn._channel.isConnected(); } }, "waiting to connect to server", 100000, log); } static void waitForCallback(final TestingConnectionCallback callback, final TestResponseProcessors.TestConnectionStateMessage.State state, final Logger log) { TestUtil.assertWithBackoff(new ConditionCheck() { @Override public boolean check() { TestResponseProcessors.TestConnectionStateMessage lastMsg = callback.getLastMsg(); return null != lastMsg && lastMsg.getState().equals(state); } }, "waiting for state " + state, 1000, log); } } class DummyRemoteExceptionHandler extends RemoteExceptionHandler { Throwable _lastException = null; public DummyRemoteExceptionHandler() { super(null, null, new DbusEventV2Factory()); } @Override public void handleException(Throwable remoteException) throws InvalidEventException, InterruptedException { _lastException = remoteException; } public Throwable getLastException() { return _lastException; } public void resetLastException() { _lastException = null; } }