/* * Copyright © 2014-2015 Cask Data, Inc. * * 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. */ package co.cask.cdap.gateway.router; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.common.io.Locations; import co.cask.cdap.gateway.apps.AppWritingtoStream; import co.cask.cdap.internal.test.AppJarHelper; import com.google.common.collect.Iterables; import com.google.common.io.ByteStreams; import com.ning.http.client.AsyncCompletionHandler; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.AsyncHttpClientConfig; import com.ning.http.client.HttpResponseBodyPart; import com.ning.http.client.Request; import com.ning.http.client.RequestBuilder; import com.ning.http.client.Response; import com.ning.http.client.providers.netty.NettyAsyncHttpProvider; import org.apache.twill.discovery.Discoverable; import org.apache.twill.discovery.DiscoveryService; import org.apache.twill.discovery.DiscoveryServiceClient; import org.apache.twill.discovery.InMemoryDiscoveryService; import org.apache.twill.filesystem.LocalLocationFactory; import org.apache.twill.filesystem.Location; import org.apache.twill.filesystem.LocationFactory; import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.RuleChain; import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Verify the ordering of events in the RouterPipeline. */ public class NettyRouterPipelineTest { private static final Logger LOG = LoggerFactory.getLogger(NettyRouterPipelineTest.class); private static final String HOSTNAME = "127.0.0.1"; private static final int MAX_UPLOAD_BYTES = 10 * 1024 * 1024; private static final String GATEWAY_NAME = Constants.Router.GATEWAY_DISCOVERY_NAME; private static final String SERVICE_NAME = Constants.Service.APP_FABRIC_HTTP; private static final DiscoveryService DISCOVERY_SERVICE = new InMemoryDiscoveryService(); public static final RouterResource ROUTER = new RouterResource(HOSTNAME, DISCOVERY_SERVICE); public static final ServerResource GATEWAY_SERVER = new ServerResource(HOSTNAME, DISCOVERY_SERVICE, SERVICE_NAME); @ClassRule public static final TemporaryFolder TMP_FOLDER = new TemporaryFolder(); @SuppressWarnings("UnusedDeclaration") @ClassRule public static TestRule chain = RuleChain.outerRule(ROUTER).around(GATEWAY_SERVER); @Before public void clearNumRequests() throws Exception { GATEWAY_SERVER.clearNumRequests(); // Wait for both servers of gatewayService to be registered Iterable<Discoverable> discoverables = ((DiscoveryServiceClient) DISCOVERY_SERVICE).discover(SERVICE_NAME); for (int i = 0; i < 50 && Iterables.size(discoverables) != 1; ++i) { TimeUnit.MILLISECONDS.sleep(50); } } @Test public void testChunkRequestSuccess() throws Exception { AsyncHttpClientConfig.Builder configBuilder = new AsyncHttpClientConfig.Builder(); final AsyncHttpClient asyncHttpClient = new AsyncHttpClient( new NettyAsyncHttpProvider(configBuilder.build()), configBuilder.build()); byte [] requestBody = generatePostData(); final Request request = new RequestBuilder("POST") .setUrl(String.format("http://%s:%d%s", HOSTNAME, ROUTER.getServiceMap().get(GATEWAY_NAME), "/v1/upload")) .setContentLength(requestBody.length) .setBody(new ByteEntityWriter(requestBody)) .build(); final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); Future<Void> future = asyncHttpClient.executeRequest(request, new AsyncCompletionHandler<Void>() { @Override public Void onCompleted(Response response) throws Exception { return null; } @Override public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception { //TimeUnit.MILLISECONDS.sleep(RANDOM.nextInt(10)); content.writeTo(byteArrayOutputStream); return super.onBodyPartReceived(content); } }); future.get(); Assert.assertArrayEquals(requestBody, byteArrayOutputStream.toByteArray()); } @Test public void testDeployNTimes() throws Exception { // regression tests for race condition during multiple deploys. deploy(100); } //Deploy word count app n times. private void deploy(int num) throws Exception { String path = String.format("http://%s:%d/v1/deploy", HOSTNAME, ROUTER.getServiceMap().get(GATEWAY_NAME)); LocationFactory lf = new LocalLocationFactory(TMP_FOLDER.newFolder()); Location programJar = AppJarHelper.createDeploymentJar(lf, AppWritingtoStream.class); GATEWAY_SERVER.setExpectedJarBytes(ByteStreams.toByteArray(Locations.newInputSupplier(programJar))); for (int i = 0; i < num; i++) { LOG.info("Deploying {}/{}", i, num); URL url = new URL(path); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); urlConn.setRequestProperty("X-Archive-Name", "Purchase-1.0.0.jar"); urlConn.setRequestMethod("POST"); urlConn.setDoOutput(true); urlConn.setDoInput(true); ByteStreams.copy(Locations.newInputSupplier(programJar), urlConn.getOutputStream()); Assert.assertEquals(200, urlConn.getResponseCode()); urlConn.getInputStream().close(); urlConn.disconnect(); } } private static class ByteEntityWriter implements Request.EntityWriter { private final byte [] bytes; private ByteEntityWriter(byte[] bytes) { this.bytes = bytes; } @Override public void writeEntity(OutputStream out) throws IOException { for (int i = 0; i < MAX_UPLOAD_BYTES; i += ServerResource.CHUNK_SIZE) { out.write(bytes, i, ServerResource.CHUNK_SIZE); } } } private static byte [] generatePostData() { byte [] bytes = new byte [MAX_UPLOAD_BYTES]; for (int i = 0; i < MAX_UPLOAD_BYTES; ++i) { bytes[i] = (byte) i; } return bytes; } }