package voldemort.server.rest; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import voldemort.rest.RestMessageHeaders; import voldemort.rest.RestUtils; import voldemort.server.VoldemortConfig; import voldemort.server.VoldemortServer; import voldemort.versioning.VectorClock; public class RestServerProtocolTests { private static String storeNameStr; private static String key1; private static String urlStr; private static String value1; private static String contentType; private static long timeOut; private static int routingType; private static VectorClock vectorClock; private static String eTag; private static VoldemortServer server; private static VoldemortConfig config; private static Logger logger = Logger.getLogger(RestServerProtocolTests.class); /* * TODO REST-Server: Hard coded storeName and urlStr. They must be retrieved * from config */ @BeforeClass public static void oneTimeSetUp() { storeNameStr = "test"; urlStr = "http://localhost:8085/"; config = VoldemortConfig.loadFromVoldemortHome("config/single_node_rest_server/"); key1 = "The longest key "; vectorClock = new VectorClock(); vectorClock.incrementVersion(config.getNodeId(), System.currentTimeMillis()); eTag = RestUtils.getSerializedVectorClock(vectorClock); value1 = "The longest value"; timeOut = 10000L; contentType = "text"; routingType = 2; server = new VoldemortServer(config); if(!server.isStarted()) server.start(); System.out.println("********************Starting REST Server********************"); } @AfterClass public static void oneTimeCleanUp() { if(server != null && server.isStarted()) { // Not using ServerTestUtils.stopVoldemortServer as test uses config // under git source control, which causes real files to be deleted. server.stop(); } } public HttpURLConnection doPut(String url, String key, String value, String storeName, String originTime, String timeOut, String routingType, String vectorClock, String ContentType, String contentLength) throws IOException { HttpURLConnection conn = createConnection(url, key, storeName, originTime, timeOut, routingType, "POST"); conn.setDoOutput(true); if(vectorClock != null) { conn.setRequestProperty(RestMessageHeaders.X_VOLD_VECTOR_CLOCK, vectorClock); } if(ContentType != null) { conn.setRequestProperty("Content-Type", ContentType); } if(contentLength != null) { conn.setRequestProperty("Content-Length", contentLength); } if(value != null) { OutputStream out = conn.getOutputStream(); out.write(value.getBytes()); out.close(); } return conn; } public HttpURLConnection doDelete(String url, String key, String storeName, String originTime, String timeOut, String routingType, String vectorClock) throws IOException { HttpURLConnection conn = createConnection(url, key, storeName, originTime, timeOut, routingType, "DELETE"); if(vectorClock != null) { conn.setRequestProperty(RestMessageHeaders.X_VOLD_VECTOR_CLOCK, vectorClock); } return conn; } public HttpURLConnection doGet(String url, String key, String storeName, String originTime, String timeOut, String routingType) throws IOException { HttpURLConnection conn = createConnection(url, key, storeName, originTime, timeOut, routingType, "GET"); return conn; } /** * Creates a basic put/get/delete request with common required headers. * * @param urlString * @param key * @param storeName * @param originTime * @param timeOut * @param routingType * @param method * @return * @throws IOException */ public HttpURLConnection createConnection(String urlString, String key, String storeName, String originTime, String timeOut, String routingType, String method) throws IOException { HttpURLConnection conn = null; URL url; String urlStr = urlString; String base64Key = ""; if(storeName != null) { urlStr += storeName + "/"; } if(key != null) { base64Key = new String(Base64.encodeBase64(key.getBytes())); url = new URL(urlStr + base64Key); } else { url = new URL(urlStr); } conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod(method); conn.setDoInput(true); if(originTime != null) { conn.setRequestProperty(RestMessageHeaders.X_VOLD_REQUEST_ORIGIN_TIME_MS, String.valueOf(originTime)); } if(timeOut != null) { conn.setRequestProperty(RestMessageHeaders.X_VOLD_REQUEST_TIMEOUT_MS, String.valueOf(timeOut)); } if(routingType != null) { conn.setRequestProperty(RestMessageHeaders.X_VOLD_ROUTING_TYPE_CODE, String.valueOf(routingType)); } return conn; } public void readErrorMessageFromResponse(HttpURLConnection conn) throws IOException { BufferedReader bufferedReader; bufferedReader = new BufferedReader(new InputStreamReader(conn.getErrorStream())); StringBuilder stringBuilder = new StringBuilder(); String line; while((line = bufferedReader.readLine()) != null) { stringBuilder.append(line); } bufferedReader.close(); logger.info(stringBuilder.toString()); } @Test public void testInvalidRoutingType() { logger.info("********** Testing invalid routing type **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doGet(urlStr, key1, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(6)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e1) { e1.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testNonNumberRoutingType() { logger.info("********** Testing non number Routing Type **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doDelete(urlStr, key1, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), "asdfa", eTag); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e1) { e1.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testMissingTimeOut() { logger.info("********** Testing missing time out **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doPut(urlStr, key1, value1, storeNameStr, String.valueOf(System.currentTimeMillis()), null, String.valueOf(routingType), eTag, contentType, String.valueOf(value1.getBytes().length)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testNonNumberTimeOut() { logger.info("********** Testing non number time out **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doGet(urlStr, key1, storeNameStr, String.valueOf(System.currentTimeMillis()), "asdfasdf", String.valueOf(routingType)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testMissingTimeStamp() { logger.info("********** Testing missing time stamp **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doDelete(urlStr, key1, storeNameStr, null, String.valueOf(timeOut), String.valueOf(routingType), eTag); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testNonNumberTimeStamp() { logger.info("********** Testing non number time out **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doPut(urlStr, key1, value1, storeNameStr, "asdfasdf", String.valueOf(timeOut), String.valueOf(routingType), eTag, contentType, String.valueOf(value1.getBytes().length)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testMissingKey() { logger.info("********** Testing missing key **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doGet(urlStr, null, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testMissingStoreName() { logger.info("********** Testing missing store name **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doDelete(urlStr, key1, null, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), eTag); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testInvalidStoreName() { logger.info("********** Testing invalid store name **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doGet(urlStr, key1, "blahblah", String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testMissingVectorClock() { logger.info("********** Testing missing vector clock **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doPut(urlStr, key1, value1, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), null, contentType, String.valueOf(value1.getBytes().length)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testInvalidVectorClock() { logger.info("********** Testing invalid vector clock **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doDelete(urlStr, key1, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), "gvkjhgvlj"); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 400); conn.disconnect(); } @Test public void testGetWithNonExistingKey() { logger.info("********** Testing get with non existing key **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doGet(urlStr, "non existing key", storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 404); conn.disconnect(); } @Test public void testGetAllWithNonExistingKey() { logger.info("********** Testing get all with all non existing keys **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doGet(urlStr, "non existing key1, non existing key2", storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 404); conn.disconnect(); } @Test public void testObsoleteVersionException() throws IOException { logger.info("********** Testing obsolete version exception **********"); // setUP deleteKeysCreated(key1, eTag); HttpURLConnection conn = null; int responseCode = -1; try { conn = doPut(urlStr, key1, value1, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), eTag, contentType, String.valueOf(value1.getBytes().length)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); if(responseCode != 200 && responseCode != 201) { fail("Initial put failed"); } else { conn.disconnect(); conn = doPut(urlStr, key1, value1, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), eTag, contentType, String.valueOf(value1.getBytes().length)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 412); conn.disconnect(); // cleanup deleteKeysCreated(key1, eTag); } @Test public void testDeleteWithNonExistingKey() { logger.info("********** Testing delete non existing key **********"); HttpURLConnection conn = null; int responseCode = -1; try { conn = doDelete(urlStr, "non existing key", storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), eTag); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 404); conn.disconnect(); } @Test public void testDeleteLowerVersion() throws IOException { logger.info("********** Testing delete lower version **********"); // setup deleteKeysCreated(key1, eTag); HttpURLConnection conn = null; int responseCode = -1; String eTag2 = null; try { conn = doPut(urlStr, key1, value1, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), eTag, contentType, String.valueOf(value1.getBytes().length)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); conn.disconnect(); if(responseCode != 200 && responseCode != 201) { fail("Initial put failed"); } else { VectorClock vc = vectorClock.incremented(config.getNodeId(), System.currentTimeMillis()); eTag2 = RestUtils.getSerializedVectorClock(vc); String value2 = "The next longest value"; conn = doPut(urlStr, key1, value2, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), eTag2, contentType, String.valueOf(value2.getBytes().length)); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); conn.disconnect(); if(responseCode != 201) { fail("Second put failed"); } else { conn = doDelete(urlStr, key1, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), eTag); responseCode = conn.getResponseCode(); logger.info("Response Code: " + responseCode + " Message: " + conn.getResponseMessage()); readErrorMessageFromResponse(conn); } } } catch(IOException e) { e.printStackTrace(); } assertEquals(responseCode, 404); conn.disconnect(); // cleanUP specific to this test case deleteKeysCreated(key1, eTag); deleteKeysCreated(key1, eTag2); } // cleanup method public void deleteKeysCreated(String key, String eTag) throws IOException { HttpURLConnection conn = null; conn = doDelete(urlStr, key, storeNameStr, String.valueOf(System.currentTimeMillis()), String.valueOf(timeOut), String.valueOf(routingType), eTag); conn.getResponseCode(); conn.disconnect(); } }