WebRequest.java :  » Testing » HttpUnit » com » meterware » httpunit » Java Open Source

Java Open Source » Testing » HttpUnit 
HttpUnit » com » meterware » httpunit » WebRequest.java
package com.meterware.httpunit;
/********************************************************************************************************************
* $Id: WebRequest.java,v 1.63 2004/09/09 22:32:41 russgold Exp $
*
* Copyright (c) 2000-2004, 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 java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.net.*;

import java.util.*;

/**
 * A request sent to a web server.
 **/
abstract
public class WebRequest {

    static final String REFERER_HEADER_NAME = "Referer";

    private static URLStreamHandler JAVASCRIPT_STREAM_HANDLER = new JavascriptURLStreamHandler();
    private static URLStreamHandler HTTPS_STREAM_HANDLER = new HttpsURLStreamHandler();

    private final ParameterHolder _parameterHolder;

    private URL          _urlBase;
    private FrameSelector  _sourceFrame;
    private String       _requestTarget;
    private String       _urlString;
    private Hashtable    _headers;
    private WebRequestSource _webRequestSource;

    private SubmitButton _button;


    /**
     * Sets the value of a header to be sent with this request. A header set here will override any matching header set
     * in the WebClient when the request is actually sent.
     */
    public void setHeaderField( String headerName, String headerValue ) {
        getHeaderDictionary().put( headerName, headerValue );
    }

    /**
     * Returns a copy of the headers to be sent with this request.
     **/
    public Dictionary getHeaders() {
        return (Dictionary) getHeaderDictionary().clone();
    }


    /**
     * Returns the final URL associated with this web request.
     **/
    public URL getURL() throws MalformedURLException {
        if (getURLBase() == null || getURLBase().toString().indexOf( "?" ) < 0) {
            return newURL( getURLBase(), getURLString() );
        } else {
            final String urlBaseString = getURLBase().toString();
            URL newurlbase = new URL( urlBaseString.substring( 0, urlBaseString.indexOf( "?" ) ) );
            return newURL( newurlbase, getURLString() );
        }
    }


    /**
     * Creates a new URL, handling the case where the relative URL begins with a '?'
     */
    private URL newURL( final URL base, final String spec ) throws MalformedURLException {
        if (spec.toLowerCase().startsWith( "javascript:" )) {
            return new URL( "javascript", null, -1, spec.substring( "javascript:".length() ), JAVASCRIPT_STREAM_HANDLER );
        } else if (spec.toLowerCase().startsWith( "https:" ) && !HttpsProtocolSupport.hasHttpsSupport()) {
            return new URL( "https", null, -1, spec.substring( "https:".length() ), HTTPS_STREAM_HANDLER );
        } else {
            if (getURLBase() == null || getURLString().indexOf( ':' ) > 0) {
                if (getURLString().indexOf(':') <= 0) {
                    throw new RuntimeException( "No protocol specified in URL '" + getURLString() + "'" );
                }
                HttpsProtocolSupport.verifyProtocolSupport( getURLString().substring( 0, getURLString().indexOf( ':' ) ) );
            }
            return spec.startsWith( "?" ) ? new URL( base + spec ) : newCombinedURL( base, spec );
        }
    }


    private URL newCombinedURL( final URL base, final String spec ) throws MalformedURLException {
        if (base == null) return new URL( getNormalizedURL( spec ) );
        else if (spec.startsWith( ".." )) return new URL( getNormalizedURL( getURLDirectory( base ) + spec ) );
        else return new URL( base, getNormalizedURL( spec ) );
    }


    private String getURLDirectory( final URL base ) {
        String url = base.toExternalForm();
        int i = url.lastIndexOf( '/' );
        return url.substring( 0, i+1 );
    }


    private String getNormalizedURL( String url ) {
        int questionIndex = url.indexOf( '?' );
        if (questionIndex < 0) return getNormalizedPath( url );
        return getNormalizedPath( url.substring( 0, questionIndex ) ) + url.substring( questionIndex );
    }


    private String getNormalizedPath( String path ) {
        if (path.lastIndexOf( "//" ) > path.lastIndexOf( "://" ) + 1) return getNormalizedPath( stripDoubleSlashes( path ) );
        if (path.indexOf( "/.." ) > 0) return getNormalizedPath( stripUpNavigation( path ) );
        if (path.indexOf( "/./" ) > 0) return getNormalizedPath( stripInPlaceNavigation( path ) );
        return path;
    }


    private String stripInPlaceNavigation( String url ) {
        int i = url.lastIndexOf( "/./" );
        return url.substring( 0, i+1 ) + url.substring( i+2 );
     }


    private String stripUpNavigation( String url ) {
        int i = url.indexOf( "/.." );
        int j = url.lastIndexOf( "/", i-1 );
        return url.substring( 0, j+1 ) + url.substring( i+ 3 );
    }


