com.jayway.restassured.internal.http.URIBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rest-assured Show documentation
Show all versions of rest-assured Show documentation
Java DSL for easy testing of REST services
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.restassured.internal.http;
import com.jayway.restassured.config.EncoderConfig;
import org.apache.commons.lang3.Validate;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.protocol.HTTP;
import java.io.UnsupportedEncodingException;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.jayway.restassured.config.EncoderConfig.encoderConfig;
/**
* This class implements a mutable URI. All set
, add
* and remove
methods affect this class' internal URI
* representation. All mutator methods support chaining, e.g.
*
* new URIBuilder("http://www.google.com/")
* .setScheme( "https" )
* .setPort( 443 )
* .setPath( "some/path" )
* .toString();
*
* A slightly more 'Groovy' version would be:
*
* new URIBuilder('http://www.google.com/').with {
* scheme = 'https'
* port = 443
* path = 'some/path'
* query = [p1:1, p2:'two']
* }.toString()
*
* @author Tom Nichols
* @author Johan Haleby
*/
public class URIBuilder implements Cloneable {
private static final String PARAMETER_SEPARATOR = "&";
private static final String NAME_VALUE_SEPARATOR = "=";
protected URI base;
private String enc;
private final boolean isUrlEncodingEnabled;
/**
* @throws IllegalArgumentException if uri is null
* @param uri
*/
public URIBuilder( URI uri, boolean urlEncodingEnabled, EncoderConfig config) throws IllegalArgumentException {
Validate.notNull(uri, "uri cannot be null");
Validate.notNull(config, "encoder config cannot be null");
this.base = uri;
this.enc = config.defaultQueryParameterCharset();
this.isUrlEncodingEnabled = urlEncodingEnabled;
}
/**
* Utility method to convert a number of type to a URI instance.
* @param uri a {@link URI}, {@link URL} or any object that produces a
* valid URI string from its toString()
result.
* @return a valid URI parsed from the given object
* @throws URISyntaxException
*/
public static URI convertToURI( Object uri ) throws URISyntaxException {
if ( uri instanceof URI ) return (URI)uri;
if ( uri instanceof URL ) return ((URL)uri).toURI();
if ( uri instanceof URIBuilder ) return ((URIBuilder)uri).toURI();
return new URI( uri.toString() ); // assume any other object type produces a valid URI string
}
/**
* Set the URI scheme, AKA the 'protocol.' e.g.
* setScheme('https')
* @throws URISyntaxException if the given scheme contains illegal characters.
*/
public URIBuilder setScheme( String scheme ) throws URISyntaxException {
this.base = new URI( scheme, base.getUserInfo(),
base.getHost(), base.getPort(), base.getPath(),
base.getQuery(), base.getFragment() );
return this;
}
public URIBuilder setPort( int port ) throws URISyntaxException {
this.base = new URI( base.getScheme(), base.getUserInfo(),
base.getHost(), port, base.getPath(),
base.getQuery(), base.getFragment() );
return this;
}
public URIBuilder setHost( String host ) throws URISyntaxException {
this.base = new URI( base.getScheme(), base.getUserInfo(),
host, base.getPort(), base.getPath(),
base.getQuery(), base.getFragment() );
return this;
}
/**
* Set the path component of this URI. The value may be absolute or
* relative to the current path.
* e.g.
* def uri = new URIBuilder( 'http://localhost/p1/p2?a=1' )
*
* uri.path = '/p3/p2'
* assert uri.toString() == 'http://localhost/p3/p2?a=1'
*
* uri.path = 'p2a'
* assert uri.toString() == 'http://localhost/p3/p2a?a=1'
*
* uri.path = '../p4'
* assert uri.toString() == 'http://localhost/p4?a=1&b=2&c=3#frag'
*
* @param path the path portion of this URI, relative to the current URI.
* @return this URIBuilder instance, for method chaining.
* @throws URISyntaxException if the given path contains characters that
* cannot be converted to a valid URI
*/
public URIBuilder setPath( String path ) throws URISyntaxException {
if(isUrlEncodingEnabled) {
this.base = base.resolve( new URI(null,null, path, base.getQuery(), base.getFragment()) );
} else {
/* Passing the path string in the URI constructor will
* double-escape path parameters and goober things up. So we have
* to create a full path+query+fragment and use URI#resolve() to
* create the new URI. */
StringBuilder sb = new StringBuilder();
if ( path != null ) sb.append( path );
sb.append( '?' );
sb.append( base.getQuery() );
String frag = base.getRawFragment();
if ( frag != null ) sb.append( '#' ).append( frag );
this.base = base.resolve( sb.toString() );
}
return this;
}
/* TODO null/ zero-size check if this is ever made public */
protected URIBuilder setQueryNVP( List nvp ) throws URISyntaxException {
/* Passing the query string in the URI constructor will
* double-escape query parameters and goober things up. So we have
* to create a full path+query+fragment and use URI#resolve() to
* create the new URI. */
StringBuilder sb = new StringBuilder();
String path = base.getRawPath();
if ( path != null ) sb.append( path );
sb.append( '?' );
sb.append( format(nvp, isUrlEncodingEnabled, enc) );
String frag = base.getRawFragment();
if ( frag != null ) sb.append( '#' ).append( frag );
this.base = base.resolve( sb.toString() );
return this;
}
/**
* Set the query portion of the URI. For query parameters with multiple
* values, put the values in a list like so:
* uri.query = [ p1:'val1', p2:['val2', 'val3'] ]
* // will produce a query string of ?p1=val1&p2=val2&p2=val3
*
* @param params a Map of parameters that will be transformed into the query string
* @return this URIBuilder instance, for method chaining.
* @throws URISyntaxException
*/
public URIBuilder setQuery( Map,?> params ) throws URISyntaxException {
if ( params == null || params.size() < 1 ) {
this.base = new URI( base.getScheme(), base.getUserInfo(),
base.getHost(), base.getPort(), base.getPath(),
null, base.getFragment() );
}
else {
List nvp = new ArrayList(params.size());
for ( Object key : params.keySet() ) {
Object value = params.get(key);
if ( value instanceof List ) {
for (Object val : (List)value )
nvp.add(new BasicNameValuePairWithNoValueSupport( key.toString(), val) );
}
else nvp.add( new BasicNameValuePairWithNoValueSupport( key.toString(), value) );
}
this.setQueryNVP( nvp );
}
return this;
}
/**
* Get the query string as a map for convenience. If any parameter contains
* multiple values (e.g. p1=one&p1=two
) both values will be
* inserted into a list for that paramter key ([p1 : ['one','two']]
*
). Note that this is not a "live" map. Therefore, you cannot
* call
* uri.query.a = 'BCD'
* You will not modify the query string but instead the generated map of
* parameters. Instead, you need to use {@link #removeQueryParam(String)}
* first, then {@link #addQueryParam(String, Object)}, or call
* {@link #setQuery(Map)} which will set the entire query string.
* @return a map of String name/value pairs representing the URI's query
* string.
*/
public Map getQuery() {
Map params = new HashMap();
List pairs = this.getQueryNVP();
for ( NameValuePair pair : pairs ) {
String key = pair.getName();
Object existing = params.get( key );
if ( existing == null ) params.put( key, pair.getValue() );
else if ( existing instanceof List )
((List)existing).add( pair.getValue() );
else {
List vals = new ArrayList(2);
vals.add( (String)existing );
vals.add( pair.getValue() );
params.put( key, vals );
}
}
return params;
}
protected List getQueryNVP() {
List nvps = URLEncodedUtils.parse( this.base, enc );
List newList = new ArrayList();
if ( nvps != null ) newList.addAll( nvps );
return newList;
}
/**
* Indicates if the given parameter is already part of this URI's query
* string.
* @param name the query parameter name
* @return true if the given parameter name is found in the query string of
* the URI.
*/
public boolean hasQueryParam( String name ) {
return getQuery().get( name ) != null;
}
/**
* Remove the given query parameter from this URI's query string.
* @param param the query name to remove
* @return this URIBuilder instance, for method chaining.
* @throws URISyntaxException
*/
public URIBuilder removeQueryParam( String param ) throws URISyntaxException {
List params = getQueryNVP();
NameValuePair found = null;
for ( NameValuePair nvp : params ) // BOO linear search. Assume the list is small.
if ( nvp.getName().equals( param ) ) {
found = nvp;
break;
}
if ( found == null ) throw new IllegalArgumentException( "Param '" + param + "' not found" );
params.remove( found );
this.setQueryNVP( params );
return this;
}
protected URIBuilder addQueryParam( NameValuePair nvp ) throws URISyntaxException {
List params = getQueryNVP();
params.add( nvp );
this.setQueryNVP( params );
return this;
}
/**
* This will append a query parameter to the existing query string. If the given
* parameter is already part of the query string, it will be appended to.
* To replace the existing value of a certain parameter, either call
* {@link #removeQueryParam(String)} first, or use {@link #getQuery()},
* modify the value in the map, then call {@link #setQuery(Map)}.
* @param param query parameter name
* @param value query parameter value (will be converted to a string if
* not null. If value
is null, it will be set as the empty
* string.
* @return this URIBuilder instance, for method chaining.
* @throws URISyntaxException if the query parameter values cannot be
* converted to a valid URI.
* @see #setQuery(Map)
*/
public URIBuilder addQueryParam( String param, Object value ) throws URISyntaxException {
List params = getQueryNVP();
params.add( new BasicNameValuePairWithNoValueSupport( param, value ) );
this.setQueryNVP( params );
return this;
}
protected URIBuilder addQueryParams( List nvp ) throws URISyntaxException {
List params = getQueryNVP();
params.addAll( nvp );
this.setQueryNVP( params );
return this;
}
/**
* Add these parameters to the URIBuilder's existing query string.
* Parameters may be passed either as a single map argument, or as a list
* of named arguments. e.g.
* uriBuilder.addQueryParams( [one:1,two:2] )
* uriBuilder.addQueryParams( three : 3 )
*
* If any of the parameters already exist in the URI query, these values
* will not replace them. Multiple values for the same
* query parameter may be added by putting them in a list. See
* {@link #setQuery(Map)}.
*
* @param params parameters to add to the existing URI query (if any).
* @return this URIBuilder instance, for method chaining.
* @throws URISyntaxException
*/
@SuppressWarnings("unchecked")
public URIBuilder addQueryParams( Map,?> params ) throws URISyntaxException {
List nvp = new ArrayList();
for ( Object key : params.keySet() ) {
Object value = params.get( key );
if ( value instanceof List ) {
for ( Object val : (List)value )
nvp.add( new BasicNameValuePairWithNoValueSupport( key.toString(), val) );
}
else nvp.add( new BasicNameValuePairWithNoValueSupport( key.toString(), value) );
}
this.addQueryParams( nvp );
return this;
}
/**
* The document fragment, without a preceeding '#'
* @param fragment
* @return this URIBuilder instance, for method chaining.
* @throws URISyntaxException if the given value contains illegal characters.
*/
public URIBuilder setFragment( String fragment ) throws URISyntaxException {
this.base = new URI( base.getScheme(), base.getUserInfo(),
base.getHost(), base.getPort(), base.getPath(),
base.getQuery(), fragment );
return this;
}
/**
* Print this builder's URI representation.
*/
@Override public String toString() {
return base.toString();
}
/**
* Convenience method to convert this object to a URL instance.
* @return this builder as a URL
* @throws MalformedURLException if the underlying URI does not represent a
* valid URL.
*/
public URL toURL() throws MalformedURLException {
return base.toURL();
}
/**
* Convenience method to convert this object to a URI instance.
* @return this builder's underlying URI representation
*/
public URI toURI() { return this.base; }
/**
* Implementation of Groovy's as
operator, to allow type
* conversion.
* @param type URL
, URL
, or String
.
* @return a representation of this URIBuilder instance in the given type
* @throws MalformedURLException if type
is URL and this
* URIBuilder instance does not represent a valid URL.
*/
public Object asType( Class> type ) throws MalformedURLException {
if ( type == URI.class ) return this.toURI();
if ( type == URL.class ) return this.toURL();
if ( type == String.class ) return this.toString();
throw new ClassCastException( "Cannot cast instance of URIBuilder to class " + type );
}
/**
* Create a copy of this URIBuilder instance.
*/
@Override
protected URIBuilder clone() {
return new URIBuilder( this.base, this.isUrlEncodingEnabled, encoderConfig().defaultQueryParameterCharset(this.enc) );
}
/**
* Determine if this URIBuilder is equal to another URIBuilder instance.
* @see URI#equals(Object)
* @return if obj
is a URIBuilder instance whose underlying
* URI implementation is equal to this one's.
*/
@Override
public boolean equals( Object obj ) {
if ( ! ( obj instanceof URIBuilder) ) return false;
return this.base.equals( ((URIBuilder)obj).toURI() );
}
/**
* Returns a String that is suitable for use as an application/x-www-form-urlencoded
* list of parameters in an HTTP PUT or HTTP POST.
*
* This is a copy of {@link URLEncodedUtils#format(java.util.List, String)} that also handles {@link BasicNameValuePairWithNoValueSupport}.
*
*
* @param parameters The parameters to include.
* @param encoding The encoding to use.
*/
private static String format (
final List extends NameValuePair> parameters,
final boolean isUrlEncodingEnabled,
final String encoding) {
final StringBuilder result = new StringBuilder();
for (final NameValuePair parameter : parameters) {
if (result.length() > 0)
result.append(PARAMETER_SEPARATOR);
final String encodedName = isUrlEncodingEnabled ? encode(parameter.getName(), encoding) : parameter.getName();
result.append(encodedName);
if(hasValue(parameter)) {
final String value = parameter.getValue();
final String encodedValue = value != null ? isUrlEncodingEnabled ? encode(value, encoding) : value : "";
result.append(NAME_VALUE_SEPARATOR);
result.append(encodedValue);
}
}
return result.toString();
}
private static boolean hasValue(NameValuePair parameter) {
if(!(parameter instanceof BasicNameValuePairWithNoValueSupport)) {
return true;
}
return ((BasicNameValuePairWithNoValueSupport) parameter).hasValue();
}
// Copy of the private method in URLEncodedUtils
public static String encode (final String content, final String encoding) {
try {
return URLEncoder.encode(content,
encoding != null ? encoding : HTTP.DEFAULT_CONTENT_CHARSET);
} catch (UnsupportedEncodingException problem) {
throw new IllegalArgumentException(problem);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy