Thread: [Httpunit-commit] SF.net SVN: httpunit:[1087] trunk/httpunit/src
Brought to you by:
russgold
From: <wol...@us...> - 2012-09-12 13:26:54
|
Revision: 1087 http://httpunit.svn.sourceforge.net/httpunit/?rev=1087&view=rev Author: wolfgang_fahl Date: 2012-09-12 13:26:44 +0000 (Wed, 12 Sep 2012) Log Message: ----------- add checks for InvocationContextFactory - clearly ServletAccessTest can not be run on it's own with this change Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/servletunit/ServletTestCase.java trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitClient.java trunk/httpunit/src/test/java/com/meterware/servletunit/HttpServletRequestTest.java trunk/httpunit/src/test/java/com/meterware/servletunit/JUnitServletTest.java trunk/httpunit/src/test/java/com/meterware/servletunit/ServletAccessTest.java Modified: trunk/httpunit/src/main/java/com/meterware/servletunit/ServletTestCase.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/servletunit/ServletTestCase.java 2012-09-12 12:21:19 UTC (rev 1086) +++ trunk/httpunit/src/main/java/com/meterware/servletunit/ServletTestCase.java 2012-09-12 13:26:44 UTC (rev 1087) @@ -30,20 +30,31 @@ abstract public class ServletTestCase extends TestCase { + /** + * construct a ServletTestCase with the given name + * @param name + */ protected ServletTestCase( String name ) { super( name ); } - /** * Returns a client object which can access the servlet context in which this test is running. */ final protected ServletUnitClient newClient() { + if (_invocationContextFactory==null) + throw new RuntimeException("ServletTestCase.newClient called before setInvocationContextFactory was called"); return ServletUnitClient.newClient( _invocationContextFactory ); } + /** + * set the invocation context factory to be used + * @param invocationContextFactory + */ static void setInvocationContextFactory( InvocationContextFactory invocationContextFactory ) { + if (invocationContextFactory==null) + throw new RuntimeException("setInvocationContextFactory called with null invocationContextFactory parameter"); _invocationContextFactory = invocationContextFactory; } Modified: trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitClient.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitClient.java 2012-09-12 12:21:19 UTC (rev 1086) +++ trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitClient.java 2012-09-12 13:26:44 UTC (rev 1087) @@ -45,6 +45,10 @@ public class ServletUnitClient extends WebClient { + private ServletUnitClient() { + throw new RuntimeException("ServletUnitClient constructor needs InvocationContextFactory parameter"); + } + /** * Creates and returns a new servlet unit client instance. **/ @@ -77,8 +81,18 @@ } + /** + * get a new Invocation Context based on a request and a frame + * @param request + * @param frame + * @return + * @throws IOException + * @throws MalformedURLException + */ InvocationContext newInvocation( WebRequest request, FrameSelector frame ) throws IOException, MalformedURLException { ByteArrayOutputStream baos = getMessageBody( request ); + if (_invocationContextFactory==null) + throw new RuntimeException("newInvocation called with null _invocationContextFactory"); return _invocationContextFactory.newInvocation( this, frame, request, getHeaderFields( request.getURL() ), baos.toByteArray() ); } @@ -141,6 +155,8 @@ private ServletUnitClient( InvocationContextFactory factory ) { + if (factory==null) + throw new RuntimeException("constructor for ServletUnitClient called with null factory parameter"); _invocationContextFactory = factory; } } Modified: trunk/httpunit/src/test/java/com/meterware/servletunit/HttpServletRequestTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/servletunit/HttpServletRequestTest.java 2012-09-12 12:21:19 UTC (rev 1086) +++ trunk/httpunit/src/test/java/com/meterware/servletunit/HttpServletRequestTest.java 2012-09-12 13:26:44 UTC (rev 1087) @@ -20,6 +20,7 @@ * *******************************************************************************************************************/ +import com.meterware.httpunit.FrameSelector; import com.meterware.httpunit.GetMethodWebRequest; import com.meterware.httpunit.HttpUnitOptions; import com.meterware.httpunit.HttpUnitUtils; @@ -38,9 +39,11 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.util.Date; +import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import java.util.Locale; @@ -788,7 +791,16 @@ assertEquals("param1 value", "red", request.getParameter("param1")); assertEquals("param2 value", hebrewValue, request.getParameter("param2")); } + + private InvocationContextFactory _dummyfactory = new InvocationContextFactory() { + public InvocationContext newInvocation( ServletUnitClient client, FrameSelector targetFrame, WebRequest request, Dictionary clientHeaders, byte[] messageBody ) throws IOException, MalformedURLException { + return new InvocationContextImpl( client, null, targetFrame, request, clientHeaders, messageBody ); + } + public HttpSession getSession( String sessionId, boolean create ) { + return _context.getValidSession( sessionId, null, create ); + } + }; @Test public void testSpecifiedCharEncoding2() throws Exception { @@ -798,7 +810,7 @@ wr.setParameter("param1", "red"); wr.setParameter("param2", hebrewValue); wr.setHeaderField("Content-Type", "application/x-www-form-urlencoded; charset=ISO-8859-8"); - ServletUnitClient client = ServletUnitClient.newClient(null); + ServletUnitClient client = ServletUnitClient.newClient(_dummyfactory); ByteArrayOutputStream messageBody = client.getMessageBody(wr); ServletUnitHttpRequest request = new ServletUnitHttpRequest(NULL_SERVLET_REQUEST, wr, _context, new Hashtable(), messageBody.toByteArray()); String parameter = request.getParameter("param2"); Modified: trunk/httpunit/src/test/java/com/meterware/servletunit/JUnitServletTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/servletunit/JUnitServletTest.java 2012-09-12 12:21:19 UTC (rev 1086) +++ trunk/httpunit/src/test/java/com/meterware/servletunit/JUnitServletTest.java 2012-09-12 13:26:44 UTC (rev 1087) @@ -247,7 +247,7 @@ } - static class MyFactory implements InvocationContextFactory { + protected static class MyFactory implements InvocationContextFactory { private static ServletRunner _runner; public InvocationContext newInvocation(ServletUnitClient client, FrameSelector targetFrame, WebRequest request, Dictionary clientHeaders, byte[] messageBody) throws IOException, MalformedURLException { Modified: trunk/httpunit/src/test/java/com/meterware/servletunit/ServletAccessTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/servletunit/ServletAccessTest.java 2012-09-12 12:21:19 UTC (rev 1086) +++ trunk/httpunit/src/test/java/com/meterware/servletunit/ServletAccessTest.java 2012-09-12 13:26:44 UTC (rev 1087) @@ -31,6 +31,10 @@ */ public class ServletAccessTest extends ServletTestCase { + /** + * construct a ServletAccessTest + * @param name + */ public ServletAccessTest(String name) { super(name); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wol...@us...> - 2012-09-12 16:26:16
|
Revision: 1090 http://httpunit.svn.sourceforge.net/httpunit/?rev=1090&view=rev Author: wolfgang_fahl Date: 2012-09-12 16:26:05 +0000 (Wed, 12 Sep 2012) Log Message: ----------- Fixes BR 2834933 There is no facility to allow recursive redirects by Aptivate Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/httpunit/ClientProperties.java trunk/httpunit/src/main/java/com/meterware/httpunit/WebWindow.java trunk/httpunit/src/test/java/com/meterware/httpunit/WebClientTest.java Modified: trunk/httpunit/src/main/java/com/meterware/httpunit/ClientProperties.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/httpunit/ClientProperties.java 2012-09-12 16:08:03 UTC (rev 1089) +++ trunk/httpunit/src/main/java/com/meterware/httpunit/ClientProperties.java 2012-09-12 16:26:05 UTC (rev 1090) @@ -160,8 +160,24 @@ _acceptGzip = acceptGzip; } + /** + * get Maximum number of redirect requests + * @return it + */ + public int getMaxRedirects() { + return _maxRedirects; + } /** + * set the maximum number of redirects + * @param maxRedirects + */ + public void setMaxRedirects( int maxRedirects ) { + _maxRedirects = maxRedirects; + } + + + /** * Returns true if the client should automatically follow page redirect requests (status 3xx). * By default, this is true. **/ @@ -294,6 +310,7 @@ private String _overrideContentType = null; private int _availWidth = 800; private int _availHeight = 600; + private int _maxRedirects = 5; private boolean _iframeSupported = true; private boolean _acceptCookies = true; @@ -333,6 +350,7 @@ _autoRedirect = source._autoRedirect; _autoRefresh = source._autoRefresh; _sendReferer = source._sendReferer; + _maxRedirects = source._maxRedirects; } Modified: trunk/httpunit/src/main/java/com/meterware/httpunit/WebWindow.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/httpunit/WebWindow.java 2012-09-12 16:08:03 UTC (rev 1089) +++ trunk/httpunit/src/main/java/com/meterware/httpunit/WebWindow.java 2012-09-12 16:26:05 UTC (rev 1090) @@ -1,8 +1,9 @@ package com.meterware.httpunit; + /******************************************************************************************************************** * $Id$ * - * Copyright (c) 2002-2008, Russell Gold + * Copyright (c) 2002-2008,2012 Russell Gold * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation @@ -23,339 +24,357 @@ import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.util.HashSet; +import java.util.Hashtable; import java.util.List; -import java.util.Set; +import java.util.Map; import org.xml.sax.SAXException; import com.meterware.httpunit.scripting.ScriptingHandler; /** * A window managed by a {@link com.meterware.httpunit.WebClient WebClient}. - * + * * @author <a href="mailto:rus...@ht...">Russell Gold</a> **/ public class WebWindow { - /** The client which created this window. **/ - private WebClient _client; + /** The client which created this window. **/ + private WebClient _client; - /** A map of frame names to current contents. **/ - private FrameHolder _frameContents; + /** A map of frame names to current contents. **/ + private FrameHolder _frameContents; - /** The name of the window, set via JavaScript. **/ - private String _name = ""; + /** The name of the window, set via JavaScript. **/ + private String _name = ""; - /** The web response containing the reference that opened this window **/ - private WebResponse _opener; + /** The web response containing the reference that opened this window **/ + private WebResponse _opener; - /** True if this window has been closed. **/ - private boolean _closed; + /** True if this window has been closed. **/ + private boolean _closed; + static final String NO_NAME = "$$HttpUnit_Window$$_"; - static final String NO_NAME = "$$HttpUnit_Window$$_"; - - /** - * The urls that have been encountered as redirect locations in the course - * of a single client-initiated request - * @since patch [ 1155415 ] Handle redirect instructions which can lead to a loop - */ - private final Set _redirects; - - /** True if seen initial request - * @since patch [ 1155415 ] Handle redirect instructions which can lead to a loop - */ - private boolean _isInitialRequest = true; - - /** - * Cache the initial client request to ensure that the _redirects - * structure gets reset. - * @since patch [ 1155415 ] Handle redirect instructions which can lead to a loop - */ - private WebRequest _initialRequest; - + /** + * The urls that have been encountered as redirect locations in the course + * of a single client-initiated request + * + * @since patch [ 1155415 ] Handle redirect instructions which can lead to a + * loop + */ + private final Map _redirects; - /** - * Returns the web client associated with this window. - */ - public WebClient getClient() { - return _client; - } + /** + * True if seen initial request + * + * @since patch [ 1155415 ] Handle redirect instructions which can lead to a + * loop + */ + private boolean _isInitialRequest = true; + /** + * Cache the initial client request to ensure that the _redirects structure + * gets reset. + * + * @since patch [ 1155415 ] Handle redirect instructions which can lead to a + * loop + */ + private WebRequest _initialRequest; - /** - * Returns true if this window has been closed. - */ - public boolean isClosed() { - return _closed; - } + /** + * Returns the web client associated with this window. + */ + public WebClient getClient() { + return _client; + } + /** + * Returns true if this window has been closed. + */ + public boolean isClosed() { + return _closed; + } - /** - * Closes this window. - */ - public void close() { - if (!_closed) _client.close( this ); - _closed = true; - } + /** + * Closes this window. + */ + public void close() { + if (!_closed) + _client.close(this); + _closed = true; + } + /** + * Returns the name of this window. Windows created through normal HTML or + * browser commands have empty names, but JavaScript can set the name. A + * name may be used as a target for a request. + */ + public String getName() { + return _name; + } - /** - * Returns the name of this window. Windows created through normal HTML or browser commands have empty names, - * but JavaScript can set the name. A name may be used as a target for a request. - */ - public String getName() { - return _name; - } + /** + * Returns the web response that contained the script which opened this + * window. + */ + public WebResponse getOpener() { + return _opener; + } + /** + * Submits a GET method request and returns a response. + * + * @exception SAXException + * thrown if there is an error parsing the retrieved page + **/ + public WebResponse getResponse(String urlString) throws IOException, + SAXException { + return getResponse(new GetMethodWebRequest(urlString)); + } - /** - * Returns the web response that contained the script which opened this window. - */ - public WebResponse getOpener() { - return _opener; - } + /** + * Submits a web request and returns a response. This is an alternate name + * for the getResponse method. + * + * @return the WebResponse or null + **/ + public WebResponse sendRequest(WebRequest request) throws IOException, + SAXException { + return getResponse(request); + } + /** + * Submits a web request and returns a response, using all state developed + * so far as stored in cookies as requested by the server. see patch [ + * 1155415 ] Handle redirect instructions which can lead to a loop + * + * @exception SAXException + * thrown if there is an error parsing the retrieved page + * @return the WebResponse or null + **/ + public WebResponse getResponse(WebRequest request) throws IOException, + SAXException { + // Need to have some sort of ExecuteAroundMethod to ensure that the + // redirects data structure gets cleared down upon exit - not + // straightforward, since this could be a recursive call + if (_isInitialRequest) { + _initialRequest = request; + _isInitialRequest = false; + } - /** - * Submits a GET method request and returns a response. - * @exception SAXException thrown if there is an error parsing the retrieved page - **/ - public WebResponse getResponse( String urlString ) throws IOException, SAXException { - return getResponse( new GetMethodWebRequest( urlString ) ); - } + WebResponse result = null; + try { + final RequestContext requestContext = new RequestContext(); + final WebResponse response = getSubframeResponse(request, + requestContext); + requestContext.runScripts(); + result = response == null ? null : response.getWindow() + .getFrameContents(response.getFrame()); // javascript might + // replace the + // response in its + // frame + } finally { + if (null != request && request.equals(_initialRequest)) { + _redirects.clear(); + _initialRequest = null; + _isInitialRequest = true; + } + } + return result; + } - /** - * Submits a web request and returns a response. This is an alternate name for the getResponse method. - * @return the WebResponse or null - **/ - public WebResponse sendRequest( WebRequest request ) throws IOException, SAXException { - return getResponse( request ); - } + /** + * get a Response from a SubFrame + * + * @param request + * @param requestContext + * @return the WebResponse or null + * @throws IOException + * @throws SAXException + */ + WebResponse getSubframeResponse(WebRequest request, + RequestContext requestContext) throws IOException, SAXException { + WebResponse response = getResource(request); + return response == null ? null : updateWindow(request.getTarget(), + response, requestContext); + } - /** - * Submits a web request and returns a response, using all state developed so far as stored in - * cookies as requested by the server. - * see patch [ 1155415 ] Handle redirect instructions which can lead to a loop - * @exception SAXException thrown if there is an error parsing the retrieved page - * @return the WebResponse or null - **/ - public WebResponse getResponse( WebRequest request ) throws IOException, SAXException { - // Need to have some sort of ExecuteAroundMethod to ensure that the - // redirects data structure gets cleared down upon exit - not - // straightforward, since this could be a recursive call - if (_isInitialRequest) { - _initialRequest = request; - _isInitialRequest = false; - } - - WebResponse result = null; - - try { - final RequestContext requestContext = new RequestContext(); - final WebResponse response = getSubframeResponse( request, requestContext ); - requestContext.runScripts(); - result = response == null ? null : response.getWindow().getFrameContents( response.getFrame() ); // javascript might replace the response in its frame - } finally { - if (null != request && request.equals(_initialRequest)) { - _redirects.clear(); - _initialRequest = null; - _isInitialRequest = true; - } - } - return result; + /** + * Updates this web client based on a received response. This includes + * updating cookies and frames. + **/ + WebResponse updateWindow(String requestTarget, WebResponse response, + RequestContext requestContext) throws IOException, SAXException { + _client.updateClient(response); + if (getClient().getClientProperties().isAutoRefresh() + && response.getRefreshRequest() != null) { + WebRequest request = response.getRefreshRequest(); + WebResponse result = getResponse(request); + return result; + } else if (shouldFollowRedirect(response)) { + delay(HttpUnitOptions.getRedirectDelay()); + return getResponse(new RedirectWebRequest(response)); + } else { + _client.updateFrameContents(this, requestTarget, response, + requestContext); + return response; } + } + /** + * Returns the resource specified by the request. Does not update the window + * or load included framesets. May return null if the resource is a + * JavaScript URL which would normally leave the client unchanged. + */ + public WebResponse getResource(WebRequest request) throws IOException { + _client.tellListeners(request); - /** - * get a Response from a SubFrame - * @param request - * @param requestContext - * @return the WebResponse or null - * @throws IOException - * @throws SAXException - */ - WebResponse getSubframeResponse( WebRequest request, RequestContext requestContext ) throws IOException, SAXException { - WebResponse response = getResource( request ); + WebResponse response = null; + String urlString = request.getURLString().trim(); + FrameSelector targetFrame = _frameContents.getTargetFrame(request); + if (urlString.startsWith("about:")) { + response = new DefaultWebResponse(_client, targetFrame, null, ""); + } else if (!HttpUnitUtils.isJavaScriptURL(urlString)) { + response = _client.createResponse(request, targetFrame); + } else { + ScriptingHandler handler = request.getSourceScriptingHandler(); + if (handler == null) + handler = getCurrentPage().getScriptingHandler(); + Object result = handler.evaluateExpression(urlString); + if (result != null) { + response = new DefaultWebResponse(_client, targetFrame, + request.getURL(), result.toString()); + } + } - return response == null ? null : updateWindow( request.getTarget(), response, requestContext ); - } + if (response != null) + _client.tellListeners(response); + return response; + } + /** + * Returns the name of the currently active frames. + **/ + public String[] getFrameNames() { + final List names = _frameContents.getActiveFrameNames(); + return (String[]) names.toArray(new String[names.size()]); + } - /** - * Updates this web client based on a received response. This includes updating - * cookies and frames. - **/ - WebResponse updateWindow( String requestTarget, WebResponse response, RequestContext requestContext ) throws IOException, SAXException { - _client.updateClient( response ); - if (getClient().getClientProperties().isAutoRefresh() && response.getRefreshRequest() != null) { - WebRequest request=response.getRefreshRequest(); - WebResponse result=getResponse( request ); - return result; - } else if (shouldFollowRedirect( response )) { - delay( HttpUnitOptions.getRedirectDelay() ); - return getResponse( new RedirectWebRequest( response ) ); - } else { - _client.updateFrameContents( this, requestTarget, response, requestContext ); - return response; - } - } + /** + * Returns true if the specified frame name is defined in this window. + */ + public boolean hasFrame(String frameName) { + return _frameContents.get(frameName) != null; + } + boolean hasFrame(FrameSelector frame) { + return _frameContents.get(frame) != null; + } - /** - * Returns the resource specified by the request. Does not update the window or load included framesets. - * May return null if the resource is a JavaScript URL which would normally leave the client unchanged. - */ - public WebResponse getResource( WebRequest request ) throws IOException { - _client.tellListeners( request ); + /** + * Returns the response associated with the specified frame name. Throws a + * runtime exception if no matching frame is defined. + **/ + public WebResponse getFrameContents(String frameName) { + WebResponse response = _frameContents.get(frameName); + if (response == null) + throw new NoSuchFrameException(frameName); + return response; + } - WebResponse response = null; - String urlString = request.getURLString().trim(); - FrameSelector targetFrame = _frameContents.getTargetFrame( request ); - if (urlString.startsWith( "about:" )) { - response = new DefaultWebResponse( _client, targetFrame, null, "" ); - } else if (!HttpUnitUtils.isJavaScriptURL( urlString )) { - response = _client.createResponse( request, targetFrame ); - } else { - ScriptingHandler handler = request.getSourceScriptingHandler(); - if (handler == null) handler = getCurrentPage().getScriptingHandler(); - Object result = handler.evaluateExpression( urlString ); - if (result != null) { - response = new DefaultWebResponse( _client, targetFrame, request.getURL(), result.toString() ); - } - } + /** + * Returns the response associated with the specified frame target. Throws a + * runtime exception if no matching frame is defined. + **/ + WebResponse getFrameContents(FrameSelector targetFrame) { + return _frameContents.getFrameContents(targetFrame); + } - if (response != null) _client.tellListeners( response ); - return response; - } + WebResponse getSubframeContents(FrameSelector frame, String subFrameName) { + return _frameContents.getSubframeContents(frame, subFrameName); + } + WebResponse getParentFrameContents(FrameSelector frame) { + return _frameContents.getParentFrameContents(frame); + } - /** - * Returns the name of the currently active frames. - **/ - public String[] getFrameNames() { - final List names = _frameContents.getActiveFrameNames(); - return (String[]) names.toArray( new String[ names.size() ] ); - } + /** + * Returns the response representing the main page in this window. + */ + public WebResponse getCurrentPage() { + return getFrameContents(WebRequest.TOP_FRAME); + } + /** + * construct a WebWindow from a given client + * + * @param client + * - the client to construct me from + */ + WebWindow(WebClient client) { + _client = client; + _frameContents = new FrameHolder(this); + _name = NO_NAME + _client.getOpenWindows().length; + _redirects = new Hashtable(); + } - /** - * Returns true if the specified frame name is defined in this window. - */ - public boolean hasFrame( String frameName ) { - return _frameContents.get( frameName ) != null; - } + WebWindow(WebClient client, WebResponse opener) { + this(client); + _opener = opener; + } + void updateFrameContents(WebResponse response, RequestContext requestContext) + throws IOException, SAXException { + response.setWindow(this); + _frameContents.updateFrames(response, response.getFrame(), + requestContext); + } - boolean hasFrame( FrameSelector frame ) { - return _frameContents.get( frame ) != null; - } + void setName(String name) { + _name = name; + } + /** + * Delays the specified amount of time. + **/ + private void delay(int numMilliseconds) { + if (numMilliseconds == 0) + return; + try { + Thread.sleep(numMilliseconds); + } catch (InterruptedException e) { + // ignore the exception + } + } - /** - * Returns the response associated with the specified frame name. - * Throws a runtime exception if no matching frame is defined. - **/ - public WebResponse getFrameContents( String frameName ) { - WebResponse response = _frameContents.get( frameName ); - if (response == null) throw new NoSuchFrameException( frameName ); - return response; - } + /** + * check whether redirect is configured + * + * @param response + * @return + */ + private boolean redirectConfigured(WebResponse response) { + boolean isAutoredirect = getClient().getClientProperties() + .isAutoRedirect(); + boolean hasLocation = response.getHeaderField("Location") != null; + int responseCode = response.getResponseCode(); + boolean result = isAutoredirect + && responseCode >= HttpURLConnection.HTTP_MOVED_PERM + && responseCode <= HttpURLConnection.HTTP_MOVED_TEMP + && hasLocation; + return result; + } - - /** - * Returns the response associated with the specified frame target. - * Throws a runtime exception if no matching frame is defined. - **/ - WebResponse getFrameContents( FrameSelector targetFrame ) { - return _frameContents.getFrameContents( targetFrame ); - } - - - WebResponse getSubframeContents( FrameSelector frame, String subFrameName ) { - return _frameContents.getSubframeContents( frame, subFrameName ); - } - - - WebResponse getParentFrameContents( FrameSelector frame ) { - return _frameContents.getParentFrameContents( frame ); - } - - - /** - * Returns the response representing the main page in this window. - */ - public WebResponse getCurrentPage() { - return getFrameContents( WebRequest.TOP_FRAME ); - } - - - /** - * construct a WebWindow from a given client - * @param client - the client to construct me from - */ - WebWindow( WebClient client ) { - _client = client; - _frameContents = new FrameHolder( this ); - _name = NO_NAME + _client.getOpenWindows().length; - _redirects = new HashSet(); - } - - - WebWindow( WebClient client, WebResponse opener ) { - this( client ); - _opener = opener; - } - - - void updateFrameContents( WebResponse response, RequestContext requestContext ) throws IOException, SAXException { - response.setWindow( this ); - _frameContents.updateFrames( response, response.getFrame(), requestContext ); - } - - - void setName( String name ) { - _name = name; - } - - - /** - * Delays the specified amount of time. - **/ - private void delay( int numMilliseconds ) { - if (numMilliseconds == 0) return; - try { - Thread.sleep( numMilliseconds ); - } catch (InterruptedException e) { - // ignore the exception - } - } - - /** - * check whether redirect is configured - * @param response - * @return - */ - private boolean redirectConfigured( WebResponse response ) { - boolean isAutoredirect=getClient().getClientProperties().isAutoRedirect(); - boolean hasLocation=response.getHeaderField( "Location" ) != null; - int responseCode=response.getResponseCode(); - boolean result=isAutoredirect - && responseCode >= HttpURLConnection.HTTP_MOVED_PERM - && responseCode <= HttpURLConnection.HTTP_MOVED_TEMP - && hasLocation; - return result; - } - - /** - * check wether we should follow the redirect given in the response - * make sure we don't run into a recursion - * @param response - * @return - */ - private boolean shouldFollowRedirect( WebResponse response ) { + /** + * check wether we should follow the redirect given in the response make + * sure we don't run into a recursion + * + * @param response + * @return + */ + private boolean shouldFollowRedirect( WebResponse response ) { // first check whether redirect is configured for this response // this is the old pre [ 1155415 ] Handle redirect instructions which can lead to a loop // shouldFollowRedirect method - just renamed @@ -380,25 +399,38 @@ switch (response.getResponseCode()) { case HttpURLConnection.HTTP_MOVED_PERM: case HttpURLConnection.HTTP_MOVED_TEMP: // Fall through - if (null != url && _redirects.contains(url)) { - // We have already been instructed to redirect to that location in - // the course of this attempt to resolve the resource - throw new RecursiveRedirectionException(url, - "Unable to process request due to redirection loop"); - } - _redirects.add(url); + int count = 0; + if (null != url) { + Integer value = (Integer)_redirects.get(url); + if (null != value) { + // We have already been instructed to redirect to that + // location in the course of this attempt to resolve the + // resource + + count = value.intValue(); + + int maxRedirects = getClient(). + getClientProperties().getMaxRedirects(); + + if (count == maxRedirects) { + throw new RecursiveRedirectionException(url, + "Maximum number of redirects exceeded"); + } + } + + count ++; + _redirects.put(url, new Integer(count)); + } break; } return redirectLocation != null; - } - - FrameSelector getTopFrame() { - return _frameContents.getTopFrame(); } + FrameSelector getTopFrame() { + return _frameContents.getTopFrame(); + } + FrameSelector getFrame(String target) { + return _frameContents.getFrame(target); + } - FrameSelector getFrame( String target ) { - return _frameContents.getFrame( target ); - } - } Modified: trunk/httpunit/src/test/java/com/meterware/httpunit/WebClientTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/httpunit/WebClientTest.java 2012-09-12 16:08:03 UTC (rev 1089) +++ trunk/httpunit/src/test/java/com/meterware/httpunit/WebClientTest.java 2012-09-12 16:26:05 UTC (rev 1090) @@ -3,7 +3,7 @@ * $Id$ * $URL$* * - * Copyright (c) 2002-2009, Russell Gold + * Copyright (c) 2002-2009,2012 Russell Gold * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation @@ -740,7 +740,77 @@ response = wc.getResponse(response.getLinks()[0].getRequest()); assertEquals("Link Referer header", getHostPath() + '/' + linkSource, response.getText().trim()); } + + /** + * test for BR 2834933 + * https://sourceforge.net/tracker/?func=detail&aid=2834933&group_id=6550&atid=106550 + * by aptivate + * @throws Exception + */ + @Test + public void testMaxRedirectsNotExceeded() throws Exception { + String resourceAName = "something/resourceA"; + String resourceBName = "something/resourceB"; + String resourceCName = "something/resourceC"; + + // Should test the following : + // loads resource A + // loads resource B + // redirects to resource A + // loads resource B + // redirects to resource A + // loads resource C + + String resourceAContent = + "<HTML>" + + "<BODY onload='redirect();'>" + + "<script type='text/javascript'>" + + "function redirect()" + + "{" + + " if (document.cookie == '')" + + " {" + + " document.cookie = 'test=1;';\n" + + " window.location.replace('/" + resourceBName + "');\n" + + " }" + + " else if (document.cookie == 'test=1')" + + " {" + + " document.cookie = 'test=2;';\n" + + " window.location.replace('/" + resourceBName + "');\n" + + " }" + + " else" + + " {" + + " window.location.replace('/" + resourceCName + "');\n" + + " }" + + "}" + + "</script>" + + "</BODY>" + + "</HTML>"; + + defineResource(resourceAName, resourceAContent, + HttpURLConnection.HTTP_OK); + + defineResource(resourceBName, "ignored content", + HttpURLConnection.HTTP_MOVED_TEMP); + addResourceHeader(resourceBName, "Location: " + getHostPath() + "/" + + resourceAName); + + defineResource(resourceCName, "ignored content", + HttpURLConnection.HTTP_OK); + + WebConversation wc = new WebConversation(); + wc.getClientProperties().setMaxRedirects(2); + + try { + wc.getResponse(getHostPath() + '/' + resourceAName); + } catch (RecursiveRedirectionException e) { + fail("Not expecting a RecursiveRedirectionException - " + + "max redirects not exceeded"); + } + } + + /** + /** * test for patch [ 1155415 ] Handle redirect instructions which can lead to a loop * This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wol...@us...> - 2012-09-12 20:41:01
|
Revision: 1093 http://httpunit.svn.sourceforge.net/httpunit/?rev=1093&view=rev Author: wolfgang_fahl Date: 2012-09-12 20:40:55 +0000 (Wed, 12 Sep 2012) Log Message: ----------- fixes BR 2883515 noscript tag parsing incorrect by Matthias Hanisch Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/httpunit/WebResponse.java trunk/httpunit/src/test/java/com/meterware/httpunit/WebPageTest.java Modified: trunk/httpunit/src/main/java/com/meterware/httpunit/WebResponse.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/httpunit/WebResponse.java 2012-09-12 20:16:44 UTC (rev 1092) +++ trunk/httpunit/src/main/java/com/meterware/httpunit/WebResponse.java 2012-09-12 20:40:55 UTC (rev 1093) @@ -1031,6 +1031,11 @@ } + /** + * replace the given text + * @param text - the text to replace + * @param contentType - the contenttype + */ public boolean replaceText( String text, String contentType ) { if (_parsingPage) return false; _responseText = text; @@ -1174,18 +1179,24 @@ return available; } - + /** + * read the tags from the given message + * @param rawMessage + * @throws UnsupportedEncodingException + * @throws MalformedURLException + */ private void readTags( byte[] rawMessage ) throws UnsupportedEncodingException, MalformedURLException { ByteTagParser parser = new ByteTagParser( rawMessage ); ByteTag tag = parser.getNextTag(); while (tag != null ) { if (tag.getName().equalsIgnoreCase( "meta" )) processMetaTag( tag ); if (tag.getName().equalsIgnoreCase( "base" )) processBaseTag( tag ); + // loop over a noscript region if (tag.getName().equalsIgnoreCase( "noscript") && HttpUnitOptions.isScriptingEnabled()) { do { tag = parser.getNextTag(); } - while (tag.getName().equalsIgnoreCase( "/noscript") ); + while (!tag.getName().equalsIgnoreCase( "/noscript") ); } tag = parser.getNextTag(); } Modified: trunk/httpunit/src/test/java/com/meterware/httpunit/WebPageTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/httpunit/WebPageTest.java 2012-09-12 20:16:44 UTC (rev 1092) +++ trunk/httpunit/src/test/java/com/meterware/httpunit/WebPageTest.java 2012-09-12 20:40:55 UTC (rev 1093) @@ -26,10 +26,12 @@ import java.io.IOException; import java.net.URL; +import org.junit.Ignore; import org.junit.Test; import org.w3c.dom.html.HTMLDocument; import org.w3c.dom.Document; import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; import static org.junit.Assert.*; @@ -173,10 +175,10 @@ assertEquals("", text); } - + //@Ignore @Test public void testTitle() throws Exception { - defineResource("SimpleTitlePage.html", + defineResource("/SimpleTitlePage.html", "<html><head><title>A Sample Page</title></head>\n" + "<body>This has no forms but it does\n" + "have <a href=\"/other.html\">an <b>active</b> link</A>\n" + @@ -191,7 +193,6 @@ assertNull("No refresh request should have been found", simplePage.getRefreshRequest()); } - @Test public void testLocalFile() throws Exception { File file = new File("temp.html"); @@ -591,6 +592,20 @@ //check the response assertTrue(resp.getText().indexOf("Success") >= 0); } + + /** + * test case for BR 2883515 + * @throws SAXException + * @throws IOException + */ + @Test + public void testInvalidNoScriptHandling() throws IOException, SAXException { + defineResource("/InvalidNoScriptPage.html","<html><body></body></html><noscript>t</noscript>"); + WebConversation wc = new WebConversation(); + WebResponse resp = wc.getResponse(getHostPath() + "/InvalidNoScriptPage.html"); + // indirectly invoke readTags + resp.replaceText("dummy", "dummy"); + } /** * Create a fragment of HTML defining JavaScript that writes a document into a different window. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wol...@us...> - 2012-09-12 21:24:08
|
Revision: 1094 http://httpunit.svn.sourceforge.net/httpunit/?rev=1094&view=rev Author: wolfgang_fahl Date: 2012-09-12 21:24:01 +0000 (Wed, 12 Sep 2012) Log Message: ----------- fix BR 2946821 getDOM changes since 1.5.4 by max Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/httpunit/dom/DocumentImpl.java trunk/httpunit/src/main/java/com/meterware/httpunit/dom/HTMLDocumentImpl.java trunk/httpunit/src/test/java/com/meterware/httpunit/XMLPageTest.java Modified: trunk/httpunit/src/main/java/com/meterware/httpunit/dom/DocumentImpl.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/httpunit/dom/DocumentImpl.java 2012-09-12 20:40:55 UTC (rev 1093) +++ trunk/httpunit/src/main/java/com/meterware/httpunit/dom/DocumentImpl.java 2012-09-12 21:24:01 UTC (rev 1094) @@ -30,7 +30,7 @@ **/ public class DocumentImpl extends NodeImpl implements Document { - private Element _documentElement; + protected Element _documentElement; static DocumentImpl createDocument() { @@ -176,6 +176,11 @@ } + /** + * import the children + * @param original + * @param copy + */ void importChildren( Node original, Node copy ) { NodeList children = original.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Modified: trunk/httpunit/src/main/java/com/meterware/httpunit/dom/HTMLDocumentImpl.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/httpunit/dom/HTMLDocumentImpl.java 2012-09-12 20:40:55 UTC (rev 1093) +++ trunk/httpunit/src/main/java/com/meterware/httpunit/dom/HTMLDocumentImpl.java 2012-09-12 21:24:01 UTC (rev 1094) @@ -258,7 +258,11 @@ public Node cloneNode( boolean deep ) { HTMLDocumentImpl copy = new HTMLDocumentImpl(); - if (deep) copy.importChildren( this, copy ); + + if (deep) { + copy.importChildren( this, copy ); + copy._documentElement=copy.getHtmlElement(); + } return copy; } Modified: trunk/httpunit/src/test/java/com/meterware/httpunit/XMLPageTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/httpunit/XMLPageTest.java 2012-09-12 20:40:55 UTC (rev 1093) +++ trunk/httpunit/src/test/java/com/meterware/httpunit/XMLPageTest.java 2012-09-12 21:24:01 UTC (rev 1094) @@ -20,11 +20,15 @@ * *******************************************************************************************************************/ +import java.io.IOException; import java.util.Iterator; import org.junit.Test; +import org.junit.Ignore; +import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; +import org.xml.sax.SAXException; import static org.junit.Assert.*; @@ -52,7 +56,8 @@ * * @throws Exception */ - public void xtestXMLisHTML() throws Exception { + @Ignore + public void testXMLisHTML() throws Exception { String originalXml = "<?xml version=\"1.0\" ?><main><title>See me now</title></main>"; defineResource("SimplePage.xml", originalXml, "text/xml"); WebConversation wc = new WebConversation(); @@ -64,6 +69,24 @@ assertNotNull("we do have an root-element", simplePage.getDOM().getDocumentElement()); assertEquals("the actual root must be the root of our test-xml", simplePage.getDOM().getDocumentElement().getTagName(), "main"); } + + /** + * test for BR 2946821 + * @throws SAXException + * @throws IOException + */ + @Test + public void testGetDocumentElement() throws IOException, SAXException { + String html="<html><body></body></html>"; + defineResource("BR2946821.html",html,"text/html"); + WebConversation wc = new WebConversation(); + WebRequest request = new GetMethodWebRequest(getHostPath() + "/BR2946821.html"); + WebResponse page = wc.getResponse(request); + assertTrue(page.isHTML()); + Document doc = page.getDOM(); + Element docElement = doc.getDocumentElement(); + assertNotNull("There should be a root element",docElement); + } @Test This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wol...@us...> - 2012-09-12 21:44:38
|
Revision: 1095 http://httpunit.svn.sourceforge.net/httpunit/?rev=1095&view=rev Author: wolfgang_fahl Date: 2012-09-12 21:44:32 +0000 (Wed, 12 Sep 2012) Log Message: ----------- fixes BR 2957505 No 'opaque' causes NPE when attempting DigestAuthentication by Jason McSwain Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/httpunit/AuthenticationChallenge.java trunk/httpunit/src/test/java/com/meterware/httpunit/WebClientTest.java Modified: trunk/httpunit/src/main/java/com/meterware/httpunit/AuthenticationChallenge.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/httpunit/AuthenticationChallenge.java 2012-09-12 21:24:01 UTC (rev 1094) +++ trunk/httpunit/src/main/java/com/meterware/httpunit/AuthenticationChallenge.java 2012-09-12 21:44:32 UTC (rev 1095) @@ -144,7 +144,8 @@ append( sb, "nonce", nonce ); append( sb, "uri", uri ); append( sb, "response", getResponse( userName, realm, password, nonce, uri, method ) ); - append( sb, "opaque", opaque ); + if (opaque!=null) + append( sb, "opaque", opaque ); } Modified: trunk/httpunit/src/test/java/com/meterware/httpunit/WebClientTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/httpunit/WebClientTest.java 2012-09-12 21:24:01 UTC (rev 1094) +++ trunk/httpunit/src/test/java/com/meterware/httpunit/WebClientTest.java 2012-09-12 21:44:32 UTC (rev 1095) @@ -520,23 +520,23 @@ WebResponse wr = wc.getResponse("http://someserver.com/sample"); assertEquals("authorization", "Basic dXNlcjpwYXNzd29yZA==", wr.getText()); } - - + /** - * Verifies one-time digest authentication with no Quality of Protection (qop). - * - * @throws Exception if an unexpected exception is thrown during the test. + * test Rfc2069 optionally with or without opaque parameter + * @param withOpaque + * @throws Exception */ - @Test - public void testRfc2069DigestAuthentication() throws Exception { + public void testRfc2069DigestAuthentication(final boolean withOpaque) throws Exception { defineResource("/dir/index.html", new PseudoServlet() { public WebResource getGetResponse() { String header = getHeader("Authorization"); if (header == null) { WebResource resource = new WebResource("not authorized", HttpURLConnection.HTTP_UNAUTHORIZED); - resource.addHeader("WWW-Authenticate: Digest realm=\"tes...@ho...\"," + - " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," + - " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""); + String headerStr="WWW-Authenticate: Digest realm=\"tes...@ho...\"," + + " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\";"; + if (withOpaque) + headerStr+=", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""; + resource.addHeader(headerStr); return resource; } else { return new WebResource(getHeader("Authorization"), "text/plain"); @@ -546,19 +546,39 @@ WebConversation wc = new WebConversation(); wc.setAuthentication("tes...@ho...", "Mufasa", "CircleOfLife"); WebResponse wr = wc.getResponse(getHostPath() + "/dir/index.html"); + String expectedHeaderStr="Digest username=\"Mufasa\"," + + " realm=\"tes...@ho...\"," + + " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," + + " uri=\"/dir/index.html\"," + + " response=\"1949323746fe6a43ef61f9606e7febea\""; + if (withOpaque) + expectedHeaderStr+=", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""; HttpHeader expectedHeader - = new HttpHeader("Digest username=\"Mufasa\"," + - " realm=\"tes...@ho...\"," + - " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," + - " uri=\"/dir/index.html\"," + - " response=\"1949323746fe6a43ef61f9606e7febea\"," + - " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""); + = new HttpHeader(expectedHeaderStr); HttpHeader actualHeader = new HttpHeader(wr.getText()); assertHeadersEquals(expectedHeader, actualHeader); } - /** + * Verifies one-time digest authentication with no Quality of Protection (qop). + * + * @throws Exception if an unexpected exception is thrown during the test. + */ + @Test + public void testRfc2069DigestAuthentication() throws Exception { + testRfc2069DigestAuthentication(true); + } + + /** + * test for BR 2957505 + * No 'opaque' causes NPE when attempting DigestAuthentication + */ + @Test + public void testRfc2069DigestAuthenticationNoOpaque() throws Exception { + testRfc2069DigestAuthentication(false); + } + + /** * Verifies one-time digest authentication with Quality of Protection (qop). * * @throws Exception @@ -597,6 +617,15 @@ assertHeadersEquals(expectedHeader, actualHeader); } + /** + * get page contents + * @param pageAddress + * @param protectionDomain + * @param userName + * @param password + * @return the page content + * @throws Exception + */ private static String getPageContents(String pageAddress, String protectionDomain, String userName, String password) throws Exception { WebConversation wc = new WebConversation(); wc.setAuthentication(protectionDomain, userName, password); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wol...@us...> - 2012-09-13 06:47:25
|
Revision: 1096 http://httpunit.svn.sourceforge.net/httpunit/?rev=1096&view=rev Author: wolfgang_fahl Date: 2012-09-13 06:47:15 +0000 (Thu, 13 Sep 2012) Log Message: ----------- fixes BR 3076917 Missing port number in URL from ServletUnitHtt by bbogg Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitHttpRequest.java trunk/httpunit/src/test/java/com/meterware/servletunit/HttpServletRequestTest.java Modified: trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitHttpRequest.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitHttpRequest.java 2012-09-12 21:44:32 UTC (rev 1095) +++ trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitHttpRequest.java 2012-09-13 06:47:15 UTC (rev 1096) @@ -735,6 +735,8 @@ try { url.append( _request.getURL().getProtocol() ).append( "://" ); url.append( _request.getURL().getHost() ); + String portPortion = _request.getURL().getPort() == -1 ? "" : (":" + _request.getURL().getPort()); + url.append(portPortion); url.append( _request.getURL().getPath() ); } catch (MalformedURLException e) { throw new RuntimeException( "unable to read URL from request: " + _request ); Modified: trunk/httpunit/src/test/java/com/meterware/servletunit/HttpServletRequestTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/servletunit/HttpServletRequestTest.java 2012-09-12 21:44:32 UTC (rev 1095) +++ trunk/httpunit/src/test/java/com/meterware/servletunit/HttpServletRequestTest.java 2012-09-13 06:47:15 UTC (rev 1096) @@ -579,21 +579,47 @@ } - @Test - public void testGetRequestURI() throws Exception { + + /** + * test getting the uri + * @param uri + * @param path + */ + public void testGetRequestURI(String uri,String path) throws Exception { + /* + http://de.wikipedia.org/wiki/Uniform_Resource_Identifier + + foo://example.com:8042/over/there?name=ferret#nose + \_/ \______________/\_________/ \_________/ \__/ + | | | | | + scheme authority path query fragment + */ ServletUnitContext context = _context; - WebRequest wr = new GetMethodWebRequest("http://localhost/simple"); + WebRequest wr = new GetMethodWebRequest(uri); ServletUnitHttpRequest request = new ServletUnitHttpRequest(NULL_SERVLET_REQUEST, wr, context, new Hashtable(), NO_MESSAGE_BODY); - assertEquals("/simple", request.getRequestURI()); - assertEquals("http://localhost/simple", request.getRequestURL().toString()); + assertEquals(path, request.getRequestURI()); + assertEquals(uri, request.getRequestURL().toString()); - wr = new GetMethodWebRequest("http://localhost/simple?foo=bar"); + wr = new GetMethodWebRequest(uri+"?foo=bar"); request = new ServletUnitHttpRequest(NULL_SERVLET_REQUEST, wr, context, new Hashtable(), NO_MESSAGE_BODY); - assertEquals("/simple", request.getRequestURI()); - assertEquals("http://localhost/simple", request.getRequestURL().toString()); + assertEquals(path, request.getRequestURI()); + assertEquals(uri, request.getRequestURL().toString()); } + + @Test + public void testGetRequestURI() throws Exception { + testGetRequestURI("http://localhost/simple","/simple"); + } + /** + * test for BR 3076917 Missing port number in URL from ServletUnitHttpRequest + * @throws Exception + */ + @Test + public void testGetRequestURIWithPort() throws Exception { + testGetRequestURI("http://localhost:8042/simple8042","/simple8042"); + } @Test public void testDefaultLocale() throws Exception { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wol...@us...> - 2012-09-13 07:35:41
|
Revision: 1097 http://httpunit.svn.sourceforge.net/httpunit/?rev=1097&view=rev Author: wolfgang_fahl Date: 2012-09-13 07:35:31 +0000 (Thu, 13 Sep 2012) Log Message: ----------- mostly fixes BR 3301056 ServletUnit handling Content-Type incorrectly by Kevin Hunter - the default Content-Type can not be set to null or any other value at this time Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/httpunit/WebResponse.java trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitHttpResponse.java Added Paths: ----------- trunk/httpunit/src/test/java/com/meterware/servletunit/ContentTypeTest.java Modified: trunk/httpunit/src/main/java/com/meterware/httpunit/WebResponse.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/httpunit/WebResponse.java 2012-09-13 06:47:15 UTC (rev 1096) +++ trunk/httpunit/src/main/java/com/meterware/httpunit/WebResponse.java 2012-09-13 07:35:31 UTC (rev 1097) @@ -1325,8 +1325,8 @@ _contentHeader = _contentType + ";charset=" + _characterSet; } else { String[] parts = HttpUnitUtils.parseContentTypeHeader( contentHeader ); - if (null != _client && null != _client.getClientProperties().getOverrideContextType()) { - _contentType = _client.getClientProperties().getOverrideContextType(); + if (null != _client && null != _client.getClientProperties().getOverrideContentType()) { + _contentType = _client.getClientProperties().getOverrideContentType(); } else { _contentType = parts[0]; } Modified: trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitHttpResponse.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitHttpResponse.java 2012-09-13 06:47:15 UTC (rev 1096) +++ trunk/httpunit/src/main/java/com/meterware/servletunit/ServletUnitHttpResponse.java 2012-09-13 07:35:31 UTC (rev 1097) @@ -487,19 +487,26 @@ return result; } + /** + * Returns the headers defined for this response. + * @param name - the name of the field to get + **/ + String getHeaderFieldDirect( String name ) { + ArrayList values; + synchronized (_headers) { + values = (ArrayList) _headers.get( name.toUpperCase() ); + } + return values == null ? null : (String) values.get( 0 ); + } + /** * Returns the headers defined for this response. + * @param name **/ String getHeaderField( String name ) { if (!_headersComplete) completeHeaders(); - - ArrayList values; - synchronized (_headers) { - values = (ArrayList) _headers.get( name.toUpperCase() ); - } - - return values == null ? null : (String) values.get( 0 ); + return getHeaderFieldDirect(name); } @@ -564,7 +571,10 @@ private void completeHeaders() { if (_headersComplete) return; addCookieHeader(); - setHeader( "Content-Type", _contentType + "; charset=" + getCharacterEncoding() ); + // BR 3301056 ServletUnit handling Content-Type incorrectly + if (getHeaderFieldDirect("Content-Type")==null) { + setHeader( "Content-Type", _contentType + "; charset=" + getCharacterEncoding() ); + } _headersComplete = true; } Added: trunk/httpunit/src/test/java/com/meterware/servletunit/ContentTypeTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/servletunit/ContentTypeTest.java (rev 0) +++ trunk/httpunit/src/test/java/com/meterware/servletunit/ContentTypeTest.java 2012-09-13 07:35:31 UTC (rev 1097) @@ -0,0 +1,158 @@ +package com.meterware.servletunit; +/******************************************************************************************************************** + * $Id: ErrorTests.java 1081 2012-09-09 17:16:39Z russgold $ + * + * Copyright (c) 2012, Russell Gold + * Copyright (c) 2011, NeoMedia Technologies, Inc.. All Rights Reserved except see below. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *******************************************************************************************************************/ + +import static org.junit.Assert.*; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.meterware.httpunit.GetMethodWebRequest; +import com.meterware.httpunit.WebClient; +import com.meterware.httpunit.WebRequest; +import com.meterware.httpunit.WebResponse; + +/** + * Tests for BR 3301056 ServletUnit handling Content-Type incorrectly + * by Kevin Hunter + * @author wf + * + */ +public class ContentTypeTest +{ + private ServletRunner runner; + + public ContentTypeTest() + { + } + + @Before + public void setUp() + { + runner = new ServletRunner(); + runner.registerServlet("/test1", TestServlet1.class.getName()); + runner.registerServlet("/test2", TestServlet2.class.getName()); + } + + @After + public void tearDown() + { + runner.shutDown(); + } + + /* + * This test case demonstrates that ServletUnit incorrectly replaces the + * content type specified by the servlet with one of its own. + * (Expected behavior would be that ServletUnit would not alter the + * response coming back from the servlet under test.) + */ + @Test + public void testProvidedContentTypeOverwritten() throws Exception + { + WebClient client = runner.newClient(); + WebRequest request = new GetMethodWebRequest("http://localhost/test1"); + WebResponse response = client.getResponse(request); + assertEquals(200, response.getResponseCode()); + assertEquals(7, response.getContentLength()); + assertEquals("<test/>", response.getText()); + assertEquals("text/xml", response.getHeaderField("Content-Type")); + assertEquals("text/xml", response.getContentType()); + } + + /* + * This servlet returns a simple XML document, with Content-Type set + * to "text/xml" + */ + public static class TestServlet1 extends HttpServlet + { + private static final long serialVersionUID = 5434730264615105319L; + + public TestServlet1() + { + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + resp.setStatus(200); + resp.addHeader("Content-Type", "text/xml"); + resp.setContentLength(7); + resp.getWriter().write("<test/>"); + } + } + + /* + * This test case demonstrates that ServletUnit incorrectly provides + * a content type when the servlet under test doesn't provide one. + * (Expected behavior would be that ServletUnit would not alter the + * response coming back from the servlet under test.) + */ + @Test + public void testContentProvidedWhenNoneSpecified() throws Exception + { + WebClient client = runner.newClient(); + WebRequest request = new GetMethodWebRequest("http://localhost/test2"); + WebResponse response = client.getResponse(request); + assertEquals(200, response.getResponseCode()); + assertEquals(7, response.getContentLength()); + assertEquals("<test/>", response.getText()); + assertNull(response.getHeaderField("No-Such-Header")); + // the BR proposes to have a default "null" content-type + // assertNull(response.getHeaderField("Content-Type")); + // unfortunately currently the default content type is "text/plain; charset=iso-8859-1" + // whereas the HttpUnitOptions default Content Type is "text/html" ... + // we'll check for the current state for the time being 2012-09-13 + assertEquals("text/plain; charset=iso-8859-1",response.getHeaderField("Content-Type")); + } + + /* + * This servlet returns a simple XML document, but omits the + * Content-Type header. + */ + public static class TestServlet2 extends HttpServlet + { + private static final long serialVersionUID = 5434730264615105319L; + + public TestServlet2() + { + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + resp.setStatus(200); + resp.setContentLength(7); + resp.getWriter().write("<test/>"); + } + } +} + Property changes on: trunk/httpunit/src/test/java/com/meterware/servletunit/ContentTypeTest.java ___________________________________________________________________ Added: svn:mime-type + text/plain This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wol...@us...> - 2012-09-12 15:55:56
|
Revision: 1088 http://httpunit.svn.sourceforge.net/httpunit/?rev=1088&view=rev Author: wolfgang_fahl Date: 2012-09-12 15:55:44 +0000 (Wed, 12 Sep 2012) Log Message: ----------- modified for BR 2825872 Cookie domains not stored correctly - ID: 2825872 by aptivate Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/httpunit/cookies/CookieJar.java trunk/httpunit/src/test/java/com/meterware/httpunit/cookies/CookieTest.java Modified: trunk/httpunit/src/main/java/com/meterware/httpunit/cookies/CookieJar.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/httpunit/cookies/CookieJar.java 2012-09-12 13:26:44 UTC (rev 1087) +++ trunk/httpunit/src/main/java/com/meterware/httpunit/cookies/CookieJar.java 2012-09-12 15:55:44 UTC (rev 1088) @@ -1,8 +1,9 @@ package com.meterware.httpunit.cookies; + /******************************************************************************************************************** * $Id$ * - * Copyright (c) 2002-2004,2008, Russell Gold + * Copyright (c) 2002-2004,2008,2012 Russell Gold * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation @@ -25,586 +26,620 @@ import java.net.URL; import java.util.*; - /** - * A collection of HTTP cookies, which can interact with cookie and set-cookie header values. - * + * A collection of HTTP cookies, which can interact with cookie and set-cookie + * header values. + * * @author <a href="mailto:rus...@ht...">Russell Gold</a> * @author <a href="mailto:dre...@or...">Drew Varner</a> **/ public class CookieJar { - private static final int DEFAULT_HEADER_SIZE = 80; + private static final int DEFAULT_HEADER_SIZE = 80; - private ArrayList _cookies = new ArrayList(); - private ArrayList _globalCookies = new ArrayList(); - private CookiePress _press; + private ArrayList _cookies = new ArrayList(); + private ArrayList _globalCookies = new ArrayList(); + private CookiePress _press; + /** + * Creates an empty cookie jar. + */ + public CookieJar() { + _press = new CookiePress(null); + } - /** - * Creates an empty cookie jar. - */ - public CookieJar() { - _press = new CookiePress( null ); - } + /** + * Creates a cookie jar which is initially populated with cookies parsed + * from the <code>Set-Cookie</code> and <code>Set-Cookie2</code> header + * fields. + * <p> + * Note that the parsing does not strictly follow the specifications, but + * attempts to imitate the behavior of popular browsers. Specifically, it + * allows cookie values to contain commas, which the Netscape standard does + * not allow for, but which is required by some servers. + * </p> + */ + public CookieJar(CookieSource source) { + _press = new CookiePress(source.getURL()); + findCookies(source.getHeaderFields("Set-Cookie"), + new RFC2109CookieRecipe()); + findCookies(source.getHeaderFields("Set-Cookie2"), + new RFC2965CookieRecipe()); + } + /** + * find the cookies in the given Header String array + * + * @param cookieHeader + * - the strings to look for cookies + * @param recipe + * - the recipe to use + */ + private void findCookies(String cookieHeader[], CookieRecipe recipe) { + for (int i = 0; i < cookieHeader.length; i++) { + recipe.findCookies(cookieHeader[i]); + } + } - /** - * Creates a cookie jar which is initially populated with cookies parsed from the <code>Set-Cookie</code> and - * <code>Set-Cookie2</code> header fields. - * <p> - * Note that the parsing does not strictly follow the specifications, but - * attempts to imitate the behavior of popular browsers. Specifically, - * it allows cookie values to contain commas, which the - * Netscape standard does not allow for, but which is required by some servers. - * </p> - */ - public CookieJar( CookieSource source ) { - _press = new CookiePress( source.getURL() ); - findCookies( source.getHeaderFields( "Set-Cookie" ), new RFC2109CookieRecipe() ); - findCookies( source.getHeaderFields( "Set-Cookie2" ), new RFC2965CookieRecipe() ); - } + /** + * Empties this cookie jar of all contents. + */ + public void clear() { + _cookies.clear(); + _globalCookies.clear(); + } + /** + * Defines a cookie to be sent to the server on every request. This bypasses + * the normal mechanism by which only certain cookies are sent based on + * their host and path. + * + * @deprecated as of 1.6, use #putCookie + **/ + public void addCookie(String name, String value) { + _globalCookies.add(new Cookie(name, value)); + } - /** - * find the cookies in the given Header String array - * @param cookieHeader - the strings to look for cookies - * @param recipe - the recipe to use - */ - private void findCookies( String cookieHeader[], CookieRecipe recipe ) { - for (int i = 0; i < cookieHeader.length; i++) { - recipe.findCookies( cookieHeader[i] ); - } - } + /** + * Defines a cookie to be sent to the server on every request. This bypasses + * the normal mechanism by which only certain cookies are sent based on + * their host and path. + * + * Values of null will result in the cookie being removed. Any other value + * will leave the cookie unchanged expect for the value. + * + * @since 1.6 + **/ + public void putCookie(String name, String value) { + boolean foundCookie = false; + for (Iterator iterator = _globalCookies.iterator(); iterator.hasNext();) { + Cookie cookie = (Cookie) iterator.next(); + if (name.equals(cookie.getName())) { + foundCookie = true; + if (value != null) { + cookie.setValue(value); + } else { + iterator.remove(); + } + } + } + for (Iterator iterator = _cookies.iterator(); iterator.hasNext();) { + Cookie cookie = (Cookie) iterator.next(); + if (name.equals(cookie.getName())) { + foundCookie = true; + if (value != null) { + cookie.setValue(value); + } else { + iterator.remove(); + } + } + } - /** - * Empties this cookie jar of all contents. - */ - public void clear() { - _cookies.clear(); - _globalCookies.clear(); - } + // only add it if it does not already exist + if (foundCookie == false) { + _globalCookies.add(new Cookie(name, value)); + } + } + /** + * Define a non-global cookie. This cookie can be overwritten by subsequent + * cookie definitions in http headers. This cookie definition requires a + * domain and path. If a global cookie is defined with the same name, this + * cookie is not added. + */ + public void putSingleUseCookie(String name, String value, String domain, + String path) { + for (Iterator iterator = _globalCookies.iterator(); iterator.hasNext();) { + Cookie cookie = (Cookie) iterator.next(); + if (name.equals(cookie.getName())) + return; + } - /** - * Defines a cookie to be sent to the server on every request. This bypasses the normal mechanism by which only - * certain cookies are sent based on their host and path. - * @deprecated as of 1.6, use #putCookie - **/ - public void addCookie( String name, String value ) { - _globalCookies.add( new Cookie( name, value ) ); - } + for (Iterator iterator = _cookies.iterator(); iterator.hasNext();) { + Cookie cookie = (Cookie) iterator.next(); + if (name.equals(cookie.getName())) + iterator.remove(); + } + _cookies.add(new Cookie(name, value, domain, path)); + } - /** - * Defines a cookie to be sent to the server on every request. This bypasses the normal mechanism by which only - * certain cookies are sent based on their host and path. - * - * Values of null will result in the cookie being removed. Any other value will leave the - * cookie unchanged expect for the value. - * - * @since 1.6 - **/ - public void putCookie( String name, String value ) { - boolean foundCookie = false; - for (Iterator iterator = _globalCookies.iterator(); iterator.hasNext();) { - Cookie cookie = (Cookie) iterator.next(); - if (name.equals( cookie.getName() )) { - foundCookie = true; - if (value != null) { - cookie.setValue(value); - } else { - iterator.remove(); - } - } - } - - for (Iterator iterator = _cookies.iterator(); iterator.hasNext();) { - Cookie cookie = (Cookie) iterator.next(); - if (name.equals( cookie.getName() )) { - foundCookie = true; - if (value != null) { - cookie.setValue(value); - } else { - iterator.remove(); - } - } - } - - // only add it if it does not already exist - if (foundCookie == false) { - _globalCookies.add( new Cookie( name, value ) ); - } - } + /** + * Returns the name of all the active cookies in this cookie jar. + **/ + public String[] getCookieNames() { + final int numGlobalCookies = _globalCookies.size(); + String[] names = new String[_cookies.size() + numGlobalCookies]; + for (int i = 0; i < numGlobalCookies; i++) { + names[i] = ((Cookie) _globalCookies.get(i)).getName(); + } + for (int i = numGlobalCookies; i < names.length; i++) { + names[i] = ((Cookie) _cookies.get(i - numGlobalCookies)).getName(); + } + return names; + } - /** - * Define a non-global cookie. This cookie can be overwritten by subsequent cookie definitions - * in http headers. This cookie definition requires a domain and path. If a global cookie is - * defined with the same name, this cookie is not added. - */ - public void putSingleUseCookie(String name, String value, String domain, String path) { - for (Iterator iterator = _globalCookies.iterator(); iterator.hasNext();) { - Cookie cookie = (Cookie) iterator.next(); - if (name.equals( cookie.getName() )) return; - } - - for (Iterator iterator = _cookies.iterator(); iterator.hasNext();) { - Cookie cookie = (Cookie) iterator.next(); - if (name.equals( cookie.getName() )) iterator.remove(); - } - - _cookies.add( new Cookie( name, value, domain, path) ); - } - - /** - * Returns the name of all the active cookies in this cookie jar. - **/ - public String[] getCookieNames() { - final int numGlobalCookies = _globalCookies.size(); - String[] names = new String[ _cookies.size() + numGlobalCookies ]; - for (int i = 0; i < numGlobalCookies; i++) { - names[i] = ((Cookie) _globalCookies.get(i)).getName(); - } - for (int i = numGlobalCookies; i < names.length; i++) { - names[i] = ((Cookie) _cookies.get( i-numGlobalCookies )).getName(); - } - return names; - } + /** + * Returns a collection containing all of the cookies in this jar. + */ + public Collection getCookies() { + final Collection collection = (Collection) _cookies.clone(); + collection.addAll(_globalCookies); + return collection; + } + /** + * Returns the value of the specified cookie. + * + * @param name + * - the name of the cookie to get the value for + * @return the value of the cookie + **/ + public String getCookieValue(String name) { + Cookie cookie = getCookie(name); + return cookie == null ? null : cookie.getValue(); + } - /** - * Returns a collection containing all of the cookies in this jar. - */ - public Collection getCookies() { - final Collection collection = (Collection) _cookies.clone(); - collection.addAll( _globalCookies ); - return collection; - } + /** + * Returns the value of the specified cookie. + **/ + public Cookie getCookie(String name) { + if (name == null) + throw new IllegalArgumentException( + "getCookieValue: no name specified"); + for (Iterator iterator = _cookies.iterator(); iterator.hasNext();) { + Cookie cookie = (Cookie) iterator.next(); + if (name.equals(cookie.getName())) + return cookie; + } + for (Iterator iterator = _globalCookies.iterator(); iterator.hasNext();) { + Cookie cookie = (Cookie) iterator.next(); + if (name.equals(cookie.getName())) + return cookie; + } + return null; + } + /** + * Returns the value of the cookie header to be sent to the specified URL. + * Will return null if no compatible cookie is defined. + **/ + public String getCookieHeaderField(URL targetURL) { + if (_cookies.isEmpty() && _globalCookies.isEmpty()) + return null; + StringBuffer sb = new StringBuffer(DEFAULT_HEADER_SIZE); + HashSet restrictedCookies = new HashSet(); + for (Iterator i = _cookies.iterator(); i.hasNext();) { + Cookie cookie = (Cookie) i.next(); + if (!cookie.mayBeSentTo(targetURL)) + continue; + restrictedCookies.add(cookie.getName()); + if (sb.length() != 0) + sb.append("; "); + sb.append(cookie.getName()).append('=').append(cookie.getValue()); + } + for (Iterator i = _globalCookies.iterator(); i.hasNext();) { + Cookie cookie = (Cookie) i.next(); + if (restrictedCookies.contains(cookie.getName())) + continue; + if (sb.length() != 0) + sb.append("; "); + sb.append(cookie.getName()).append('=').append(cookie.getValue()); + } + return sb.length() == 0 ? null : sb.toString(); + } - /** - * Returns the value of the specified cookie. - * @param name - the name of the cookie to get the value for - * @return the value of the cookie - **/ - public String getCookieValue( String name ) { - Cookie cookie = getCookie( name ); - return cookie == null ? null : cookie.getValue(); - } + /** + * Updates the cookies maintained in this cookie jar with those in another + * cookie jar. Any duplicate cookies in the new jar will replace those in + * this jar. + **/ + public void updateCookies(CookieJar newJar) { + for (Iterator i = newJar._cookies.iterator(); i.hasNext();) { + addUniqueCookie((Cookie) i.next()); + } + } + /** + * Add the cookie to this jar, replacing any previous matching cookie. + */ + void addUniqueCookie(Cookie cookie) { + _cookies.remove(cookie); + for (Iterator i = _cookies.iterator(); i.hasNext();) { + Cookie c = (Cookie) i.next(); + if (c.getName().equals(cookie.getName())) { + if (compareDomain(c.getDomain(), cookie.getDomain())) { + if (c.getPath() != null && cookie.getPath() != null + && c.getPath().equals(cookie.getPath())) { + i.remove(); + } + } + } + } + _cookies.add(cookie); + } - /** - * Returns the value of the specified cookie. - **/ - public Cookie getCookie( String name ) { - if (name == null) throw new IllegalArgumentException( "getCookieValue: no name specified" ); - for (Iterator iterator = _cookies.iterator(); iterator.hasNext();) { - Cookie cookie = (Cookie) iterator.next(); - if (name.equals( cookie.getName() )) return cookie; - } - for (Iterator iterator = _globalCookies.iterator(); iterator.hasNext();) { - Cookie cookie = (Cookie) iterator.next(); - if (name.equals( cookie.getName() )) return cookie; - } - return null; - } + /** + * compare the two domains given for "cookie-equality" + * + * @param domain + * @param newDomain + * @return + */ + private boolean compareDomain(String domain, String newDomain) { + if (domain.charAt(0) == '.' && newDomain.endsWith(domain)) { + return true; + } + if (newDomain.charAt(0) == '.' && domain.endsWith(newDomain)) { + return true; + } + return domain.equals(newDomain); + } - /** - * Returns the value of the cookie header to be sent to the specified URL. - * Will return null if no compatible cookie is defined. - **/ - public String getCookieHeaderField( URL targetURL ) { - if (_cookies.isEmpty() && _globalCookies.isEmpty()) return null; - StringBuffer sb = new StringBuffer( DEFAULT_HEADER_SIZE ); - HashSet restrictedCookies = new HashSet(); - for (Iterator i = _cookies.iterator(); i.hasNext();) { - Cookie cookie = (Cookie) i.next(); - if (!cookie.mayBeSentTo( targetURL )) continue; - restrictedCookies.add( cookie.getName() ); - if (sb.length() != 0) sb.append( "; " ); - sb.append( cookie.getName() ).append( '=' ).append( cookie.getValue() ); - } - for (Iterator i = _globalCookies.iterator(); i.hasNext();) { - Cookie cookie = (Cookie) i.next(); - if (restrictedCookies.contains( cookie.getName() )) continue; - if (sb.length() != 0) sb.append( "; " ); - sb.append( cookie.getName() ).append( '=' ).append( cookie.getValue() ); - } - return sb.length() == 0 ? null : sb.toString(); - } + /** + * base class for the cookie recipies - there are two different + * implementations of this + */ + abstract class CookieRecipe { + /** + * Extracts cookies from a cookie header. Works in conjunction with a + * cookie press class, which actually creates the cookies and adds them + * to the jar as appropriate. + * + * 1. Parse the header into tokens, separated by ',' and ';' (respecting + * single and double quotes) 2. Process tokens from the end: a. if the + * token contains an '=' we have a name/value pair. Add them to the + * cookie press, which will decide if it is a cookie name or an + * attribute name. b. if the token is a reserved word, flush the cookie + * press and continue. c. otherwise, add the token to the cookie press, + * passing along the last character of the previous token. + */ + void findCookies(String cookieHeader) { + Vector tokens = getCookieTokens(cookieHeader); - /** - * Updates the cookies maintained in this cookie jar with those in another cookie jar. Any duplicate cookies in - * the new jar will replace those in this jar. - **/ - public void updateCookies( CookieJar newJar ) { - for (Iterator i = newJar._cookies.iterator(); i.hasNext();) { - addUniqueCookie( (Cookie) i.next() ); - } - } + for (int i = tokens.size() - 1; i >= 0; i--) { + String token = (String) tokens.elementAt(i); + int equalsIndex = getEqualsIndex(token); + if (equalsIndex != -1) { + _press.addTokenWithEqualsSign(this, token, equalsIndex); + } else if (isCookieReservedWord(token)) { + _press.clear(); + } else { + _press.addToken(token, lastCharOf((i == 0) ? "" + : (String) tokens.elementAt(i - 1))); + } + } + } - /** - * Add the cookie to this jar, replacing any previous matching cookie. - */ - void addUniqueCookie( Cookie cookie ) { - _cookies.remove( cookie ); - for (Iterator i = _cookies.iterator(); i.hasNext();) { - Cookie c = (Cookie) i.next(); - if (c.getName().equals(cookie.getName())) { - if (compareDomain(c.getDomain(), cookie.getDomain())) { - if (c.getPath() != null && cookie.getPath() != null - && c.getPath().equals(cookie.getPath())) { - i.remove(); - } - } - } - } - _cookies.add( cookie ); - } + private char lastCharOf(String string) { + return (string.length() == 0) ? ' ' : string + .charAt(string.length() - 1); + } - /** - * compare the two domains given for "cookie-equality" - * - * @param domain - * @param newDomain - * @return + /** + * Returns the index (if any) of the equals sign separating a cookie + * name from the its value. Equals signs at the end of the token are + * ignored in this calculation, since they may be part of a + * Base64-encoded value. */ - private boolean compareDomain(String domain, String newDomain) { - if (domain.charAt(0) == '.' && newDomain.endsWith(domain)) { - return true; + private int getEqualsIndex(String token) { + if (!token.endsWith("==")) { + return token.indexOf('='); + } else { + return getEqualsIndex(token.substring(0, token.length() - 2)); } - if (newDomain.charAt(0) == '.' && domain.endsWith(newDomain)) { - return true; - } - - return domain.equals(newDomain); } - /** - * base class for the cookie recipies - there are two different - * implementations of this - */ - abstract class CookieRecipe { + /** + * Tokenizes a cookie header and returns the tokens in a + * <code>Vector</code>. handles the broken syntax for expires= fields + * ... + * + * @param cookieHeader + * - the header to read + * @return a Vector of cookieTokens as name=value pairs + **/ + private Vector getCookieTokens(String cookieHeader) { + StringReader sr = new StringReader(cookieHeader); + StreamTokenizer st = new StreamTokenizer(sr); + Vector tokens = new Vector(); - /** - * Extracts cookies from a cookie header. Works in conjunction with a cookie press class, which actually creates - * the cookies and adds them to the jar as appropriate. - * - * 1. Parse the header into tokens, separated by ',' and ';' (respecting single and double quotes) - * 2. Process tokens from the end: - * a. if the token contains an '=' we have a name/value pair. Add them to the cookie press, which - * will decide if it is a cookie name or an attribute name. - * b. if the token is a reserved word, flush the cookie press and continue. - * c. otherwise, add the token to the cookie press, passing along the last character of the previous token. - */ - void findCookies( String cookieHeader ) { - Vector tokens = getCookieTokens( cookieHeader ); + // clear syntax tables of the StreamTokenizer + st.resetSyntax(); - for (int i = tokens.size() - 1; i >= 0; i--) { - String token = (String) tokens.elementAt( i ); + // set all characters as word characters + st.wordChars(0, Character.MAX_VALUE); - int equalsIndex = getEqualsIndex( token ); - if (equalsIndex != -1) { - _press.addTokenWithEqualsSign( this, token, equalsIndex ); - } else if (isCookieReservedWord( token )) { - _press.clear(); - } else { - _press.addToken( token, lastCharOf( (i == 0) ? "" : (String) tokens.elementAt( i - 1 ) ) ); - } - } - } + // set up characters for quoting + st.quoteChar('"'); // double quotes + st.quoteChar('\''); // single quotes + // set up characters to separate tokens + st.whitespaceChars(59, 59); // semicolon + // and here we run into trouble ... + // see http://www.mnot.net/blog/2006/10/27/cookie_fun + // ... Notice something about the above? It uses a comma inside of + // the date, + // without quoting the value. This makes it difficult for generic + // processors to handle the Set-Cookie header. + st.whitespaceChars(44, 44); // comma - private char lastCharOf( String string ) { - return (string.length() == 0) ? ' ' : string.charAt( string.length()-1 ); - } + try { + while (st.nextToken() != StreamTokenizer.TT_EOF) { + String tokenContent = st.sval; + // fix expires comma delimiter token problem + if (tokenContent.toLowerCase().startsWith("expires=")) { + if (st.nextToken() != StreamTokenizer.TT_EOF) { + tokenContent += "," + st.sval; + } // if + } // if + tokenContent = tokenContent.trim(); + tokens.addElement(tokenContent); + } + } catch (IOException ioe) { + // this will never happen with a StringReader + } + sr.close(); + return tokens; + } + abstract protected boolean isCookieAttribute(String stringLowercase); - /** - * Returns the index (if any) of the equals sign separating a cookie name from the its value. - * Equals signs at the end of the token are ignored in this calculation, since they may be - * part of a Base64-encoded value. - */ - private int getEqualsIndex( String token ) { - if (!token.endsWith( "==" )) { - return token.indexOf( '=' ); - } else { - return getEqualsIndex( token.substring( 0, token.length()-2 ) ); - } - } + abstract protected boolean isCookieReservedWord(String token); + } - /** - * Tokenizes a cookie header and returns the tokens in a - * <code>Vector</code>. - * handles the broken syntax for expires= fields ... - * @param cookieHeader - the header to read - * @return a Vector of cookieTokens as name=value pairs - **/ - private Vector getCookieTokens(String cookieHeader) { - StringReader sr = new StringReader(cookieHeader); - StreamTokenizer st = new StreamTokenizer(sr); - Vector tokens = new Vector(); + /** + * cookie Factory - creates cookies for URL s + * + */ + class CookiePress { - // clear syntax tables of the StreamTokenizer - st.resetSyntax(); + // the current value + private StringBuffer _value = new StringBuffer(); + private HashMap _attributes = new HashMap(); + private URL _sourceURL; - // set all characters as word characters - st.wordChars(0,Character.MAX_VALUE); + /** + * create a cookie press for the given URL + * + * @param sourceURL + */ + public CookiePress(URL sourceURL) { + _sourceURL = sourceURL; + } - // set up characters for quoting - st.quoteChar( '"' ); //double quotes - st.quoteChar( '\'' ); //single quotes + /** + * clear the attributes and the cookie value + */ + void clear() { + _value.setLength(0); + _attributes.clear(); + } - // set up characters to separate tokens - st.whitespaceChars(59,59); //semicolon - // and here we run into trouble ... - // see http://www.mnot.net/blog/2006/10/27/cookie_fun - // ... Notice something about the above? It uses a comma inside of the date, - // without quoting the value. This makes it difficult for generic processors to handle the Set-Cookie header. - st.whitespaceChars(44,44); //comma + /** + * add the token content + * + * @param token + * @param lastChar + */ + void addToken(String token, char lastChar) { + _value.insert(0, token); + if (lastChar != '=') + _value.insert(0, ','); + } - try { - while (st.nextToken() != StreamTokenizer.TT_EOF) { - String tokenContent=st.sval; - // fix expires comma delimiter token problem - if (tokenContent.toLowerCase().startsWith("expires=")) { - if (st.nextToken() != StreamTokenizer.TT_EOF) { - tokenContent+=","+st.sval; - } // if - } // if - tokenContent=tokenContent.trim(); - tokens.addElement( tokenContent ); - } - } - catch (IOException ioe) { - // this will never happen with a StringReader - } - sr.close(); - return tokens; - } + /** + * add from a token + * + * @param recipe + * - the recipe to use + * @param token + * - the token to use + * @param equalsIndex + * - the position of the equal sign + */ + void addTokenWithEqualsSign(CookieRecipe recipe, String token, + int equalsIndex) { + final String name = token.substring(0, equalsIndex).trim(); + final String value = token.substring(equalsIndex + 1).trim(); + _value.insert(0, value); + final String fvalue = _value.toString(); + if (recipe.isCookieAttribute(name.toLowerCase())) { + _attributes.put(name.toLowerCase(), value); + } else { + addCookieIfValid(new Cookie(name, fvalue, _attributes)); + _attributes.clear(); + } + _value.setLength(0); + } + /** + * add the given cookie if it is valid + * + * @param cookie + */ + private void addCookieIfValid(Cookie cookie) { + if (acceptCookie(cookie)) + addUniqueCookie(cookie); + } - abstract protected boolean isCookieAttribute( String stringLowercase ); + /** + * accept the given cookie + * + * @param cookie + * @return + */ + private boolean acceptCookie(Cookie cookie) { + if (cookie.getPath() == null) { + cookie.setPath(getParentPath(_sourceURL.getPath())); + } else { + int status = getPathAttributeStatus(cookie.getPath(), + _sourceURL.getPath()); + if (status != CookieListener.ACCEPTED) { + reportCookieRejected(status, cookie.getPath(), + cookie.getName()); + return false; + } + } + if (cookie.getDomain() == null) { + cookie.setDomain(_sourceURL.getHost()); + } else if (!CookieProperties.isDomainMatchingStrict() + && cookie.getDomain() + .equalsIgnoreCase(_sourceURL.getHost())) { + cookie.setDomain(_sourceURL.getHost()); + } else { + int status = getDomainAttributeStatus(cookie, + _sourceURL.getHost()); + if (status != CookieListener.ACCEPTED) { + reportCookieRejected(status, cookie.getDomain(), + cookie.getName()); + return false; + } + } - abstract protected boolean isCookieReservedWord( String token ); + return true; + } - } + private String getParentPath(String path) { + int rightmostSlashIndex = path.lastIndexOf('/'); + return rightmostSlashIndex < 0 ? "/" : path.substring(0, + rightmostSlashIndex); + } + private int getPathAttributeStatus(String pathAttribute, + String sourcePath) { + if (!CookieProperties.isPathMatchingStrict() + || sourcePath.length() == 0 + || sourcePath.startsWith(pathAttribute)) { + return CookieListener.ACCEPTED; + } else { + return CookieListener.PATH_NOT_PREFIX; + } + } - /** - * cookie Factory - creates cookies for URL s - * - */ - class CookiePress { + /** + * get the domainAttribute Status for the given cookie with the + * given sourceHost + * + * @see http://wp.netscape.com/newsref/std/cookie_spec.html + * @param cookie - the cookie to use + * @param sourceHost + * @return + */ + private int getDomainAttributeStatus(Cookie cookie, String sourceHost) { + String domainAttribute = cookie.getDomain(); + // patch according to [ 1476380 ] Cookies incorrectly rejected + // despite valid domain + if (domainAttribute.equals(sourceHost)) { + return CookieListener.ACCEPTED; + } + if (!domainAttribute.startsWith(".")) + domainAttribute = '.' + domainAttribute; - // the current value - private StringBuffer _value = new StringBuffer(); - private HashMap _attributes = new HashMap(); - private URL _sourceURL; + if (domainAttribute.lastIndexOf('.') == 0) { + return CookieListener.DOMAIN_ONE_DOT; + } else if (!sourceHost.endsWith(domainAttribute)) { + return CookieListener.DOMAIN_NOT_SOURCE_SUFFIX; + } else if (CookieProperties.isDomainMatchingStrict() + && sourceHost.lastIndexOf(domainAttribute) > sourceHost + .indexOf('.')) { + return CookieListener.DOMAIN_TOO_MANY_LEVELS; + } else { + // modified for Bugreport 2825872 Cookie domains not stored correctly - ID: 2825872 + // http://sourceforge.net/tracker/?func=detail&aid=2825872&group_id=6550&atid=106550 + cookie.setDomain(domainAttribute); + return CookieListener.ACCEPTED; + } + } + private boolean reportCookieRejected(int reason, String attribute, + String source) { + CookieProperties.reportCookieRejected(reason, attribute, source); + return false; + } - /** - * create a cookie press for the given URL - * @param sourceURL - */ - public CookiePress( URL sourceURL ) { - _sourceURL = sourceURL; - } + } + /** + * Parses cookies according to <a + * href="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</a> + * + * <br /> + * These cookies come from the <code>Set-Cookie:</code> header + **/ + class RFC2109CookieRecipe extends CookieRecipe { - /** - * clear the attributes and the cookie value - */ - void clear() { - _value.setLength(0); - _attributes.clear(); - } + /** + * check whether the given lower case String is a cookie attribute + * + * @param stringLowercase + * - the string to check + * @return true - if the string is the name of a valid cookie attribute + */ + protected boolean isCookieAttribute(String stringLowercase) { + return stringLowercase.equals("path") + || stringLowercase.equals("domain") + || stringLowercase.equals("expires") + || stringLowercase.equals("comment") + || stringLowercase.equals("max-age") + || stringLowercase.equals("version"); + } + protected boolean isCookieReservedWord(String token) { + return token.equalsIgnoreCase("secure"); + } + } - /** - * add the token content - * @param token - * @param lastChar - */ - void addToken( String token, char lastChar ) { - _value.insert( 0, token ); - if (lastChar != '=') _value.insert( 0, ',' ); - } + /** + * Parses cookies according to <a + * href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a> + * + * <br /> + * These cookies come from the <code>Set-Cookie2:</code> header + **/ + class RFC2965CookieRecipe extends CookieRecipe { + protected boolean isCookieAttribute(String stringLowercase) { + return stringLowercase.equals("path") + || stringLowercase.equals("domain") + || stringLowercase.equals("comment") + || stringLowercase.equals("commenturl") + || stringLowercase.equals("max-age") + || stringLowercase.equals("version") + || stringLowercase.equals("$version") + || stringLowercase.equals("port"); + } - /** - * add from a token - * @param recipe - the recipe to use - * @param token - the token to use - * @param equalsIndex - the position of the equal sign - */ - void addTokenWithEqualsSign( CookieRecipe recipe, String token, int equalsIndex ) { - final String name = token.substring( 0, equalsIndex ).trim(); - final String value= token.substring( equalsIndex + 1 ).trim(); - _value.insert( 0, value ); - final String fvalue=_value.toString(); - if (recipe.isCookieAttribute( name.toLowerCase() )) { - _attributes.put( name.toLowerCase(), value ); - } else { - addCookieIfValid( new Cookie( name, fvalue, _attributes ) ); - _attributes.clear(); - } - _value.setLength(0); - } + protected boolean isCookieReservedWord(String token) { + return token.equalsIgnoreCase("discard") + || token.equalsIgnoreCase("secure"); + } + } - - /** - * add the given cookie if it is valid - * @param cookie - */ - private void addCookieIfValid( Cookie cookie ) { - if (acceptCookie( cookie )) addUniqueCookie( cookie ); - } - - - /** - * accept the given cookie - * @param cookie - * @return - */ - private boolean acceptCookie( Cookie cookie ) { - if (cookie.getPath() == null) { - cookie.setPath( getParentPath( _sourceURL.getPath() ) ); - } else { - int status = getPathAttributeStatus( cookie.getPath(), _sourceURL.getPath() ); - if (status != CookieListener.ACCEPTED) { - reportCookieRejected( status, cookie.getPath(), cookie.getName() ); - return false; - } - } - - if (cookie.getDomain() == null) { - cookie.setDomain( _sourceURL.getHost() ); - } else if (!CookieProperties.isDomainMatchingStrict() && cookie.getDomain().equalsIgnoreCase( _sourceURL.getHost() )) { - cookie.setDomain( _sourceURL.getHost() ); - } else { - int status = getDomainAttributeStatus( cookie.getDomain(), _sourceURL.getHost() ); - if (status != CookieListener.ACCEPTED) { - reportCookieRejected( status, cookie.getDomain(), cookie.getName() ); - return false; - } - } - - return true; - } - - - private String getParentPath( String path ) { - int rightmostSlashIndex = path.lastIndexOf( '/' ); - return rightmostSlashIndex < 0 ? "/" : path.substring( 0, rightmostSlashIndex ); - } - - - private int getPathAttributeStatus( String pathAttribute, String sourcePath ) { - if (!CookieProperties.isPathMatchingStrict() || sourcePath.length() == 0 || sourcePath.startsWith( pathAttribute )) { - return CookieListener.ACCEPTED; - } else { - return CookieListener.PATH_NOT_PREFIX; - } - } - - - /** - * get the domainAttribute Status for the given domainAttribute with the given sourceHost - * @see http://wp.netscape.com/newsref/std/cookie_spec.html - * @param domainAttribute - * @param sourceHost - * @return - */ - private int getDomainAttributeStatus( String domainAttribute, String sourceHost ) { - // patch according to [ 1476380 ] Cookies incorrectly rejected despite valid domain - if (domainAttribute.equals(sourceHost)) { - return CookieListener.ACCEPTED; - } - if (!domainAttribute.startsWith(".")) - domainAttribute = '.' + domainAttribute; - - if (domainAttribute.lastIndexOf('.') == 0) { - return CookieListener.DOMAIN_ONE_DOT; - } else if (!sourceHost.endsWith( domainAttribute )) { - return CookieListener.DOMAIN_NOT_SOURCE_SUFFIX; - } else if (CookieProperties.isDomainMatchingStrict() && - sourceHost.lastIndexOf( domainAttribute ) > sourceHost.indexOf( '.' )) { - return CookieListener.DOMAIN_TOO_MANY_LEVELS; - } else { - return CookieListener.ACCEPTED; - } - } - - private boolean reportCookieRejected( int reason, String attribute, String source ) { - CookieProperties.reportCookieRejected( reason, attribute, source ); - return false; - } - - } - - - /** - * Parses cookies according to - * <a href="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</a> - * - * <br /> - * These cookies come from the <code>Set-Cookie:</code> header - **/ - class RFC2109CookieRecipe extends CookieRecipe { - - /** - * check whether the given lower case String is a cookie attribute - * @param stringLowercase - the string to check - * @return true - if the string is the name of a valid cookie attribute - */ - protected boolean isCookieAttribute( String stringLowercase ) { - return stringLowercase.equals("path") || - stringLowercase.equals("domain") || - stringLowercase.equals("expires") || - stringLowercase.equals("comment") || - stringLowercase.equals("max-age") || - stringLowercase.equals("version"); - } - - - protected boolean isCookieReservedWord( String token ) { - return token.equalsIgnoreCase( "secure" ); - } - } - - - /** - * Parses cookies according to - * <a href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a> - * - * <br /> - * These cookies come from the <code>Set-Cookie2:</code> header - **/ - class RFC2965CookieRecipe extends CookieRecipe { - - protected boolean isCookieAttribute( String stringLowercase ) { - return stringLowercase.equals("path") || - stringLowercase.equals("domain") || - stringLowercase.equals("comment") || - stringLowercase.equals("commenturl") || - stringLowercase.equals("max-age") || - stringLowercase.equals("version") || - stringLowercase.equals("$version") || - stringLowercase.equals("port"); - } - - - protected boolean isCookieReservedWord( String token ) { - return token.equalsIgnoreCase( "discard" ) || token.equalsIgnoreCase( "secure" ); - } - } - - } Modified: trunk/httpunit/src/test/java/com/meterware/httpunit/cookies/CookieTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/httpunit/cookies/CookieTest.java 2012-09-12 13:26:44 UTC (rev 1087) +++ trunk/httpunit/src/test/java/com/meterware/httpunit/cookies/CookieTest.java 2012-09-12 15:55:44 UTC (rev 1088) @@ -141,10 +141,17 @@ private void checkAcceptance(int index, boolean shouldAccept, String urlString, String specifiedDomain, String specifiedPath) throws MalformedURLException { + CookieJar jar = newJar(urlString, specifiedDomain, specifiedPath); if (shouldAccept) { - assertNotNull("Rejected cookie " + index + "( " + specifiedDomain + " from " + urlString + ") should have been accepted", jar.getCookie("name")); + // modified for Bugreport 2825872 Cookie domains not stored correctly - ID: 2825872 + // http://sourceforge.net/tracker/?func=detail&aid=2825872&group_id=6550&atid=106550 + Cookie cookie = jar.getCookie( "name" ); + assertNotNull( "Rejected cookie " + index + "( " + specifiedDomain + " from " + urlString + ") should have been accepted", cookie ); + URL url = new URL( "http://" + urlString ); + assertTrue( "Cookie " + index + " should be sent to the url ", cookie.mayBeSentTo(url)); + } else { assertNull("Cookie " + index + " should have been rejected", jar.getCookie("name")); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wol...@us...> - 2012-09-12 16:08:11
|
Revision: 1089 http://httpunit.svn.sourceforge.net/httpunit/?rev=1089&view=rev Author: wolfgang_fahl Date: 2012-09-12 16:08:03 +0000 (Wed, 12 Sep 2012) Log Message: ----------- adds fix for BR BR 2822957 Cannot remove file upload parameter once set in WebForm by Chris Wilson Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/httpunit/WebForm.java trunk/httpunit/src/test/java/com/meterware/httpunit/FileUploadTest.java Modified: trunk/httpunit/src/main/java/com/meterware/httpunit/WebForm.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/httpunit/WebForm.java 2012-09-12 15:55:44 UTC (rev 1088) +++ trunk/httpunit/src/main/java/com/meterware/httpunit/WebForm.java 2012-09-12 16:08:03 UTC (rev 1089) @@ -1,24 +1,25 @@ package com.meterware.httpunit; + /******************************************************************************************************************** -* $Id$ -* -* Copyright (c) 2000-2008, Russell Gold -* -* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -* documentation files (the "Software"), to deal in the Software without restriction, including without limitation -* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and -* to permit persons to whom the Software is furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all copies or substantial portions -* of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -* THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -* DEALINGS IN THE SOFTWARE. -* -*******************************************************************************************************************/ + * $Id$ + * + * Copyright (c) 2000-2008,2012 Russell Gold + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *******************************************************************************************************************/ import com.meterware.httpunit.scripting.IdentifiedDelegate; import com.meterware.httpunit.scripting.NamedDelegate; import com.meterware.httpunit.scripting.ScriptableDelegate; @@ -41,1130 +42,1165 @@ import org.w3c.dom.html.HTMLCollection; import org.xml.sax.SAXException; - /** - * This class represents a form in an HTML page. Users of this class may examine the parameters - * defined for the form, the structure of the form (as a DOM), or the text of the form. They - * may also create a {@link WebRequest} to simulate the submission of the form. - * + * This class represents a form in an HTML page. Users of this class may examine + * the parameters defined for the form, the structure of the form (as a DOM), or + * the text of the form. They may also create a {@link WebRequest} to simulate + * the submission of the form. + * * @author <a href="mailto:rus...@ht...">Russell Gold</a> **/ public class WebForm extends WebRequestSource { - private final static String[] NO_VALUES = new String[0]; + private final static String[] NO_VALUES = new String[0]; - private Button[] _buttons; + private Button[] _buttons; - /** The submit buttons in this form. **/ - private SubmitButton[] _submitButtons; + /** The submit buttons in this form. **/ + private SubmitButton[] _submitButtons; - /** The character set in which the form will be submitted. **/ - private String _characterSet; + /** The character set in which the form will be submitted. **/ + private String _characterSet; - private Vector _buttonVector; + private Vector _buttonVector; - private FormControl[] _presetParameters; - private ArrayList _presets; + private FormControl[] _presetParameters; + private ArrayList _presets; - private ElementRegistry _registry; + private ElementRegistry _registry; + /** Predicate to match a link's name. **/ + public final static HTMLElementPredicate MATCH_NAME; + private HTMLFormElement _domElement; + /** + * Submits this form using the web client from which it was originally + * obtained. + **/ + public WebResponse submit() throws IOException, SAXException { + return submit(getDefaultButton()); + } - /** Predicate to match a link's name. **/ - public final static HTMLElementPredicate MATCH_NAME; - private HTMLFormElement _domElement; + /** + * Submits this form using the web client from which it was originally + * obtained. Will usually return the result of that submission; however, if + * the submit button's 'onclick' or the form's 'onsubmit' event is triggered + * and inhibits the submission, will return the updated contents of the + * frame containing this form. + **/ + public WebResponse submit(SubmitButton button) throws IOException, + SAXException { + WebResponse result = submit(button, 0, 0); + return result; + } + /** + * Submits this form using the web client from which it was originally + * obtained. Will usually return the result of that submission; however, if + * the submit button's 'onclick' or the form's 'onsubmit' event is triggered + * and inhibits the submission, will return the updated contents of the + * frame containing this form. + * + * @since 1.6 + **/ + public WebResponse submit(SubmitButton button, int x, int y) + throws IOException, SAXException { + WebResponse result = null; + if (button == null) + throw new IllegalSubmitButtonException("?", "?"); + button.doOnClickSequence(x, y); + result = getCurrentFrameContents(); + return result; + } - /** - * Submits this form using the web client from which it was originally obtained. - **/ - public WebResponse submit() throws IOException, SAXException { - return submit( getDefaultButton() ); - } + /** + * Submits this form using the web client from which it was originally + * obtained, ignoring any buttons defined for the form. + * + * @since 1.6 + **/ + public WebResponse submitNoButton() throws SAXException, IOException { + return submit(SubmitButton.createFakeSubmitButton(this /* fake */)); + } + protected WebResponse submitRequest(String event, WebRequest request) + throws IOException, SAXException { + try { + return super.submitRequest(event, request); + } catch (UnknownServiceException e) { + throw new UnsupportedActionException("HttpUnit does not support " + + request.getURL().getProtocol() + + " URLs in form submissions"); + } + } - /** - * Submits this form using the web client from which it was originally obtained. - * Will usually return the result of that submission; however, if the submit button's 'onclick' - * or the form's 'onsubmit' event is triggered and - * inhibits the submission, will return the updated contents of the frame containing this form. - **/ - public WebResponse submit( SubmitButton button ) throws IOException, SAXException { - WebResponse result=submit(button,0,0); - return result; - } + /** + * Submits the form without also invoking the button's "onclick" event. + */ + WebResponse doFormSubmit(SubmitButton button) throws IOException, + SAXException { + return submitRequest(getAttribute("onsubmit"), getRequest(button)); + } + WebResponse doFormSubmit(SubmitButton button, int x, int y) + throws IOException, SAXException { + return submitRequest(getAttribute("onsubmit"), getRequest(button, x, y)); + } - /** - * Submits this form using the web client from which it was originally obtained. - * Will usually return the result of that submission; however, if the submit button's 'onclick' - * or the form's 'onsubmit' event is triggered and - * inhibits the submission, will return the updated contents of the frame containing this form. - * @since 1.6 - **/ - public WebResponse submit( SubmitButton button, int x, int y ) throws IOException, SAXException { - WebResponse result=null; - if (button == null) throw new IllegalSubmitButtonException( "?", "?" ); - button.doOnClickSequence(x, y); - result=getCurrentFrameContents(); - return result; - } + /** + * Returns the method defined for this form. + **/ + public String getMethod() { + return getAttribute("method", "GET"); + } + /** + * Returns the action defined for this form. + **/ + public String getAction() { + return getDestination(); + } - /** - * Submits this form using the web client from which it was originally obtained, ignoring any buttons defined for the form. - * @since 1.6 - **/ - public WebResponse submitNoButton() throws SAXException, IOException { - return submit( SubmitButton.createFakeSubmitButton( this /* fake */ ) ); - } + /** + * Returns true if a parameter with given name exists in this form. + **/ + public boolean hasParameterNamed(String soughtName) { + return getFormParameters().containsKey(soughtName); + } + /** + * Returns true if a parameter starting with a given name exists, + **/ + public boolean hasParameterStartingWithPrefix(String prefix) { + String[] names = getParameterNames(); + for (int i = 0; i < names.length; i++) { + if (names[i].startsWith(prefix)) + return true; + } + return false; + } - protected WebResponse submitRequest( String event, WebRequest request ) throws IOException, SAXException { - try { - return super.submitRequest( event, request ); - } catch (UnknownServiceException e) { - throw new UnsupportedActionException( "HttpUnit does not support " + request.getURL().getProtocol() + " URLs in form submissions" ); - } - } + /** + * Returns an array containing all of the buttons defined for this form. + **/ + public Button[] getButtons() { + if (_buttons == null) { + FormControl[] controls = getFormControls(); + ArrayList buttonList = new ArrayList(); + for (int i = 0; i < controls.length; i++) { + FormControl control = controls[i]; + if (control instanceof Button) + buttonList.add(control); + } + _buttons = (Button[]) buttonList.toArray(new Button[buttonList + .size()]); + } + return _buttons; + } + public Button getButton(HTMLElementPredicate predicate, Object criteria) { + Button[] buttons = getButtons(); + for (int i = 0; i < buttons.length; i++) { + if (predicate.matchesCriteria(buttons[i], criteria)) + return buttons[i]; + } + return null; + } - /** - * Submits the form without also invoking the button's "onclick" event. - */ - WebResponse doFormSubmit( SubmitButton button ) throws IOException, SAXException { - return submitRequest( getAttribute( "onsubmit" ), getRequest( button ) ); - } + /** + * Convenience method which returns the button with the specified ID. + */ + public Button getButtonWithID(String buttonID) { + return getButton(Button.WITH_ID, buttonID); + } + /** + * Returns an array containing the submit buttons defined for this form. + **/ + public SubmitButton[] getSubmitButtons() { + if (_submitButtons == null) { + Vector buttons = getSubmitButtonVector(); + _submitButtons = new SubmitButton[buttons.size()]; + buttons.copyInto(_submitButtons); + } + return _submitButtons; + } - WebResponse doFormSubmit( SubmitButton button, int x, int y ) throws IOException, SAXException { - return submitRequest( getAttribute( "onsubmit" ), getRequest( button, x, y ) ); - } + /** + * Returns the submit button defined in this form with the specified name. + * If more than one such button exists, will return the first found. If no + * such button is found, will return null. + **/ + public SubmitButton getSubmitButton(String name) { + SubmitButton[] buttons = getSubmitButtons(); + for (int i = 0; i < buttons.length; i++) { + if (buttons[i].getName().equals(name)) { + return buttons[i]; + } + } + return null; + } + /** + * Returns the submit button defined in this form with the specified name + * and value. If more than one such button exists, will return the first + * found. If no such button is found, will return null. + **/ + public SubmitButton getSubmitButton(String name, String value) { + SubmitButton[] buttons = getSubmitButtons(); + for (int i = 0; i < buttons.length; i++) { + if (buttons[i].getName().equals(name) + && buttons[i].getValue().equals(value)) { + return buttons[i]; + } + } + return null; + } - /** - * Returns the method defined for this form. - **/ - public String getMethod() { - return getAttribute( "method", "GET" ); - } + /** + * Returns the submit button defined in this form with the specified ID. If + * more than one such button exists, will return the first found. If no such + * button is found, will return null. + **/ + public SubmitButton getSubmitButtonWithID(String ID) { + SubmitButton[] buttons = getSubmitButtons(); + for (int i = 0; i < buttons.length; i++) { + if (buttons[i].getID().equals(ID)) { + return buttons[i]; + } + } + return null; + } + /** + * Creates and returns a web request which will simulate the submission of + * this form with a button with the specified name and value. + **/ + public WebRequest getRequest(String submitButtonName, + String submitButtonValue) { + SubmitButton sb = getSubmitButton(submitButtonName, submitButtonValue); + if (sb == null) + throw new IllegalSubmitButtonException(submitButtonName, + submitButtonValue); + return getRequest(sb); + } - /** - * Returns the action defined for this form. - **/ - public String getAction() { - return getDestination(); - } + /** + * Creates and returns a web request which will simulate the submission of + * this form with a button with the specified name. + **/ + public WebRequest getRequest(String submitButtonName) { + SubmitButton sb = getSubmitButton(submitButtonName); + if (sb == null) + throw new IllegalSubmitButtonException(submitButtonName, ""); + return getRequest(sb); + } + /** + * Creates and returns a web request which will simulate the submission of + * this form by pressing the specified button. If the button is null, + * simulates the pressing of the default button. + **/ + public WebRequest getRequest(SubmitButton button) { + return getRequest(button, 0, 0); + } - /** - * Returns true if a parameter with given name exists in this form. - **/ - public boolean hasParameterNamed( String soughtName ) { - return getFormParameters().containsKey( soughtName ); - } + /** + * Creates and returns a web request which will simulate the submission of + * this form by pressing the specified button. If the button is null, + * simulates the pressing of the default button. + * + * @param button + * - the submitbutton to be pressed - may be null + * @param x + * - the x position + * @param y + * - the y position + **/ + public WebRequest getRequest(SubmitButton button, int x, int y) { + if (button == null) + button = getDefaultButton(); + if (HttpUnitOptions.getParameterValuesValidated()) { + if (button == null) { + throw new IllegalUnnamedSubmitButtonException(); + } else if (button.isFake()) { + // bypass checks + } else if (!getSubmitButtonVector().contains(button)) { + throw new IllegalSubmitButtonException(button); + } else if (!button.wasEnabled()) { + // this is too late for the check of isDisabled() + // onclick has already been done ... + // [ 1289151 ] Order of events in button.click() is wrong + button.throwDisabledException(); + } + } - /** - * Returns true if a parameter starting with a given name exists, - **/ - public boolean hasParameterStartingWithPrefix( String prefix ) { - String[] names = getParameterNames(); - for (int i = 0; i < names.length; i++) { - if (names[i].startsWith( prefix )) return true; - } - return false; - } + SubmitButton[] buttons = getSubmitButtons(); + for (int i = 0; i < buttons.length; i++) { + buttons[i].setPressed(false); + } + button.setPressed(true); + if (getMethod().equalsIgnoreCase("post")) { + return new PostMethodWebRequest(this, button, x, y); + } else { + return new GetMethodWebRequest(this, + WebRequest.newParameterHolder(this), button, x, y); + } + } - /** - * Returns an array containing all of the buttons defined for this form. - **/ - public Button[] getButtons() { - if (_buttons == null) { - FormControl[] controls = getFormControls(); - ArrayList buttonList = new ArrayList(); - for (int i = 0; i < controls.length; i++) { - FormControl control = controls[ i ]; - if (control instanceof Button) buttonList.add( control ); - } - _buttons = (Button[]) buttonList.toArray( new Button[ buttonList.size() ] ); - } - return _buttons; - } + /** + * Creates and returns a web request which includes the specified button. If + * no button is specified, will include the default button, if any. No + * parameter validation will be done on the returned request and no scripts + * will be run when it is submitted. + **/ + public WebRequest newUnvalidatedRequest(SubmitButton button) { + return newUnvalidatedRequest(button, 0, 0); + } + /** + * Creates and returns a web request which includes the specified button and + * position. If no button is specified, will include the default button, if + * any. No parameter validation will be done on the returned request and no + * scripts will be run when it is submitted. + **/ + public WebRequest newUnvalidatedRequest(SubmitButton button, int x, int y) { + if (button == null) + button = getDefaultButton(); - public Button getButton( HTMLElementPredicate predicate, Object criteria ) { - Button[] buttons = getButtons(); - for (int i = 0; i < buttons.length; i++) { - if (predicate.matchesCriteria( buttons[i], criteria )) return buttons[i]; - } - return null; - } + SubmitButton[] buttons = getSubmitButtons(); + for (int i = 0; i < buttons.length; i++) { + buttons[i].setPressed(false); + } + button.setPressed(true); + if (getMethod().equalsIgnoreCase("post")) { + return new PostMethodWebRequest(this, new UncheckedParameterHolder( + this), button, x, y); + } else { + return new GetMethodWebRequest(this, new UncheckedParameterHolder( + this), button, x, y); + } + } - /** - * Convenience method which returns the button with the specified ID. - */ - public Button getButtonWithID( String buttonID ) { - return getButton( Button.WITH_ID, buttonID ); - } + private WebRequest getScriptedSubmitRequest() { + SubmitButton[] buttons = getSubmitButtons(); + for (int i = 0; i < buttons.length; i++) { + buttons[i].setPressed(false); + } + if (getMethod().equalsIgnoreCase("post")) { + return new PostMethodWebRequest(this); + } else { + return new GetMethodWebRequest(this); + } - /** - * Returns an array containing the submit buttons defined for this form. - **/ - public SubmitButton[] getSubmitButtons() { - if (_submitButtons == null) { - Vector buttons = getSubmitButtonVector(); - _submitButtons = new SubmitButton[ buttons.size() ]; - buttons.copyInto( _submitButtons ); - } - return _submitButtons; - } + } + /** + * Returns the default value of the named parameter. If the parameter does + * not exist returns null. + **/ + public String getParameterValue(String name) { + String[] values = getParameterValues(name); + return values.length == 0 ? null : values[0]; + } - /** - * Returns the submit button defined in this form with the specified name. - * If more than one such button exists, will return the first found. - * If no such button is found, will return null. - **/ - public SubmitButton getSubmitButton( String name ) { - SubmitButton[] buttons = getSubmitButtons(); - for (int i = 0; i < buttons.length; i++) { - if (buttons[i].getName().equals( name )) { - return buttons[i]; - } - } - return null; - } + /** + * Returns the displayed options defined for the specified parameter name. + **/ + public String[] getOptions(String name) { + return getParameter(name).getOptions(); + } + /** + * Returns the option values defined for the specified parameter name. + **/ + public String[] getOptionValues(String name) { + return getParameter(name).getOptionValues(); + } - /** - * Returns the submit button defined in this form with the specified name and value. - * If more than one such button exists, will return the first found. - * If no such button is found, will return null. - **/ - public SubmitButton getSubmitButton( String name, String value ) { - SubmitButton[] buttons = getSubmitButtons(); - for (int i = 0; i < buttons.length; i++) { - if (buttons[i].getName().equals( name ) && buttons[i].getValue().equals( value )) { - return buttons[i]; - } - } - return null; - } + /** + * Returns true if the named parameter accepts multiple values. + **/ + public boolean isMultiValuedParameter(String name) { + return getParameter(name).isMultiValuedParameter(); + } + /** + * Returns the number of text parameters in this form with the specified + * name. + **/ + public int getNumTextParameters(String name) { + return getParameter(name).getNumTextParameters(); + } - /** - * Returns the submit button defined in this form with the specified ID. - * If more than one such button exists, will return the first found. - * If no such button is found, will return null. - **/ - public SubmitButton getSubmitButtonWithID( String ID ) { - SubmitButton[] buttons = getSubmitButtons(); - for (int i = 0; i < buttons.length; i++) { - if (buttons[i].getID().equals( ID )) { - return buttons[i]; - } - } - return null; - } + /** + * Returns true if the named parameter accepts free-form text. + **/ + public boolean isTextParameter(String name) { + return getParameter(name).isTextParameter(); + } + /** + * Returns true if this form is to be submitted using mime encoding (the + * default is URL encoding). + **/ + public boolean isSubmitAsMime() { + return "multipart/form-data".equalsIgnoreCase(getAttribute("enctype")); + } - /** - * Creates and returns a web request which will simulate the submission of this form with a button with the specified name and value. - **/ - public WebRequest getRequest( String submitButtonName, String submitButtonValue ) { - SubmitButton sb = getSubmitButton( submitButtonName, submitButtonValue ); - if (sb == null) throw new IllegalSubmitButtonException( submitButtonName, submitButtonValue ); - return getRequest( sb ); - } + public FormScriptable getScriptableObject() { + return (FormScriptable) getScriptingHandler(); + } + /** + * Resets all parameters to their initial values. + */ + public void reset() { + if (handleEvent("onreset")) + resetControls(); + } - /** - * Creates and returns a web request which will simulate the submission of this form with a button with the specified name. - **/ - public WebRequest getRequest( String submitButtonName ) { - SubmitButton sb = getSubmitButton( submitButtonName ); - if (sb == null) throw new IllegalSubmitButtonException( submitButtonName, "" ); - return getRequest( sb ); - } + private void resetControls() { + FormControl[] controls = getFormControls(); + for (int i = 0; i < controls.length; i++) { + controls[i].reset(); + } + } + public ScriptableDelegate newScriptable() { + return new Scriptable(); + } - /** - * Creates and returns a web request which will simulate the submission of this form by pressing the specified button. - * If the button is null, simulates the pressing of the default button. - **/ - public WebRequest getRequest( SubmitButton button ) { - return getRequest( button, 0, 0 ); - } + // ---------------------------------- WebRequestSource methods + // -------------------------------- + /** + * Returns the character set encoding for this form. + **/ + public String getCharacterSet() { + return _characterSet; + } - /** - * Creates and returns a web request which will simulate the submission of this form by pressing the specified button. - * If the button is null, simulates the pressing of the default button. - * @param button - the submitbutton to be pressed - may be null - * @param x - the x position - * @param y - the y position - **/ - public WebRequest getRequest( SubmitButton button, int x, int y ) { - if (button == null) - button = getDefaultButton(); + /** + * Returns true if the named parameter accepts files for upload. + **/ + public boolean isFileParameter(String name) { + return getParameter(name).isFileParameter(); + } - if (HttpUnitOptions.getParameterValuesValidated()) { - if (button == null) { - throw new IllegalUnnamedSubmitButtonException(); - } else if (button.isFake()) { - // bypass checks - } else if (!getSubmitButtonVector().contains( button )) { - throw new IllegalSubmitButtonException( button ); - } else if (!button.wasEnabled()) { - // this is too late for the check of isDisabled() - // onclick has already been done ... - // [ 1289151 ] Order of events in button.click() is wrong - button.throwDisabledException(); - } - } + /** + * Returns an array containing the names of the parameters defined for this + * form. + **/ + public String[] getParameterNames() { + ArrayList parameterNames = new ArrayList(getFormParameters().keySet()); + return (String[]) parameterNames.toArray(new String[parameterNames + .size()]); + } - SubmitButton[] buttons = getSubmitButtons(); - for (int i = 0; i < buttons.length; i++) { - buttons[i].setPressed( false ); - } - button.setPressed( true ); + /** + * Returns the multiple default values of the named parameter. + **/ + public String[] getParameterValues(String name) { + final FormParameter parameter = getParameter(name); + return parameter.getValues(); + } - if (getMethod().equalsIgnoreCase( "post" )) { - return new PostMethodWebRequest( this, button, x, y ); - } else { - return new GetMethodWebRequest( this, WebRequest.newParameterHolder( this ), button, x, y ); - } - } + /** + * Returns true if the named parameter is read-only. If more than one + * control exists with the same name, will return true only if all such + * controls are read-only. + **/ + public boolean isReadOnlyParameter(String name) { + return getParameter(name).isReadOnlyParameter(); + } + /** + * Returns true if the named parameter is disabled. If more than one control + * exists with the same name, will return true only if all such controls are + * read-only. + **/ + public boolean isDisabledParameter(String name) { + return getParameter(name).isDisabledParameter(); + } - /** - * Creates and returns a web request which includes the specified button. If no button is specified, will include - * the default button, if any. No parameter validation will be done on the returned request and no scripts - * will be run when it is submitted. - **/ - public WebRequest newUnvalidatedRequest( SubmitButton button ) { - return newUnvalidatedRequest( button, 0, 0 ); - } + /** + * Returns true if the named parameter is hidden. If more than one control + * exists with the same name, will return true only if all such controls are + * hidden. + **/ + public boolean isHiddenParameter(String name) { + return getParameter(name).isHiddenParameter(); + } + /** + * Creates and returns a web request which will simulate the submission of + * this form with an unnamed submit button. + **/ + public WebRequest getRequest() { + return getRequest((SubmitButton) null); + } - /** - * Creates and returns a web request which includes the specified button and position. If no button is specified, - * will include the default button, if any. No parameter validation will be done on the returned request - * and no scripts will be run when it is submitted. - **/ - public WebRequest newUnvalidatedRequest( SubmitButton button, int x, int y ) { - if (button == null) button = getDefaultButton(); + /** + * Creates and returns a web request based on the current state of this + * form. No parameter validation will be done and there is no guarantee over + * the order of parameters transmitted. + */ + public WebRequest newUnvalidatedRequest() { + return newUnvalidatedRequest(null); + } - SubmitButton[] buttons = getSubmitButtons(); - for (int i = 0; i < buttons.length; i++) { - buttons[i].setPressed( false ); - } - button.setPressed( true ); + /** + * Records a parameter defined by including it in the destination URL. + * Ignores any parameters whose name matches a form control. + **/ + protected void addPresetParameter(String name, String value) { + FormControl[] formControls = getFormControls(); + for (int i = 0; i < formControls.length; i++) { + if (formControls[i].getName().equals(name)) + return; + } + _presets.add(new PresetFormParameter(this, name, value)); + } - if (getMethod().equalsIgnoreCase( "post" )) { - return new PostMethodWebRequest( this, new UncheckedParameterHolder( this ), button, x, y ); - } else { - return new GetMethodWebRequest( this, new UncheckedParameterHolder( this ), button, x, y ); - } - } + protected String getEmptyParameterValue() { + return null; + } + // ---------------------------------- ParameterHolder methods + // -------------------------------- - private WebRequest getScriptedSubmitRequest() { - SubmitButton[] buttons = getSubmitButtons(); - for (int i = 0; i < buttons.length; i++) { - buttons[i].setPressed( false ); - } + /** + * Specifies the position at which an image button (if any) was clicked. + **/ + void selectImageButtonPosition(SubmitButton imageButton, int x, int y) { + imageButton.setLocation(x, y); + } - if (getMethod().equalsIgnoreCase( "post" )) { - return new PostMethodWebRequest( this ); - } else { - return new GetMethodWebRequest( this ); - } + /** + * Iterates through the fixed, predefined parameters in this holder, + * recording them in the supplied parameter processor.\ These parameters + * always go on the URL, no matter what encoding method is used. + **/ - } + void recordPredefinedParameters(ParameterProcessor processor) + throws IOException { + FormControl[] controls = getPresetParameters(); + for (int i = 0; i < controls.length; i++) { + controls[i].addValues(processor, getCharacterSet()); + } + } + /** + * Iterates through the parameters in this holder, recording them in the + * supplied parameter processor. + **/ + public void recordParameters(ParameterProcessor processor) + throws IOException { + FormControl[] controls = getFormControls(); + for (int i = 0; i < controls.length; i++) { + controls[i].addValues(processor, getCharacterSet()); + } + } - /** - * Returns the default value of the named parameter. If the parameter does not exist returns null. - **/ - public String getParameterValue( String name ) { - String[] values = getParameterValues( name ); - return values.length == 0 ? null : values[0]; - } + /** + * Removes a parameter name from this collection. + **/ + public void removeParameter(String name) { + setParameter(name, NO_VALUES); + } + /** + * Sets the value of a parameter in this form. + * + * @param name + * - the name of the parameter + * @param value + * - the value of the parameter + **/ + public void setParameter(String name, String value) { + setParameter(name, new String[] { value }); + } - /** - * Returns the displayed options defined for the specified parameter name. - **/ - public String[] getOptions( String name ) { - return getParameter( name ).getOptions(); - } + /** + * Sets the multiple values of a parameter in this form. This is generally + * used when there are multiple controls with the same name in the form. + * @param name + * @param values + */ + public void setParameter(String name, final String[] values) { + FormParameter parameter = getParameter(name); + if (parameter.isUnknown()) + throw new NoSuchParameterException(name); + if (parameter.isFileParameter()) { + if (values == NO_VALUES) { + parameter.setFiles(new UploadFileSpec[0]); + } else { + throw new InvalidFileParameterException(name, values); + } + } else { + parameter.setValues(values); + } + } - /** - * Returns the option values defined for the specified parameter name. - **/ - public String[] getOptionValues( String name ) { - return getParameter( name ).getOptionValues(); - } + /** + * Sets the multiple values of a file upload parameter in a web request. + **/ + public void setParameter(String name, UploadFileSpec[] files) { + FormParameter parameter = getParameter(name); + if ((parameter == null) || (!parameter.isFileParameter())) + throw new NoSuchParameterException(name); + parameter.setFiles(files); + } + /** + * Sets the single value of a file upload parameter in this form. A more + * convenient way to do this than using + * {@link #setParameter(String,com.meterware.httpunit.protocol.UploadFileSpec[])} + * + * @since 1.6 + */ + public void setParameter(String name, File file) { + setParameter(name, new UploadFileSpec[] { new UploadFileSpec(file) }); + } - /** - * Returns true if the named parameter accepts multiple values. - **/ - public boolean isMultiValuedParameter( String name ) { - return getParameter( name ).isMultiValuedParameter(); - } + /** + * Toggles the value of the specified checkbox parameter. + * + * @param name + * the name of the checkbox parameter + * @throws IllegalArgumentException + * if the specified parameter is not a checkbox or there is more + * than one control with that name. + * @since 1.5.4 + */ + public void toggleCheckbox(String name) { + FormParameter parameter = getParameter(name); + if (parameter == null) + throw new NoSuchParameterException(name); + parameter.toggleCheckbox(); + } + /** + * Toggles the value of the specified checkbox parameter. + * + * @param name + * the name of the checkbox parameter + * @param value + * of the checkbox parameter + * @throws IllegalArgumentException + * if the specified parameter is not a checkbox or if there is + * no checkbox with the specified name and value. + * @since 1.6 + */ + public void toggleCheckbox(String name, String value) { + FormParameter parameter = getParameter(name); + if (parameter == null) + throw new NoSuchParameterException(name); + parameter.toggleCheckbox(value); + } - /** - * Returns the number of text parameters in this form with the specified name. - **/ - public int getNumTextParameters( String name ) { - return getParameter( name ).getNumTextParameters(); - } + /** + * Sets the value of the specified checkbox parameter. + * + * @param name + * the name of the checkbox parameter + * @param state + * the new state of the checkbox + * @throws IllegalArgumentException + * if the specified parameter is not a checkbox or there is more + * than one control with that name. + * @since 1.5.4 + */ + public void setCheckbox(String name, boolean state) { + FormParameter parameter = getParameter(name); + if (parameter == null) + throw new NoSuchParameterException(name); + parameter.setValue(state); + } + /** + * Sets the value of the specified checkbox parameter. + * + * @param name + * the name of the checkbox parameter + * @param value + * of the checkbox parameter + * @param state + * the new state of the checkbox + * @throws IllegalArgumentException + * if the specified parameter is not a checkbox or if there is + * no checkbox with the specified name and value. + * @since 1.6 + */ + public void setCheckbox(String name, String value, boolean state) { + FormParameter parameter = getParameter(name); + if (parameter == null) + throw new NoSuchParameterException(name); + parameter.setValue(value, state); + } - /** - * Returns true if the named parameter accepts free-form text. - **/ - public boolean isTextParameter( String name ) { - return getParameter( name ).isTextParameter(); - } + /** + * + * Scriptable implementation for the WebForm + * + */ + public class Scriptable extends HTMLElementScriptable implements + NamedDelegate, IdentifiedDelegate, FormScriptable { + public String getAction() { + return WebForm.this.getAction(); + } + public void setAction(String newAction) { + setDestination(newAction); + _presetParameters = null; + } - /** - * Returns true if this form is to be submitted using mime encoding (the default is URL encoding). - **/ - public boolean isSubmitAsMime() { - return "multipart/form-data".equalsIgnoreCase( getAttribute( "enctype" ) ); - } + public void submit() throws IOException, SAXException { + submitRequest(getScriptedSubmitRequest()); + } + public void reset() throws IOException, SAXException { + resetControls(); + } - public FormScriptable getScriptableObject() { - return (FormScriptable) getScriptingHandler(); - } + /** + * return the name of the WebForm + * + * @return the name + */ + public String getName() { + return WebForm.this.getName().length() != 0 ? WebForm.this + .getName() : WebForm.this.getID(); + } - /** - * Resets all parameters to their initial values. - */ - public void reset() { - if (handleEvent("onreset")) - resetControls(); - } + /** + * return the id of the WebForm + * + * @return the id + */ + public String getID() { + return WebForm.this.getID(); + } + /** + * get the Object for the given propertyName + * + * @param propertyName + * - the name of the property to get + * @return the Object for the property + */ + public Object get(String propertyName) { + if (propertyName.equals("target")) { + return getTarget(); + } else if (propertyName.equals("action")) { + return getAction(); + } else if (propertyName.equals("length")) { + return new Integer(getFormControls().length); + } else { + final FormParameter parameter = getParameter(propertyName); + if (!parameter.isUnknown()) + return parameter.getScriptableObject(); + FormControl control = getControlWithID(propertyName); + return control == null ? super.get(propertyName) : control + .getScriptingHandler(); + } + } - private void resetControls() { - FormControl[] controls = getFormControls(); - for (int i = 0; i < controls.length; i++) { - controls[i].reset(); - } - } + /** + * Sets the value of the named property. Will throw a runtime exception + * if the property does not exist or cannot accept the specified value. + * + * @param propertyName + * - the name of the property + * @param value + * - the new value + **/ + public void set(String propertyName, Object value) { + if (propertyName.equals("target")) { + setTargetAttribute(value.toString()); + } else if (propertyName.equals("action")) { + setAction(value.toString()); + } else if (propertyName.equals("name")) { + getElement().setAttribute("name", value.toString()); + } else if (value instanceof String) { + setParameterValue(propertyName, (String) value); + } else if (value instanceof Number) { + setParameterValue(propertyName, + HttpUnitUtils.trimmedValue((Number) value)); + } else { + super.set(propertyName, value); + } + } + public void setParameterValue(String name, String value) { + final Object scriptableObject = getParameter(name) + .getScriptableObject(); + if (scriptableObject instanceof ScriptableDelegate) { + ((ScriptableDelegate) scriptableObject).set("value", value); + } else if (scriptableObject instanceof ScriptableDelegate[]) { + ((ScriptableDelegate[]) scriptableObject)[0] + .set("value", value); + } + } - public ScriptableDelegate newScriptable() { - return new Scriptable(); - } + public ScriptableDelegate[] getElementDelegates() { + FormControl[] controls = getFormControls(); + ScriptableDelegate[] result = new ScriptableDelegate[controls.length]; + for (int i = 0; i < result.length; i++) { + result[i] = (ScriptableDelegate) controls[i] + .getScriptingHandler(); + } + return result; + } -//---------------------------------- WebRequestSource methods -------------------------------- + public ScriptableDelegate[] getElementsByTagName(String name) + throws SAXException { + return getDelegates(getHTMLPage().getElementsByTagName( + getElement(), name)); + } - /** - * Returns the character set encoding for this form. - **/ - public String getCharacterSet() { - return _characterSet; - } + Scriptable() { + super(WebForm.this); + } + } + // ---------------------------------- package members + // -------------------------------- - /** - * Returns true if the named parameter accepts files for upload. - **/ - public boolean isFileParameter( String name ) { - return getParameter( name ).isFileParameter(); - } + /** + * Contructs a web form given the URL of its source page and the DOM + * extracted from that page. + **/ + WebForm(WebResponse response, URL baseURL, Node node, FrameSelector frame, + String defaultTarget, String characterSet, ElementRegistry registry) { + super(response, node, baseURL, "action", frame, defaultTarget); + _characterSet = characterSet; + _registry = registry; + _domElement = (HTMLFormElement) node; + } + /** + * Returns the form control which is part of this form with the specified + * ID. + */ + public FormControl getControlWithID(String id) { + FormControl[] controls = getFormControls(); + for (int i = 0; i < controls.length; i++) { + FormControl control = controls[i]; + if (control.getID().equals(id)) + return control; + } + return null; + } - /** - * Returns an array containing the names of the parameters defined for this form. - **/ - public String[] getParameterNames() { - ArrayList parameterNames = new ArrayList( getFormParameters().keySet() ); - return (String[]) parameterNames.toArray( new String[ parameterNames.size() ] ); - } + // ---------------------------------- private members + // -------------------------------- + private SubmitButton getDefaultButton() { + if (getSubmitButtons().length == 1) { + return getSubmitButtons()[0]; + } else { + return getSubmitButton(""); + } + } - /** - * Returns the multiple default values of the named parameter. - **/ - public String[] getParameterValues( String name ) { - final FormParameter parameter = getParameter( name ); - return parameter.getValues(); - } + /** + * get the Vector of submit buttons - will always contain at least one + * button - if the original vector has none a faked submit button will be + * added + * + * @return a Vector with the submit buttons + */ + private Vector getSubmitButtonVector() { + if (_buttonVector == null) { + _buttonVector = new Vector(); + FormControl[] controls = getFormControls(); + for (int i = 0; i < controls.length; i++) { + FormControl control = controls[i]; + if (control instanceof SubmitButton) { + SubmitButton sb = (SubmitButton) control; + sb.rememberEnableState(); + _buttonVector.add(sb); + } + } + /** + * make sure that there is always at least one submit button if none + * is in the Vector add a faked one + */ + if (_buttonVector.isEmpty()) + _buttonVector.addElement(SubmitButton + .createFakeSubmitButton(this)); + } + return _buttonVector; + } - /** - * Returns true if the named parameter is read-only. If more than one control exists with the same name, - * will return true only if all such controls are read-only. - **/ - public boolean isReadOnlyParameter( String name ) { - return getParameter( name ).isReadOnlyParameter(); - } + private FormControl[] getPresetParameters() { + if (_presetParameters == null) { + _presets = new ArrayList(); + loadDestinationParameters(); + _presetParameters = (FormControl[]) _presets + .toArray(new FormControl[_presets.size()]); + } + return _presetParameters; + } + FormControl newFormControl(Node child) { + return FormControl.newFormParameter(this, child); + } - /** - * Returns true if the named parameter is disabled. If more than one control exists with the same name, - * will return true only if all such controls are read-only. - **/ - public boolean isDisabledParameter( String name ) { - return getParameter( name ).isDisabledParameter(); - } + /** + * Returns an array of form parameter attributes for this form. + **/ + private FormControl[] getFormControls() { + HTMLCollection controlElements = _domElement.getElements(); + FormControl[] controls = new FormControl[controlElements.getLength()]; + for (int i = 0; i < controls.length; i++) { + controls[i] = getControlForNode(controlElements.item(i)); + } + return controls; + } + private FormControl getControlForNode(Node node) { + if (_registry.hasNode(node)) { + return (FormControl) _registry.getRegisteredElement(node); + } else { + return (FormControl) _registry.registerElement(node, + newFormControl(node)); + } + } - /** - * Returns true if the named parameter is hidden. If more than one control exists with the same name, - * will return true only if all such controls are hidden. - **/ - public boolean isHiddenParameter( String name ) { - return getParameter( name ).isHiddenParameter(); - } + /** + * get the form parameter with the given name + * + * @param name + * @return the form parameter with this name + */ + public FormParameter getParameter(String name) { + final FormParameter parameter = ((FormParameter) getFormParameters() + .get(name)); + return parameter != null ? parameter : FormParameter + .getUNKNOWN_PARAMETER(); + } + /** + * Returns a map of parameter name to form parameter objects. Each form + * parameter object represents the set of form controls with a particular + * name. Unnamed parameters are ignored. + */ + private Map getFormParameters() { + Map formParameters = new HashMap(); + loadFormParameters(formParameters, getPresetParameters()); + loadFormParameters(formParameters, getFormControls()); + return formParameters; + } - /** - * Creates and returns a web request which will simulate the submission of this form with an unnamed submit button. - **/ - public WebRequest getRequest() { - return getRequest( (SubmitButton) null ); - } + private void loadFormParameters(Map formParameters, FormControl[] controls) { + for (int i = 0; i < controls.length; i++) { + if (controls[i].getName().length() == 0) + continue; + FormParameter parameter = (FormParameter) formParameters + .get(controls[i].getName()); + if (parameter == null) { + parameter = new FormParameter(); + formParameters.put(controls[i].getName(), parameter); + } + parameter.addControl(controls[i]); + } + } + static { + MATCH_NAME = new HTMLElementPredicate() { + public boolean matchesCriteria(Object htmlElement, Object criteria) { + return HttpUnitUtils.matches(((WebForm) htmlElement).getName(), + (String) criteria); + } + }; - /** - * Creates and returns a web request based on the current state of this form. No parameter validation will be done - * and there is no guarantee over the order of parameters transmitted. - */ - public WebRequest newUnvalidatedRequest() { - return newUnvalidatedRequest( null ); - } + } + // ===========================---===== exception class + // NoSuchParameterException ========================================= - /** - * Records a parameter defined by including it in the destination URL. Ignores any parameters whose name matches - * a form control. - **/ - protected void addPresetParameter( String name, String value ) { - FormControl[] formControls = getFormControls(); - for (int i = 0; i < formControls.length; i++) { - if (formControls[i].getName().equals( name)) return; - } - _presets.add( new PresetFormParameter( this, name, value ) ); - } + /** + * This exception is thrown on an attempt to set a file parameter to a non + * file type + **/ + class InvalidFileParameterException extends + IllegalRequestParameterException { + /** + * construct a new InvalidFileParameterException for the given parameter + * name and value list + * + * @param parameterName + * @param values + */ + InvalidFileParameterException(String parameterName, String[] values) { + _parameterName = parameterName; + _values = values; + } - protected String getEmptyParameterValue() { - return null; - } + /** + * get the message for this exception + */ + public String getMessage() { + String valueList = ""; + String delim = ""; + for (int i = 0; i < _values.length; i++) { + valueList += delim + "'" + _values[i] + "'"; + delim = ", "; + } + String msg = "The file parameter with the name '" + _parameterName + + "' must have type File but the string values " + + valueList + " where supplied"; + return msg; + } + private String _parameterName; + private String[] _values; + } -//---------------------------------- ParameterHolder methods -------------------------------- + /** + * This exception is thrown on an attempt to set a parameter to a value not + * permitted to it by the form. + **/ + class NoSuchParameterException extends IllegalRequestParameterException { + NoSuchParameterException(String parameterName) { + _parameterName = parameterName; + } - /** - * Specifies the position at which an image button (if any) was clicked. - **/ - void selectImageButtonPosition( SubmitButton imageButton, int x, int y ) { - imageButton.setLocation( x, y ); - } + public String getMessage() { + return "No parameter named '" + _parameterName + + "' is defined in the form"; + } + private String _parameterName; - /** - * Iterates through the fixed, predefined parameters in this holder, recording them in the supplied parameter processor.\ - * These parameters always go on the URL, no matter what encoding method is used. - **/ + } - void recordPredefinedParameters( ParameterProcessor processor ) throws IOException { - FormControl[] controls = getPresetParameters(); - for (int i = 0; i < controls.length; i++) { - controls[i].addValues( processor, getCharacterSet() ); - } - } + // ============================= exception class + // IllegalUnnamedSubmitButtonException + // ====================================== + /** + * This exception is thrown on an attempt to define a form request with a + * button not defined on that form. + **/ + class IllegalUnnamedSubmitButtonException extends + IllegalRequestParameterException { - /** - * Iterates through the parameters in this holder, recording them in the supplied parameter processor. - **/ - public void recordParameters( ParameterProcessor processor ) throws IOException { - FormControl[] controls = getFormControls(); - for (int i = 0; i < controls.length; i++) { - controls[i].addValues( processor, getCharacterSet() ); - } - } + IllegalUnnamedSubmitButtonException() { + } + public String getMessage() { + return "This form has more than one submit button, none unnamed. You must specify the button to be used."; + } - /** - * Removes a parameter name from this collection. - **/ - public void removeParameter( String name ) { - setParameter( name, NO_VALUES ); - } + } + // ============================= exception class + // IllegalSubmitButtonException ====================================== - /** - * Sets the value of a parameter in this form. - * @param name - the name of the parameter - * @param value - the value of the parameter - **/ - public void setParameter( String name, String value ) { - setParameter( name, new String[] { value } ); - } + /** + * This exception is thrown on an attempt to define a form request with a + * button not defined on that form. + **/ + class IllegalSubmitButtonException extends IllegalRequestParameterException { + IllegalSubmitButtonException(SubmitButton button) { + _name = button.getName(); + _value = button.getValue(); + } - /** - * Sets the multiple values of a parameter in this form. This is generally used when there are multiple - * controls with the same name in the form. - */ - public void setParameter( String name, final String[] values ) { - FormParameter parameter = getParameter( name ); - if (parameter.isUnknown()) throw new NoSuchParameterException( name ); - if (parameter.isFileParameter()) { - throw new InvalidFileParameterException(name,values); - } - parameter.setValues( values ); - } + IllegalSubmitButtonException(String name, String value) { + _name = name; + _value = value; + } + public String getMessage() { + return "Specified submit button (name=\"" + _name + "\" value=\"" + + _value + "\") not part of this form."; + } - /** - * Sets the multiple values of a file upload parameter in a web request. - **/ - public void setParameter( String name, UploadFileSpec[] files ) { - FormParameter parameter = getParameter( name ); - if ((parameter == null) || (!parameter.isFileParameter())) - throw new NoSuchParameterException( name ); - parameter.setFiles( files ); - } + private String _name; + private String _value; + } - /** - * Sets the single value of a file upload parameter in this form. - * A more convenient way to do this than using {@link #setParameter(String,com.meterware.httpunit.protocol.UploadFileSpec[])} - * @since 1.6 - */ - public void setParameter( String name, File file ) { - setParameter( name, new UploadFileSpec[] { new UploadFileSpec( file ) } ); - } + // ============================= exception class + // IllegalUnnamedSubmitButtonException + // ====================================== +} - /** - * Toggles the value of the specified checkbox parameter. - * @param name the name of the checkbox parameter - * @throws IllegalArgumentException if the specified parameter is not a checkbox or there is more than one - * control with that name. - * @since 1.5.4 - */ - public void toggleCheckbox( String name ) { - FormParameter parameter = getParameter( name ); - if (parameter == null) throw new NoSuchParameterException( name ); - parameter.toggleCheckbox(); - } +// ========================================== class PresetFormParameter +// ================================================= +class PresetFormParameter extends FormControl { - /** - * Toggles the value of the specified checkbox parameter. - * @param name the name of the checkbox parameter - * @param value of the checkbox parameter - * @throws IllegalArgumentException if the specified parameter is not a checkbox or if there is no checkbox - * with the specified name and value. - * @since 1.6 - */ - public void toggleCheckbox( String name, String value ) { - FormParameter parameter = getParameter( name ); - if (parameter == null) throw new NoSuchParameterException( name ); - parameter.toggleCheckbox( value ); - } + PresetFormParameter(WebForm form, String name, String value) { + super(form); + _name = name; + _value = value; + } + /** + * Returns the name of this control.. + **/ + public String getName() { + return _name; + } - /** - * Sets the value of the specified checkbox parameter. - * @param name the name of the checkbox parameter - * @param state the new state of the checkbox - * @throws IllegalArgumentException if the specified parameter is not a checkbox or there is more than one - * control with that name. - * @since 1.5.4 - */ - public void setCheckbox( String name, boolean state ) { - FormParameter parameter = getParameter( name ); - if (parameter == null) throw new NoSuchParameterException( name ); - parameter.setValue( state ); - } + /** + * Returns true if this control is read-only. + **/ + public boolean isReadOnly() { + return true; + } + /** + * Returns true if this control accepts free-form text. + **/ + public boolean isTextControl() { + return true; + } - /** - * Sets the value of the specified checkbox parameter. - * @param name the name of the checkbox parameter - * @param value of the checkbox parameter - * @param state the new state of the checkbox - * @throws IllegalArgumentException if the specified parameter is not a checkbox or if there is no checkbox - * with the specified name and value. - * @since 1.6 - */ - public void setCheckbox( String name, String value, boolean state ) { - FormParameter parameter = getParameter( name ); - if (parameter == null) throw new NoSuchParameterException( name ); - parameter.setValue( value, state ); - } + /** + * Remove any required values for this control from the list, throwing an + * exception if they are missing. + **/ + void claimRequiredValues(List values) { + if (_value != null) + claimValueIsRequired(values, _value); + } + public String getType() { + return UNDEFINED_TYPE; + } - /** - * - * Scriptable implementation for the WebForm - ... [truncated message content] |
From: <wol...@us...> - 2012-11-12 08:39:37
|
Revision: 1119 http://httpunit.svn.sourceforge.net/httpunit/?rev=1119&view=rev Author: wolfgang_fahl Date: 2012-11-12 08:39:31 +0000 (Mon, 12 Nov 2012) Log Message: ----------- javadoc comment added, Test for no servleet mapping defined added Modified Paths: -------------- trunk/httpunit/src/main/java/com/meterware/servletunit/WebApplication.java trunk/httpunit/src/test/java/com/meterware/servletunit/ConfigTest.java Modified: trunk/httpunit/src/main/java/com/meterware/servletunit/WebApplication.java =================================================================== --- trunk/httpunit/src/main/java/com/meterware/servletunit/WebApplication.java 2012-10-22 11:06:16 UTC (rev 1118) +++ trunk/httpunit/src/main/java/com/meterware/servletunit/WebApplication.java 2012-11-12 08:39:31 UTC (rev 1119) @@ -787,6 +787,11 @@ } + /** + * get the Servlet + * @return the Servlet from the configuration + * @throws ServletException - e.g. if no configuration is available + */ public Servlet getServlet() throws ServletException { if (getConfiguration() == null) throw new HttpNotFoundException( "No servlet mapping defined", _url ); Modified: trunk/httpunit/src/test/java/com/meterware/servletunit/ConfigTest.java =================================================================== --- trunk/httpunit/src/test/java/com/meterware/servletunit/ConfigTest.java 2012-10-22 11:06:16 UTC (rev 1118) +++ trunk/httpunit/src/test/java/com/meterware/servletunit/ConfigTest.java 2012-11-12 08:39:31 UTC (rev 1119) @@ -20,9 +20,11 @@ * *******************************************************************************************************************/ +import com.meterware.httpunit.HttpNotFoundException; import com.meterware.httpunit.WebClient; import com.meterware.httpunit.WebResponse; import org.junit.Test; +import org.xml.sax.SAXException; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -52,8 +54,31 @@ assertEquals("content type", "text/plain", response.getContentType()); assertEquals("servlet name is " + ConfigServlet.class.getName(), response.getText()); } + + @Test + /** + * Test added by WF 2012-11-12 to answer question on developers mailing list + * @throws Exception + */ + public void testInvalidConfig() throws Exception { + final String resourceName = "something/interesting"; + ServletRunner sr = new ServletRunner(); + sr.registerServlet(resourceName, ConfigServlet.class.getName()); + WebClient wc = sr.newClient(); + try { + WebResponse response = wc.getResponse("http://localhost/" + "ISB/"+ resourceName); + fail("No Exception thrown"); + } catch (Throwable th) { + // com.meterware.httpunit.HttpNotFoundException: + // Error on HTTP request: 404 No servlet mapping defined [http://localhost/ISB/something/interesting] + String expected="Error on HTTP request: 404 No servlet mapping defined [http://localhost/ISB/something/interesting]"; + assertTrue("HttpNotFoundException expected",th instanceof HttpNotFoundException); + assertEquals("wrong exception message",expected,th.getMessage()); + } + } + @Test public void testContextAttributes() throws Exception { final String servlet1Name = "something/interesting"; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |