/******************************************************************************* * 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.server.internal; import java.util.Date; import java.util.GregorianCalendar; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.EntityTag; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.ext.RuntimeDelegate; import javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate; import org.apache.wink.common.internal.utils.HttpDateParser; import org.apache.wink.server.internal.servlet.MockServletInvocationTest; import org.apache.wink.test.mock.MockRequestConstructor; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; public class PreconditionsTest extends MockServletInvocationTest { private static final String THE_CONTENT = ">>>The Content<<<"; private final static HeaderDelegate<EntityTag> etagHeaderDelegate = RuntimeDelegate .getInstance() .createHeaderDelegate(EntityTag.class); @Override protected Class<?>[] getClasses() { return new Class[] {ConditionalGetResource.class}; } @Path("get/{variable}") public static class ConditionalGetResource { @GET @Produces("text/plain") public Object get(@Context Request request, @PathParam("variable") String variable) { Date lastModified = null; try { lastModified = new Date(Long.parseLong(variable)); } catch (NumberFormatException e) { } ResponseBuilder evaluatePreconditions = null; if (lastModified != null) { evaluatePreconditions = request.evaluatePreconditions(lastModified); } else { evaluatePreconditions = request.evaluatePreconditions(etagHeaderDelegate.fromString("\"" + variable + "\"")); } if (evaluatePreconditions != null) { return evaluatePreconditions.build(); } return THE_CONTENT; } @GET @Path("null") @Produces("text/plain") public Object getNull(@Context Request request) { ResponseBuilder evaluatePreconditions = request.evaluatePreconditions(new Date()); if (evaluatePreconditions != null) { return evaluatePreconditions.build(); } return THE_CONTENT; } @GET @Produces("text/plain") @Path("etag/{etag}") public Object get2(@Context Request request, @PathParam("variable") String date, @PathParam("etag") String etag) { ResponseBuilder evaluatePreconditions = request.evaluatePreconditions(new Date(Long.parseLong(date)), etagHeaderDelegate .fromString("\"" + etag + "\"")); if (evaluatePreconditions != null) { return evaluatePreconditions.build(); } return THE_CONTENT; } @PUT @Produces("text/plain") @Path("etag/{etag}") public Object put2(@Context Request request, @PathParam("variable") String date, @PathParam("etag") String etag) { ResponseBuilder evaluatePreconditions = request.evaluatePreconditions(new Date(Long.parseLong(date)), etagHeaderDelegate .fromString("\"" + etag + "\"")); if (evaluatePreconditions != null) { return evaluatePreconditions.build(); } return THE_CONTENT; } @PUT @Produces("text/plain") public Object put(@Context Request request, @PathParam("variable") String variable) { Date lastModified = null; try { lastModified = new Date(Long.parseLong(variable)); } catch (NumberFormatException e) { } ResponseBuilder evaluatePreconditions = null; if (lastModified != null) { evaluatePreconditions = request.evaluatePreconditions(lastModified); } else { evaluatePreconditions = request.evaluatePreconditions(etagHeaderDelegate.fromString("\"" + variable + "\"")); } if (evaluatePreconditions != null) { return evaluatePreconditions.build(); } return THE_CONTENT; } @GET @Produces("text/plain") @Path("nonexistyet") public Object getResourceDoesNotExistYet(@Context Request request) { ResponseBuilder evaluatePreconditions = request.evaluatePreconditions(); if (evaluatePreconditions != null) { return evaluatePreconditions.build(); } return THE_CONTENT; } } // public void testNormalGet() throws Exception { MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/0/null", "*/*"); MockHttpServletResponse response = invoke(request); assertEquals("status", 200, response.getStatus()); assertEquals("content", THE_CONTENT, response.getContentAsString()); } public void testConditionModified() throws Exception { Date date_to_return = new GregorianCalendar(2007, 11, 06, 10, 0, 0).getTime(); Date modified_since = new GregorianCalendar(2007, 11, 07, 10, 0, 0).getTime(); // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + String .valueOf(date_to_return.getTime()), "*/*"); request.addHeader(HttpHeaders.IF_MODIFIED_SINCE, HttpDateParser.toHttpDate(modified_since)); MockHttpServletResponse response = invoke(request); assertEquals("status", 304, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + String .valueOf(date_to_return.getTime()), "*/*"); request.addHeader(HttpHeaders.IF_MODIFIED_SINCE, HttpDateParser.toHttpDate(modified_since)); response = invoke(request); assertEquals("status", 304, response.getStatus()); } public void testConditionNotModified() throws Exception { Date date_to_return = new GregorianCalendar(2007, 11, 07, 10, 0, 0).getTime(); Date modified_since = new GregorianCalendar(2007, 11, 06, 10, 0, 0).getTime(); // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + String .valueOf(date_to_return.getTime()), "*/*"); request.addHeader(HttpHeaders.IF_MODIFIED_SINCE, HttpDateParser.toHttpDate(modified_since)); MockHttpServletResponse response = invoke(request); assertEquals("status", 200, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + String .valueOf(date_to_return.getTime()), "*/*"); request.addHeader(HttpHeaders.IF_MODIFIED_SINCE, HttpDateParser.toHttpDate(modified_since)); response = invoke(request); assertEquals("status", 200, response.getStatus()); } public void testConditionModifiedAndMatches() throws Exception { Date date_to_return = new GregorianCalendar(2007, 11, 07, 10, 0, 0).getTime(); Date modified_since = new GregorianCalendar(2007, 11, 06, 10, 0, 0).getTime(); String etag = "blablabla"; // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + String .valueOf(date_to_return.getTime()) + "/etag/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_MODIFIED_SINCE, HttpDateParser.toHttpDate(modified_since)); request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"notmatch\""); MockHttpServletResponse response = invoke(request); assertEquals("status", 200, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + String .valueOf(date_to_return.getTime()) + "/etag/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_MODIFIED_SINCE, HttpDateParser.toHttpDate(modified_since)); request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"notmatch\""); response = invoke(request); assertEquals("status", 200, response.getStatus()); } public void testConditionUnModifiedAndMatches() throws Exception { Date date_to_return = new GregorianCalendar(2007, 11, 07, 10, 0, 0).getTime(); Date modified_since = new GregorianCalendar(2007, 11, 06, 10, 0, 0).getTime(); String etag = "blablabla"; // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + String .valueOf(date_to_return.getTime()) + "/etag/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_UNMODIFIED_SINCE, HttpDateParser .toHttpDate(modified_since)); MockHttpServletResponse response = invoke(request); assertEquals("status", 412, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + String .valueOf(date_to_return.getTime()) + "/etag/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_MATCH, "\"" + etag + "\""); response = invoke(request); assertEquals("status", 200, response.getStatus()); } public void testConditionIfMatches() throws Exception { String etag = "blablabla"; // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_MATCH, "\"" + etag + "\""); MockHttpServletResponse response = invoke(request); assertEquals("status", 200, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_MATCH, "\"" + etag + "\""); response = invoke(request); assertEquals("status", 200, response.getStatus()); } /** * ensure multiple etags are supported */ public void testConditionIfMatchesMultipleOnSingleHeader() throws Exception { String etag = "blablabla"; // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_MATCH, "\"atlantic\",\"" + etag + "\",\"pacific\""); MockHttpServletResponse response = invoke(request); assertEquals("status", 200, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_MATCH, "\"atlantic\",\"" + etag + "\",\"pacific\""); response = invoke(request); assertEquals("status", 200, response.getStatus()); } /** * ETags not wrapped in quotes are invalid. See * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.11 */ public void testConditionIfMatchesUnquoted() throws Exception { String etag = "blablabla"; MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); // no quotes request.addHeader(HttpHeaders.IF_MATCH, etag); MockHttpServletResponse response = invoke(request); assertEquals("status", 400, response.getStatus()); request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); // beginning quote only request.addHeader(HttpHeaders.IF_MATCH, "\"" + etag); response = invoke(request); assertEquals("status", 400, response.getStatus()); request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); // end quote only request.addHeader(HttpHeaders.IF_MATCH, etag + "\""); response = invoke(request); assertEquals("status", 400, response.getStatus()); } /** * ETags not wrapped in quotes are invalid. See * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.11 */ public void testConditionIfNoneMatchesUnquoted() throws Exception { String etag = "blablabla"; MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); // no quotes request.addHeader(HttpHeaders.IF_NONE_MATCH, etag); MockHttpServletResponse response = invoke(request); assertEquals("status", 400, response.getStatus()); request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); // beginning quote only request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"" + etag); response = invoke(request); assertEquals("status", 400, response.getStatus()); request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); // end quote only request.addHeader(HttpHeaders.IF_NONE_MATCH, etag + "\""); response = invoke(request); assertEquals("status", 400, response.getStatus()); } public void testConditionGetNotIfMatches() throws Exception { String etag = "blablabla"; // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_MATCH, "\"notmatch\""); MockHttpServletResponse response = invoke(request); assertEquals("status", 412, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_MATCH, "\"notmatch\""); response = invoke(request); assertEquals("status", 412, response.getStatus()); } public void testConditionIfNoneMatches() throws Exception { String etag = "blablabla"; // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"notmatch\""); MockHttpServletResponse response = invoke(request); assertEquals("status", 200, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"notmatch\""); response = invoke(request); assertEquals("status", 200, response.getStatus()); } /** * ensure multiple etags are supported. These are strange tests; I need to * ensure it goes through the IF_NONE_MATCH code and hits the second string * in the IF_NONE_MATCH header, so I want a 304 or 412 response. */ public void testConditionIfNoneMatchesMultipleOnSingleHeader() throws Exception { String etag = "blablabla"; // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"atlantic\",\"" + etag + "\",\"pacific\""); MockHttpServletResponse response = invoke(request); assertEquals("status", 304, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"atlantic\",\"" + etag + "\",\"pacific\""); response = invoke(request); assertEquals("status", 412, response.getStatus()); } public void testConditionNoteIfNoneMatches() throws Exception { String etag = "blablabla"; // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"" + etag + "\""); MockHttpServletResponse response = invoke(request); assertEquals("status", 304, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + etag, "*/*"); request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"" + etag + "\""); response = invoke(request); assertEquals("status", 412, response.getStatus()); } public void testConditionalIfUnModified() throws Exception { Date date_to_return = new GregorianCalendar(2007, 11, 06, 10, 0, 0).getTime(); Date modified_since = new GregorianCalendar(2007, 11, 07, 10, 0, 0).getTime(); // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + String .valueOf(date_to_return.getTime()), "*/*"); request.addHeader(HttpHeaders.IF_UNMODIFIED_SINCE, HttpDateParser .toHttpDate(modified_since)); MockHttpServletResponse response = invoke(request); assertEquals("status", 200, response.getStatus()); assertEquals("content", THE_CONTENT, response.getContentAsString()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + String .valueOf(date_to_return.getTime()), "*/*"); request.addHeader(HttpHeaders.IF_UNMODIFIED_SINCE, HttpDateParser .toHttpDate(modified_since)); response = invoke(request); assertEquals("status", 200, response.getStatus()); assertEquals("content", THE_CONTENT, response.getContentAsString()); } public void testConditionalIfNotUnModified() throws Exception { Date date_to_return = new GregorianCalendar(2007, 11, 07, 10, 0, 0).getTime(); Date modified_since = new GregorianCalendar(2007, 11, 06, 10, 0, 0).getTime(); // GET MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/" + String .valueOf(date_to_return.getTime()), "*/*"); request.addHeader(HttpHeaders.IF_UNMODIFIED_SINCE, HttpDateParser .toHttpDate(modified_since)); MockHttpServletResponse response = invoke(request); assertEquals("status", 412, response.getStatus()); // PUT request = MockRequestConstructor.constructMockRequest("PUT", "get/" + String .valueOf(date_to_return.getTime()), "*/*"); request.addHeader(HttpHeaders.IF_UNMODIFIED_SINCE, HttpDateParser .toHttpDate(modified_since)); response = invoke(request); assertEquals("status", 412, response.getStatus()); } public void testConditionalIfNotExistYet() throws Exception { // no headers yet MockHttpServletRequest request = MockRequestConstructor.constructMockRequest("GET", "get/abcd/nonexistyet", "*/*"); MockHttpServletResponse response = invoke(request); assertEquals("status", 200, response.getStatus()); assertEquals("content", THE_CONTENT, response.getContentAsString()); // GET // If-Match request = MockRequestConstructor.constructMockRequest("GET", "get/abcd/nonexistyet", "*/*"); request.addHeader(HttpHeaders.IF_MATCH, "*"); response = invoke(request); assertEquals("status", 412, response.getStatus()); // If-Match request = MockRequestConstructor.constructMockRequest("GET", "get/abcd/nonexistyet", "*/*"); request.addHeader(HttpHeaders.IF_MATCH, "\"randomEntityTag\""); response = invoke(request); assertEquals("status", 412, response.getStatus()); // If-None-Match request = MockRequestConstructor.constructMockRequest("GET", "get/abcd/nonexistyet", "*/*"); request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"randomEntityTag\""); response = invoke(request); assertEquals("status", 200, response.getStatus()); assertEquals("content", THE_CONTENT, response.getContentAsString()); // If-None-Match request = MockRequestConstructor.constructMockRequest("GET", "get/abcd/nonexistyet", "*/*"); request.addHeader(HttpHeaders.IF_NONE_MATCH, "*"); response = invoke(request); assertEquals("status", 200, response.getStatus()); assertEquals("content", THE_CONTENT, response.getContentAsString()); } }