/**
* Copyright (c) 2008-2011 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://www.sonatype.com/products/nexus/attributions.
*
* This program is free software: you can redistribute it and/or modify it only under the terms of the GNU Affero General
* Public License Version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License Version 3
* for more details.
*
* You should have received a copy of the GNU Affero General Public License Version 3 along with this program. If not, see
* http://www.gnu.org/licenses.
*
* Sonatype Nexus (TM) Open Source Version is available from Sonatype, Inc. Sonatype and Sonatype Nexus are trademarks of
* Sonatype, Inc. Apache Maven is a trademark of the Apache Foundation. M2Eclipse is a trademark of the Eclipse Foundation.
* All other trademarks are the property of their respective owners.
*/
package org.sonatype.nexus.restlight.testharness;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* {@link RESTTestFixture} implementation meant to capture a single HTTP GET exchange. It has the ability to capture
* expectations about the request URI (exact or via regex pattern) and the request HTTP headers. It can also manage a
* response {@link Document} and map of response HTTP headers for sending back to the client upon successful request
* validation. This implementation also provides a GET-validating {@link Handler} implementation for use in the HTTP
* {@link Server} instance, which is managed by the abstract base class.
*/
public class GETFixture
extends AbstractRESTTestFixture
{
private Document responseDocument;
private String uriPattern;
private String exactURI;
private boolean strictHeaders;
private Map<String, Set<String>> expectedRequestHeaders;
private Map<String, Set<String>> responseHeaders;
public GETFixture( final String user, final String password )
{
super( user, password );
}
/**
* Retrieve the map of HTTP headers expected to be present in the client request.
*/
public Map<String, Set<String>> getExpectedRequestHeaders()
{
return expectedRequestHeaders;
}
/**
* Set the map of HTTP headers expected to be present in the client request.
*/
public void setExpectedRequestHeaders( final Map<String, Set<String>> requestHeaders )
{
this.expectedRequestHeaders = requestHeaders;
}
/**
* Retrieve the map of HTTP headers that should be injected into the response after a client request has been
* validated.
*/
public Map<String, Set<String>> getResponseHeaders()
{
return responseHeaders;
}
/**
* Set the map of HTTP headers that should be injected into the response after a client request has been validated.
*/
public void setResponseHeaders( final Map<String, Set<String>> responseHeaders )
{
this.responseHeaders = responseHeaders;
}
/**
* Retrieve the exact URI expectation for this fixture. If set, the client request must match it exactly (once the
* base URL is removed, of course) or the {@link Handler} will return a 404 response code.
*/
public String getExactURI()
{
return exactURI;
}
/**
* Set the exact URI expectation for this fixture. If set, the client request must match it exactly (once the base
* URL is removed, of course) or the {@link Handler} will return a 404 response code.
*/
public void setExactURI( final String exactURI )
{
this.exactURI = exactURI;
}
/**
* Retrieve the URI pattern (regular expression) expectation for this fixture. If set, the client request must match
* this regex (once the base URL is removed, of course) or the {@link Handler} will return a 404 response code.
*/
public String getURIPattern()
{
return uriPattern;
}
/**
* Set the URI pattern (regular expression) expectation for this fixture. If set, the client request must match this
* regex (once the base URL is removed, of course) or the {@link Handler} will return a 404 response code.
*/
public void setURIPattern( final String uriPattern )
{
this.uriPattern = uriPattern;
}
/**
* Retrieve the response {@link Document} instance to be used in reply to a client request handled by this fixture.
*/
public Document getResponseDocument()
{
return responseDocument;
}
/**
* Set the response {@link Document} instance to be used in reply to a client request handled by this fixture.
*/
public void setResponseDocument( final Document doc )
{
this.responseDocument = doc;
}
/**
* Return the strict flag value. If set, all request header expectations, and only those, must match exactly with
* those specified in the client's request.
*/
public boolean isHeaderCheckStrict()
{
return strictHeaders;
}
/**
* Set the strict flag value. If set, all request header expectations, and only those, must match exactly with those
* specified in the client's request.
*/
public void setStrictHeaders( final boolean strictHeaders )
{
this.strictHeaders = strictHeaders;
}
/**
* {@inheritDoc}
*
* If the client request isn't a GET method, return a 400 HTTP response code. If the expected request headers don't
* match those supplied by the client request, return a 400 HTTP response code. If the URI doesn't match one or more
* of the URI expectations, or the response document is null, a 404 HTTP response code is returned. If everything
* checks out, the HTTP response code is 200, and the response document will be serialized in pretty format to the
* body.
*/
public Handler getTestHandler()
{
Handler handler = new AbstractHandler()
{
public void handle( final String target, final HttpServletRequest request, final HttpServletResponse response, final int dispatch )
throws IOException, ServletException
{
Logger logger = LogManager.getLogger( GETFixture.class );
if ( !"get".equalsIgnoreCase( request.getMethod() ) )
{
logger.error( "Not a GET method: " + request.getMethod() );
response.sendError( HttpServletResponse.SC_BAD_REQUEST, "Wrong method: " + request.getMethod() );
}
if ( !checkExpectedRequestHeaders( request, isHeaderCheckStrict() ) )
{
logger.error( "Wrong request headers." );
response.sendError( HttpServletResponse.SC_BAD_REQUEST, "Wrong headers." );
}
addResponseHeaders( response );
String uri = getExactURI();
String matchUri = getURIPattern();
if ( uri != null )
{
if ( !request.getRequestURI().equals( uri ) )
{
logger.error( "Exact URI check is wrong.\nExpected: " + uri + "\nActual: " + request.getRequestURI() );
response.sendError( HttpServletResponse.SC_NOT_FOUND );
}
}
else if ( matchUri != null )
{
if ( !request.getRequestURI().matches( matchUri ) )
{
logger.error( "URI pattern check is wrong.\nExpected: " + matchUri + "\nActual: " + request.getRequestURI() );
response.sendError( HttpServletResponse.SC_NOT_FOUND );
}
}
Document doc = getResponseDocument();
if ( doc == null )
{
logger.info( "No response document set. Returning HTTP 404 status." );
response.sendError( HttpServletResponse.SC_NOT_FOUND );
}
else
{
response.setContentType( "application/xml" );
response.setStatus( HttpServletResponse.SC_OK );
new XMLOutputter( Format.getPrettyFormat() ).output( doc, response.getOutputStream() );
response.flushBuffer();
}
( (Request) request ).setHandled( true );
}
};
return handler;
}
/**
* {@inheritDoc}
*/
public GETFixture copy()
{
GETFixture fixture = new GETFixture( getAuthUser(), getAuthPassword() );
fixture.setExpectedRequestHeaders( getExpectedRequestHeaders() );
fixture.setExactURI( getExactURI() );
fixture.setDebugEnabled( isDebugEnabled() );
fixture.setResponseDocument( getResponseDocument() );
fixture.setResponseHeaders( getResponseHeaders() );
fixture.setStrictHeaders( isHeaderCheckStrict() );
fixture.setURIPattern( getURIPattern() );
return fixture;
}
protected void addResponseHeaders( final HttpServletResponse response )
{
if ( getResponseHeaders() != null )
{
for ( Map.Entry<String, Set<String>> headers : getResponseHeaders().entrySet() )
{
String key = headers.getKey();
for ( String value : headers.getValue() )
{
response.addHeader( key, value );
}
}
}
}
@SuppressWarnings( "unchecked" )
protected boolean checkExpectedRequestHeaders( final HttpServletRequest request, final boolean strict )
{
if ( getExpectedRequestHeaders() != null )
{
Map<String, Set<String>> requestHeaders = new HashMap<String, Set<String>>( getExpectedRequestHeaders() );
for ( Map.Entry<String, Set<String>> headerValues : requestHeaders.entrySet() )
{
Set<String> values = new HashSet<String>( headerValues.getValue() );
Enumeration<String> detected = request.getHeaders( headerValues.getKey() );
if ( detected != null )
{
while ( detected.hasMoreElements() )
{
if ( strict && !values.remove( detected.nextElement() ) )
{
return false;
}
}
if ( !values.isEmpty() )
{
return false;
}
}
}
}
return true;
}
}