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