    private String stripDoubleSlashes( String url ) {
        int i = url.lastIndexOf( "//" );
        return url.substring( 0, i ) + url.substring( i+1 );
    }


    /**
     * Returns the target for this web request.
     */
    public String getTarget() {
        return _requestTarget;
    }


    /**
     * Returns the frame from which this request originates.
     */
    FrameSelector getSourceFrame() {
        return _sourceFrame;
    }


    /**
     * Returns the HTTP method defined for this request.
     **/
    abstract
    public String getMethod();


    /**
     * Returns the query string defined for this request. The query string is sent to the HTTP server as part of
     * the request header. This default implementation returns an empty string.
     **/
    public String getQueryString() {
        return "";
    }


//------------------------------------- ParameterCollection methods ------------------------------------


    /**
     * Sets the value of a parameter in a web request.
     **/
    public void setParameter( String name, String value ) {
        _parameterHolder.setParameter( name, value );
    }


    /**
     * Sets the multiple values of a parameter in a web request.
     **/
    public void setParameter( String name, String[] values ) {
        _parameterHolder.setParameter( name, values );
    }


    /**
     * Sets the multiple values of a file upload parameter in a web request.
     **/
    public void setParameter( String parameterName, UploadFileSpec[] files ) {
        if (!maySelectFile( parameterName )) throw new IllegalNonFileParameterException( parameterName );
        if (!isMimeEncoded()) throw new MultipartFormRequiredException();
        _parameterHolder.setParameter( parameterName, files );
    }


    /**
     * Specifies the click position for the submit button. When a user clioks on an image button, not only the name
     * and value of the button, but also the position of the mouse at the time of the click is submitted with the form.
     * This method allows the caller to override the position selected when this request was created.
     *
     * @exception IllegalRequestParameterException thrown if the request was not created from a form with an image button.
     **/
    public void setImageButtonClickPosition( int x, int y ) throws IllegalRequestParameterException {
        if (_button == null) throw new IllegalButtonPositionException();
        _parameterHolder.selectImageButtonPosition( _button, x, y );
    }


    /**
     * Returns true if the specified parameter is a file field.
     **/
    public boolean isFileParameter( String name ) {
        return _parameterHolder.isFileParameter( name );
    }


    /**
     * Sets the file for a parameter upload in a web request.
     **/
    public void selectFile( String parameterName, File file ) {
        setParameter( parameterName, new UploadFileSpec[] { new UploadFileSpec( file ) } );
    }


    /**
     * Sets the file for a parameter upload in a web request.
     **/
    public void selectFile( String parameterName, File file, String contentType ) {
        setParameter( parameterName, new UploadFileSpec[] { new UploadFileSpec( file, contentType ) } );
    }


    /**
     * Sets the file for a parameter upload in a web request.
     **/
    public void selectFile( String parameterName, String fileName, InputStream inputStream, String contentType ) {
        setParameter( parameterName, new UploadFileSpec[] { new UploadFileSpec( fileName, inputStream, contentType ) } );
    }


    /**
     * Returns an array of all parameter names defined as part of this web request.
     * @since 1.3.1
     **/
    public String[] getRequestParameterNames() {
        final HashSet names = new HashSet();
        ParameterProcessor pp = new ParameterProcessor() {
            public void addParameter( String name, String value, String characterSet ) throws IOException {
                names.add( name );
            }
            public void addFile( String parameterName, UploadFileSpec fileSpec ) throws IOException {
                names.add( parameterName );
            }
        };

        try {
            _parameterHolder.recordPredefinedParameters( pp );
            _parameterHolder.recordParameters( pp );
        } catch (IOException e) {}

        return (String[]) names.toArray( new String[ names.size() ] );
    }


    /**
     * Returns the value of a parameter in this web request.
     * @return the value of the named parameter, or empty string
     *         if it is not set.
     **/
    public String getParameter( String name ) {
        String[] values = getParameterValues( name );
        return values.length == 0 ? "" : values[0];
    }


    /**
     * Returns the multiple default values of the named parameter.
     **/
    public String[] getParameterValues( String name ) {
        return _parameterHolder.getParameterValues( name );
    }


    /**
     * Removes a parameter from this web request.
     **/
    public void removeParameter( String name ) {
        _parameterHolder.removeParameter( name );
    }


//------------------------------------- Object methods ------------------------------------


    public String toString() {
        return getMethod() + " request for (" + getURLBase() + ") " + getURLString();
    }



//------------------------------------- protected members ------------------------------------


    /**
     * Constructs a web request using an absolute URL string.
     **/
    protected WebRequest( String urlString ) {
        this( null, urlString );
    }


