/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.wink.itest.request; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Request; import junit.framework.TestCase; import org.apache.commons.httpclient.HttpException; import org.apache.wink.client.ClientResponse; import org.apache.wink.client.Resource; import org.apache.wink.client.RestClient; import org.apache.wink.test.integration.ServerEnvironmentInfo; public class WinkRequestMethodsTest extends TestCase { final private static SimpleDateFormat rfc1123Format = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH); final private static SimpleDateFormat rfc850Format = new SimpleDateFormat( "EEEE, dd-MMM-yy HH:mm:ss zzz", Locale.ENGLISH); final private static SimpleDateFormat asctimeDateFormat = new SimpleDateFormat( "EEE MMM dd HH:mm:ss yyyy", Locale.ENGLISH); final private static SimpleDateFormat asctimeDateFormatWithOneDigit = new SimpleDateFormat( "EEE MMM d HH:mm:ss yyyy", Locale.ENGLISH); { /* * the implementation allows you to set a different time zone on the * requests for If-Modified-Since headers and it will do the * "right thing" (this is more leniant). However, asctime does not have * a time zone specified so it is assumed that all datetimes are in * GMT/UTC so the tests have to assume that the GMT time zone is used. */ asctimeDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); asctimeDateFormatWithOneDigit.setTimeZone(TimeZone.getTimeZone("GMT")); } private static String getBaseURI() { if (ServerEnvironmentInfo.isRestFilterUsed()) { return ServerEnvironmentInfo.getBaseURI(); } return ServerEnvironmentInfo.getBaseURI() + "/request"; } protected RestClient client; @Override public void setUp() { client = new RestClient(); } /** * Tests the {@link Request#evaluatePreconditions(Date)} that uses the * <code>If-Modified-Since</code> header and the RFC 1123 date format. * * @throws HttpException * @throws IOException */ public void testEvaluateDateIfModifiedSinceUsingRFC1123Format() throws HttpException, IOException { checkIfModifiedSinceUsingSuppliedDateFormat(rfc1123Format); } /** * Tests the {@link Request#evaluatePreconditions(Date)} that uses the * <code>If-Modified-Since</code> header and the RFC 850 date format. * * @throws HttpException * @throws IOException */ public void testEvaluateDateIfModifiedSinceUsingRFC850Format() throws HttpException, IOException { checkIfModifiedSinceUsingSuppliedDateFormat(rfc850Format); } /** * Tests the {@link Request#evaluatePreconditions(Date)} that uses the * <code>If-Modified-Since</code> header and the Asctime date format. * * @throws HttpException * @throws IOException */ public void testEvaluateDateIfModifiedSinceUsingAscTimeFormat() throws HttpException, IOException { SimpleDateFormat formatter = (Calendar.getInstance().get(Calendar.DAY_OF_MONTH) < 10) ? asctimeDateFormatWithOneDigit : asctimeDateFormat; checkIfModifiedSinceUsingSuppliedDateFormat(formatter); } private void checkIfModifiedSinceUsingSuppliedDateFormat(SimpleDateFormat formatter) throws IOException, HttpException { Date d2 = new Date(System.currentTimeMillis() - 120000); Date d = new Date(System.currentTimeMillis() - 60000); /* * get the time zone for the server */ Resource dateResource = client.resource(getBaseURI() + "/context/request/timezone"); ClientResponse response = dateResource.get(); assertEquals(200, response.getStatusCode()); /* * sets a last modified date */ dateResource = client.resource(getBaseURI() + "/context/request/date"); DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH); // DateFormat.getDateTimeInstance(); response = dateResource.contentType("text/string").put(dateFormat.format(d)); assertEquals(204, response.getStatusCode()); formatter.setTimeZone(TimeZone.getTimeZone("GMT")); response = dateResource.header(HttpHeaders.IF_MODIFIED_SINCE, formatter.format(d)).get(); /* * verifies that if the exact date is sent in and used in * If-Modified-Since header, then the server will be ok and that it will * return 304 */ assertEquals(304, response.getStatusCode()); /* * verifies that if no If-Modified-Since header is sent, then the server * will be ok and the Request instance won't build a response. */ dateResource = client.resource(getBaseURI() + "/context/request/date"); response = dateResource.get(); assertEquals(200, response.getStatusCode()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals("the date: " + rfc1123Format.format(d), response.getEntity(String.class)); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals(rfc1123Format.format(d), response.getHeaders() .getFirst(HttpHeaders.LAST_MODIFIED)); rfc1123Format.setTimeZone(TimeZone.getDefault()); /* * verifies that using Last-Modified response header sent by server as * If-Modified-Since request header, then the server will return a 304 */ String lastModified = response.getHeaders().getFirst(HttpHeaders.LAST_MODIFIED); dateResource = client.resource(getBaseURI() + "/context/request/date"); response = dateResource.header(HttpHeaders.IF_MODIFIED_SINCE, lastModified).get(); assertEquals(304, response.getStatusCode()); /* * verifies that using a If-Modified-Since earlier than the * Last-Modified response header sent by server then the server will * return a 200 with entity */ formatter.setTimeZone(TimeZone.getTimeZone("GMT")); dateResource = client.resource(getBaseURI() + "/context/request/date"); response = dateResource.header(HttpHeaders.IF_MODIFIED_SINCE, formatter.format(d2)).get(); assertEquals(200, response.getStatusCode()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals("the date: " + rfc1123Format.format(d), response.getEntity(String.class)); rfc1123Format.setTimeZone(TimeZone.getDefault()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals(rfc1123Format.format(d), response.getHeaders() .getFirst(HttpHeaders.LAST_MODIFIED)); rfc1123Format.setTimeZone(TimeZone.getDefault()); /* * verifies that using a If-Modified-Since later than the Last-Modified * response header sent by server, then the server will return a 304 */ formatter.setTimeZone(TimeZone.getTimeZone("GMT")); dateResource = client.resource(getBaseURI() + "/context/request/date"); response = dateResource.header(HttpHeaders.IF_MODIFIED_SINCE, formatter.format(new Date())).get(); assertEquals(304, response.getStatusCode()); } /** * Tests the {@link Request#evaluatePreconditions(Date)} that uses the * <code>If-Unmodified-Since</code> header using RFC 1123. * * @throws HttpException * @throws IOException */ public void testEvaluateDateIfUnmodifiedSinceUsingRFC1123() throws HttpException, IOException { checkIfUnmodifiedSinceUsingSuppliedDateFormat(rfc1123Format); } /** * Tests the {@link Request#evaluatePreconditions(Date)} that uses the * <code>If-Unmodified-Since</code> header using RFC 850. * * @throws HttpException * @throws IOException */ public void testEvaluateDateIfUnmodifiedSinceUsingRFC850() throws HttpException, IOException { checkIfUnmodifiedSinceUsingSuppliedDateFormat(rfc850Format); } /** * Tests the {@link Request#evaluatePreconditions(Date)} that uses the * <code>If-Unmodified-Since</code> header using Asctime. * * @throws HttpException * @throws IOException */ public void testEvaluateDateIfUnmodifiedSinceUsingAscTime() throws HttpException, IOException { SimpleDateFormat dateFormat = (Calendar.getInstance().get(Calendar.DAY_OF_MONTH) < 10) ? asctimeDateFormatWithOneDigit : asctimeDateFormat; checkIfUnmodifiedSinceUsingSuppliedDateFormat(dateFormat); } private void checkIfUnmodifiedSinceUsingSuppliedDateFormat(SimpleDateFormat formatter) throws IOException, HttpException { Date d2 = new Date(System.currentTimeMillis() - 120000); Date d = new Date(System.currentTimeMillis() - 60000); /* * get the time zone for the server */ ClientResponse response = client.resource(getBaseURI() + "/context/request/timezone").get(); assertEquals(200, response.getStatusCode()); DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH); // DateFormat.getDateTimeInstance(); response = client.resource(getBaseURI() + "/context/request/date").contentType("text/string") .put(dateFormat.format(d)); assertEquals(204, response.getStatusCode()); /* * verifies that if the exact date is sent in and used in * If-Unmodified-Since header, then the server will be ok and that it * will return 200 */ formatter.setTimeZone(TimeZone.getTimeZone("GMT")); response = client.resource(getBaseURI() + "/context/request/date") .header(HttpHeaders.IF_UNMODIFIED_SINCE, formatter.format(d)).get(); assertEquals(200, response.getStatusCode()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals("the date: " + rfc1123Format.format(d), response.getEntity(String.class)); rfc1123Format.setTimeZone(TimeZone.getDefault()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals(rfc1123Format.format(d), response.getHeaders().getFirst("Last-Modified")); rfc1123Format.setTimeZone(TimeZone.getDefault()); /* * verifies that if no If-Unmodified-Since header is sent, then the * server will be ok and the Request instance won't build a response. */ response = client.resource(getBaseURI() + "/context/request/date").get(); assertEquals(200, response.getStatusCode()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals("the date: " + rfc1123Format.format(d), response.getEntity(String.class)); rfc1123Format.setTimeZone(TimeZone.getDefault()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals(rfc1123Format.format(d), response.getHeaders() .getFirst(HttpHeaders.LAST_MODIFIED)); rfc1123Format.setTimeZone(TimeZone.getDefault()); /* * verifies that using Last-Modified response header sent by server as * If-Unmodified-Since request header, then the server will return the * entity */ String lastModified = response.getHeaders().getFirst(HttpHeaders.LAST_MODIFIED); response = client.resource(getBaseURI() + "/context/request/date") .header(HttpHeaders.IF_UNMODIFIED_SINCE, lastModified).get(); assertEquals(200, response.getStatusCode()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals("the date: " + rfc1123Format.format(d), response.getEntity(String.class)); rfc1123Format.setTimeZone(TimeZone.getDefault()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals(rfc1123Format.format(d), response.getHeaders() .getFirst(HttpHeaders.LAST_MODIFIED)); rfc1123Format.setTimeZone(TimeZone.getDefault()); /* * verifies that using a If-Unmodified-Since earlier than the * Last-Modified response header sent by server then the server will * return a 412 */ formatter.setTimeZone(TimeZone.getTimeZone("GMT")); response = client.resource(getBaseURI() + "/context/request/date") .header(HttpHeaders.IF_UNMODIFIED_SINCE, formatter.format(d2)).get(); assertEquals(412, response.getStatusCode()); formatter.setTimeZone(TimeZone.getTimeZone("GMT")); response = client.resource(getBaseURI() + "/context/request/date") .header(HttpHeaders.IF_UNMODIFIED_SINCE, formatter.format(new Date())).get(); /* * verifies that using a If-Unmodified-Since later than the * Last-Modified response header sent by server, then the server will * return 200 and the entity */ assertEquals(200, response.getStatusCode()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals("the date: " + rfc1123Format.format(d), response.getEntity(String.class)); rfc1123Format.setTimeZone(TimeZone.getDefault()); rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); assertEquals(rfc1123Format.format(d), response.getHeaders() .getFirst(HttpHeaders.LAST_MODIFIED)); rfc1123Format.setTimeZone(TimeZone.getDefault()); } /** * Tests the * {@link Request#evaluatePreconditions(javax.ws.rs.core.EntityTag)} that * uses the <code>If-Match</code> header and a strong ETag. * * @throws HttpException * @throws IOException */ public void testEvaluateEtagIfMatchStrong() throws HttpException, IOException { try { checkETagIfMatch("\"myentitytagABCXYZ\"", false); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); } } /** * Tests the * {@link Request#evaluatePreconditions(javax.ws.rs.core.EntityTag)} that * uses the <code>If-Match</code> header and a weak ETag. * * @throws HttpException * @throws IOException */ public void testEvaluateEtagIfMatchWeak() throws HttpException, IOException { try { checkETagIfMatch("\"myentitytagABCXYZ\"", true); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); } } /** * Tests the * {@link Request#evaluatePreconditions(javax.ws.rs.core.EntityTag)} that * uses the <code>If-Match</code> header. * * @throws HttpException * @throws IOException */ private void checkETagIfMatch(String etag, boolean isEntityTagWeak) throws HttpException, IOException { final String justTheTag = etag; final String setETag = isEntityTagWeak ? "W/" + justTheTag : justTheTag; String isWeak = isEntityTagWeak ? "true" : "false"; ClientResponse response = client.resource(getBaseURI() + "/context/request/etag").contentType("text/string") .put(setETag); assertEquals(204, response.getStatusCode()); response = client.resource(getBaseURI() + "/context/request/etag").header(HttpHeaders.IF_MATCH, setETag).get(); assertEquals(200, response.getStatusCode()); assertEquals("the etag: " + justTheTag + isWeak, response.getEntity(String.class)); /* * verifies that a request without an If-Match header will still proceed */ response = client.resource(getBaseURI() + "/context/request/etag").get(); assertEquals(200, response.getStatusCode()); assertEquals("the etag: " + justTheTag + isWeak, response.getEntity(String.class)); /* * verifies that a misquoted entity tag is not a valid entity tag */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_MATCH, setETag.substring(1, setETag.length() - 1)).get(); assertEquals(400, response.getStatusCode()); /* * verifies that a misquoted entity tag is not a valid entity tag */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_MATCH, setETag.substring(0, setETag.length() - 1)).get(); assertEquals(400, response.getStatusCode()); /* * verifies that a misquoted entity tag is not a valid entity tag */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_MATCH, setETag.substring(1, setETag.length())).get(); assertEquals(400, response.getStatusCode()); /* * verifies that if an etag is sent that does not match the server etag, * that a 412 is returned */ response = client.resource(getBaseURI() + "/context/request/etag").header(HttpHeaders.IF_MATCH, "\"someothervalue\"") .get(); assertEquals(412, response.getStatusCode()); /* * verifies that if multiple etags are sent that do not match the server * etag, that a 412 is returned */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_MATCH, "\"austin\", \"powers\"").get(); assertEquals(412, response.getStatusCode()); /* * verifies that if multiple etags are sent that do not match the server * etag, that a 412 is returned */ response = client.resource(getBaseURI() + "/context/request/etag").header(HttpHeaders.IF_MATCH, "\"austin\", " + setETag + " , \"powers\"") .get(); assertEquals(200, response.getStatusCode()); assertEquals("the etag: " + justTheTag + isWeak, response.getEntity(String.class)); } /** * Tests the * {@link Request#evaluatePreconditions(javax.ws.rs.core.EntityTag)} that * uses the <code>If-None-Match</code> header with strong entity tag. * * @throws HttpException * @throws IOException */ public void testEvaluateEtagIfNoneMatchStrong() throws HttpException, IOException { checkETagIfNoneMatch("\"myentitytagABCXYZ\"", false); } /** * Tests the * {@link Request#evaluatePreconditions(javax.ws.rs.core.EntityTag)} that * uses the <code>If-None-Match</code> header with weak entity tag. * * @throws HttpException * @throws IOException */ public void testEvaluateEtagIfNoneMatchWeak() throws HttpException, IOException { checkETagIfNoneMatch("\"myentitytagABCXYZ\"", true); } /** * Tests the * {@link Request#evaluatePreconditions(javax.ws.rs.core.EntityTag)} that * uses the <code>If-None-Match</code> header. * * @throws HttpException * @throws IOException */ private void checkETagIfNoneMatch(String etag, boolean isEntityTagWeak) throws HttpException, IOException { final String justTheTag = etag; final String setETag = isEntityTagWeak ? "W/" + justTheTag : justTheTag; String isWeak = isEntityTagWeak ? "true" : "false"; /* * sets an entity tag */ ClientResponse response = client.resource(getBaseURI() + "/context/request/etag").contentType("text/string") .put(setETag); assertEquals(204, response.getStatusCode()); /* * verifies that if the exact etag is sent in, then the response is a * 304 */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, setETag).get(); assertEquals(304, response.getStatusCode()); assertEquals(setETag, response.getHeaders().getFirst(HttpHeaders.ETAG)); /* * verifies that if a "*" etag is sent in, then the response returns a * 304 */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, "\"*\"").get(); assertEquals(304, response.getStatusCode()); assertEquals(setETag, response.getHeaders().getFirst(HttpHeaders.ETAG)); /* * verifies that if a "*" etag is sent in, then the response returns a * 412 */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, "\"*\"").post(null); assertEquals(412, response.getStatusCode()); assertEquals(setETag, response.getHeaders().getFirst(HttpHeaders.ETAG)); /* * verifies that if the set etag is sent in, then the response returns a * 412 */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, setETag).post(null); assertEquals(412, response.getStatusCode()); assertEquals(setETag, response.getHeaders().getFirst(HttpHeaders.ETAG)); /* * verifies that a request without an If-None-Match header will still * proceed */ response = client.resource(getBaseURI() + "/context/request/etag").get(); assertEquals(200, response.getStatusCode()); assertEquals("the etag: " + justTheTag + isWeak, response.getEntity(String.class)); /* * verifies that a request without an If-None-Match header will still * proceed */ response = client.resource(getBaseURI() + "/context/request/etag").get(); assertEquals(200, response.getStatusCode()); assertEquals("the etag: " + justTheTag + isWeak, response.getEntity(String.class)); /* * verifies that an unquoted entity tag is invalid */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, setETag.substring(1, setETag.length() - 1)) .get(); assertEquals(400, response.getStatusCode()); /* * verifies that a misquoted entity tag is invalid */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, setETag.substring(0, setETag.length() - 1)) .get(); assertEquals(400, response.getStatusCode()); /* * verifies that a misquoted entity tag is invalid */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, setETag.substring(1, setETag.length())).get(); assertEquals(400, response.getStatusCode()); /* * verifies that if an etag is sent that does not match the server etag, * that request is allowed to proceed */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, "\"someothervalue\"").get(); assertEquals(200, response.getStatusCode()); assertEquals("the etag: " + justTheTag + isWeak, response.getEntity(String.class)); /* * verifies that if multiple etags are sent that do not match the server * etag, that the request is allowed to proceed */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, "\"austin\", \"powers\"").get(); assertEquals(200, response.getStatusCode()); assertEquals("the etag: " + justTheTag + isWeak, response.getEntity(String.class)); /* * verifies that if multiple etags are sent that do not match the server * etag, then a 200 and the request entity is returned */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, "\"austin\", \"powers\"").post(null); assertEquals(200, response.getStatusCode()); assertEquals("the etag: " + justTheTag + isWeak, response.getEntity(String.class)); /* * verifies that if multiple etags are sent that do match the server * etag, that a 304 is returned */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, "\"austin\", " + setETag + " , \"powers\"") .get(); assertEquals(304, response.getStatusCode()); assertEquals(setETag, response.getHeaders().getFirst(HttpHeaders.ETAG)); /* * verifies that a request with an If-None-Match header will fail */ response = client.resource(getBaseURI() + "/context/request/etag") .header(HttpHeaders.IF_NONE_MATCH, "\"austin\", " + setETag + " , \"powers\"") .post(null); assertEquals(412, response.getStatusCode()); assertEquals(setETag, response.getHeaders().getFirst(HttpHeaders.ETAG)); } // TODO: add selectVariant tests by querying the various // /context/request/variant/* paths }