/** * Copyright 2015 Google Inc. 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. */ package com.google.apphosting.vmruntime.jetty9; import com.google.appengine.api.memcache.MemcacheServicePb.MemcacheIncrementResponse; import com.google.appengine.api.memcache.MemcacheServicePb.MemcacheIncrementResponse.IncrementStatusCode; import com.google.appengine.api.taskqueue.TaskQueuePb.TaskQueueAddRequest; import com.google.appengine.api.taskqueue.TaskQueuePb.TaskQueueBulkAddRequest; import com.google.appengine.api.taskqueue.TaskQueuePb.TaskQueueBulkAddResponse; import com.google.appengine.api.taskqueue.TaskQueuePb.TaskQueueBulkAddResponse.TaskResult; import com.google.appengine.api.taskqueue.TaskQueuePb.TaskQueueServiceError.ErrorCode; import com.google.apphosting.api.ApiBasePb.VoidProto; import com.google.apphosting.api.ApiProxy; //import com.google.apphosting.datastore.DatastoreV3Pb.Transaction; import com.google.apphosting.api.DatastorePb.Transaction; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpVersion; import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; import org.apache.commons.httpclient.methods.PostMethod; import java.net.HttpURLConnection; /** * Misc individual Jetty9 vmengines tests. * */ public class VmRuntimeJettyKitchenSink2Test extends VmRuntimeTestBase { /** * Test that the count servlet was loaded, and that local state is preserved * between requests. * * @throws Exception */ public void testCountLocal() throws Exception { for (int i = 1; i <= 5; i++) { String[] lines = fetchUrl(createUrl("/count?type=local")); assertEquals(1, lines.length); assertEquals("" + i, lines[0].trim()); } } /** * Test that abandoned transactions are aborted when the request completes. * * @throws Exception */ public void testAbandonTransaction() throws Exception { long transactionId = 123456789012345L; FakeableVmApiProxyDelegate fakeApiProxy = new FakeableVmApiProxyDelegate(); ApiProxy.setDelegate(fakeApiProxy); // If the filter is installed we expect 2 requests. // First: a call to datastore_v3/BeginTransaction // Second: a call to datastore_v3/Rollback Transaction transactionResponse = new Transaction(); transactionResponse.setHandle(transactionId); transactionResponse.setApp(PROJECT); fakeApiProxy.addApiResponse(transactionResponse); // Response to datastore_v3/BeginTransaction. fakeApiProxy.addApiResponse(new VoidProto()); // Response to datastore_v3/Rollback. String[] lines = fetchUrl(createUrl("/abandonTxn")); assertEquals(1, lines.length); assertEquals("" + transactionId, lines[0].trim()); assertEquals(2, fakeApiProxy.requests.size()); assertEquals("BeginTransaction", fakeApiProxy.requests.get(0).methodName); assertEquals("Rollback", fakeApiProxy.requests.get(1).methodName); } /** * Test that blob upload requests are intercepted by the blob upload filter. * * @throws Exception */ public void testBlobUpload() throws Exception { String postData = "--==boundary\r\n" + "Content-Type: message/external-body; " + "charset=ISO-8859-1; blob-key=\"blobkey:blob-0\"\r\n" + "Content-Disposition: form-data; " + "name=upload-0; filename=\"file-0.jpg\"\r\n" + "\r\n" + "Content-Type: image/jpeg\r\n" + "Content-Length: 1024\r\n" + "X-AppEngine-Upload-Creation: 2009-04-30 17:12:51.675929\r\n" + "Content-Disposition: form-data; " + "name=upload-0; filename=\"file-0.jpg\"\r\n" + "\r\n" + "\r\n" + "--==boundary\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "Content-Disposition: form-data; name=text1\r\n" + "Content-Length: 10\r\n" + "\r\n" + "Testing.\r\n" + "\r\n" + "\r\n" + "--==boundary--"; HttpClient httpClient = new HttpClient(); httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000); PostMethod blobPost = new PostMethod(createUrl("/upload-blob").toString()); blobPost.addRequestHeader("Content-Type", "multipart/form-data; boundary=\"==boundary\""); blobPost.addRequestHeader("X-AppEngine-BlobUpload", "true"); blobPost.setRequestBody(postData); int httpCode = httpClient.executeMethod(blobPost); assertEquals(302, httpCode); Header redirUrl = blobPost.getResponseHeader("Location"); assertEquals("http://" + getServerHost() + "/serve-blob?key=blobkey:blob-0", redirUrl.getValue()); } /** * Test that the count servlet was loaded, and that memcache calls are * forwarded through the VmApiProxyDelegate. * * @throws Exception */ public void testCountMemcache() throws Exception { // Replace the API proxy delegate so we can fake API responses. FakeableVmApiProxyDelegate fakeApiProxy = new FakeableVmApiProxyDelegate(); ApiProxy.setDelegate(fakeApiProxy); for (int i = 1; i <= 5; i++) { MemcacheIncrementResponse responsePb = MemcacheIncrementResponse.newBuilder() .setIncrementStatus(IncrementStatusCode.OK).setNewValue(i).build(); fakeApiProxy.addApiResponse(responsePb); String[] lines = fetchUrl(createUrl("/count?type=memcache")); assertEquals(1, lines.length); assertEquals("" + i, lines[0].trim()); } } /** * Test that the deferredTask handler is installed. */ public void testDeferredTask() throws Exception { // Replace the API proxy delegate so we can fake API responses. FakeableVmApiProxyDelegate fakeApiProxy = new FakeableVmApiProxyDelegate(); ApiProxy.setDelegate(fakeApiProxy); // Add a api response so the task queue api is happy. TaskQueueBulkAddResponse taskAddResponse = new TaskQueueBulkAddResponse(); TaskResult taskResult = taskAddResponse.addTaskResult(); taskResult.setResult(ErrorCode.OK.getValue()); taskResult.setChosenTaskName("abc"); fakeApiProxy.addApiResponse(taskAddResponse); // Issue a deferredTaskRequest with payload. String testData = "0987654321acbdefghijklmn"; String[] lines = fetchUrl(createUrl("/testTaskQueue?deferredTask=1&deferredData=" + testData)); TaskQueueBulkAddRequest request = new TaskQueueBulkAddRequest(); request.parseFrom(fakeApiProxy.getLastRequest().requestData); assertEquals(1, request.addRequestSize()); TaskQueueAddRequest addRequest = request.getAddRequest(0); assertEquals(TaskQueueAddRequest.RequestMethod.POST.getValue(), addRequest.getMethod()); // Pull out the request and fire it at the app. HttpClient httpClient = new HttpClient(); httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(30000); PostMethod post = new PostMethod(createUrl(addRequest.getUrl()).toString()); post.getParams().setVersion(HttpVersion.HTTP_1_0); // Add the required Task queue header, plus any headers from the request. post.addRequestHeader("X-AppEngine-QueueName", "1"); for (TaskQueueAddRequest.Header header : addRequest.headers()) { post.addRequestHeader(header.getKey(), header.getValue()); } post.setRequestEntity(new ByteArrayRequestEntity(addRequest.getBodyAsBytes())); int httpCode = httpClient.executeMethod(post); assertEquals(HttpURLConnection.HTTP_OK, httpCode); // Verify that the task was handled and that the payload is correct. lines = fetchUrl(createUrl("/testTaskQueue?getLastPost=1")); assertEquals("deferredData:" + testData, lines[lines.length - 1]); } }