    /**
     * Constructs a web request using a base URL and a relative URL string.
     **/
    protected WebRequest( URL urlBase, String urlString ) {
        this( urlBase, urlString, TOP_FRAME );
    }


    /**
     * Constructs a web request using a base request and a relative URL string.
     **/
    protected WebRequest( WebRequest baseRequest, String urlString, String target ) throws MalformedURLException {
        this( baseRequest.getURL(), urlString, target );
    }


    /**
     * Constructs a web request using a base URL, a relative URL string, and a target.
     **/
    protected WebRequest( URL urlBase, String urlString, String target ) {
        this( urlBase, urlString, FrameSelector.TOP_FRAME, target );
    }


    /**
     * Constructs a web request using a base URL, a relative URL string, and a target.
     **/
    protected WebRequest( URL urlBase, String urlString, FrameSelector frame, String target ) {
        this( urlBase, urlString, frame, target, new UncheckedParameterHolder() );
    }


    /**
     * Constructs a web request from a form.
     **/
    protected WebRequest( WebForm sourceForm, ParameterHolder parameterHolder, SubmitButton button, int x, int y ) {
        this( sourceForm, parameterHolder );
        if (button != null && button.isImageButton() && button.getName().length() > 0) {
            _button = button;
            _parameterHolder.selectImageButtonPosition( _button, x, y );
        }
    }


    protected WebRequest( WebRequestSource requestSource, ParameterHolder parameterHolder ) {
        this( requestSource.getBaseURL(), requestSource.getRelativePage(), requestSource.getFrame(), requestSource.getTarget(), parameterHolder );
        _webRequestSource = requestSource;
        setHeaderField( REFERER_HEADER_NAME, requestSource.getBaseURL().toExternalForm() );
    }


    static ParameterHolder newParameterHolder( WebRequestSource requestSource ) {
        if (HttpUnitOptions.getParameterValuesValidated()) {
            return requestSource;
        } else {
            return new UncheckedParameterHolder( requestSource );
        }
    }


    /**
     * Constructs a web request using a base URL, a relative URL string, and a target.
     **/
    private WebRequest( URL urlBase, String urlString, FrameSelector sourceFrame, String requestTarget, ParameterHolder parameterHolder ) {
        _urlBase   = urlBase;
        _sourceFrame = sourceFrame;
        _requestTarget = requestTarget;
        _urlString = urlString.toLowerCase().startsWith( "http" ) ? escape( urlString ) : urlString;
        _parameterHolder = parameterHolder;
    }


    private static String escape( String urlString ) {
        if (urlString.indexOf( ' ' ) < 0) return urlString;
        StringBuffer sb = new StringBuffer();

        int start = 0;
        do {
            int index = urlString.indexOf( ' ', start );
            if (index < 0) {
                sb.append( urlString.substring( start ) );
                break;
            } else {
                sb.append( urlString.substring( start, index ) ).append( "%20" );
                start = index+1;
            }
        } while (true);
        return sb.toString();
    }


    /**
     * Returns true if selectFile may be called with this parameter.
     */
    protected boolean maySelectFile( String parameterName )
    {
        return isFileParameter( parameterName );
    }


    /**
     * Selects whether MIME-encoding will be used for this request. MIME-encoding changes the way the request is sent
     * and is required for requests which include file parameters. This method may only be called for a POST request
     * which was not created from a form.
     **/
    protected void setMimeEncoded( boolean mimeEncoded )
    {
        _parameterHolder.setSubmitAsMime( mimeEncoded );
    }


    /**
     * Returns true if this request is to be MIME-encoded.
     **/
    protected boolean isMimeEncoded() {
        return _parameterHolder.isSubmitAsMime();
    }


    /**
     * Returns the content type of this request. If null, no content is specified.
     */
    protected String getContentType() {
        return null;
    }


    /**
     * Returns the character set required for this request.
     **/
    final
    protected String getCharacterSet() {
        return _parameterHolder.getCharacterSet();
    }


    /**
     * Performs any additional processing necessary to complete the request.
     **/
    protected void completeRequest( URLConnection connection ) throws IOException {
        if (connection instanceof HttpURLConnection) ((HttpURLConnection) connection).setRequestMethod( getMethod() );
    }


    /**
     * Writes the contents of the message body to the specified stream.
     */
    protected void writeMessageBody( OutputStream stream ) throws IOException {
    }


    final
    protected URL getURLBase() {
        return _urlBase;
    }


//------------------------------------- protected members ---------------------------------------------


    protected String getURLString() {
        final String queryString = getQueryString();
        if (queryString.length() == 0) {
            return _urlString;
        } else {
            return _urlString + "?" + queryString;
        }
    }


    final
    protected ParameterHolder getParameterHolder() {
        return _parameterHolder;
    }


//---------------------------------- package members --------------------------------

