/* * Copyright (C) 2011-2012 Intel Corporation * All rights reserved. */ package com.intel.mtwilson.security.http; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.*; /** * This component encodes values in www-urlencoded format, using UTF-8 encoding * to allow the use of any Unicode character. If UTF-8 encoding is not available * on your platform, the class will throw UnsupportedEncodingException. * * Parameters are appended to the string in alphabetical order by key and * then by value. * * @since 0.5.1 * @author jbuhacoff */ public class HttpRequestURL { private String path = null; private String query = null; public HttpRequestURL() { path = ""; query = ""; } public HttpRequestURL(String path, String query) { this.path = path; this.query = query; } /** * using Map<String,Object> because due to generics type erasure it's not possible to have * separate constructors for Map<String,String> and Map<String,String[]> and Map<String,List<String>> **/ public HttpRequestURL(String path, Map<String,Object> query) throws UnsupportedEncodingException { this.path = path; this.query = queryString(query); } @Override public String toString() { return path + ( query.isEmpty() ? "" : "?"+query ); } /** * The query parameters can be String or String[] for multi-valued parameters. * They will be sorted by key in alphabetical order, then by value in alphabetical order. * * If the query object is null then an empty query string will be returned. * * @param query * @return query string like name=John&age=30 */ private String queryString(Map<String,Object> query) throws UnsupportedEncodingException { if( query == null ) { return ""; } List<String> keyList = new ArrayList<>(query.keySet()); Collections.sort(keyList); List<String> queryParam = new ArrayList<>(); for(String key : keyList) { Object value = query.get(key); if( value == null ) { queryParam.add(singleValue(key, "")); } else if( value instanceof String ) { queryParam.add(singleValue(key, (String)value)); // String.format("%s=%s", urlencode(key), urlencode((String)value))); } else if( value instanceof String[] ) { queryParam.addAll(multiValue(key, (String[])value)); } else if( value instanceof List ) { queryParam.addAll(multiValue(key, (List<String>)value)); } else { queryParam.add(singleValue(key, value.toString())); } } return join(queryParam, "&"); } private String singleValue(String key, String value) throws UnsupportedEncodingException { return String.format("%s=%s", urlencode(key), urlencode(value)); } private List<String> multiValue(String key, String[] value) throws UnsupportedEncodingException { List<String> paramList = new ArrayList<>(); List<String> list = Arrays.asList((String[])value); Collections.sort(list); for(String v : list) { paramList.add(String.format("%s=%s", urlencode(key), urlencode((String)v))); } return paramList; } private List<String> multiValue(String key, List<String> valueList) throws UnsupportedEncodingException { List<String> paramList = new ArrayList<>(); Collections.sort(valueList); for(String v : valueList) { paramList.add(String.format("%s=%s", urlencode(key), urlencode((String)v))); } return paramList; } /** * Encodes a URL parameter name or value with the following rules: * * The alphanumeric characters a-z, A-Z, and 0-9 remain the same * The special characters dot, hyphen, star, and underscore remain the same * The space character is converted to %20 (not a plus sign) * All other characters are unsafe and are converted to a sequence of * bytes using UTF-8 and then each byte is encoded using the %xy hexadecimal * representation. * * The output of this function is SIMILAR to URLEncoder but differs in * the handling of spaces. This function emits %20 for a space whereas * URLEncoder emits a plus sign for a space. * * If the value is null, an empty string will be returned. * * @param value * @return * @throws UnsupportedEncodingException */ private String urlencode(String value) throws UnsupportedEncodingException { if( value == null ) { return ""; } return URLEncoder.encode(value, "UTF-8").replace("+", "%20"); // replace + with %20 to be compatible with PHP's rawurlencode } /** * Joins the elements returned by any iterator using the given separator. If the iterator * is of String then the toString method is called on the elements. * If the iterator has no elements, an empty string is returned. * @param <T> * @param it * @param separator * @return a String that contains the iterator elements joined with the separator */ private static String join(Collection<String> collection, final String separator) { Iterator<String> it = collection.iterator(); if( it.hasNext() ) { StringBuilder buffer = new StringBuilder(); buffer.append(it.next()); while(it.hasNext()) { buffer.append(separator).append(it.next()); } return buffer.toString(); } else { return ""; } } }