org.jboss.resteasy.reactive.common.headers.HeaderUtil Maven / Gradle / Ivy
package org.jboss.resteasy.reactive.common.headers;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.EntityTag;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.ext.RuntimeDelegate;
import org.jboss.resteasy.reactive.common.util.CookieParser;
import org.jboss.resteasy.reactive.common.util.DateUtil;
import org.jboss.resteasy.reactive.common.util.MediaTypeHelper;
import org.jboss.resteasy.reactive.common.util.WeightedLanguage;
/**
* These work for MultivaluedMap with String and Object
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class HeaderUtil {
private static final ClassValue> HEADER_DELEGATE_CACHE = new ClassValue<>() {
@Override
protected RuntimeDelegate.HeaderDelegate> computeValue(Class type) {
return RuntimeDelegate.getInstance().createHeaderDelegate(type);
}
};
public static String headerToString(Object obj) {
if (obj instanceof String) {
return (String) obj;
} else {
RuntimeDelegate.HeaderDelegate delegate = HEADER_DELEGATE_CACHE.get(obj.getClass());
if (delegate != null) {
return delegate.toString(obj);
}
return obj.toString();
}
}
public static Set getAllowedMethods(MultivaluedMap headers) {
List extends Object> allowed = headers.get(HttpHeaders.ALLOW);
if ((allowed == null) || allowed.isEmpty()) {
return Collections.emptySet();
}
Set allowedMethods = new HashSet<>();
for (Object header : allowed) {
if (header instanceof String) {
String stringHeader = ((String) header);
if (stringHeader.indexOf(',') != -1) {
for (String str : stringHeader.split(",")) {
String trimmed = str.trim();
if (!trimmed.isEmpty()) {
allowedMethods.add(trimmed.toUpperCase());
}
}
} else {
allowedMethods.add(stringHeader.trim().toUpperCase());
}
} else {
allowedMethods.add(HeaderUtil.headerToString(header).toUpperCase());
}
}
return allowedMethods;
}
public static Date getDate(MultivaluedMap headers) {
return firstHeaderToDate(HttpHeaders.DATE, headers);
}
public static Date getLastModified(MultivaluedMap headers) {
return firstHeaderToDate(HttpHeaders.LAST_MODIFIED, headers);
}
private static Date firstHeaderToDate(String date, MultivaluedMap headers) {
Object d = headers.getFirst(date);
if (d == null)
return null;
if (d instanceof Date)
return (Date) d;
return DateUtil.parseDate(d.toString());
}
public static URI getLocation(MultivaluedMap headers) {
Object uri = headers.getFirst(HttpHeaders.LOCATION);
if (uri == null) {
return null;
}
if (uri instanceof URI) {
return (URI) uri;
}
String str = null;
if (uri instanceof String) {
str = (String) uri;
} else {
str = HeaderUtil.headerToString(uri);
}
return URI.create(str);
}
public static MediaType getMediaType(MultivaluedMap headers) {
Object first = headers.getFirst(HttpHeaders.CONTENT_TYPE);
if (first instanceof String contentType) {
return MediaType.valueOf(contentType);
} else {
return (MediaType) first;
}
}
public static Locale getLanguage(MultivaluedMap headers) {
Object obj = headers.getFirst(HttpHeaders.CONTENT_LANGUAGE);
if (obj == null) {
return null;
}
if (obj instanceof Locale) {
return (Locale) obj;
}
return Locale.forLanguageTag(HeaderUtil.headerToString(obj));
}
public static int getLength(MultivaluedMap headers) {
Object obj = headers.getFirst(HttpHeaders.CONTENT_LENGTH);
if (obj == null) {
return -1;
}
if (obj instanceof Integer) {
return (Integer) obj;
}
return Integer.parseInt(HeaderUtil.headerToString(obj));
}
public static Map getCookies(MultivaluedMap headers) {
List list = headers.get(HttpHeaders.COOKIE);
if (list == null)
return Collections.emptyMap();
Map cookies = new HashMap();
for (Object obj : list) {
if (obj instanceof Cookie cookie) {
cookies.put(cookie.getName(), cookie);
} else {
String str = headerToString(obj);
for (Cookie cookie : CookieParser.parseCookies(str)) {
cookies.put(cookie.getName(), cookie);
}
}
}
return cookies;
}
public static Map getNewCookies(MultivaluedMap headers) {
List> list = headers.get(HttpHeaders.SET_COOKIE);
if ((list == null) || list.isEmpty()) {
return Collections.emptyMap();
}
Map cookies = new HashMap<>();
for (Object obj : list) {
if (obj instanceof NewCookie cookie) {
cookies.put(cookie.getName(), cookie);
} else {
String str = HeaderUtil.headerToString(obj);
NewCookie cookie = NewCookie.valueOf(str);
cookies.put(cookie.getName(), cookie);
}
}
return cookies;
}
public static EntityTag getEntityTag(MultivaluedMap headers) {
Object d = headers.getFirst(HttpHeaders.ETAG);
if (d == null) {
return null;
}
if (d instanceof EntityTag) {
return (EntityTag) d;
}
return EntityTag.valueOf(HeaderUtil.headerToString(d));
}
public static String getHeaderString(MultivaluedMap headers, String name) {
List extends Object> list = headers.get(name);
if (list == null) {
return null;
}
if (list.size() == 1) {
return headerToString(list.get(0));
}
StringBuilder sb = new StringBuilder();
for (Object s : list) {
if (!sb.isEmpty()) {
sb.append(",");
}
sb.append(headerToString(s));
}
return sb.toString();
}
@SuppressWarnings(value = "unchecked")
public static void setAllow(MultivaluedMap headers, String[] methods) {
if (methods == null) {
headers.remove("Allow");
return;
}
StringBuilder builder = new StringBuilder();
boolean isFirst = true;
for (String l : methods) {
if (isFirst) {
isFirst = false;
} else {
builder.append(", ");
}
builder.append(l);
}
headers.putSingle("Allow", builder.toString());
}
@SuppressWarnings(value = "unchecked")
public static void setAllow(MultivaluedMap headers, Set methods) {
if (methods == null) {
headers.remove("Allow");
return;
}
StringBuilder builder = new StringBuilder();
boolean isFirst = true;
for (String l : methods) {
if (isFirst) {
isFirst = false;
} else {
builder.append(", ");
}
builder.append(l);
}
headers.putSingle("Allow", builder.toString());
}
public static boolean isContentLengthZero(MultivaluedMap httpHeaders) {
if (httpHeaders == null)
return false;
@SuppressWarnings(value = "unchecked")
String contentLength = (String) httpHeaders.getFirst(HttpHeaders.CONTENT_LENGTH);
if (contentLength != null) {
long length = Long.parseLong(contentLength);
if (length == 0)
return true;
}
return false;
}
public static List getAcceptableMediaTypes(MultivaluedMap headers) {
List> accepts = headers.get(HttpHeaders.ACCEPT);
if (accepts == null || accepts.isEmpty()) {
return Collections.singletonList(MediaType.WILDCARD_TYPE);
}
List list = new ArrayList<>();
for (Object obj : accepts) {
if (obj instanceof MediaType) {
list.add((MediaType) obj);
continue;
}
String accept;
if (obj instanceof String) {
accept = (String) obj;
} else {
accept = headerToString(obj);
}
if (accept.indexOf(',') != -1) {
StringTokenizer tokenizer = new StringTokenizer(accept, ",");
while (tokenizer.hasMoreElements()) {
String item = tokenizer.nextToken().trim();
list.add(MediaType.valueOf(item));
}
} else {
list.add(MediaType.valueOf(accept.trim()));
}
}
MediaTypeHelper.sortByWeight(list);
return list;
}
public static List getAcceptableLanguages(MultivaluedMap headers) {
List> accepts = headers.get(HttpHeaders.ACCEPT_LANGUAGE);
if (accepts == null || accepts.isEmpty())
return Collections.emptyList();
List languages = new ArrayList();
for (Object obj : accepts) {
if (obj instanceof Locale) {
languages.add(new WeightedLanguage((Locale) obj, 1.0F));
continue;
}
String accept = headerToString(obj);
if (accept.indexOf(',') != -1) {
StringTokenizer tokenizer = new StringTokenizer(accept, ",");
while (tokenizer.hasMoreElements()) {
String item = tokenizer.nextToken().trim();
languages.add(WeightedLanguage.parse(item));
}
} else {
languages.add(WeightedLanguage.parse(accept.trim()));
}
}
Collections.sort(languages);
List list = new ArrayList<>(languages.size());
for (WeightedLanguage language : languages)
list.add(language.getLocale());
return list;
}
/**
* Extracts a quoted value from a header that has a given key. For instance if the header is
*
* content-disposition=form-data; name="my field"
* and the key is name then "my field" will be returned without the quotes.
*
*
* @param header The header
* @param key The key that identifies the token to extract
* @return The token, or null if it was not found
*/
public static String extractQuotedValueFromHeader(final String header, final String key) {
int keypos = 0;
int pos = -1;
boolean whiteSpace = true;
boolean inQuotes = false;
for (int i = 0; i < header.length() - 1; ++i) { //-1 because we need room for the = at the end
//TODO: a more efficient matching algorithm
char c = header.charAt(i);
if (inQuotes) {
if (c == '"') {
inQuotes = false;
}
} else {
if (key.charAt(keypos) == c && (whiteSpace || keypos > 0)) {
keypos++;
whiteSpace = false;
} else if (c == '"') {
keypos = 0;
inQuotes = true;
whiteSpace = false;
} else {
keypos = 0;
whiteSpace = c == ' ' || c == ';' || c == '\t';
}
if (keypos == key.length()) {
if (header.charAt(i + 1) == '=') {
pos = i + 2;
break;
} else {
keypos = 0;
}
}
}
}
if (pos == -1) {
return null;
}
int end;
int start = pos;
if (header.charAt(start) == '"') {
start++;
for (end = start; end < header.length(); ++end) {
char c = header.charAt(end);
if (c == '"') {
break;
}
}
return header.substring(start, end);
} else {
//no quotes
for (end = start; end < header.length(); ++end) {
char c = header.charAt(end);
if (c == ' ' || c == '\t' || c == ';') {
break;
}
}
return header.substring(start, end);
}
}
/**
* Extracts a quoted value from a header that has a given key. For instance if the header is
*
* content-disposition=form-data; filename*="utf-8''test.txt"
* and the key is filename* then "test.txt" will be returned after extracting character set and language
* (following RFC 2231) and performing URL decoding to the value using the specified encoding
*
* @param header The header
* @param key The key that identifies the token to extract
* @return The token, or null if it was not found
*/
public static String extractQuotedValueFromHeaderWithEncoding(final String header, final String key) {
String value = extractQuotedValueFromHeader(header, key);
if (value != null) {
return value;
}
value = extractQuotedValueFromHeader(header, key + "*");
if (value != null) {
int characterSetDelimiter = value.indexOf('\'');
int languageDelimiter = value.lastIndexOf('\'', characterSetDelimiter + 1);
String characterSet = value.substring(0, characterSetDelimiter);
try {
String fileNameURLEncoded = value.substring(languageDelimiter + 1);
return URLDecoder.decode(fileNameURLEncoded, characterSet);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
return null;
}
}