    /** The target indicating the topmost frame of a window. **/
    static final String TOP_FRAME = "_top";

    /** The target indicating the parent of a frame. **/
    static final String PARENT_FRAME = "_parent";

    /** The target indicating a new, empty window. **/
    static final String NEW_WINDOW = "_blank";

    /** The target indicating the same frame. **/
    static final String SAME_FRAME = "_self";

    WebRequestSource getWebRequestSource() {
        return _webRequestSource;
    }


    Hashtable getHeaderDictionary() {
        if (_headers == null) {
            _headers = new Hashtable();
            if (getContentType() != null) _headers.put( "Content-Type", getContentType() );
        }
        return _headers;
    }


    String getReferer() {
        return _headers == null ? null : (String) _headers.get( REFERER_HEADER_NAME );
    }

}


class URLEncodedString implements ParameterProcessor {

    private StringBuffer _buffer = new StringBuffer( HttpUnitUtils.DEFAULT_BUFFER_SIZE );

    private boolean _haveParameters = false;


    public String getString() {
        return _buffer.toString();
    }


    public void addParameter( String name, String value, String characterSet ) {
        if (_haveParameters) _buffer.append( '&' );
        _buffer.append( encode( name, characterSet ) );
        if (value != null) _buffer.append( '=' ).append( encode( value, characterSet ) );
        _haveParameters = true;
    }


    public void addFile( String parameterName, UploadFileSpec fileSpec ) {
        throw new RuntimeException( "May not URL-encode a file upload request" );
    }


    /**
     * Returns a URL-encoded version of the string, including all eight bits, unlike URLEncoder, which strips the high bit.
     **/
    private String encode( String source, String characterSet ) {
        if (characterSet.equalsIgnoreCase( HttpUnitUtils.DEFAULT_CHARACTER_SET )) {
            return URLEncoder.encode( source );
        } else {
            try {
                byte[] rawBytes = source.getBytes( characterSet );
                StringBuffer result = new StringBuffer( 3*rawBytes.length );
                for (int i = 0; i < rawBytes.length; i++) {
                    int candidate = rawBytes[i] & 0xff;
                    if (candidate == ' ') {
                        result.append( '+' );
                    } else if ((candidate >= 'A' && candidate <= 'Z') ||
                               (candidate >= 'a' && candidate <= 'z') ||
                               (candidate == '.') || (candidate == '-' ) ||
                               (candidate == '*') || (candidate == '_') ||
                               (candidate >= '0' && candidate <= '9')) {
                        result.append( (char) rawBytes[i] );
                    } else if (candidate < 16) {
                        result.append( "%0" ).append( Integer.toHexString( candidate ).toUpperCase() );
                    } else {
                        result.append( '%' ).append( Integer.toHexString( candidate ).toUpperCase() );
                    }
                }
                return result.toString();
            } catch (java.io.UnsupportedEncodingException e) {
                return "???";    // XXX should pass the exception through as IOException ultimately
            }
        }
    }

}





//======================================== class JavaScriptURLStreamHandler ============================================


class JavascriptURLStreamHandler extends URLStreamHandler {

    protected URLConnection openConnection( URL u ) throws IOException {
        return null;
    }
}


//======================================== class HttpsURLStreamHandler ============================================


class HttpsURLStreamHandler extends URLStreamHandler {

    protected URLConnection openConnection( URL u ) throws IOException {
        throw new RuntimeException( "https support requires the Java Secure Sockets Extension. See http://java.sun.com/products/jsse" );
    }
}


//============================= exception class IllegalNonFileParameterException ======================================


/**
 * This exception is thrown on an attempt to set a non-file parameter to a file value.
 **/
class IllegalNonFileParameterException extends IllegalRequestParameterException {


    IllegalNonFileParameterException( String parameterName ) {
        _parameterName = parameterName;
    }


    public String getMessage() {
        return "Parameter '" + _parameterName + "' is not a file parameter and may not be set to a file value.";
    }


    private String _parameterName;

}


//============================= exception class MultipartFormRequiredException ======================================


/**
 * This exception is thrown on an attempt to set a file parameter in a form that does not specify MIME encoding.
 **/
class MultipartFormRequiredException extends IllegalRequestParameterException {


    MultipartFormRequiredException() {
    }


    public String getMessage() {
        return "The request does not use multipart/form-data encoding, and cannot be used to upload files ";
    }

}


//============================= exception class IllegalButtonPositionException ======================================


/**
 * This exception is thrown on an attempt to set a file parameter in a form that does not specify MIME encoding.
 **/
class IllegalButtonPositionException extends IllegalRequestParameterException {


    IllegalButtonPositionException() {
    }


    public String getMessage() {
        return "The request was not created with an image button, and cannot accept an image button click position";
    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.