001package com.nimbusds.oauth2.sdk.util; 002 003 004import java.io.UnsupportedEncodingException; 005import java.net.MalformedURLException; 006import java.net.URL; 007import java.net.URLDecoder; 008import java.net.URLEncoder; 009import java.util.HashMap; 010import java.util.Map; 011import java.util.StringTokenizer; 012 013 014/** 015 * URL operations. 016 */ 017public class URLUtils { 018 019 020 /** 021 * The default UTF-8 character set. 022 */ 023 public static final String CHARSET = "utf-8"; 024 025 026 /** 027 * Gets the base part (protocol, host, port and path) of the specified 028 * URL. 029 * 030 * @param url The URL. May be {@code null}. 031 * 032 * @return The base part of the URL, {@code null} if the original URL 033 * is {@code null} or doesn't specify a protocol. 034 */ 035 public static URL getBaseURL(final URL url) { 036 037 if (url == null) 038 return null; 039 040 try { 041 return new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath()); 042 043 } catch (MalformedURLException e) { 044 045 return null; 046 } 047 } 048 049 050 /** 051 * Serialises the specified map of parameters into a URL query string. 052 * The parameter keys and values are 053 * {@code application/x-www-form-urlencoded} encoded. 054 * 055 * <p>Note that the '?' character preceding the query string in GET 056 * requests is not included in the returned string. 057 * 058 * <p>Example query string: 059 * 060 * <pre> 061 * response_type=code 062 * &client_id=s6BhdRkqt3 063 * &state=xyz 064 * &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 065 * </pre> 066 * 067 * <p>The opposite method is {@link #parseParameters}. 068 * 069 * @param params A map of the URL query parameters. May be empty or 070 * {@code null}. 071 * 072 * @return The serialised URL query string, empty if no parameters. 073 */ 074 public static String serializeParameters(final Map<String,String> params) { 075 076 if (params == null || params.isEmpty()) 077 return ""; 078 079 StringBuilder sb = new StringBuilder(); 080 081 for (Map.Entry<String,String> entry: params.entrySet()) { 082 083 if (entry.getKey() == null || entry.getValue() == null) 084 continue; 085 086 try { 087 String encodedKey = URLEncoder.encode(entry.getKey(), CHARSET); 088 String encodedValue = URLEncoder.encode(entry.getValue(), CHARSET); 089 090 if (sb.length() > 0) 091 sb.append('&'); 092 093 sb.append(encodedKey); 094 sb.append('='); 095 sb.append(encodedValue); 096 097 } catch (UnsupportedEncodingException e) { 098 099 // UTF-8 should always be supported 100 } 101 } 102 103 return sb.toString(); 104 } 105 106 107 /** 108 * Parses the specified URL query string into a parameter map. If a 109 * parameter has multiple values only the first one will be saved. The 110 * parameter keys and values are 111 * {@code application/x-www-form-urlencoded} decoded. 112 * 113 * <p>Note that the '?' character preceding the query string in GET 114 * requests must not be included. 115 * 116 * <p>Example query string: 117 * 118 * <pre> 119 * response_type=code 120 * &client_id=s6BhdRkqt3 121 * &state=xyz 122 * &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 123 * </pre> 124 * 125 * <p>The opposite method {@link #serializeParameters}. 126 * 127 * @param query The URL query string to parse. May be {@code null}. 128 * 129 * @return A map of the URL query parameters, empty if none are found. 130 */ 131 public static Map<String,String> parseParameters(final String query) { 132 133 Map<String,String> params = new HashMap<>(); 134 135 if (query == null) 136 return params; // empty map 137 138 try { 139 StringTokenizer st = new StringTokenizer(query, "&"); 140 141 while(st.hasMoreTokens()) { 142 143 String param = st.nextToken(); 144 145 String pair[] = param.split("="); 146 147 String key = URLDecoder.decode(pair[0], CHARSET); 148 149 // Save the first value only 150 if (params.containsKey(key)) 151 continue; 152 153 String value = ""; 154 155 if (pair.length > 1) 156 value = URLDecoder.decode(pair[1], CHARSET); 157 158 params.put(key, value); 159 } 160 161 } catch (UnsupportedEncodingException e) { 162 163 // UTF-8 should always be supported 164 } 165 166 return params; 167 } 168 169 170 /** 171 * Prevents instantiation. 172 */ 173 private URLUtils() { 174 175 // do nothing 176 } 177}